diff --git a/services/identity/Cargo.lock b/services/identity/Cargo.lock --- a/services/identity/Cargo.lock +++ b/services/identity/Cargo.lock @@ -20,6 +20,54 @@ "libc", ] +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.70" @@ -99,17 +147,6 @@ "syn 2.0.28", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -637,42 +674,43 @@ [[package]] name = "clap" -version = "3.2.23" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ - "atty", - "bitflags", + "clap_builder", "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +dependencies = [ + "anstream", + "anstyle", "clap_lex", - "indexmap", - "once_cell", "strsim", - "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "codespan-reporting" @@ -684,6 +722,12 @@ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "comm-opaque2" version = "0.2.0" @@ -1302,15 +1346,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.2.6" @@ -1824,12 +1859,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "outref" version = "0.5.1" @@ -1962,30 +1991,6 @@ "syn 1.0.109", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.66" @@ -2630,12 +2635,6 @@ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.40" @@ -3023,6 +3022,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.1" diff --git a/services/identity/Cargo.toml b/services/identity/Cargo.toml --- a/services/identity/Cargo.toml +++ b/services/identity/Cargo.toml @@ -9,7 +9,7 @@ prost = "0.11" tokio = { version = "1.24", features = ["macros", "rt-multi-thread"] } ed25519-dalek = "1" -clap = { version = "3.1.12", features = ["derive"] } +clap = { version = "4.4", features = ["derive", "env"] } derive_more = "0.99" aws-config = "0.54.0" aws-sdk-dynamodb = "0.24.0" diff --git a/services/identity/Dockerfile b/services/identity/Dockerfile --- a/services/identity/Dockerfile +++ b/services/identity/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.67 as builder +FROM rust:1.70-bullseye as builder RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ build-essential cmake git libgtest-dev libssl-dev zlib1g-dev \ diff --git a/services/identity/src/config.rs b/services/identity/src/config.rs --- a/services/identity/src/config.rs +++ b/services/identity/src/config.rs @@ -1,4 +1,5 @@ use base64::{engine::general_purpose, DecodeError, Engine as _}; +use clap::{Parser, Subcommand}; use once_cell::sync::Lazy; use std::{collections::HashSet, env, fmt, fs, io, path}; use tracing::{error, info}; @@ -9,15 +10,56 @@ TUNNELBROKER_GRPC_ENDPOINT, }; -pub static CONFIG: Lazy = - Lazy::new(|| Config::load().expect("failed to load config")); +/// Raw CLI arguments, should be only used internally to create ServerConfig +static CLI: Lazy = Lazy::new(Cli::parse); -pub(super) fn load_config() { - Lazy::force(&CONFIG); +pub static CONFIG: Lazy = Lazy::new(|| { + ServerConfig::from_cli(&CLI).expect("Failed to load server config") +}); + +pub(super) fn parse_cli_command() -> &'static Command { + &Lazy::force(&CLI).command +} + +pub(super) fn load_server_config() -> &'static ServerConfig { + Lazy::force(&CONFIG) +} + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +#[clap(propagate_version = true)] +struct Cli { + #[clap(subcommand)] + command: Command, + + /// AWS Localstack service URL + #[arg(long, global = true)] + #[arg(env = LOCALSTACK_ENDPOINT)] + localstack_endpoint: Option, + + /// Tunnelbroker gRPC endpoint + #[arg(long, global = true)] + #[arg(env = TUNNELBROKER_GRPC_ENDPOINT)] + #[arg(default_value = DEFAULT_TUNNELBROKER_ENDPOINT)] + tunnelbroker_endpoint: String, +} + +#[derive(Subcommand)] +pub enum Command { + /// Runs the server + Server, + /// Generates and persists a keypair to use for PAKE registration and login + Keygen { + #[arg(short, long)] + #[arg(default_value = SECRETS_DIRECTORY)] + dir: String, + }, + /// Populates the `identity-users` table in DynamoDB from MySQL + PopulateDB, } #[derive(Clone)] -pub struct Config { +pub struct ServerConfig { pub localstack_endpoint: Option, // Opaque 2.0 server secrets pub server_setup: comm_opaque2::ServerSetup, @@ -27,44 +69,31 @@ pub tunnelbroker_endpoint: String, } -impl Config { - fn load() -> Result { - let localstack_endpoint = env::var(LOCALSTACK_ENDPOINT).ok(); - let tunnelbroker_endpoint = match env::var(TUNNELBROKER_GRPC_ENDPOINT) { - Ok(val) => { - info!("Using Tunnelbroker endpoint from env var: {}", val); - val - } - Err(std::env::VarError::NotPresent) => { - let val = DEFAULT_TUNNELBROKER_ENDPOINT; - info!("Falling back to default Tunnelbroker endpoint: {}", val); - val.to_string() - } - Err(e) => { - error!( - "Failed to read environment variable {}: {:?}", - TUNNELBROKER_GRPC_ENDPOINT, e - ); - return Err(Error::Env(e)); - } - }; +impl ServerConfig { + fn from_cli(cli: &Cli) -> Result { + if !matches!(cli.command, Command::Server) { + panic!("ServerConfig is only available for the `server` command"); + } + + info!("Tunnelbroker endpoint: {}", &cli.tunnelbroker_endpoint); + if let Some(endpoint) = &cli.localstack_endpoint { + info!("Using Localstack endpoint: {}", endpoint); + } let mut path_buf = path::PathBuf::new(); path_buf.push(SECRETS_DIRECTORY); path_buf.push(SECRETS_SETUP_FILE); let server_setup = get_server_setup(path_buf.as_path())?; - let reserved_usernames = get_reserved_usernames_set()?; - let keyserver_public_key = env::var(KEYSERVER_PUBLIC_KEY).ok(); Ok(Self { - localstack_endpoint, + localstack_endpoint: cli.localstack_endpoint.clone(), + tunnelbroker_endpoint: cli.tunnelbroker_endpoint.clone(), server_setup, reserved_usernames, keyserver_public_key, - tunnelbroker_endpoint, }) } @@ -73,9 +102,9 @@ } } -impl fmt::Debug for Config { +impl fmt::Debug for ServerConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Config") + f.debug_struct("ServerConfig") .field("server_keypair", &"** redacted **") .field("keyserver_auth_token", &"** redacted **") .field("localstack_endpoint", &self.localstack_endpoint) diff --git a/services/identity/src/main.rs b/services/identity/src/main.rs --- a/services/identity/src/main.rs +++ b/services/identity/src/main.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use clap::{Parser, Subcommand}; +use config::Command; use database::DatabaseClient; use moka::future::Cache; use tonic::transport::Server; @@ -23,8 +23,7 @@ mod token; mod tunnelbroker; -use config::load_config; -use constants::{IDENTITY_SERVICE_SOCKET_ADDR, SECRETS_DIRECTORY}; +use constants::IDENTITY_SERVICE_SOCKET_ADDR; use cors::cors_layer; use keygen::generate_and_persist_keypair; use tracing::{self, info, Level}; @@ -34,28 +33,6 @@ use grpc_services::authenticated::auth_proto::identity_client_service_server::IdentityClientServiceServer as AuthServer; use grpc_services::authenticated::AuthenticatedService; -#[derive(Parser)] -#[clap(author, version, about, long_about = None)] -#[clap(propagate_version = true)] -struct Cli { - #[clap(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] -enum Commands { - /// Runs the server - Server, - /// Generates and persists a keypair to use for PAKE registration and login - Keygen { - #[clap(short, long)] - #[clap(default_value_t = String::from(SECRETS_DIRECTORY))] - dir: String, - }, - /// Populates the `identity-users` table in DynamoDB from MySQL - PopulateDB, -} - #[tokio::main] async fn main() -> Result<(), Box> { let filter = EnvFilter::builder() @@ -66,13 +43,12 @@ let subscriber = tracing_subscriber::fmt().with_env_filter(filter).finish(); tracing::subscriber::set_global_default(subscriber)?; - let cli = Cli::parse(); - match &cli.command { - Commands::Keygen { dir } => { + match config::parse_cli_command() { + Command::Keygen { dir } => { generate_and_persist_keypair(dir)?; } - Commands::Server => { - load_config(); + Command::Server => { + config::load_server_config(); let addr = IDENTITY_SERVICE_SOCKET_ADDR.parse()?; let aws_config = aws_config::from_env().region("us-east-2").load().await; let database_client = DatabaseClient::new(&aws_config); @@ -103,7 +79,7 @@ .serve(addr) .await?; } - Commands::PopulateDB => unimplemented!(), + Command::PopulateDB => unimplemented!(), } Ok(())