This commit is contained in:
c0repwn3r 2023-02-02 18:38:39 -05:00
commit 83982551ca
Signed by: core
GPG Key ID: FDBF740DADDCEECF
15 changed files with 3476 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "dnapi"]
path = dnapi
url = https://github.com/DefinedNet/dnapi

1569
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

5
Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[workspace]
members = [
"trifid-api",
"tfclient"
]

32
README.md Normal file
View File

@ -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 |

110
api.txt Normal file
View File

@ -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":{}}

1
dnapi Submodule

@ -0,0 +1 @@
Subproject commit 6f56f055f9912755979e27942a91efcf794a82dc

BIN
dnclient Executable file

Binary file not shown.

8
tfclient/Cargo.toml Normal file
View File

@ -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]

3
tfclient/src/main.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

1541
trifid-api/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

11
trifid-api/Cargo.toml Normal file
View File

@ -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
trifid-api/src/db.rs Normal file
View File

84
trifid-api/src/format.rs Normal file
View File

@ -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)
}
}

101
trifid-api/src/main.rs Normal file
View File

@ -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;
}

8
trifid-api/src/util.rs Normal file
View File

@ -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)
}