224 lines
8.9 KiB
Rust
224 lines
8.9 KiB
Rust
//! Models for interacting with the Defined Networking API.
|
|
|
|
use base64_serde::base64_serde_type;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// The version 1 `DNClient` API endpoint
|
|
pub const ENDPOINT_V1: &str = "/v1/dnclient";
|
|
|
|
/// The `CheckForUpdate` message type
|
|
pub const CHECK_FOR_UPDATE: &str = "CheckForUpdate";
|
|
/// The `DoUpdate` message type
|
|
pub const DO_UPDATE: &str = "DoUpdate";
|
|
|
|
base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `RequestV1` is the version 1 `DNClient` request message.
|
|
pub struct RequestV1 {
|
|
/// Version is always 1
|
|
pub version: i32,
|
|
#[serde(rename = "hostID")]
|
|
/// The Host ID of this dnclient instance
|
|
pub host_id: String,
|
|
/// The counter last returned by the server
|
|
pub counter: u32,
|
|
/// A base64-encoded message. This must be previously base64-encoded, as the signature is signed over the base64-encoded data.
|
|
pub message: String,
|
|
#[serde(with = "Base64Standard")]
|
|
/// An ed25519 signature over the `message`, which can be verified with the host's previously enrolled ed25519 public key
|
|
pub signature: Vec<u8>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `RequestWrapper` wraps a `DNClient` request message. It consists of a
|
|
/// type and value, with the type string indicating how to interpret the value blob.
|
|
pub struct RequestWrapper {
|
|
#[serde(rename = "type")]
|
|
/// The type of the message. Used to determine how `value` is encoded
|
|
pub message_type: String,
|
|
/// A base64-encoded arbitrary message, the type of which is stated in `message_type`
|
|
#[serde(with = "b64_as")]
|
|
pub value: Vec<u8>,
|
|
/// The timestamp of when this message was sent. Follows the format `%Y-%m-%dT%H:%M:%S.%f%:z`, or:
|
|
/// <4-digit year>-<two-digit-month>-<two-digit-day>T<two-digit-hour, 24-hour>:<two-digit-minute>:<two-digit-second>.<nanoseconds, zero-padded><offset with semicolon>
|
|
/// For example:
|
|
/// `2023-03-29T09:56:42.380006369-04:00`
|
|
/// would represent `29 March 03, 2023, 09:56:42.380006369 UTC-4`
|
|
pub timestamp: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `SignedResponseWrapper` contains a response message and a signature to validate inside `data`.
|
|
pub struct SignedResponseWrapper {
|
|
/// The response data contained in this message
|
|
pub data: SignedResponse,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `SignedResponse` contains a response message and a signature to validate.
|
|
pub struct SignedResponse {
|
|
/// The API version - always 1
|
|
pub version: i32,
|
|
#[serde(with = "Base64Standard")]
|
|
/// The Base64-encoded message signed inside this message
|
|
pub message: Vec<u8>,
|
|
#[serde(with = "Base64Standard")]
|
|
/// The ed25519 signature over the `message`
|
|
pub signature: Vec<u8>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `CheckForUpdateResponseWrapper` contains a response to `CheckForUpdate` inside "data."
|
|
pub struct CheckForUpdateResponseWrapper {
|
|
/// The response data contained in this message
|
|
pub data: CheckForUpdateResponse,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `CheckForUpdateResponse` is the response generated for a `CheckForUpdate` request.
|
|
pub struct CheckForUpdateResponse {
|
|
#[serde(rename = "updateAvailable")]
|
|
/// Set to true if a config update is available
|
|
pub update_available: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `DoUpdateRequest` is the request sent for a `DoUpdate` request.
|
|
pub struct DoUpdateRequest {
|
|
#[serde(rename = "edPubkeyPEM")]
|
|
#[serde(with = "Base64Standard")]
|
|
/// The new ed25519 public key that should be used for future API requests
|
|
pub ed_pubkey_pem: Vec<u8>,
|
|
#[serde(rename = "dhPubkeyPEM")]
|
|
#[serde(with = "Base64Standard")]
|
|
/// The new ECDH public key that the Nebula certificate should be signed for
|
|
pub dh_pubkey_pem: Vec<u8>,
|
|
#[serde(with = "Base64Standard")]
|
|
/// A randomized value used to uniquely identify this request.
|
|
/// The original client uses a randomized, 16-byte value here, which dnapi-rs replicates
|
|
pub nonce: Vec<u8>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// A server response to a `DoUpdateRequest`, with the updated config and key information
|
|
pub struct DoUpdateResponse {
|
|
#[serde(with = "Base64Standard")]
|
|
/// The base64-encoded Nebula config. It does **NOT** have a private-key, which must be inserted explicitly before Nebula can be ran
|
|
pub config: Vec<u8>,
|
|
/// The new config counter. It is unknown what the purpose of this is, but the original client keeps track of it and it is used later in the api
|
|
pub counter: u32,
|
|
#[serde(with = "Base64Standard")]
|
|
/// The same base64-encoded nonce that was sent in the `DoUpdateRequest`.
|
|
pub nonce: Vec<u8>,
|
|
#[serde(rename = "trustedKeys")]
|
|
#[serde(with = "Base64Standard")]
|
|
/// A new set of trusted ed25519 keys that can be used by the server to sign messages.
|
|
pub trusted_keys: Vec<u8>,
|
|
}
|
|
|
|
/// The REST enrollment endpoint
|
|
pub const ENROLL_ENDPOINT: &str = "/v2/enroll";
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `EnrollRequest` is issued to the `ENROLL_ENDPOINT` to enroll this `dnclient` with a dnapi organization
|
|
pub struct EnrollRequest {
|
|
/// The enrollment code given by the API server.
|
|
pub code: String,
|
|
#[serde(rename = "dhPubkey")]
|
|
#[serde(with = "Base64Standard")]
|
|
/// The ECDH public-key that should be used to sign the Nebula certificate given to this node.
|
|
pub dh_pubkey: Vec<u8>,
|
|
#[serde(rename = "edPubkey")]
|
|
#[serde(with = "Base64Standard")]
|
|
/// The Ed25519 public-key that this node will use to sign messages sent to the API.
|
|
pub ed_pubkey: Vec<u8>,
|
|
/// The timestamp of when this request was sent. Follows the format `%Y-%m-%dT%H:%M:%S.%f%:z`, or:
|
|
/// <4-digit year>-<two-digit-month>-<two-digit-day>T<two-digit-hour, 24-hour>:<two-digit-minute>:<two-digit-second>.<nanoseconds, zero-padded><offset with semicolon>
|
|
/// For example:
|
|
/// `2023-03-29T09:56:42.380006369-04:00`
|
|
/// would represent `29 March 03, 2023, 09:56:42.380006369 UTC-4`
|
|
pub timestamp: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
#[serde(untagged)]
|
|
/// The response to an `EnrollRequest`
|
|
pub enum EnrollResponse {
|
|
/// A successful enrollment, with a `data` field pointing to an `EnrollResponseData`
|
|
Success {
|
|
/// The response data from this response
|
|
data: EnrollResponseData,
|
|
},
|
|
/// An unsuccessful enrollment, with an `errors` field pointing to an array of `APIError`s.
|
|
Error {
|
|
/// A list of `APIError`s that happened while trying to enroll. `APIErrors` is a type alias to `Vec<APIError>`
|
|
errors: APIErrors,
|
|
},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// The data included in an successful enrollment.
|
|
pub struct EnrollResponseData {
|
|
#[serde(with = "Base64Standard")]
|
|
/// The base64-encoded Nebula config. It does **NOT** have a private-key, which must be inserted explicitly before Nebula can be ran
|
|
pub config: Vec<u8>,
|
|
#[serde(rename = "hostID")]
|
|
/// The server-side Host ID that this node now has.
|
|
pub host_id: String,
|
|
/// The new config counter. It is unknown what the purpose of this is, but the original client keeps track of it and it is used later in the api
|
|
pub counter: u32,
|
|
#[serde(rename = "trustedKeys")]
|
|
#[serde(with = "Base64Standard")]
|
|
/// A new set of trusted ed25519 keys that can be used by the server to sign messages.
|
|
pub trusted_keys: Vec<u8>,
|
|
/// The organization data that this node is now a part of
|
|
pub organization: EnrollResponseDataOrg,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// The organization data that this node is now a part of
|
|
pub struct EnrollResponseDataOrg {
|
|
/// The organization ID that this node is now a part of
|
|
pub id: String,
|
|
/// The name of the organization that this node is now a part of
|
|
pub name: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
/// `APIError` represents a single error returned in an API error response.
|
|
pub struct APIError {
|
|
/// The error code
|
|
pub code: String,
|
|
/// The human-readable error message
|
|
pub message: String,
|
|
/// An optional path to where the error occured
|
|
pub path: Option<String>,
|
|
}
|
|
|
|
/// A type alias to a array of `APIErrors`. Just for parity with dnapi.
|
|
pub type APIErrors = Vec<APIError>;
|
|
|
|
mod b64_as {
|
|
use base64::Engine;
|
|
use serde::{Deserialize, Serialize};
|
|
use serde::{Deserializer, Serializer};
|
|
|
|
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
|
|
let base64 = base64::engine::general_purpose::STANDARD.encode(v);
|
|
<String>::serialize(&base64, s)
|
|
}
|
|
|
|
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
|
|
let base64 = <Option<String>>::deserialize(d)?;
|
|
base64.map_or_else(
|
|
|| Ok(vec![]),
|
|
|v| {
|
|
base64::engine::general_purpose::STANDARD
|
|
.decode(v.as_bytes())
|
|
.map_err(serde::de::Error::custom)
|
|
},
|
|
)
|
|
}
|
|
}
|