diff --git a/Cargo.lock b/Cargo.lock index 7608e40..aba578a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,7 +14,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" dependencies = [ - "bitflags 1.3.2", + "bitflags", "bytes", "futures-core", "futures-sink", @@ -52,7 +52,7 @@ dependencies = [ "actix-utils", "ahash 0.8.3", "base64 0.21.0", - "bitflags 1.3.2", + "bitflags", "brotli", "bytes", "bytestring", @@ -609,41 +609,12 @@ dependencies = [ "num-traits", ] -[[package]] -name = "bindgen" -version = "0.66.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" -dependencies = [ - "bitflags 2.3.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.16", - "which", -] - [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" - [[package]] name = "bitvec" version = "1.0.1" @@ -804,15 +775,6 @@ dependencies = [ "jobserver", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -851,24 +813,13 @@ dependencies = [ "inout", ] -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ - "bitflags 1.3.2", + "bitflags", "clap_derive 3.2.25", "clap_lex 0.2.4", "indexmap", @@ -895,7 +846,7 @@ checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", + "bitflags", "clap_lex 0.4.1", "strsim", ] @@ -1054,7 +1005,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" dependencies = [ - "bitflags 1.3.2", + "bitflags", "crossterm_winapi", "libc", "mio", @@ -1560,12 +1511,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "gloo-timers" version = "0.2.6" @@ -1578,15 +1523,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gobuild" -version = "0.1.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e156a4ddbf3deb5e8116946c111413bd9a5679bdc1536c78a60618a7a9ac9e" -dependencies = [ - "cc", -] - [[package]] name = "h2" version = "0.3.19" @@ -1924,28 +1860,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "libm" version = "0.1.4" @@ -2071,21 +1991,13 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nebula-ffi" -version = "0.1.0" -dependencies = [ - "bindgen", - "gobuild", -] - [[package]] name = "nix" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cfg-if", "libc", "static_assertions", @@ -2168,7 +2080,7 @@ version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -2321,12 +2233,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "pem" version = "1.1.1" @@ -2383,7 +2289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags 1.3.2", + "bitflags", "cfg-if", "concurrent-queue", "libc", @@ -2410,16 +2316,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "prettyplease" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9825a04601d60621feed79c4e6b56d65db77cdca55cef43b46b0de1096d1c282" -dependencies = [ - "proc-macro2", - "syn 2.0.16", -] - [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -2561,7 +2457,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -2570,7 +2466,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -2723,12 +2619,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc_version" version = "0.4.0" @@ -2744,7 +2634,7 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags 1.3.2", + "bitflags", "errno", "io-lifetimes", "libc", @@ -2985,7 +2875,7 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2855b3715770894e67cbfa3df957790aa0c9edc3bf06efa1a84d77fa0839d1" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -3132,12 +3022,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - [[package]] name = "signal-hook" version = "0.3.15" @@ -3265,7 +3149,7 @@ dependencies = [ "atoi", "base64 0.13.1", "bigdecimal", - "bitflags 1.3.2", + "bitflags", "byteorder", "bytes", "chrono", @@ -3431,7 +3315,7 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "tfcli" -version = "0.1.0" +version = "0.1.1" dependencies = [ "clap 4.2.7", "dirs 5.0.1", @@ -3446,7 +3330,7 @@ dependencies = [ [[package]] name = "tfclient" -version = "0.2.0" +version = "0.1.9" dependencies = [ "base64 0.21.0", "base64-serde", @@ -3459,7 +3343,6 @@ dependencies = [ "hex", "ipnet", "log", - "nebula-ffi", "openssl-sys", "reqwest", "serde", @@ -3750,7 +3633,7 @@ dependencies = [ [[package]] name = "trifid-api" -version = "0.1.0" +version = "0.1.3" dependencies = [ "actix-cors", "actix-request-identifier", @@ -4073,17 +3956,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - [[package]] name = "whoami" version = "1.4.0" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8a4b58f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM rust:latest + +COPY trifid-pki /trifid-pki +COPY dnapi-rs /dnapi-rs +COPY trifid-api /trifid-api + +RUN cd /trifid-api && cargo build --release && cp target/release/trifid-api /bin/trifid-api + +CMD ["/bin/trifid-api"] diff --git a/README.md b/README.md index 2fbff0d..581a97f 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,9 @@ 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 | +| Automatic config update polling by dnclient | Yes | Yes | +| Group-based firewalling | Yes | Yes | +| All config features | Yes | Not officially | | SSO authentication | Not yet | Yes | | Open-source server | Yes | No | @@ -33,3 +34,6 @@ tfclient feature table: | Poll the API server for config updates | Yes | Yes | | Secure Ed25519 signing for API communication | Yes | Yes | +# Documentation + +Documentation work is underway. You can find a link to all documentation on the main project page [here](https://hub.e3t.cc/~core/trifid). diff --git a/docs/tfclient/index.md b/docs/tfclient/index.md new file mode 100644 index 0000000..4777063 --- /dev/null +++ b/docs/tfclient/index.md @@ -0,0 +1,3 @@ +# tfclient - a Rust DNClient alternative + +TODO \ No newline at end of file diff --git a/docs/tfclient/untested_os.md b/docs/tfclient/untested_os.md new file mode 100644 index 0000000..8685001 --- /dev/null +++ b/docs/tfclient/untested_os.md @@ -0,0 +1,13 @@ +# Help us out - test tfclient on new platforms + +There are [lots](index.md#where-does-trifid-work) of operating systems in which `tfclient` *should* work, but due to a lack of devices, it cannot be tested. If you have a system running one of these devices and want to help out the project, please test tfclient for functionality on your device! + +
+ Notice: While tfclient is almost always perfectly safe, there are always risks to running untested software on production machines. Be careful out there! +
+ +Any findings, positive or negative, can be posted straight to [trifid-devel](https://lists.e3t.cc/~core/trifid-devel). + +Thanks! + +[Return home](index.md) \ No newline at end of file diff --git a/docs/tfclient/why_not_this_os.md b/docs/tfclient/why_not_this_os.md new file mode 100644 index 0000000..d34e562 --- /dev/null +++ b/docs/tfclient/why_not_this_os.md @@ -0,0 +1,9 @@ +# Why cant tfclient support XXXXX? + +There are limits to what operating systems and architectures tfclient can support. As you may know, tfclient is based upon the [Nebula](https://github.com/slackhq/nebula) project, maintained by Slack. tfclient can only support architectures and operating systems that [Nebula itself supports](https://github.com/slackhq/nebula/releases/). + +In addition, tfclient, being written in Rust, can only function in environments [where Rust compiles well](https://doc.rust-lang.org/nightly/rustc/platform-support.html). For example, this rules out MIPS - `rustc` currently does not function correctly on that architecture, and thus tfclient cannot, and likely never will support that architecture. + +Did Nebula add a new architecture, and we missed it? Rust supports a new architecture in a new release? [Let us know!](https://lists.e3t.cc/~core/trifid-devel) We love adding support for new systems whenever we can. + +Did we miss an architecture? If your architecture is supported by BOTH Nebula and Rust, but you still get `This architecture is not supported yet :(` when trying to build tfclient, we might have missed you - [get in touch](https://lists.e3t.cc/~core/trifid-devel) and we will work with you to get your system supported :D \ No newline at end of file diff --git a/index.md b/index.md index c1da8bc..5460e67 100644 --- a/index.md +++ b/index.md @@ -15,15 +15,42 @@ The API implementation is tested with the official dnclient implementation, and - [tfcli documentation](./docs/tfcli/index.md) - [tfclient documentation](./docs/tfclient/index.md) - [trifid-api documentation](./docs/trifid-api/index.md) + + - [dnapi-rs documentation](https://docs.rs/dnapi-rs) - [dnapi-rs on crates.io](https://crates.io/crates/dnapi-rs) + + - [trifid-pki documentation](https://docs.rs/trifid-pki) - [trifid-pki on crates.io](https://crates.io/crates/trifid-pki) + + - [trifid git repository](https://git.e3t.cc/~core/trifid) + + - [trifid announcements mailing list](https://lists.e3t.cc/~core/trifid-announce) - [trifid patch mailing list](https://lists.e3t.cc/~core/trifid-devel) - [trifid discussion mailing list](https://lists.e3t.cc/~core/trifid-discuss) +# Where does trifid work? + +| Operating System | trifid-api? | +|-------------------------|-----------------------------------------------------------------------------------------| +| Windows amd64 | [Yes](docs/tfclient/index.md) [(untested - help wanted!)](docs/tfclient/untested_os.md) | +| Windows arm64 | [Yes](docs/tfclient/index.md) [(untested - help wanted!)](docs/tfclient/untested_os.md) | +| Darwin (macOS) | [Yes](docs/tfclient/index.md) [(untested - help wanted!)](docs/tfclient/untested_os.md) | +| FreeBSD amd64 | [Yes](docs/tfclient/index.md) [(untested - help wanted!)](docs/tfclient/untested_os.md) | +| Linux i386 | [Yes](docs/tfclient/index.md) | +| Linux amd64 | [Yes](docs/tfclient/index.md) | +| Linux armv5 | [Yes](docs/tfclient/index.md) | +| Linux armv6 | [Yes](docs/tfclient/index.md) | +| Linux armv7 | [Yes](docs/tfclient/index.md) | +| Linux aarch64 | [Yes](docs/tfclient/index.md) | +| Android | Almost [(help wanted!)](docs/trifid_mobile/help.md) | +| iOS | Almost [(help wanted!)](docs/trifid_mobile/help.md) | +| Other operating systems | No, and [likely never will be](docs/tfclient/why_not_this_os.md) | + + # Get in touch Have a question about trifid? Check out the [discussion mailing list](https://lists.e3t.cc/~core/trifid-discuss)! `trifid-discuss` is a mailing list for end-user discussion and questions related to the trifid project. diff --git a/tfcli/Cargo.toml b/tfcli/Cargo.toml index 673a357..fbebddd 100644 --- a/tfcli/Cargo.toml +++ b/tfcli/Cargo.toml @@ -1,7 +1,12 @@ [package] name = "tfcli" -version = "0.1.0" +version = "0.1.1" edition = "2021" +description = "Command-line client for managing trifid-api" +license = "GPL-3.0-or-later" +documentation = "https://git.e3t.cc/~core/trifid" +homepage = "https://git.e3t.cc/~core/trifid" +repository = "https://git.e3t.cc/~core/trifid" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -14,4 +19,4 @@ tokio = { version = "1", features = ["full"] } dirs = "5.0.1" qr2term = "0.3.1" ipnet = "2.7.2" -serde_json = "1.0.96" \ No newline at end of file +serde_json = "1.0.96" diff --git a/tfcli/src/host.rs b/tfcli/src/host.rs index 8e611ba..d550f80 100644 --- a/tfcli/src/host.rs +++ b/tfcli/src/host.rs @@ -4,7 +4,7 @@ use std::net::{Ipv4Addr, SocketAddrV4}; use serde::{Deserialize, Serialize}; use url::{Url}; use crate::api::APIErrorResponse; -use crate::{HostCommands}; +use crate::{HostCommands, HostOverrideCommands}; pub async fn host_main(command: HostCommands, server: Url) -> Result<(), Box> { match command { @@ -14,7 +14,12 @@ pub async fn host_main(command: HostCommands, server: Url) -> Result<(), Box delete_host(id, server).await, HostCommands::Update { id, listen_port, static_address, name, ip, role } => update_host(id, listen_port, static_address, name, ip, role, server).await, HostCommands::Block { id } => block_host(id, server).await, - HostCommands::Enroll { id } => enroll_host(id, server).await + HostCommands::Enroll { id } => enroll_host(id, server).await, + HostCommands::Overrides { command } => match command { + HostOverrideCommands::List { id } => list_overrides(id, server).await, + HostOverrideCommands::Set { id, key, boolean, string, numeric } => set_override(id, key, boolean, numeric, string, server).await, + HostOverrideCommands::Unset { id, key } => unset_override(id, key, server).await + } } } @@ -370,7 +375,7 @@ pub async fn enroll_host(id: String, server: Url) -> Result<(), Box> let token = format!("{} {}", session_token, auth_token); - let res = client.post(server.join(&format!("/v1/hosts/{}/enrollment-code", id))?).bearer_auth(token).send().await?; + let res = client.post(server.join(&format!("/v1/hosts/{}/enrollment-code", id))?).header("content-length", 0).bearer_auth(token).send().await?; if res.status().is_success() { let resp: EnrollmentResponse = res.json().await?; @@ -413,4 +418,223 @@ pub async fn block_host(id: String, server: Url) -> Result<(), Box> { } Ok(()) -} \ No newline at end of file +} + +#[derive(Serialize, Deserialize)] +pub struct HostConfigOverrideResponse { + pub data: HostConfigOverrideData +} + +#[derive(Serialize, Deserialize)] +pub struct HostConfigOverrideData { + pub overrides: Vec +} + +#[derive(Serialize, Deserialize)] +pub struct HostConfigOverrideDataOverride { + pub key: String, + pub value: HostConfigOverrideDataOverrideValue +} + +#[derive(Serialize, Deserialize)] +#[serde(untagged)] +pub enum HostConfigOverrideDataOverrideValue { + Boolean(bool), + Numeric(i64), + Other(String) +} + +pub async fn list_overrides(id: String, server: Url) -> Result<(), Box> { + let client = reqwest::Client::new(); + + // load session token + let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token"); + let session_token = fs::read_to_string(&sess_token_store)?; + let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token"); + let auth_token = fs::read_to_string(&auth_token_store)?; + + let token = format!("{} {}", session_token, auth_token); + + let res = client.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token).send().await?; + + if res.status().is_success() { + let resp: HostConfigOverrideResponse = res.json().await?; + + for c_override in &resp.data.overrides { + println!(" Key: {}", c_override.key); + println!("Value: {}", match &c_override.value { + HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v), + HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v), + HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v) + }); + } + + if resp.data.overrides.is_empty() { + println!("No overrides found"); + } + } else { + let resp: APIErrorResponse = res.json().await?; + + eprintln!("[error] Error looking up config overrides: {} {}", resp.errors[0].code, resp.errors[0].message); + + std::process::exit(1); + } + + Ok(()) +} + +#[derive(Serialize, Deserialize)] +pub struct SetOverrideRequest { + pub overrides: Vec +} + +pub async fn set_override(id: String, key: String, boolean: Option, numeric: Option, other: Option, server: Url) -> Result<(), Box> { + if boolean.is_none() && numeric.is_none() && other.is_none() { + eprintln!("[error] no value provided: you must provide at least --boolean, --numeric, or --string"); + std::process::exit(1); + } else if boolean.is_some() && numeric.is_some() || boolean.is_some() && other.is_some() || numeric.is_some() && other.is_some() { + eprintln!("[error] multiple values provided: you must provide only one of --boolean, --numeric, or --string"); + std::process::exit(1); + } + + let val; + + if let Some(v) = boolean { + val = HostConfigOverrideDataOverrideValue::Boolean(v); + } else if let Some(v) = numeric { + val = HostConfigOverrideDataOverrideValue::Numeric(v); + } else if let Some(v) = other { + val = HostConfigOverrideDataOverrideValue::Other(v); + } else { + unreachable!(); + } + + let client = reqwest::Client::new(); + + // load session token + let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token"); + let session_token = fs::read_to_string(&sess_token_store)?; + let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token"); + let auth_token = fs::read_to_string(&auth_token_store)?; + + let token = format!("{} {}", session_token, auth_token); + + let res = client.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).send().await?; + + if res.status().is_success() { + let resp: HostConfigOverrideResponse = res.json().await?; + + let mut others: Vec = vec![]; + + for c_override in resp.data.overrides { + if c_override.key != key { + others.push(c_override); + } + } + + others.push(HostConfigOverrideDataOverride { + key, + value: val, + }); + + let res = client.put(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).json(&SetOverrideRequest { + overrides: others, + }).send().await?; + + if res.status().is_success() { + let resp: HostConfigOverrideResponse = res.json().await?; + + for c_override in &resp.data.overrides { + println!(" Key: {}", c_override.key); + println!("Value: {}", match &c_override.value { + HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v), + HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v), + HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v) + }); + } + + if resp.data.overrides.is_empty() { + println!("No overrides found"); + } + + println!("Override set successfully"); + } else { + let resp: APIErrorResponse = res.json().await?; + + eprintln!("[error] Error setting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message); + + std::process::exit(1); + } + } else { + let resp: APIErrorResponse = res.json().await?; + + eprintln!("[error] Error setting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message); + + std::process::exit(1); + } + + Ok(()) +} + +pub async fn unset_override(id: String, key: String, server: Url) -> Result<(), Box> { + let client = reqwest::Client::new(); + + // load session token + let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token"); + let session_token = fs::read_to_string(&sess_token_store)?; + let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token"); + let auth_token = fs::read_to_string(&auth_token_store)?; + + let token = format!("{} {}", session_token, auth_token); + + let res = client.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).send().await?; + + if res.status().is_success() { + let resp: HostConfigOverrideResponse = res.json().await?; + + let mut others: Vec = vec![]; + + for c_override in resp.data.overrides { + if c_override.key != key { + others.push(c_override); + } + } + + let res = client.put(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).json(&SetOverrideRequest { + overrides: others, + }).send().await?; + + if res.status().is_success() { + let resp: HostConfigOverrideResponse = res.json().await?; + + for c_override in &resp.data.overrides { + println!(" Key: {}", c_override.key); + println!("Value: {}", match &c_override.value { + HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v), + HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v), + HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v) + }); + } + + if resp.data.overrides.is_empty() { + println!("No overrides found"); + } + + println!("Override unset successfully"); + } else { + let resp: APIErrorResponse = res.json().await?; + + eprintln!("[error] Error unsetting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message); + + std::process::exit(1); + } + } else { + let resp: APIErrorResponse = res.json().await?; + + eprintln!("[error] Error unsetting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message); + + std::process::exit(1); + } + + Ok(()) +} diff --git a/tfcli/src/main.rs b/tfcli/src/main.rs index 2c834ab..adb6529 100644 --- a/tfcli/src/main.rs +++ b/tfcli/src/main.rs @@ -203,6 +203,40 @@ pub enum HostCommands { Enroll { #[clap(short, long)] id: String + }, + /// Manage config overrides set on the host + Overrides { + #[command(subcommand)] + command: HostOverrideCommands + } +} + +#[derive(Subcommand, Debug)] +pub enum HostOverrideCommands { + /// List the config overrides set on the host + List { + #[clap(short, long)] + id: String + }, + /// Set a config override on the host + Set { + #[clap(short, long)] + id: String, + #[clap(short, long)] + key: String, + #[clap(short, long)] + boolean: Option, + #[clap(short, long)] + numeric: Option, + #[clap(short, long)] + string: Option + }, + /// Unset a config override on the host + Unset { + #[clap(short, long)] + id: String, + #[clap(short, long)] + key: String } } diff --git a/tfclient/Cargo.toml b/tfclient/Cargo.toml index 30749f8..aeb67c6 100644 --- a/tfclient/Cargo.toml +++ b/tfclient/Cargo.toml @@ -40,4 +40,4 @@ flate2 = "1.0.25" tar = "0.4.38" hex = "0.4.3" tempfile = "3.4.0" -sha2 = "0.10.6" \ No newline at end of file +sha2 = "0.10.6" diff --git a/trifid-api/Cargo.toml b/trifid-api/Cargo.toml index 711bce2..48723ea 100644 --- a/trifid-api/Cargo.toml +++ b/trifid-api/Cargo.toml @@ -1,7 +1,12 @@ [package] name = "trifid-api" -version = "0.1.0" +version = "0.1.4" edition = "2021" +description = "Pure-rust Defined Networking compatible management server" +license = "GPL-3.0-or-later" +documentation = "https://git.e3t.cc/~core/trifid" +homepage = "https://git.e3t.cc/~core/trifid" +repository = "https://git.e3t.cc/~core/trifid" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/trifid-api/src/codegen/mod.rs b/trifid-api/src/codegen/mod.rs index 101a046..1b8df99 100644 --- a/trifid-api/src/codegen/mod.rs +++ b/trifid-api/src/codegen/mod.rs @@ -17,7 +17,8 @@ use crate::AppState; use ed25519_dalek::SigningKey; use ipnet::Ipv4Net; use log::{debug, error}; -use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; +use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter}; +use serde_yaml::{Mapping, Value}; use trifid_api_entities::entity::{ firewall_rule, host, host_config_override, host_static_address, network, organization, signing_ca, @@ -156,7 +157,7 @@ pub async fn generate_config( punchy: Some(NebulaConfigPunchy { punch: true, respond: true, - delay: "".to_string(), + delay: "1s".to_string(), }), cipher: NebulaConfigCipher::Aes, preferred_ranges: vec![], @@ -191,7 +192,7 @@ pub async fn generate_config( cidr: None, }]), }), - routines: 0, + routines: 1, stats: None, local_range: None, }; @@ -203,15 +204,15 @@ pub async fn generate_config( for (key, kv_value) in &info.config_overrides { // split up the key // a.b.c.d = ['a']['b']['c']['d'] = value - let key_split = key.split('.'); + let key_split = key.split('.').collect::>(); let mut current_val = &mut value; - for key_iter in key_split { - current_val = current_val.get_mut(key_iter).ok_or("Invalid key-value override")?; + for key_iter in &key_split[..key_split.len()-1] { + current_val = current_val.as_mapping_mut().unwrap().entry(Value::String(key_iter.to_string())).or_insert(Value::Mapping(Mapping::new())); } - *current_val = serde_yaml::from_str(kv_value)?; + current_val.as_mapping_mut().unwrap().insert(Value::String(key_split[key_split.len()-1].to_string()), serde_yaml::from_str(kv_value)?); } let config_str_merged = serde_yaml::to_string(&value)?; @@ -237,12 +238,12 @@ pub async fn collect_info<'a>( }; let host_config_overrides = trifid_api_entities::entity::host_config_override::Entity::find() - .filter(host_config_override::Column::Id.eq(&host.id)) + .filter(host_config_override::Column::Host.eq(&host.id)) .all(&db.conn) .await?; let _host_static_addresses = trifid_api_entities::entity::host_static_address::Entity::find() - .filter(host_static_address::Column::Id.eq(&host.id)) + .filter(host_static_address::Column::Host.eq(&host.id)) .all(&db.conn) .await?; @@ -267,8 +268,7 @@ pub async fn collect_info<'a>( let hosts = trifid_api_entities::entity::host::Entity::find() .filter(host::Column::Network.eq(&network.id)) - .filter(host::Column::IsRelay.eq(true)) - .filter(host::Column::IsLighthouse.eq(true)) + .filter(Condition::any().add(host::Column::IsRelay.eq(true)).add(host::Column::IsLighthouse.eq(true))) .all(&db.conn) .await?; @@ -356,7 +356,7 @@ pub async fn collect_info<'a>( } else { format!("{}-{}", u.port_range_from, u.port_range_to) }), - proto: Some(u.protocol.clone()), + proto: Some(u.protocol.clone().to_lowercase()), ca_name: None, ca_sha: None, host: if u.allowed_role_id.is_some() { diff --git a/trifid-api/src/main.rs b/trifid-api/src/main.rs index 263b90d..583c122 100644 --- a/trifid-api/src/main.rs +++ b/trifid-api/src/main.rs @@ -119,6 +119,8 @@ async fn main() -> Result<(), Box> { .service(routes::v2::enroll::enroll) .service(routes::v1::dnclient::dnclient) .service(routes::v2::whoami::whoami) + .service(routes::v1::hosts::get_host_overrides) + .service(routes::v1::hosts::update_host_overrides) }) .bind(CONFIG.server.bind)? .run() diff --git a/trifid-api/src/routes/v1/dnclient.rs b/trifid-api/src/routes/v1/dnclient.rs index 07f9b5e..87cea0b 100644 --- a/trifid-api/src/routes/v1/dnclient.rs +++ b/trifid-api/src/routes/v1/dnclient.rs @@ -266,7 +266,7 @@ pub async fn dnclient( ks.signing_keys.push(KSSigningKey { id: ks.current_signing_key + 1, - key: SigningKey::generate(&mut OsRng), + key: ks.signing_keys[0].key.clone(), }); ks.current_signing_key += 1; @@ -322,7 +322,7 @@ pub async fn dnclient( let signing_key = host_in_ks .signing_keys .iter() - .find(|u| u.id == (req.counter as u64) - 1) + .find(|u| u.id == (req.counter as u64)) .unwrap(); let msg = DoUpdateResponse { diff --git a/trifid-api/src/routes/v1/hosts.rs b/trifid-api/src/routes/v1/hosts.rs index 38568fb..f6fd78e 100644 --- a/trifid-api/src/routes/v1/hosts.rs +++ b/trifid-api/src/routes/v1/hosts.rs @@ -76,7 +76,8 @@ use serde::{Deserialize, Serialize}; use std::net::{Ipv4Addr, SocketAddrV4}; use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; -use trifid_api_entities::entity::{host, host_static_address, network, organization}; +use trifid_api_entities::entity::{host, host_config_override, host_static_address, network, organization}; +use trifid_api_entities::entity::prelude::HostConfigOverride; #[derive(Serialize, Deserialize)] pub struct ListHostsRequestOpts { @@ -2328,3 +2329,495 @@ pub async fn create_host_and_enrollment_code( metadata: CreateHostAndCodeResponseMetadata {}, }) } + +#[derive(Serialize, Deserialize)] +pub struct HostConfigOverrideResponse { + pub data: HostConfigOverrideData +} + +#[derive(Serialize, Deserialize)] +pub struct HostConfigOverrideData { + pub overrides: Vec +} + +#[derive(Serialize, Deserialize)] +pub struct HostConfigOverrideDataOverride { + pub key: String, + pub value: HostConfigOverrideDataOverrideValue +} + +#[derive(Serialize, Deserialize)] +#[serde(untagged)] +pub enum HostConfigOverrideDataOverrideValue { + Boolean(bool), + Numeric(i64), + Other(String) +} + +#[get("/v1/hosts/{host_id}/config-overrides")] +pub async fn get_host_overrides(id: Path, req_info: HttpRequest, db: Data) -> HttpResponse { + // For this endpoint, you either need to be a fully authenticated user OR a token with hosts:read + let session_info = enforce_2fa(&req_info, &db.conn) + .await + .unwrap_or(TokenInfo::NotPresent); + let api_token_info = enforce_api_token(&req_info, &["hosts:read"], &db.conn) + .await + .unwrap_or(TokenInfo::NotPresent); + + // If neither are present, throw an error + if matches!(session_info, TokenInfo::NotPresent) + && matches!(api_token_info, TokenInfo::NotPresent) + { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: "This endpoint requires either a fully authenticated user or a token with the hosts:read scope".to_string(), + path: None, + } + ], + }); + } + + // If both are present, throw an error + if matches!(session_info, TokenInfo::AuthToken(_)) + && matches!(api_token_info, TokenInfo::ApiToken(_)) + { + return HttpResponse::BadRequest().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), + message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), + path: None + } + ], + }); + } + + let org_id = match api_token_info { + TokenInfo::ApiToken(tkn) => tkn.organization, + _ => { + // we have a session token, which means we have to do a db request to get the organization that this user owns + let user = match session_info { + TokenInfo::AuthToken(tkn) => tkn.session_info.user, + _ => unreachable!(), + }; + + let org = match organization::Entity::find() + .filter(organization::Column::Owner.eq(user.id)) + .one(&db.conn) + .await + { + Ok(r) => r, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error performing the database request, please try again later.".to_string(), + path: None, + } + ], + }); + } + }; + + if let Some(org) = org { + org.id + } else { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_NO_ORG".to_string(), + message: "This user does not own any organizations. Try using an API token instead.".to_string(), + path: None + } + ], + }); + } + } + }; + + let net_id; + + let net = match network::Entity::find() + .filter(network::Column::Organization.eq(&org_id)) + .one(&db.conn) + .await + { + Ok(r) => r, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error performing the database request, please try again later.".to_string(), + path: None, + } + ], + }); + } + }; + + if let Some(net) = net { + net_id = net.id; + } else { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_NO_NET".to_string(), + message: "This user does not own any networks. Try using an API token instead." + .to_string(), + path: None, + }], + }); + } + + let host = match host::Entity::find() + .filter(host::Column::Id.eq(id.into_inner())) + .one(&db.conn) + .await + { + Ok(h) => h, + Err(e) => { + error!("Database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error with the database query. Please try again later." + .to_string(), + path: None, + }], + }); + } + }; + + let host = match host { + Some(h) => h, + None => { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: + "This resource does not exist or you do not have permission to access it." + .to_string(), + path: None, + }], + }) + } + }; + + if host.network != net_id { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: "This resource does not exist or you do not have permission to access it." + .to_string(), + path: None, + }], + }); + } + + let config_overrides = match trifid_api_entities::entity::host_config_override::Entity::find().filter(host_config_override::Column::Host.eq(host.id)).all(&db.conn).await { + Ok(h) => h, + Err(e) => { + error!("Database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error with the database query. Please try again later." + .to_string(), + path: None, + }], + }); + } + }; + + let overrides: Vec = config_overrides.iter().map(|u| { + let val; + if u.value == "true" || u.value == "false" { + val = HostConfigOverrideDataOverrideValue::Boolean(u.value == "true"); + } else if u.value.chars().all(|c| c.is_numeric()) { + val = HostConfigOverrideDataOverrideValue::Numeric(u.value.parse().unwrap()); + } else { + val = HostConfigOverrideDataOverrideValue::Other(u.value.clone()); + } + HostConfigOverrideDataOverride { + key: u.key.clone(), + value: val, + } + }).collect(); + + HttpResponse::Ok().json(HostConfigOverrideResponse { + data: HostConfigOverrideData { + overrides, + }, + }) +} + +#[derive(Serialize, Deserialize)] +pub struct UpdateOverridesRequest { + pub overrides: Vec +} + +#[put("/v1/hosts/{host_id}/config-overrides")] +pub async fn update_host_overrides(id: Path, req: Json, req_info: HttpRequest, db: Data) -> HttpResponse { + // For this endpoint, you either need to be a fully authenticated user OR a token with hosts:read + let session_info = enforce_2fa(&req_info, &db.conn) + .await + .unwrap_or(TokenInfo::NotPresent); + let api_token_info = enforce_api_token(&req_info, &["hosts:read"], &db.conn) + .await + .unwrap_or(TokenInfo::NotPresent); + + // If neither are present, throw an error + if matches!(session_info, TokenInfo::NotPresent) + && matches!(api_token_info, TokenInfo::NotPresent) + { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: "This endpoint requires either a fully authenticated user or a token with the hosts:read scope".to_string(), + path: None, + } + ], + }); + } + + // If both are present, throw an error + if matches!(session_info, TokenInfo::AuthToken(_)) + && matches!(api_token_info, TokenInfo::ApiToken(_)) + { + return HttpResponse::BadRequest().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), + message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), + path: None + } + ], + }); + } + + let org_id = match api_token_info { + TokenInfo::ApiToken(tkn) => tkn.organization, + _ => { + // we have a session token, which means we have to do a db request to get the organization that this user owns + let user = match session_info { + TokenInfo::AuthToken(tkn) => tkn.session_info.user, + _ => unreachable!(), + }; + + let org = match organization::Entity::find() + .filter(organization::Column::Owner.eq(user.id)) + .one(&db.conn) + .await + { + Ok(r) => r, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error performing the database request, please try again later.".to_string(), + path: None, + } + ], + }); + } + }; + + if let Some(org) = org { + org.id + } else { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_NO_ORG".to_string(), + message: "This user does not own any organizations. Try using an API token instead.".to_string(), + path: None + } + ], + }); + } + } + }; + + let net_id; + + let net = match network::Entity::find() + .filter(network::Column::Organization.eq(&org_id)) + .one(&db.conn) + .await + { + Ok(r) => r, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error performing the database request, please try again later.".to_string(), + path: None, + } + ], + }); + } + }; + + if let Some(net) = net { + net_id = net.id; + } else { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_NO_NET".to_string(), + message: "This user does not own any networks. Try using an API token instead." + .to_string(), + path: None, + }], + }); + } + + let host = match host::Entity::find() + .filter(host::Column::Id.eq(id.into_inner())) + .one(&db.conn) + .await + { + Ok(h) => h, + Err(e) => { + error!("Database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error with the database query. Please try again later." + .to_string(), + path: None, + }], + }); + } + }; + + let host = match host { + Some(h) => h, + None => { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: + "This resource does not exist or you do not have permission to access it." + .to_string(), + path: None, + }], + }) + } + }; + + if host.network != net_id { + return HttpResponse::Unauthorized().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_UNAUTHORIZED".to_string(), + message: "This resource does not exist or you do not have permission to access it." + .to_string(), + path: None, + }], + }); + } + + let config_overrides = match trifid_api_entities::entity::host_config_override::Entity::find().filter(host_config_override::Column::Host.eq(&host.id)).all(&db.conn).await { + Ok(h) => h, + Err(e) => { + error!("Database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error with the database query. Please try again later." + .to_string(), + path: None, + }], + }); + } + }; + + for c_override in config_overrides { + match c_override.delete(&db.conn).await { + Ok(_) => (), + Err(e) => { + error!("Database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error with the database query. Please try again later." + .to_string(), + path: None, + }], + }); + } + } + } + + for c_override in &req.overrides { + let db_override = host_config_override::Model { + id: random_id("override"), + key: c_override.key.clone(), + value: match &c_override.value { + HostConfigOverrideDataOverrideValue::Boolean(v) => v.to_string(), + HostConfigOverrideDataOverrideValue::Numeric(v) => v.to_string(), + HostConfigOverrideDataOverrideValue::Other(v) => v.clone(), + }, + host: host.id.clone(), + }; + match db_override.into_active_model().insert(&db.conn).await { + Ok(_) => (), + Err(e) => { + error!("Database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error with the database query. Please try again later." + .to_string(), + path: None, + }], + }); + } + } + } + + let config_overrides = match trifid_api_entities::entity::host_config_override::Entity::find().filter(host_config_override::Column::Host.eq(&host.id)).all(&db.conn).await { + Ok(h) => h, + Err(e) => { + error!("Database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error with the database query. Please try again later." + .to_string(), + path: None, + }], + }); + } + }; + + let overrides: Vec = config_overrides.iter().map(|u| { + let val; + if u.value == "true" || u.value == "false" { + val = HostConfigOverrideDataOverrideValue::Boolean(u.value == "true"); + } else if u.value.chars().all(|c| c.is_numeric()) || u.value.starts_with('-') && u.value.chars().collect::>()[1..].iter().all(|c| c.is_numeric()) { + val = HostConfigOverrideDataOverrideValue::Numeric(u.value.parse().unwrap()); + } else { + val = HostConfigOverrideDataOverrideValue::Other(u.value.clone()); + } + HostConfigOverrideDataOverride { + key: u.key.clone(), + value: val, + } + }).collect(); + + HttpResponse::Ok().json(HostConfigOverrideResponse { + data: HostConfigOverrideData { + overrides, + }, + }) +} \ No newline at end of file diff --git a/trifid-api/trifid_api_entities/Cargo.toml b/trifid-api/trifid_api_entities/Cargo.toml index 73b20f4..141303c 100644 --- a/trifid-api/trifid_api_entities/Cargo.toml +++ b/trifid-api/trifid_api_entities/Cargo.toml @@ -2,6 +2,11 @@ name = "trifid_api_entities" version = "0.1.0" edition = "2021" +description = "Database entities for trifid-api" +license = "GPL-3.0-or-later" +documentation = "https://git.e3t.cc/~core/trifid" +homepage = "https://git.e3t.cc/~core/trifid" +repository = "https://git.e3t.cc/~core/trifid" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/trifid-api/trifid_api_migration/Cargo.toml b/trifid-api/trifid_api_migration/Cargo.toml index f090efb..b760105 100644 --- a/trifid-api/trifid_api_migration/Cargo.toml +++ b/trifid-api/trifid_api_migration/Cargo.toml @@ -2,7 +2,11 @@ name = "trifid_api_migration" version = "0.1.0" edition = "2021" -publish = false +description = "Database migrations for trifid-api" +license = "GPL-3.0-or-later" +documentation = "https://git.e3t.cc/~core/trifid" +homepage = "https://git.e3t.cc/~core/trifid" +repository = "https://git.e3t.cc/~core/trifid" [lib] name = "trifid_api_migration" diff --git a/trifid-api/trifid_data/tfks.toml b/trifid-api/trifid_data/tfks.toml deleted file mode 100644 index e527be7..0000000 --- a/trifid-api/trifid_data/tfks.toml +++ /dev/null @@ -1,361 +0,0 @@ -[[hosts]] -id = "host-IPNHZ2XBXJDY2WYOYG7709CBJ8" -current_signing_key = 1 -current_client_key = 2 -current_config = 2 -current_cert = 2 - -[[hosts.certs]] -id = 1 - -[hosts.certs.cert] -signature = [112, 198, 103, 65, 58, 33, 254, 185, 255, 1, 204, 111, 236, 234, 55, 143, 24, 27, 104, 53, 89, 106, 209, 53, 201, 35, 248, 55, 109, 120, 219, 26, 171, 234, 181, 70, 174, 177, 12, 121, 190, 67, 73, 104, 218, 2, 139, 120, 116, 174, 106, 120, 56, 162, 143, 162, 143, 199, 237, 151, 215, 129, 245, 8] - -[hosts.certs.cert.details] -name = "asd" -ips = ["10.17.2.3/15"] -subnets = [] -groups = ["role:role-A4YTNBOMCFJNK5OAKHQCUUVIL8"] -public_key = [10, 175, 118, 186, 191, 43, 172, 0, 152, 238, 83, 31, 38, 79, 189, 76, 149, 38, 157, 84, 200, 210, 0, 95, 37, 169, 196, 77, 214, 209, 91, 10] -is_ca = false -issuer = "9a4dd7cb5c3a086b0173f126bbf20b85ac7886a2129d2f8573acc2e20f09ec1f" - -[hosts.certs.cert.details.not_before] -secs_since_epoch = 1684171628 -nanos_since_epoch = 68795993 - -[hosts.certs.cert.details.not_after] -secs_since_epoch = 1716312428 -nanos_since_epoch = 68796023 - -[[hosts.certs]] -id = 2 - -[hosts.certs.cert] -signature = [134, 249, 92, 208, 133, 181, 164, 230, 242, 79, 132, 140, 164, 28, 159, 165, 55, 176, 140, 73, 208, 50, 53, 184, 178, 242, 62, 90, 55, 187, 245, 231, 22, 89, 161, 9, 181, 56, 135, 163, 93, 102, 69, 34, 51, 139, 158, 181, 5, 207, 2, 87, 100, 236, 215, 116, 109, 43, 186, 148, 200, 235, 99, 7] - -[hosts.certs.cert.details] -name = "addsd" -ips = ["10.17.2.3/15"] -subnets = [] -groups = ["role:role-A4YTNBOMCFJNK5OAKHQCUUVIL8"] -public_key = [78, 139, 195, 146, 198, 211, 251, 196, 238, 154, 134, 158, 111, 25, 198, 228, 195, 108, 242, 146, 16, 45, 98, 155, 152, 116, 114, 218, 226, 137, 182, 11] -is_ca = false -issuer = "9a4dd7cb5c3a086b0173f126bbf20b85ac7886a2129d2f8573acc2e20f09ec1f" - -[hosts.certs.cert.details.not_before] -secs_since_epoch = 1684171718 -nanos_since_epoch = 140841799 - -[hosts.certs.cert.details.not_after] -secs_since_epoch = 1716312518 -nanos_since_epoch = 140841859 - -[[hosts.config]] -id = 1 - -[hosts.config.config] -routines = 0 - -[hosts.config.config.pki] -ca = """ ------BEGIN NEBULA CERTIFICATE-----\r -Cl0KK2NvcmVAY29yZWRvZXMuZGV2J3MgT3JnYW5pemF0aW9uIFNpZ25pbmcgQ0Eo\r -y7iEowYwy+2S0AY6II2RV3kVBopKoTe3j+aT1LbZuWTR/5oQGra185GB5W63QAES\r -QGRgfmRuJOzhtWwwU4BGMo47uoncMGV41sz1NYcvwmruwhJDaYYJ51DLz3v5bYZV\r -LCxfFB661cvoq1OZ7G5ZcgY=\r ------END NEBULA CERTIFICATE-----\r -""" -cert = """ ------BEGIN NEBULA CERTIFICATE-----\r -CoYBCgNhc2QSCYOExFCAgPj/DyIkcm9sZTpyb2xlLUE0WVROQk9NQ0ZKTks1T0FL\r -SFFDVVVWSUw4KOzWiaMGMOyys7IGOiAKr3a6vyusAJjuUx8mT71MlSadVMjSAF8l\r -qcRN1tFbCkogmk3Xy1w6CGsBc/Emu/ILhax4hqISnS+Fc6zC4g8J7B8SQHDGZ0E6\r -If65/wHMb+zqN48YG2g1WWrRNckj+DdteNsaq+q1Rq6xDHm+Q0lo2gKLeHSuang4\r -oo+ij8ftl9eB9Qg=\r ------END NEBULA CERTIFICATE-----\r -""" -disconnect_invalid = true - -[hosts.config.config.lighthouse] -interval = 60 - -[hosts.config.config.listen] -host = "[::]" -read_buffer = 10485760 -write_buffer = 10485760 - -[hosts.config.config.punchy] -punch = true -respond = true -delay = "" - -[hosts.config.config.relay] - -[hosts.config.config.tun] -dev = "trifid1" -drop_local_broadcast = true -drop_multicast = true - -[hosts.config.config.firewall] -inbound = [] - -[[hosts.config.config.firewall.outbound]] -port = "any" -proto = "any" -host = "any" - -[[hosts.config]] -id = 2 - -[hosts.config.config] -routines = 0 - -[hosts.config.config.pki] -ca = """ ------BEGIN NEBULA CERTIFICATE-----\r -Cl0KK2NvcmVAY29yZWRvZXMuZGV2J3MgT3JnYW5pemF0aW9uIFNpZ25pbmcgQ0Eo\r -y7iEowYwy+2S0AY6II2RV3kVBopKoTe3j+aT1LbZuWTR/5oQGra185GB5W63QAES\r -QGRgfmRuJOzhtWwwU4BGMo47uoncMGV41sz1NYcvwmruwhJDaYYJ51DLz3v5bYZV\r -LCxfFB661cvoq1OZ7G5ZcgY=\r ------END NEBULA CERTIFICATE-----\r -""" -cert = """ ------BEGIN NEBULA CERTIFICATE-----\r -CogBCgVhZGRzZBIJg4TEUICA+P8PIiRyb2xlOnJvbGUtQTRZVE5CT01DRkpOSzVP\r -QUtIUUNVVVZJTDgoxteJowYwxrOzsgY6IE6Lw5LG0/vE7pqGnm8ZxuTDbPKSEC1i\r -m5h0ctriibYLSiCaTdfLXDoIawFz8Sa78guFrHiGohKdL4VzrMLiDwnsHxJAhvlc\r -0IW1pObyT4SMpByfpTewjEnQMjW4svI+Wje79ecWWaEJtTiHo11mRSIzi561Bc8C\r -V2Ts13RtK7qUyOtjBw==\r ------END NEBULA CERTIFICATE-----\r -""" -disconnect_invalid = true - -[hosts.config.config.lighthouse] -interval = 60 - -[hosts.config.config.listen] -host = "[::]" -read_buffer = 10485760 -write_buffer = 10485760 - -[hosts.config.config.punchy] -punch = true -respond = true -delay = "" - -[hosts.config.config.relay] - -[hosts.config.config.tun] -dev = "trifid1" -drop_local_broadcast = true -drop_multicast = true - -[hosts.config.config.firewall] -inbound = [] - -[[hosts.config.config.firewall.outbound]] -port = "any" -proto = "any" -host = "any" - -[[hosts.signing_keys]] -id = 0 -key = [108, 174, 65, 117, 166, 239, 62, 150, 81, 111, 185, 79, 158, 206, 104, 43, 163, 224, 206, 219, 147, 71, 158, 88, 103, 149, 113, 152, 123, 41, 78, 255] - -[[hosts.signing_keys]] -id = 1 -key = [119, 226, 183, 227, 53, 121, 14, 141, 125, 165, 249, 103, 28, 60, 102, 111, 242, 63, 26, 52, 87, 29, 29, 114, 11, 62, 138, 121, 213, 245, 193, 212] - -[[hosts.client_keys]] -id = 1 -dh_pub = [10, 175, 118, 186, 191, 43, 172, 0, 152, 238, 83, 31, 38, 79, 189, 76, 149, 38, 157, 84, 200, 210, 0, 95, 37, 169, 196, 77, 214, 209, 91, 10] -ed_pub = [135, 237, 110, 71, 189, 155, 246, 66, 50, 229, 80, 254, 93, 99, 35, 29, 87, 138, 132, 193, 118, 216, 218, 60, 142, 178, 42, 126, 182, 25, 31, 103] - -[[hosts.client_keys]] -id = 2 -dh_pub = [78, 139, 195, 146, 198, 211, 251, 196, 238, 154, 134, 158, 111, 25, 198, 228, 195, 108, 242, 146, 16, 45, 98, 155, 152, 116, 114, 218, 226, 137, 182, 11] -ed_pub = [178, 77, 253, 159, 81, 137, 20, 14, 184, 230, 73, 111, 130, 129, 15, 184, 114, 90, 133, 147, 178, 252, 197, 75, 82, 33, 21, 5, 38, 238, 57, 84] - -[[hosts]] -id = "host-2PXIOHLPQA3CQL8O7XD6CXMMRM" -current_signing_key = 1 -current_client_key = 2 -current_config = 2 -current_cert = 2 - -[[hosts.certs]] -id = 1 - -[hosts.certs.cert] -signature = [160, 205, 80, 112, 16, 205, 155, 249, 221, 26, 47, 128, 2, 59, 15, 102, 153, 174, 61, 35, 207, 233, 42, 242, 212, 28, 133, 40, 189, 1, 234, 67, 24, 109, 152, 248, 130, 96, 48, 104, 69, 0, 178, 30, 103, 76, 33, 179, 216, 92, 191, 89, 6, 236, 136, 216, 9, 208, 189, 16, 140, 132, 209, 2] - -[hosts.certs.cert.details] -name = "testhost4" -ips = ["10.17.4.2/15"] -subnets = [] -groups = ["role:role-A4YTNBOMCFJNK5OAKHQCUUVIL8"] -public_key = [40, 175, 28, 13, 183, 102, 108, 21, 53, 79, 113, 191, 101, 74, 77, 151, 66, 146, 250, 155, 196, 38, 178, 44, 41, 186, 71, 1, 152, 237, 245, 93] -is_ca = false -issuer = "9a4dd7cb5c3a086b0173f126bbf20b85ac7886a2129d2f8573acc2e20f09ec1f" - -[hosts.certs.cert.details.not_before] -secs_since_epoch = 1684172253 -nanos_since_epoch = 219759539 - -[hosts.certs.cert.details.not_after] -secs_since_epoch = 1716313053 -nanos_since_epoch = 219759579 - -[[hosts.certs]] -id = 2 - -[hosts.certs.cert] -signature = [54, 210, 5, 3, 189, 187, 221, 142, 238, 142, 175, 248, 12, 128, 6, 58, 99, 44, 248, 198, 51, 3, 152, 118, 113, 46, 41, 191, 138, 15, 120, 103, 170, 24, 229, 27, 241, 182, 236, 220, 51, 117, 224, 118, 191, 25, 84, 111, 100, 15, 53, 234, 132, 214, 213, 66, 95, 8, 44, 162, 212, 60, 151, 13] - -[hosts.certs.cert.details] -name = "testhost4" -ips = ["10.17.4.2/15"] -subnets = [] -groups = ["role:role-A4YTNBOMCFJNK5OAKHQCUUVIL8"] -public_key = [4, 249, 63, 6, 25, 145, 63, 132, 106, 48, 243, 192, 249, 159, 185, 160, 196, 146, 24, 7, 241, 160, 121, 122, 212, 249, 19, 213, 158, 105, 142, 86] -is_ca = false -issuer = "9a4dd7cb5c3a086b0173f126bbf20b85ac7886a2129d2f8573acc2e20f09ec1f" - -[hosts.certs.cert.details.not_before] -secs_since_epoch = 1684172313 -nanos_since_epoch = 739770378 - -[hosts.certs.cert.details.not_after] -secs_since_epoch = 1716313113 -nanos_since_epoch = 739770429 - -[[hosts.config]] -id = 1 - -[hosts.config.config] -routines = 0 - -[hosts.config.config.pki] -ca = """ ------BEGIN NEBULA CERTIFICATE-----\r -Cl0KK2NvcmVAY29yZWRvZXMuZGV2J3MgT3JnYW5pemF0aW9uIFNpZ25pbmcgQ0Eo\r -y7iEowYwy+2S0AY6II2RV3kVBopKoTe3j+aT1LbZuWTR/5oQGra185GB5W63QAES\r -QGRgfmRuJOzhtWwwU4BGMo47uoncMGV41sz1NYcvwmruwhJDaYYJ51DLz3v5bYZV\r -LCxfFB661cvoq1OZ7G5ZcgY=\r ------END NEBULA CERTIFICATE-----\r -""" -cert = """ ------BEGIN NEBULA CERTIFICATE-----\r -CowBCgl0ZXN0aG9zdDQSCYKIxFCAgPj/DyIkcm9sZTpyb2xlLUE0WVROQk9NQ0ZK\r -Tks1T0FLSFFDVVVWSUw4KN3biaMGMN23s7IGOiAorxwNt2ZsFTVPcb9lSk2XQpL6\r -m8QmsiwpukcBmO31XUogmk3Xy1w6CGsBc/Emu/ILhax4hqISnS+Fc6zC4g8J7B8S\r -QKDNUHAQzZv53RovgAI7D2aZrj0jz+kq8tQchSi9AepDGG2Y+IJgMGhFALIeZ0wh\r -s9hcv1kG7IjYCdC9EIyE0QI=\r ------END NEBULA CERTIFICATE-----\r -""" -disconnect_invalid = true - -[hosts.config.config.lighthouse] -am_lighthouse = true -interval = 60 - -[hosts.config.config.listen] -host = "[::]" -port = 5679 -read_buffer = 10485760 -write_buffer = 10485760 - -[hosts.config.config.punchy] -punch = true -respond = true -delay = "" - -[hosts.config.config.relay] - -[hosts.config.config.tun] -dev = "trifid1" -drop_local_broadcast = true -drop_multicast = true - -[hosts.config.config.firewall] -inbound = [] - -[[hosts.config.config.firewall.outbound]] -port = "any" -proto = "any" -host = "any" - -[[hosts.config]] -id = 2 - -[hosts.config.config] -routines = 0 - -[hosts.config.config.pki] -ca = """ ------BEGIN NEBULA CERTIFICATE-----\r -Cl0KK2NvcmVAY29yZWRvZXMuZGV2J3MgT3JnYW5pemF0aW9uIFNpZ25pbmcgQ0Eo\r -y7iEowYwy+2S0AY6II2RV3kVBopKoTe3j+aT1LbZuWTR/5oQGra185GB5W63QAES\r -QGRgfmRuJOzhtWwwU4BGMo47uoncMGV41sz1NYcvwmruwhJDaYYJ51DLz3v5bYZV\r -LCxfFB661cvoq1OZ7G5ZcgY=\r ------END NEBULA CERTIFICATE-----\r -""" -cert = """ ------BEGIN NEBULA CERTIFICATE-----\r -CowBCgl0ZXN0aG9zdDQSCYKIxFCAgPj/DyIkcm9sZTpyb2xlLUE0WVROQk9NQ0ZK\r -Tks1T0FLSFFDVVVWSUw4KJnciaMGMJm4s7IGOiAE+T8GGZE/hGow88D5n7mgxJIY\r -B/GgeXrU+RPVnmmOVkogmk3Xy1w6CGsBc/Emu/ILhax4hqISnS+Fc6zC4g8J7B8S\r -QDbSBQO9u92O7o6v+AyABjpjLPjGMwOYdnEuKb+KD3hnqhjlG/G27NwzdeB2vxlU\r -b2QPNeqE1tVCXwgsotQ8lw0=\r ------END NEBULA CERTIFICATE-----\r -""" -disconnect_invalid = true - -[hosts.config.config.lighthouse] -am_lighthouse = true -interval = 60 - -[hosts.config.config.listen] -host = "[::]" -port = 5677 -read_buffer = 10485760 -write_buffer = 10485760 - -[hosts.config.config.punchy] -punch = true -respond = true -delay = "" - -[hosts.config.config.relay] - -[hosts.config.config.tun] -dev = "trifid1" -drop_local_broadcast = true -drop_multicast = true - -[hosts.config.config.firewall] -inbound = [] - -[[hosts.config.config.firewall.outbound]] -port = "any" -proto = "any" -host = "any" - -[[hosts.signing_keys]] -id = 0 -key = [255, 84, 221, 121, 87, 225, 7, 12, 236, 8, 209, 175, 98, 20, 119, 146, 92, 177, 79, 121, 24, 243, 247, 113, 106, 212, 183, 155, 208, 55, 219, 135] - -[[hosts.signing_keys]] -id = 1 -key = [98, 159, 193, 58, 183, 156, 75, 17, 70, 103, 112, 6, 71, 197, 167, 152, 99, 210, 199, 40, 49, 13, 101, 72, 57, 34, 221, 237, 142, 29, 144, 175] - -[[hosts.client_keys]] -id = 1 -dh_pub = [40, 175, 28, 13, 183, 102, 108, 21, 53, 79, 113, 191, 101, 74, 77, 151, 66, 146, 250, 155, 196, 38, 178, 44, 41, 186, 71, 1, 152, 237, 245, 93] -ed_pub = [247, 172, 97, 223, 43, 24, 248, 133, 118, 219, 227, 72, 95, 25, 167, 179, 115, 225, 73, 211, 161, 216, 95, 140, 151, 59, 118, 39, 122, 136, 144, 245] - -[[hosts.client_keys]] -id = 2 -dh_pub = [4, 249, 63, 6, 25, 145, 63, 132, 106, 48, 243, 192, 249, 159, 185, 160, 196, 146, 24, 7, 241, 160, 121, 122, 212, 249, 19, 213, 158, 105, 142, 86] -ed_pub = [55, 82, 153, 75, 220, 207, 87, 221, 50, 200, 77, 9, 242, 136, 64, 91, 60, 96, 31, 100, 58, 162, 150, 147, 109, 109, 117, 188, 164, 217, 248, 140] diff --git a/trifid-api/trifiddata/tfks.toml b/trifid-api/trifiddata/tfks.toml new file mode 100644 index 0000000..05aaf7a --- /dev/null +++ b/trifid-api/trifiddata/tfks.toml @@ -0,0 +1,98 @@ +[[hosts]] +id = "host-CAJDSM36900G2MFGFNIODZUG2G" +current_signing_key = 0 +current_client_key = 1 +current_config = 1 +current_cert = 1 + +[[hosts.certs]] +id = 1 + +[hosts.certs.cert] +signature = [254, 145, 36, 202, 32, 234, 248, 2, 147, 188, 207, 151, 147, 246, 100, 22, 114, 174, 221, 91, 62, 0, 48, 23, 106, 196, 75, 27, 116, 203, 68, 41, 110, 118, 85, 76, 230, 19, 128, 163, 134, 96, 121, 9, 227, 100, 174, 173, 144, 145, 149, 79, 189, 252, 126, 4, 113, 231, 141, 69, 77, 108, 48, 4] + +[hosts.certs.cert.details] +name = "Test Host" +ips = ["10.16.1.1/15"] +subnets = [] +groups = ["role:role-IRN57I1D5L3EOI3CDDU2TNBS5W"] +public_key = [43, 106, 145, 119, 55, 49, 4, 83, 171, 182, 60, 167, 213, 135, 126, 129, 148, 82, 15, 219, 155, 146, 132, 112, 141, 147, 46, 131, 207, 175, 199, 15] +is_ca = false +issuer = "90e0d7de2b241f3cdd7005e392f5c9e45277e2735edf90602424cbec786b6f32" + +[hosts.certs.cert.details.not_before] +secs_since_epoch = 1687446533 +nanos_since_epoch = 957200472 + +[hosts.certs.cert.details.not_after] +secs_since_epoch = 1719587333 +nanos_since_epoch = 957200502 + +[[hosts.config]] +id = 1 + +[hosts.config.config.pki] +ca = """ +-----BEGIN NEBULA CERTIFICATE-----\r +Cl0KK2NvcmVAY29yZWRvZXMuZGV2J3MgT3JnYW5pemF0aW9uIFNpZ25pbmcgQ0Eo\r +sN2upAYwsJK90QY6IHetWlUyvE9ka3Q4OFRaJGeOLH98I5uqEwO0temq88RJQAES\r +QG7FI2jb0M83FUYpqX70a4DlQA4EvsyjuOiDp2gm0jn5lgaPe3rZbuYuJ114zSO4\r +9QZKdFiTpeGoZkaikJJz0ws=\r +-----END NEBULA CERTIFICATE-----\r +""" +cert = """ +-----BEGIN NEBULA CERTIFICATE-----\r +CowBCglUZXN0IEhvc3QSCYGCwFCAgPj/DyIkcm9sZTpyb2xlLUlSTjU3STFENUwz\r +RU9JM0NERFUyVE5CUzVXKIXI0aQGMIWk+7MGOiArapF3NzEEU6u2PKfVh36BlFIP\r +25uShHCNky6Dz6/HD0ogkODX3iskHzzdcAXjkvXJ5FJ34nNe35BgJCTL7HhrbzIS\r +QP6RJMog6vgCk7zPl5P2ZBZyrt1bPgAwF2rESxt0y0QpbnZVTOYTgKOGYHkJ42Su\r +rZCRlU+9/H4EceeNRU1sMAQ=\r +-----END NEBULA CERTIFICATE-----\r +""" +disconnect_invalid = true + +[hosts.config.config.lighthouse] +interval = 60 + +[hosts.config.config.listen] +host = "[::]" +read_buffer = 10485760 +write_buffer = 10485760 + +[hosts.config.config.punchy] +punch = true +respond = true + +[hosts.config.config.relay] + +[hosts.config.config.tun] +dev = "trifid1" +drop_local_broadcast = true +drop_multicast = true + +[[hosts.config.config.firewall.inbound]] +port = "any" +proto = "icmp" +host = "any" + +[[hosts.config.config.firewall.outbound]] +port = "any" +proto = "any" +host = "any" + +[hosts.config.config.stats] +type = "prometheus" +listen = "0.0.0.0:8788" +path = "/metrics" +interval = "10s" +message_metrics = true +lighthouse_metrics = true + +[[hosts.signing_keys]] +id = 0 +key = [23, 88, 206, 16, 216, 58, 12, 80, 3, 178, 254, 16, 93, 137, 109, 69, 27, 111, 30, 32, 27, 194, 171, 200, 109, 69, 29, 45, 199, 174, 119, 46] + +[[hosts.client_keys]] +id = 1 +dh_pub = [43, 106, 145, 119, 55, 49, 4, 83, 171, 182, 60, 167, 213, 135, 126, 129, 148, 82, 15, 219, 155, 146, 132, 112, 141, 147, 46, 131, 207, 175, 199, 15] +ed_pub = [62, 181, 68, 68, 230, 78, 207, 233, 92, 252, 148, 118, 38, 84, 233, 54, 98, 220, 174, 146, 240, 37, 197, 19, 254, 137, 181, 241, 240, 83, 14, 74]