From a3328e0714e6ab6beb3e10332421c02ae5c66e02 Mon Sep 17 00:00:00 2001 From: Daniel Lundin Date: Sat, 18 Jan 2025 12:15:59 +0100 Subject: [PATCH] WIP: Registration --- Cargo.lock | 149 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 +- agent/Cargo.toml | 1 + api.json | 55 ++++++++++++++ controller/Cargo.toml | 5 ++ controller/src/api.rs | 2 + controller/src/lib.rs | 1 + controller/src/node/api.rs | 70 +++++++++++++++++ controller/src/node/mod.rs | 3 + flake.nix | 1 + 10 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 controller/src/node/api.rs create mode 100644 controller/src/node/mod.rs diff --git a/Cargo.lock b/Cargo.lock index e2d7e26..86ec307 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -307,8 +307,10 @@ checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.52.6", ] @@ -467,6 +469,68 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "debug-ignore" version = "1.0.5" @@ -491,6 +555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -614,6 +679,31 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -676,6 +766,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "flate2" version = "1.0.35" @@ -1254,6 +1350,12 @@ dependencies = [ "syn", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -1283,6 +1385,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -1822,6 +1925,7 @@ name = "patagia-agent" version = "0.2.0" dependencies = [ "anyhow", + "chrono", "clap", "futures", "instrumentation", @@ -1839,12 +1943,17 @@ name = "patagia-controller" version = "0.2.0" dependencies = [ "anyhow", + "chrono", "clap", "dropshot", + "ed25519-dalek", + "hex", "http", "instrumentation", + "rand", "schemars", "serde", + "serde_with", "slog", "slog-async", "sqlx", @@ -2305,6 +2414,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.43" @@ -2415,6 +2533,7 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ + "bytes", "chrono", "dyn-clone", "schemars_derive", @@ -2572,6 +2691,36 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" diff --git a/Cargo.toml b/Cargo.toml index d61baf6..fdf3949 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ name = "patagia-run" [workspace.dependencies] anyhow = "1.0.95" +chrono = "0.4.39" clap = { version = "4.5.26", features = [ "derive", "deprecated", @@ -32,14 +33,19 @@ clap = { version = "4.5.26", features = [ "string", ] } dropshot = "0.15.1" +ed25519-dalek = { version = "2.1.1", features = ["pem", "rand_core"] } futures = "0.3" +hex = "0.4.3" http = "1.2.0" once_cell = "1.20.2" progenitor = "0.9" +rand = "0.8.5" reqwest = { version = "0.12.12", features = ["json", "stream", "rustls-tls"] } -schemars = "0.8.21" +schemars = { version = "0.8.21", features = ["bytes", "chrono", "derive"] } semver = "1.0.24" serde = { version = "1.0.217", features = ["derive"] } +serde_with = { version = "3.12.0", features = ["base64", "hex", "macros", "std"] } +# serde_with_macros = { version = "3.12.0", features = ["schemars_0_8"] } slog = "2.7.0" slog-async = "2.8.0" tokio = { version = "1.43.0", features = ["full"] } diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 2305f9f..8cc49b3 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -6,6 +6,7 @@ version.workspace = true [dependencies] anyhow.workspace = true +chrono.workspace = true clap.workspace = true futures.workspace = true instrumentation = { path = "../instrumentation" } diff --git a/api.json b/api.json index 4ea6803..ae26182 100644 --- a/api.json +++ b/api.json @@ -5,6 +5,33 @@ "version": "1.0.0" }, "paths": { + "/nodes/register": { + "get": { + "tags": [ + "node" + ], + "summary": "Get registration info", + "operationId": "get_registration_info", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegistrationInfo" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/users": { "get": { "tags": [ @@ -141,6 +168,31 @@ "request_id" ] }, + "RegistrationInfo": { + "type": "object", + "properties": { + "challenge": { + "type": "string" + }, + "expiration": { + "type": "string", + "format": "date-time" + }, + "node_id": { + "type": "string", + "format": "uuid" + }, + "public_key": { + "type": "string" + } + }, + "required": [ + "challenge", + "expiration", + "node_id", + "public_key" + ] + }, "User": { "description": "User", "type": "object", @@ -210,6 +262,9 @@ } }, "tags": [ + { + "name": "node" + }, { "name": "user" } diff --git a/controller/Cargo.toml b/controller/Cargo.toml index 7157501..6b2f178 100644 --- a/controller/Cargo.toml +++ b/controller/Cargo.toml @@ -7,12 +7,17 @@ version.workspace = true [dependencies] anyhow.workspace = true +chrono.workspace = true clap.workspace = true dropshot.workspace = true +ed25519-dalek.workspace = true +hex.workspace = true http.workspace = true instrumentation = { path = "../instrumentation" } +rand.workspace = true schemars.workspace = true serde.workspace = true +serde_with.workspace = true slog-async.workspace = true slog.workspace = true sqlx = { version = "0.8.3", default-features = false, features = [ diff --git a/controller/src/api.rs b/controller/src/api.rs index 5be86ce..2063611 100644 --- a/controller/src/api.rs +++ b/controller/src/api.rs @@ -4,6 +4,7 @@ use dropshot::ApiDescription; use std::sync::Arc; use crate::context::ControllerContext; +use crate::node; use crate::user; use crate::version; @@ -11,6 +12,7 @@ type ControllerApiDescription = ApiDescription>; pub fn api() -> Result { let mut api = ControllerApiDescription::new(); + node::register_api(&mut api)?; user::register_api(&mut api)?; api.register(version::version)?; Ok(api) diff --git a/controller/src/lib.rs b/controller/src/lib.rs index 2d12df1..d408e5c 100644 --- a/controller/src/lib.rs +++ b/controller/src/lib.rs @@ -1,5 +1,6 @@ pub mod api; pub mod context; +mod node; mod user; mod version; diff --git a/controller/src/node/api.rs b/controller/src/node/api.rs new file mode 100644 index 0000000..cf51771 --- /dev/null +++ b/controller/src/node/api.rs @@ -0,0 +1,70 @@ +use chrono::DateTime; +use dropshot::{endpoint, HttpError, HttpResponseOk, RequestContext}; +use dropshot::{ApiDescription, ApiDescriptionRegisterError}; +use rand::RngCore; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use serde_with::base64::Base64; +use serde_with::base64::Crypt; +use serde_with::serde_as; +use trace_request::trace_request; +use uuid::Uuid; + +use std::sync::Arc; + +use crate::context::ControllerContext; + +pub fn register_api( + api: &mut ApiDescription>, +) -> Result<(), ApiDescriptionRegisterError> { + api.register(get_registration_info) + // api.register(register_node) +} + +#[serde_as] +#[derive(Deserialize, JsonSchema, Serialize)] +struct RegistrationInfo { + node_id: Uuid, + expiration: DateTime, + + // #[serde_as(as = "Base64")] + // #[serde_as(as = "Base64")] + #[serde_as(as = "serde_with::base64::Base64")] + // #[serde_as(as = "serde_with::hex::Hex")] + #[schemars(with = "String")] + challenge: [u8; 60], + + // #[serde_as(as = "serde_with::base64::Base64")] + #[serde_as(as = "serde_with::hex::Hex")] + // #[serde_as(as = "Base64")] + // #[serde_as(as = "serde_with::hex::Hex")] + #[schemars(with = "String")] + public_key: [u8; 32], +} + +/// Get registration info +#[endpoint { + method = GET, + path = "/nodes/register", + tags = [ "node" ], +}] +#[trace_request] +async fn get_registration_info( + rqctx: RequestContext>, +) -> Result, HttpError> { + tracing::debug!("Registration info"); + + let rng = &mut rand::rngs::OsRng; + let mut challenge = [0u8; 60]; + rng.fill_bytes(&mut challenge); + let key = ed25519_dalek::SigningKey::generate(rng); + + let info = RegistrationInfo { + node_id: Uuid::new_v4(), + expiration: chrono::Utc::now(), + challenge, + public_key: key.verifying_key().to_bytes(), + }; + + Ok(HttpResponseOk(info)) +} diff --git a/controller/src/node/mod.rs b/controller/src/node/mod.rs new file mode 100644 index 0000000..f35db4c --- /dev/null +++ b/controller/src/node/mod.rs @@ -0,0 +1,3 @@ +mod api; + +pub use self::api::register_api; diff --git a/flake.nix b/flake.nix index ac97f6e..8ad0312 100644 --- a/flake.nix +++ b/flake.nix @@ -146,6 +146,7 @@ rust-dev-toolchain sqls sqlx-cli + tpm2-tools watchexec ] ++ commonArgs.buildInputs;