work
This commit is contained in:
commit
83982551ca
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "dnapi"]
|
||||||
|
path = dnapi
|
||||||
|
url = https://github.com/DefinedNet/dnapi
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"trifid-api",
|
||||||
|
"tfclient"
|
||||||
|
]
|
|
@ -0,0 +1,32 @@
|
||||||
|
# trifid
|
||||||
|
|
||||||
|
trifid is an open-souce reimplementation of the [Defined Networking](https://defined.net) management protocol for [Nebula](https://github.com/slackhq/nebula) networks.
|
||||||
|
|
||||||
|
It includes a reimplementation of the [API Server](https://api.defined.net), the [Web UI](https://admin.defined.net), and `dnclient` - all fully compatible with the original implementation.
|
||||||
|
|
||||||
|
The API implementation is tested with the official dnclient implementaiton, and the dnclient implementation is tested with the official API server, to ensure complete feature parity between the two.
|
||||||
|
|
||||||
|
The original Web UI does **not** work with trifid-api quite yet, as we haven't finished reverse engineering that API. Feature table below:
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
trifid-api feature table:
|
||||||
|
|
||||||
|
| Feature | trifid-api | api.defined.net |
|
||||||
|
|---------------------------------------------|--------------|-----------------|
|
||||||
|
| Enroll in sites with dnclient | Yes | Yes |
|
||||||
|
| Automatic config update polling by dnclient | Not yet | Yes |
|
||||||
|
| Group-based firewalling | Not yet | Yes |
|
||||||
|
| SSO authentication | Not yet | Yes |
|
||||||
|
| Extensive API documentation | In the works | No |
|
||||||
|
| Open-source server | Yes | No |
|
||||||
|
| All functionality avaliable via API | Yes | No |
|
||||||
|
|
||||||
|
tfclient feature table:
|
||||||
|
|
||||||
|
| Feature | tfclient | dnclient |
|
||||||
|
|--------------------------------------------|----------|----------|
|
||||||
|
| Enroll in trifid-api/api.defined.net sites | Not yet | Yes |
|
||||||
|
| Automatic VPN profile setup | Not yet | Yes |
|
||||||
|
| CLI for using the full API | Not yet | No |
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
POST /v2/enroll HTTP/1.1
|
||||||
|
Host: localhost:8087
|
||||||
|
User-Agent: dnclient/0.1.8
|
||||||
|
Content-Length: 472
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Connection: close
|
||||||
|
|
||||||
|
{"code":"xM22QsIzd4F0nLDTbh86RCSYwelfU_Hshqt-7u4yy_Y","dhPubkey":"LS0tLS1CRUdJTiBORUJVTEEgWDI1NTE5IFBVQkxJQyBLRVktLS0tLQpqZW9aaDZZYUNNSHZKK04zWGRlQ1hCbHo3dm5saTBjL1NlQ1hVR3lYbEIwPQotLS0tLUVORCBORUJVTEEgWDI1NTE5IFBVQkxJQyBLRVktLS0tLQo=","edPubkey":"LS0tLS1CRUdJTiBORUJVTEEgRUQyNTUxOSBQVUJMSUMgS0VZLS0tLS0KWHE0RG9mUGJoQzBubjc4VEhRWUxhNC83V1Ixei9iU1kzSm9pRzNRZ1VMcz0KLS0tLS1FTkQgTkVCVUxBIEVEMjU1MTkgUFVCTElDIEtFWS0tLS0tCg==","timestamp":"2023-02-01T13:24:56.380006369-05:00"}
|
||||||
|
|
||||||
|
HTTP/2 200 OK
|
||||||
|
Cache-Control: no-store
|
||||||
|
Content-Security-Policy: default-src 'none'
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Strict-Transport-Security: max-age=31536000; includeSubdomains
|
||||||
|
Vary: Origin
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-Frame-Options: DENY
|
||||||
|
X-Request-Id: IRZBCFJUXZKLAMF4VC2TE6LQ4I
|
||||||
|
Content-Length: 3370
|
||||||
|
Date: Wed, 01 Feb 2023 18:24:36 GMT
|
||||||
|
|
||||||
|
{"data":{"config":"ZmlyZXdhbGw6CiAgaW5ib3VuZDoKICAtIGRlc2NyaXB0aW9uOiBBbGxvd3MgcGluZyByZXF1ZXN0cyBmcm9tIG90aGVyIGhvc3RzCiAgICBob3N0OiBhbnkKICAgIHBvcnQ6IGFueQogICAgcHJvdG86IGljbXAKICBvdXRib3VuZDoKICAtIGhvc3Q6IGFueQogICAgcG9ydDogYW55CiAgICBwcm90bzogYW55CmxpZ2h0aG91c2U6CiAgYW1fbGlnaHRob3VzZTogdHJ1ZQpsaXN0ZW46CiAgaG9zdDogMC4wLjAuMAogIHBvcnQ6IDQyNDIKICByZWFkX2J1ZmZlcjogMTA0ODU3NjAKICB3cml0ZV9idWZmZXI6IDEwNDg1NzYwCmxvZ2dpbmc6CiAgZm9ybWF0OiB0ZXh0CiAgbGV2ZWw6IGluZm8KcGtpOgogIGJsb2NrbGlzdDogW10KICBjYTogfAogICAgLS0tLS1CRUdJTiBORUJVTEEgQ0VSVElGSUNBVEUtLS0tLQogICAgQ213S09rTmxjblJwWm1sallYUmxJRUYxZEdodmNtbDBlU0JtYjNJZ1kyOXlaVUJqYjNKbFpHOWxjeTVrWlhZbgogICAgY3lCUGNtZGhibWw2WVhScGIyNG96dC9wbmdZd3pzYnVyUVk2SUE2Z2Nwano0RDRHTEJkQTFCUXhtUi9mQ1R4cwogICAgSjZQdStCZGxGaXgzZmxBVVFBRVNRQnpjUUg2MWxWdVFWTlpDbXZZenpVYnN6SHJQSEt5Zi9EMnB2c01XandhSAogICAgLzZNdUwxRlhwYmpqWkpURnFyRklkTzFpSXZiVTBRRC9Ka1pySndOa09Rcz0KICAgIC0tLS0tRU5EIE5FQlVMQSBDRVJUSUZJQ0FURS0tLS0tCiAgICAtLS0tLUJFR0lOIE5FQlVMQSBDRVJUSUZJQ0FURS0tLS0tCiAgICBDbXdLT2tObGNuUnBabWxqWVhSbElFRjFkR2h2Y21sMGVTQm1iM0lnWTI5eVpVQmpiM0psWkc5bGN5NWtaWFluCiAgICBjeUJQY21kaGJtbDZZWFJwYjI0b3p0L3BuZ1l3enRENHZBWTZJQ1c4V2w4a0RoWDROYTVKSjI5ZUpYTnNaSEgrCiAgICB0NlNIb3hJR3ErOEJ0MUpSUUFFU1FEWXRsYmZCMnpyR1dRNGRzbU10MFRQbEg2NHlEQkQ1NWVkY1c1b3lBcWdOCiAgICBSbXhza0wyTldWYW1ZQURsNWJGUnQ4UEVDZTIrZ0Rkc0pYRHFpUkU3b1E0PQogICAgLS0tLS1FTkQgTkVCVUxBIENFUlRJRklDQVRFLS0tLS0KICAgIC0tLS0tQkVHSU4gTkVCVUxBIENFUlRJRklDQVRFLS0tLS0KICAgIENtd0tPa05sY25ScFptbGpZWFJsSUVGMWRHaHZjbWwwZVNCbWIzSWdZMjl5WlVCamIzSmxaRzlsY3k1a1pYWW4KICAgIGN5QlBjbWRoYm1sNllYUnBiMjRvenQvcG5nWXd6cmY5eXdZNklBbWtPSXpFSVAvd0Z5dk90eHR4SDlXU1lWZEkKICAgIDBRa2NqckxqTkJrbk11WmlRQUVTUUV1cEwzUnBodTYwRDUyZzJEdDBtZ3ZrQlQ2TEFnR2dTOGxHeVBmMmxmZ2oKICAgIGNvSklSQ3VveXVQZmExMkthTVU5TmcwRWhrV2RYTVVRRWkzRHJKZEVSUVk9CiAgICAtLS0tLUVORCBORUJVTEEgQ0VSVElGSUNBVEUtLS0tLQogIGNlcnQ6IHwKICAgIC0tLS0tQkVHSU4gTkVCVUxBIENFUlRJRklDQVRFLS0tLS0KICAgIENuTUtCV05vWldOckVnbUNnTUJRZ0lENC93OGlEM0p2YkdVNlRHbG5hSFJvYjNWelpTaTQydXFlQmpETnh1NnQKICAgIEJqb2dqZW9aaDZZYUNNSHZKK04zWGRlQ1hCbHo3dm5saTBjL1NlQ1hVR3lYbEIxS0lDZVZFdWZBTWJDVC9RbW0KICAgIDVEU2t2UWJXOUdsNHRCZ1gyY0JXZUJTSS91Z0FFa0MwbXRKNUJ5R1hodFlSalFpWWNHN0pIZHlnVG1MUVBSWjcKICAgIFZ4WFZTWXFFaHIvblR6RHlUa20yZVl3VUY5V0kyY2hHRG9XR3JOa1d6TzFmQzRDOXRhMEkKICAgIC0tLS0tRU5EIE5FQlVMQSBDRVJUSUZJQ0FURS0tLS0tCiAgZGlzY29ubmVjdF9pbnZhbGlkOiB0cnVlCnB1bmNoeToKICBwdW5jaDogZmFsc2UKICByZXNwb25kOiBmYWxzZQpyZWxheToKICBhbV9yZWxheTogdHJ1ZQpzdGF0aWNfaG9zdF9tYXA6CiAgMTAuMTYuMC4xOgogIC0gMS4yLjMuNDo0MjQyCiAgMTAuMTYuMC4yOgogIC0gMS4yLjMuNDo0MjQyCiAgMTAuMTYuMC4zOgogIC0gNC4zLjIuMTo0MjQyCnR1bjoKICBkZXY6IGRlZmluZWQxCiAgZHJvcF9sb2NhbF9icm9hZGNhc3Q6IHRydWUKICBkcm9wX211bHRpY2FzdDogdHJ1ZQogIG10dTogMTMwMAo=","hostID":"host-C6TNZPJLOTNSPHFSJGEYPS7JHM","counter":1,"trustedKeys":"LS0tLS1CRUdJTiBORUJVTEEgRUQyNTUxOSBQVUJMSUMgS0VZLS0tLS0KRHFCeW1QUGdQZ1lzRjBEVUZER1pIOThKUEd3bm8rNzRGMlVXTEhkK1VCUT0KLS0tLS1FTkQgTkVCVUxBIEVEMjU1MTkgUFVCTElDIEtFWS0tLS0tCi0tLS0tQkVHSU4gTkVCVUxBIEVEMjU1MTkgUFVCTElDIEtFWS0tLS0tCkpieGFYeVFPRmZnMXJra25iMTRsYzJ4a2NmNjNwSWVqRWdhcjd3RzNVbEU9Ci0tLS0tRU5EIE5FQlVMQSBFRDI1NTE5IFBVQkxJQyBLRVktLS0tLQotLS0tLUJFR0lOIE5FQlVMQSBFRDI1NTE5IFBVQkxJQyBLRVktLS0tLQpDYVE0ak1RZy8vQVhLODYzRzNFZjFaSmhWMGpSQ1J5T3N1TTBHU2N5NW1JPQotLS0tLUVORCBORUJVTEEgRUQyNTUxOSBQVUJMSUMgS0VZLS0tLS0K","organization":{"id":"org-56I3FGHQBB4TYQJ6QENKCUAMD4","name":"core@coredoes.dev's Organization"}},"metadata":{}}
|
||||||
|
|
||||||
|
|
||||||
|
POST /v1/dnclient HTTP/2
|
||||||
|
Host: localhost:8087
|
||||||
|
User-Agent: dnclient/0.1.8
|
||||||
|
Content-Length: 300
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
|
||||||
|
{"version":1,"hostID":"host-C6TNZPJLOTNSPHFSJGEYPS7JHM","counter":1,"message":"eyJ0eXBlIjoiQ2hlY2tGb3JVcGRhdGUiLCJ2YWx1ZSI6bnVsbCwidGltZXN0YW1wIjoiMjAyMy0wMi0wMVQxMzoyNDo1Ny4wMDU1OTA4NC0wNTowMCJ9","signature":"1VApA+Ls08bY+TaDyR0XQ/3kBBJ1u5FSY665VaTjr6IwcGFm7ngj5xuOXZtoKz3mLkg9fsHjARd+MAfCkPFdDQ=="}
|
||||||
|
|
||||||
|
HTTP/2 200 OK
|
||||||
|
Cache-Control: no-store
|
||||||
|
Content-Security-Policy: default-src 'none'
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Strict-Transport-Security: max-age=31536000; includeSubdomains
|
||||||
|
Vary: Origin
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-Frame-Options: DENY
|
||||||
|
X-Request-Id: WJW3OBZCX7O2XDEAPHAZIMVERM
|
||||||
|
Content-Length: 49
|
||||||
|
Date: Wed, 01 Feb 2023 18:24:36 GMT
|
||||||
|
|
||||||
|
{"data":{"updateAvailable":false},"metadata":{}}
|
||||||
|
|
||||||
|
POST /v1/dnclient HTTP/2
|
||||||
|
Host: 127.0.0.1:8087
|
||||||
|
User-Agent: dnclient/0.1.8
|
||||||
|
Content-Length: 304
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
|
||||||
|
{"version":1,"hostID":"host-C6TNZPJLOTNSPHFSJGEYPS7JHM","counter":1,"message":"eyJ0eXBlIjoiQ2hlY2tGb3JVcGRhdGUiLCJ2YWx1ZSI6bnVsbCwidGltZXN0YW1wIjoiMjAyMy0wMi0wMVQxMzoyNzoyMC4yMDczMjU2NzgtMDU6MDAifQ==","signature":"EImd4OPnMqF2JqgudzioxkRMMCms617GnZhjy5KQ9Gfl0ui2mMpLTGu4iAvfMGu0KmrlT+dpxLD8l+NYjSeVCg=="}
|
||||||
|
|
||||||
|
HTTP/2 200 OK
|
||||||
|
Cache-Control: no-store
|
||||||
|
Content-Security-Policy: default-src 'none'
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Strict-Transport-Security: max-age=31536000; includeSubdomains
|
||||||
|
Vary: Origin
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-Frame-Options: DENY
|
||||||
|
X-Request-Id: TGXXBORPW2PUAZDLCK2UOZGBQY
|
||||||
|
Content-Length: 49
|
||||||
|
Date: Wed, 01 Feb 2023 18:26:59 GMT
|
||||||
|
|
||||||
|
{"data":{"updateAvailable":false},"metadata":{}}
|
||||||
|
|
||||||
|
POST /v1/dnclient HTTP/2
|
||||||
|
Host: 127.0.0.1:8087
|
||||||
|
User-Agent: dnclient/0.1.8
|
||||||
|
Content-Length: 300
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
|
||||||
|
{"version":1,"hostID":"host-C6TNZPJLOTNSPHFSJGEYPS7JHM","counter":1,"message":"eyJ0eXBlIjoiQ2hlY2tGb3JVcGRhdGUiLCJ2YWx1ZSI6bnVsbCwidGltZXN0YW1wIjoiMjAyMy0wMi0wMVQxMzozMDoyMC4yMDc3NTc2OS0wNTowMCJ9","signature":"bUZln8ykcVa3ZUjJFmTphSrCULkyLa+s9VvbJj6xou8WSlKcXoZtewxuyOKe6ZQaVBTABOPZvQ/YWwkj5SgsDg=="}
|
||||||
|
|
||||||
|
HTTP/2 200 OK
|
||||||
|
Cache-Control: no-store
|
||||||
|
Content-Security-Policy: default-src 'none'
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Strict-Transport-Security: max-age=31536000; includeSubdomains
|
||||||
|
Vary: Origin
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-Frame-Options: DENY
|
||||||
|
X-Request-Id: GX4BUG7DHV47ZVRFGWZ7CYDU74
|
||||||
|
Content-Length: 48
|
||||||
|
Date: Wed, 01 Feb 2023 18:29:59 GMT
|
||||||
|
|
||||||
|
{"data":{"updateAvailable":true},"metadata":{}}
|
||||||
|
|
||||||
|
POST /v1/dnclient HTTP/2
|
||||||
|
Host: 127.0.0.1:8087
|
||||||
|
User-Agent: dnclient/0.1.8
|
||||||
|
Content-Length: 1024
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
|
||||||
|
{"version":1,"hostID":"host-C6TNZPJLOTNSPHFSJGEYPS7JHM","counter":1,"message":"eyJ0eXBlIjoiRG9VcGRhdGUiLCJ2YWx1ZSI6ImV5SmxaRkIxWW10bGVWQkZUU0k2SWt4VE1IUk1VekZEVWxWa1NsUnBRazlTVlVwV1ZFVkZaMUpWVVhsT1ZGVjRUMU5DVVZaVlNrMVRWVTFuVXpCV1dreFRNSFJNVXpCTFlVZGFWVTFWVGtWaVJrWTFXbTFLYlZKVmFFeFZWM1ExV1d4Q05WRlhPWEpWVld4SFlVUk9TbGxVWkhOVGJGWlNaVVJXWVdJelNrTk5SREJMVEZNd2RFeFRNVVpVYTFGblZHdFdRMVpWZUVKSlJWWkZUV3BWTVUxVWEyZFZSbFpEVkVWc1JFbEZkRVpYVXpCMFRGTXdkRU5uUFQwaUxDSmthRkIxWW10bGVWQkZUU0k2SWt4VE1IUk1VekZEVWxWa1NsUnBRazlTVlVwV1ZFVkZaMWRFU1RGT1ZFVTFTVVpDVmxGcmVFcFJlVUpNVWxacmRFeFRNSFJNVVhCc1RqQlNRbFZ0VFhsWFJtaE9UMVpaZG1WVVNrTmhSbHBXV2xWb2QyTnJjM2hsUlRGcFRXdEpkMUV5ZEV0V00wb3pWVVU0ZUZKV1ZrWlFVVzkwVEZNd2RFeFZWazlTUTBKUFVsVktWbFJGUldkWFJFa3hUbFJGTlVsR1FsWlJhM2hLVVhsQ1RGSldhM1JNVXpCMFRGRnZQU0lzSW01dmJtTmxJam9pV0ZwUVZuSTFPSEp4WVVsdU1GaDZORkpIUVRkMFVUMDlJbjA9IiwidGltZXN0YW1wIjoiMjAyMy0wMi0wMVQxMzozMDoyMC40MzgwMTYzNTktMDU6MDAifQ==","signature":"NeMMe4ZisvyxVolalR+4WG+X/uogR4hBN94Q/0x/xD6xgNJK9FaoJDjlKUyjuEsjT3cBx0t9FDvu3Fb3DUJTDg=="}
|
||||||
|
|
||||||
|
HTTP/2 200 OK
|
||||||
|
Cache-Control: no-store
|
||||||
|
Content-Security-Policy: default-src 'none'
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Strict-Transport-Security: max-age=31536000; includeSubdomains
|
||||||
|
Vary: Origin
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-Frame-Options: DENY
|
||||||
|
X-Request-Id: LCVYDE6NIVSICOERUVSCYYXIMI
|
||||||
|
Date: Wed, 01 Feb 2023 18:30:00 GMT
|
||||||
|
|
||||||
|
{"data":{"version":1,"message":"eyJjb25maWciOiJabWx5WlhkaGJHdzZDaUFnYVc1aWIzVnVaRG9LSUNBdElHUmxjMk55YVhCMGFXOXVPaUJCYkd4dmQzTWdjR2x1WnlCeVpYRjFaWE4wY3lCbWNtOXRJRzkwYUdWeUlHaHZjM1J6Q2lBZ0lDQm9iM04wT2lCaGJua0tJQ0FnSUhCdmNuUTZJR0Z1ZVFvZ0lDQWdjSEp2ZEc4NklHbGpiWEFLSUNCdmRYUmliM1Z1WkRvS0lDQXRJR2h2YzNRNklHRnVlUW9nSUNBZ2NHOXlkRG9nWVc1NUNpQWdJQ0J3Y205MGJ6b2dZVzU1Q214cFoyaDBhRzkxYzJVNkNpQWdZVzFmYkdsbmFIUm9iM1Z6WlRvZ2RISjFaUXBzYVhOMFpXNDZDaUFnYUc5emREb2dNQzR3TGpBdU1Bb2dJSEJ2Y25RNklEUXlORElLSUNCeVpXRmtYMkoxWm1abGNqb2dNVEEwT0RVM05qQUtJQ0IzY21sMFpWOWlkV1ptWlhJNklERXdORGcxTnpZd0NteHZaMmRwYm1jNkNpQWdabTl5YldGME9pQjBaWGgwQ2lBZ2JHVjJaV3c2SUdsdVptOEtjR3RwT2dvZ0lHSnNiMk5yYkdsemREb2dXMTBLSUNCallUb2dmQW9nSUNBZ0xTMHRMUzFDUlVkSlRpQk9SVUpWVEVFZ1EwVlNWRWxHU1VOQlZFVXRMUzB0TFFvZ0lDQWdRMjEzUzA5clRteGpibEp3V20xc2FsbFlVbXhKUlVZeFpFZG9kbU50YkRCbFUwSnRZak5KWjFreU9YbGFWVUpxWWpOS2JGcEhPV3hqZVRWcldsaFpiZ29nSUNBZ1kzbENVR050WkdoaWJXdzJXVmhTY0dJeU5HOTZkQzl3Ym1kWmQzcHpZblZ5VVZrMlNVRTJaMk53YW5vMFJEUkhURUprUVRGQ1VYaHRVaTltUTFSNGN3b2dJQ0FnU2paUWRTdENaR3hHYVhnelpteEJWVkZCUlZOUlFucGpVVWcyTVd4V2RWRldUbHBEYlhaWmVucFZZbk42U0hKUVNFdDVaaTlFTW5CMmMwMVhhbmRoU0FvZ0lDQWdMelpOZFV3eFJsaHdZbXBxV2twVVJuRnlSa2xrVHpGcFNYWmlWVEJSUkM5S2ExcHlTbmRPYTA5UmN6MEtJQ0FnSUMwdExTMHRSVTVFSUU1RlFsVk1RU0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDaUFnSUNBdExTMHRMVUpGUjBsT0lFNUZRbFZNUVNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2lBZ0lDQkRiWGRMVDJ0T2JHTnVVbkJhYld4cVdWaFNiRWxGUmpGa1IyaDJZMjFzTUdWVFFtMWlNMGxuV1RJNWVWcFZRbXBpTTBwc1drYzViR041Tld0YVdGbHVDaUFnSUNCamVVSlFZMjFrYUdKdGJEWlpXRkp3WWpJMGIzcDBMM0J1WjFsM2VuUkVOSFpCV1RaSlExYzRWMnc0YTBSb1dEUk9ZVFZLU2pJNVpVcFlUbk5hU0VnckNpQWdJQ0IwTmxOSWIzaEpSM0VyT0VKME1VcFNVVUZGVTFGRVdYUnNZbVpDTW5weVIxZFJOR1J6YlUxME1GUlFiRWcyTkhsRVFrUTFOV1ZrWTFjMWIzbEJjV2RPQ2lBZ0lDQlNiWGh6YTB3eVRsZFdZVzFaUVVSc05XSkdVblE0VUVWRFpUSXJaMFJrYzBwWVJIRnBVa1UzYjFFMFBRb2dJQ0FnTFMwdExTMUZUa1FnVGtWQ1ZVeEJJRU5GVWxSSlJrbERRVlJGTFMwdExTMEtJQ0FnSUMwdExTMHRRa1ZIU1U0Z1RrVkNWVXhCSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLSUNBZ0lFTnRkMHRQYTA1c1kyNVNjRnB0YkdwWldGSnNTVVZHTVdSSGFIWmpiV3d3WlZOQ2JXSXpTV2RaTWpsNVdsVkNhbUl6U214YVJ6bHNZM2sxYTFwWVdXNEtJQ0FnSUdONVFsQmpiV1JvWW0xc05sbFlVbkJpTWpSdmVuUXZjRzVuV1hkNmNtWTVlWGRaTmtsQmJXdFBTWHBGU1ZBdmQwWjVkazkwZUhSNFNEbFhVMWxXWkVrS0lDQWdJREJSYTJOcWNreHFUa0pyYmsxMVdtbFJRVVZUVVVWMWNFd3pVbkJvZFRZd1JEVXlaekpFZERCdFozWnJRbFEyVEVGblIyZFRPR3hIZVZCbU1teG1aMm9LSUNBZ0lHTnZTa2xTUTNWdmVYVlFabUV4TWt0aFRWVTVUbWN3UldoclYyUllUVlZSUldrelJISktaRVZTVVZrOUNpQWdJQ0F0TFMwdExVVk9SQ0JPUlVKVlRFRWdRMFZTVkVsR1NVTkJWRVV0TFMwdExRb2dJR05sY25RNklId0tJQ0FnSUMwdExTMHRRa1ZIU1U0Z1RrVkNWVXhCSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLSUNBZ0lFTnVUVXRDVjA1dldsZE9ja1ZuYlVOblRVSlJaMGxFTkM5M09HbEVNMHAyWWtkVk5sUkhiRzVoU0ZKdllqTldlbHBUYWpnelQzRmxRbXBFVG5oMU5uUUtJQ0FnSUVKcWIyZGxOMFJCVW1NeVdGaE5PVll2ZVRKQ2FGWlZaVWh3Y2tzeGVFMWlNa0l3UTJ0S1YzSjNVRTh4UlZWR1MwbERaVlpGZFdaQlRXSkRWQzlSYlcwS0lDQWdJRFZFVTJ0MlVXSlhPVWRzTkhSQ1oxZ3lZMEpYWlVKVFNTOTFaMEZGYTBKcWIybFZTelJJT0dObmRtaDJUV0ZhTkcxclkxZGxlVmxKUml0Vk4xbHZLMWtLSUNBZ0lEWnZkazQwVldWNU5VcHhibVl3YkV0M1RtRjZiRm81SzNsemIxRmFkU3RoTjNVMFRIazRkWGRYTVVweVkwcG1SMmt4VVVZS0lDQWdJQzB0TFMwdFJVNUVJRTVGUWxWTVFTQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENpQWdaR2x6WTI5dWJtVmpkRjlwYm5aaGJHbGtPaUIwY25WbENuQjFibU5vZVRvS0lDQndkVzVqYURvZ1ptRnNjMlVLSUNCeVpYTndiMjVrT2lCbVlXeHpaUXB5Wld4aGVUb0tJQ0JoYlY5eVpXeGhlVG9nZEhKMVpRcHpkR0YwYVdOZmFHOXpkRjl0WVhBNkNpQWdNVEF1TVRZdU1DNHhPZ29nSUMwZ01TNHlMak11TkRvME1qUXlDaUFnTVRBdU1UWXVNQzR5T2dvZ0lDMGdNUzR5TGpNdU5EbzBNalF5Q2lBZ01UQXVNVFl1TUM0ek9nb2dJQzBnTkM0ekxqSXVNVG8wTWpReUNpQWdNVEF1TVRZdU1DNDBPZ29nSUMwZ05TNDJMamN1T0RvME1qUXlDblIxYmpvS0lDQmtaWFk2SUdSbFptbHVaV1F4Q2lBZ1pISnZjRjlzYjJOaGJGOWljbTloWkdOaGMzUTZJSFJ5ZFdVS0lDQmtjbTl3WDIxMWJIUnBZMkZ6ZERvZ2RISjFaUW9nSUcxMGRUb2dNVE13TUFvPSIsImNvdW50ZXIiOjIsIm5vbmNlIjoiWFpQVnI1OHJxYUluMFh6NFJHQTd0UT09IiwidHJ1c3RlZEtleXMiOiJMUzB0TFMxQ1JVZEpUaUJPUlVKVlRFRWdSVVF5TlRVeE9TQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1JIRkNlVzFRVUdkUVoxbHpSakJFVlVaRVIxcElPVGhLVUVkM2JtOHJOelJHTWxWWFRFaGtLMVZDVVQwS0xTMHRMUzFGVGtRZ1RrVkNWVXhCSUVWRU1qVTFNVGtnVUZWQ1RFbERJRXRGV1MwdExTMHRDaTB0TFMwdFFrVkhTVTRnVGtWQ1ZVeEJJRVZFTWpVMU1Ua2dVRlZDVEVsRElFdEZXUzB0TFMwdENrcGllR0ZZZVZGUFJtWm5NWEpyYTI1aU1UUnNZeko0YTJObU5qTndTV1ZxUldkaGNqZDNSek5WYkVVOUNpMHRMUzB0UlU1RUlFNUZRbFZNUVNCRlJESTFOVEU1SUZCVlFreEpReUJMUlZrdExTMHRMUW90TFMwdExVSkZSMGxPSUU1RlFsVk1RU0JGUkRJMU5URTVJRkJWUWt4SlF5QkxSVmt0TFMwdExRcERZVkUwYWsxUlp5OHZRVmhMT0RZelJ6TkZaakZhU21oV01HcFNRMUo1VDNOMVRUQkhVMk41TlcxSlBRb3RMUzB0TFVWT1JDQk9SVUpWVEVFZ1JVUXlOVFV4T1NCUVZVSk1TVU1nUzBWWkxTMHRMUzBLIn0=","signature":"M3cZGUZdXyArIw3qdH/1GXlPUqE/YiP5VeZLU876RtJGm7SBBB5Cl3I5ODjvQmEiF2okffmr9zd884k5izh6AQ=="},"metadata":{}}
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6f56f055f9912755979e27942a91efcf794a82dc
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "tfclient"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "trifid-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||||
|
base64 = "0.21.0"
|
||||||
|
log = "0.4.17"
|
|
@ -0,0 +1,84 @@
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use base64::{decode, Engine};
|
||||||
|
use rocket::info;
|
||||||
|
use crate::format::PEMValidationError::{IncorrectSegmentLength, InvalidBase64Data, MissingStartSentinel};
|
||||||
|
use crate::util::base64decode;
|
||||||
|
|
||||||
|
pub const ED_PUBKEY_START_STR: &str = "-----BEGIN NEBULA ED25519 PUBLIC KEY-----";
|
||||||
|
pub const ED_PUBKEY_END_STR: &str = "-----END NEBULA ED25519 PUBLIC KEY-----";
|
||||||
|
|
||||||
|
pub const DH_PUBKEY_START_STR: &str = "-----BEGIN NEBULA X25519 PUBLIC KEY-----";
|
||||||
|
pub const DH_PUBKEY_END_STR: &str = "-----END NEBULA X25519 PUBLIC KEY-----";
|
||||||
|
|
||||||
|
pub enum PEMValidationError {
|
||||||
|
MissingStartSentinel,
|
||||||
|
InvalidBase64Data,
|
||||||
|
MissingEndSentinel,
|
||||||
|
IncorrectSegmentLength(usize, usize)
|
||||||
|
}
|
||||||
|
impl Display for PEMValidationError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::MissingEndSentinel => write!(f, "Missing ending sentinel"),
|
||||||
|
Self::MissingStartSentinel => write!(f, "Missing starting sentinel"),
|
||||||
|
Self::InvalidBase64Data => write!(f, "invalid base64 data"),
|
||||||
|
Self::IncorrectSegmentLength(expected, got) => write!(f, "incorrect number of segments, expected {} got {}", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_ed_pubkey_pem(pubkey: &str) -> Result<(), PEMValidationError> {
|
||||||
|
let segments = pubkey.split('\n');
|
||||||
|
let segs = segments.collect::<Vec<&str>>();
|
||||||
|
if segs.len() < 3 {
|
||||||
|
return Err(IncorrectSegmentLength(3, segs.len()))
|
||||||
|
}
|
||||||
|
if segs[0] != ED_PUBKEY_START_STR {
|
||||||
|
return Err(MissingStartSentinel)
|
||||||
|
}
|
||||||
|
if base64decode(segs[1]).is_err() {
|
||||||
|
return Err(InvalidBase64Data)
|
||||||
|
}
|
||||||
|
if segs[2] != ED_PUBKEY_END_STR {
|
||||||
|
return Err(MissingStartSentinel)
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_dh_pubkey_pem(pubkey: &str) -> Result<(), PEMValidationError> {
|
||||||
|
let segments = pubkey.split('\n');
|
||||||
|
let segs = segments.collect::<Vec<&str>>();
|
||||||
|
if segs.len() < 3 {
|
||||||
|
return Err(IncorrectSegmentLength(3, segs.len()))
|
||||||
|
}
|
||||||
|
if segs[0] != DH_PUBKEY_START_STR {
|
||||||
|
return Err(MissingStartSentinel)
|
||||||
|
}
|
||||||
|
if base64decode(segs[1]).is_err() {
|
||||||
|
return Err(InvalidBase64Data)
|
||||||
|
}
|
||||||
|
if segs[2] != DH_PUBKEY_END_STR {
|
||||||
|
return Err(MissingStartSentinel)
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_ed_pubkey_base64(pubkey: &str) -> Result<(), PEMValidationError> {
|
||||||
|
match base64decode(pubkey) {
|
||||||
|
Ok(k) => validate_ed_pubkey_pem(match std::str::from_utf8(k.as_ref()) {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(_) => return Err(InvalidBase64Data)
|
||||||
|
}),
|
||||||
|
Err(_) => Err(InvalidBase64Data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_dh_pubkey_base64(pubkey: &str) -> Result<(), PEMValidationError> {
|
||||||
|
match base64decode(pubkey) {
|
||||||
|
Ok(k) => validate_dh_pubkey_pem(match std::str::from_utf8(k.as_ref()) {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(_) => return Err(InvalidBase64Data)
|
||||||
|
}),
|
||||||
|
Err(_) => Err(InvalidBase64Data)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
use rocket::{routes, post};
|
||||||
|
use rocket::{serde::Serialize};
|
||||||
|
use rocket::http::{ContentType, Status};
|
||||||
|
use rocket::serde::Deserialize;
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use crate::format::{validate_dh_pubkey_base64, validate_ed_pubkey_base64};
|
||||||
|
|
||||||
|
pub mod format;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct EnrollRequest {
|
||||||
|
pub code: String,
|
||||||
|
#[serde(rename = "dhPubkey")]
|
||||||
|
pub dh_pubkey: String,
|
||||||
|
#[serde(rename = "edPubkey")]
|
||||||
|
pub ed_pubkey: String,
|
||||||
|
pub timestamp: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct EnrollResponseMetadata {}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct EnrollResponseOrganization {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct EnrollResponseData {
|
||||||
|
pub config: String,
|
||||||
|
pub host_id: String,
|
||||||
|
pub counter: i64,
|
||||||
|
pub trusted_keys: String,
|
||||||
|
pub organization: EnrollResponseOrganization,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct EnrollResponse {
|
||||||
|
pub data: EnrollResponseData,
|
||||||
|
pub metadata: EnrollResponseMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct APIError {
|
||||||
|
errors: Vec<APIErrorSingular>
|
||||||
|
}
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct APIErrorSingular {
|
||||||
|
code: String,
|
||||||
|
message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ERR_MSG_MALFORMED_REQUEST: &str = "unable to parse the request body - is it valid JSON, using correct types?";
|
||||||
|
pub const ERR_MSG_MALFORMED_REQUEST_CODE: &str = "ERR_MALFORMED_REQUEST";
|
||||||
|
|
||||||
|
#[post("/v2/enroll", data = "<request>")]
|
||||||
|
fn enroll_endpoint(request: String) -> Result<(ContentType, Json<EnrollResponse>), (Status, Json<APIError>)> {
|
||||||
|
let request: EnrollRequest = match rocket::serde::json::from_str(request.as_str()) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
return Err((Status::BadRequest, Json(APIError { errors: vec![APIErrorSingular { code: ERR_MSG_MALFORMED_REQUEST_CODE.to_string(), message: format!("{} - {}", ERR_MSG_MALFORMED_REQUEST, e) }]})))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// validate request
|
||||||
|
if let Err(e) = validate_dh_pubkey_base64(request.dh_pubkey.as_str()) {
|
||||||
|
return Err((Status::BadRequest, Json(APIError { errors: vec![APIErrorSingular { code: ERR_MSG_MALFORMED_REQUEST_CODE.to_string(), message: format!("{} - invalid dhPubkey - {}", ERR_MSG_MALFORMED_REQUEST, e) }]})))
|
||||||
|
}
|
||||||
|
if let Err(e) = validate_ed_pubkey_base64(request.ed_pubkey.as_str()) {
|
||||||
|
return Err((Status::BadRequest, Json(APIError { errors: vec![APIErrorSingular { code: ERR_MSG_MALFORMED_REQUEST_CODE.to_string(), message: format!("{} - invalid edPubkey - {}", ERR_MSG_MALFORMED_REQUEST, e) }]})))
|
||||||
|
}
|
||||||
|
Ok((ContentType::JSON, Json(EnrollResponse {
|
||||||
|
data: EnrollResponseData {
|
||||||
|
config: "sdf".to_string(),
|
||||||
|
host_id: "sdf".to_string(),
|
||||||
|
counter: 0,
|
||||||
|
trusted_keys: "sdf".to_string(),
|
||||||
|
organization: EnrollResponseOrganization { id: "99s98d9878fds".to_string(), name: "e3team CA".to_string() },
|
||||||
|
},
|
||||||
|
metadata: EnrollResponseMetadata {},
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/v1/dnclient")]
|
||||||
|
fn dnclient_endpoint() -> &'static str {
|
||||||
|
"DNClient functionality is not yet implemented"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[rocket::main]
|
||||||
|
async fn main() {
|
||||||
|
let _ = rocket::build().mount("/", routes![enroll_endpoint, dnclient_endpoint]).launch().await;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
use base64::Engine;
|
||||||
|
|
||||||
|
pub fn base64decode(val: &str) -> Result<Vec<u8>, base64::DecodeError> {
|
||||||
|
base64::engine::general_purpose::STANDARD.decode(val)
|
||||||
|
}
|
||||||
|
pub fn base64encode(val: Vec<u8>) -> String {
|
||||||
|
base64::engine::general_purpose::STANDARD.encode(val)
|
||||||
|
}
|
Loading…
Reference in New Issue