This commit is contained in:
c0repwn3r 2023-06-25 22:36:18 -04:00
commit 3bdf4bc1e1
Signed by: core
GPG Key ID: FDBF740DADDCEECF
20 changed files with 977 additions and 531 deletions

160
Cargo.lock generated
View File

@ -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"

9
Dockerfile Normal file
View File

@ -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"]

View File

@ -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).

3
docs/tfclient/index.md Normal file
View File

@ -0,0 +1,3 @@
# tfclient - a Rust DNClient alternative
TODO

View File

@ -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!
<div class="alert alert-warning">
<strong>Notice</strong>: While tfclient is almost always perfectly safe, there are always risks to running untested software on production machines. Be careful out there!
</div>
Any findings, positive or negative, can be posted straight to [trifid-devel](https://lists.e3t.cc/~core/trifid-devel).
Thanks!
[Return home](index.md)

View File

@ -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

View File

@ -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.

View File

@ -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"
serde_json = "1.0.96"

View File

@ -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<dyn Error>> {
match command {
@ -14,7 +14,12 @@ pub async fn host_main(command: HostCommands, server: Url) -> Result<(), Box<dyn
HostCommands::Delete { id } => 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<dyn Error>>
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<dyn Error>> {
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub struct HostConfigOverrideResponse {
pub data: HostConfigOverrideData
}
#[derive(Serialize, Deserialize)]
pub struct HostConfigOverrideData {
pub overrides: Vec<HostConfigOverrideDataOverride>
}
#[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<dyn Error>> {
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<HostConfigOverrideDataOverride>
}
pub async fn set_override(id: String, key: String, boolean: Option<bool>, numeric: Option<i64>, other: Option<String>, server: Url) -> Result<(), Box<dyn Error>> {
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<HostConfigOverrideDataOverride> = 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<dyn Error>> {
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<HostConfigOverrideDataOverride> = 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(())
}

View File

@ -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<bool>,
#[clap(short, long)]
numeric: Option<i64>,
#[clap(short, long)]
string: Option<String>
},
/// Unset a config override on the host
Unset {
#[clap(short, long)]
id: String,
#[clap(short, long)]
key: String
}
}

View File

@ -40,4 +40,4 @@ flate2 = "1.0.25"
tar = "0.4.38"
hex = "0.4.3"
tempfile = "3.4.0"
sha2 = "0.10.6"
sha2 = "0.10.6"

View File

@ -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

View File

@ -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::<Vec<_>>();
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() {

View File

@ -119,6 +119,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
.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()

View File

@ -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 {

View File

@ -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<HostConfigOverrideDataOverride>
}
#[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<String>, req_info: HttpRequest, db: Data<AppState>) -> 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<HostConfigOverrideDataOverride> = 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<HostConfigOverrideDataOverride>
}
#[put("/v1/hosts/{host_id}/config-overrides")]
pub async fn update_host_overrides(id: Path<String>, req: Json<UpdateOverridesRequest>, req_info: HttpRequest, db: Data<AppState>) -> 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<HostConfigOverrideDataOverride> = 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::<Vec<_>>()[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,
},
})
}

View File

@ -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

View File

@ -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"

View File

@ -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]

View File

@ -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]