diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b43425c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +resolver = "2" +members = ["./meta/agent", "./meta/common", "./meta/pusher"] + +[profile.release] +strip = true +lto = true +opt-level = "s" diff --git a/meta/agent/Cargo.toml b/meta/agent/Cargo.toml new file mode 100644 index 0000000..b217a9e --- /dev/null +++ b/meta/agent/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ewconfig-agent" +publish = false +edition = "2021" + +[dependencies] +ewconfig-meta-common = { path = "../common" } +clap = { version = "4.5.23", features = ["derive"] } +serde = { version = "^1.0", features = ["derive"] } +ssh-key = { version = "0.6.7", features = ["ed25519"] } +reqwest = { version = "0.12.9", features = ["json", "blocking"] } +serde_json = "^1.0" +directories = "5.0.1" +rand = "0.8.5" +log = "0.4.22" +anyhow = "1.0.94" +url = "2.5.4" + +[[bin]] +name = "ewconfig-agent" diff --git a/meta/agent/src/crypto.rs b/meta/agent/src/crypto.rs new file mode 100644 index 0000000..57eb67b --- /dev/null +++ b/meta/agent/src/crypto.rs @@ -0,0 +1,58 @@ +//! Cryptography helper functions + +use std::path::{Path, PathBuf}; + +use anyhow::Context; + +/// Get the default path for the agent's private key +pub fn get_default_key_path() -> PathBuf { + directories::BaseDirs::new() + .unwrap() + .home_dir() + .join(".config/ewpratten/config-agent-key") +} + +/// If a key does not exist, generate a new one +fn make_key_if_not_exists(private_key_path: &Path) { + log::debug!("Ensuring keypair exists as {:?}", private_key_path); + if !private_key_path.exists() { + // Generate a new ed25519 keypair + log::info!("Generating a new ed25519 keypair for this system"); + let private_key = + ssh_key::PrivateKey::random(&mut rand::rngs::OsRng, ssh_key::Algorithm::Ed25519) + .unwrap(); + let public_key = private_key.public_key(); + + // Make sure the parent directory exists + std::fs::create_dir_all(private_key_path.parent().unwrap()) + .with_context(|| "Failed to create keypair directory structure") + .unwrap(); + + // Write the private key to disk + std::fs::write( + &private_key_path, + private_key + .to_openssh(ssh_key::LineEnding::default()) + .unwrap(), + ) + .with_context(|| "Failed to write private key to disk") + .unwrap(); + + // Write the public key to disk + std::fs::write( + &private_key_path.with_extension("pub"), + public_key.to_openssh().unwrap(), + ) + .with_context(|| "Failed to write public key to disk") + .unwrap(); + } +} + +/// Get the public key for this agent +pub fn get_public_key(private_key_path: &Path) -> ssh_key::PublicKey { + make_key_if_not_exists(private_key_path); + std::fs::read_to_string(&private_key_path.with_extension("pub")) + .unwrap() + .parse::() + .unwrap() +} diff --git a/meta/agent/src/main.rs b/meta/agent/src/main.rs new file mode 100644 index 0000000..4cdc434 --- /dev/null +++ b/meta/agent/src/main.rs @@ -0,0 +1,59 @@ +mod crypto; + +use std::path::PathBuf; + +use clap::Parser; + +/// Evan's configuration agent +#[derive(Parser, Debug)] +#[command(version, about, long_about)] +struct Args { + #[clap(subcommand)] + command: Command, + + /// Enable verbose logging + #[clap(short, long)] + verbose: bool, +} + +/// CLI commands +#[derive(Parser, Debug)] +enum Command { + /// Check for any updates + Check { + /// Use a custom endpoint + endpoint: Option, + + /// Use a custom private key file + private_key: Option, + }, + + /// Print this machine's public key + #[clap(alias = "pubkey")] + PublicKey, + + /// Refresh the system + Refresh { + /// Use a custom endpoint + endpoint: Option, + + /// Use a custom private key file + private_key: Option, + }, +} + +pub fn main() { + // Parse the command line arguments + let args: Args = Args::parse(); + + // Handle commands + match args.command { + Command::Check { .. } => todo!(), + Command::PublicKey => { + let key_path = crypto::get_default_key_path(); + let public_key = crypto::get_public_key(&key_path); + println!("{}", public_key.to_openssh().unwrap()); + } + Command::Refresh { .. } => todo!(), + } +} diff --git a/meta/common/Cargo.toml b/meta/common/Cargo.toml new file mode 100644 index 0000000..158f57d --- /dev/null +++ b/meta/common/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ewconfig-meta-common" +publish = false +edition = "2021" + +[dependencies] +age = { version = "0.11.1", features = ["ssh"] } +serde = { version = "^1.0", features = ["derive"] } +serde_json = "^1.0" \ No newline at end of file diff --git a/meta/common/src/lib.rs b/meta/common/src/lib.rs new file mode 100644 index 0000000..3196df6 --- /dev/null +++ b/meta/common/src/lib.rs @@ -0,0 +1,2 @@ +//! Common library for ewconfig-pusher and ewconfig-agent +pub mod metadata; \ No newline at end of file diff --git a/meta/common/src/metadata/mod.rs b/meta/common/src/metadata/mod.rs new file mode 100644 index 0000000..8559986 --- /dev/null +++ b/meta/common/src/metadata/mod.rs @@ -0,0 +1 @@ +pub mod package; \ No newline at end of file diff --git a/meta/common/src/metadata/package.rs b/meta/common/src/metadata/package.rs new file mode 100644 index 0000000..e69de29 diff --git a/meta/pusher/Cargo.toml b/meta/pusher/Cargo.toml new file mode 100644 index 0000000..6a66f50 --- /dev/null +++ b/meta/pusher/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ewconfig-pusher" +publish = false +edition = "2021" + +[dependencies] +ewconfig-meta-common = { path = "../common" } +serde = { version = "^1.0", features = ["derive"] } + +[[bin]] +name = "ewconfig-pusher" \ No newline at end of file diff --git a/meta/pusher/src/main.rs b/meta/pusher/src/main.rs new file mode 100644 index 0000000..3e9b348 --- /dev/null +++ b/meta/pusher/src/main.rs @@ -0,0 +1,5 @@ + + +pub fn main() { + +} \ No newline at end of file diff --git a/nodes/laptop.yml b/nodes/laptop.yml new file mode 100644 index 0000000..47cbed4 --- /dev/null +++ b/nodes/laptop.yml @@ -0,0 +1,10 @@ +name: "Evan's Laptop" +keys: + - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFxw4Tk29wqUIqR8YrESeqrlCZ2cZoiKt5OVBv6zGqQI + +selector: + os: macos + serial: + +packages: + - network-utils \ No newline at end of file diff --git a/packages/1password-extras/ewpkg.yml b/packages/1password-extras/ewpkg.yml new file mode 100644 index 0000000..ce236eb --- /dev/null +++ b/packages/1password-extras/ewpkg.yml @@ -0,0 +1,4 @@ +name: 1Password Extras +bin: + - path: pkg://bin/1p-bridge + os: [linux, macos] \ No newline at end of file diff --git a/packages/network-utils/ewpkg.yml b/packages/network-utils/ewpkg.yml new file mode 100644 index 0000000..8a0e744 --- /dev/null +++ b/packages/network-utils/ewpkg.yml @@ -0,0 +1,21 @@ +name: Network Utils +bin: + - path: pkg://bin/aspath + - path: pkg://bin/dig-authoritative + - path: pkg://bin/ifpi + - path: pkg://bin/legacy-ssh + - path: pkg://bin/localexpose + - path: pkg://bin/mtr-graph + - path: pkg://bin/tcp-reflect + - path: pkg://bin/udp-reflect + - path: pkg://bin/watch-ports + - path: pkg://bin/wg-cat + - path: pkg://bin/wg-edit + - path: pkg://bin/wg-genzone + - path: pkg://bin/wg-get-client-ip + - path: pkg://bin/wg-handshakes + - path: pkg://bin/wg-list-interfaces + - path: pkg://bin/wg-peer-to-ipv6 + - path: pkg://bin/wg-reload + - path: pkg://bin/wg-restart + - path: pkg://bin/whois-util