733 lines
20 KiB
Rust
733 lines
20 KiB
Rust
// trifid-api, an open source reimplementation of the Defined Networking nebula management server.
|
|
// Copyright (C) 2023 c0repwn3r
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
use ipnet::{IpNet, Ipv4Net};
|
|
use log::error;
|
|
use once_cell::sync::Lazy;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
use std::error::Error;
|
|
use std::fs;
|
|
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
|
use std::path::PathBuf;
|
|
use std::time::SystemTime;
|
|
|
|
use trifid_pki::cert::deserialize_nebula_certificate_from_pem;
|
|
|
|
pub static CONFIG: Lazy<TrifidConfig> = Lazy::new(|| {
|
|
let config_str = match fs::read_to_string("/etc/trifid/config.toml") {
|
|
Ok(str) => str,
|
|
Err(e) => {
|
|
error!("Unable to read config file: {}", e);
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
match toml::from_str(&config_str) {
|
|
Ok(cfg) => cfg,
|
|
Err(e) => {
|
|
error!("Unable to parse config file: {}", e);
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
});
|
|
|
|
#[derive(Serialize, Debug, Deserialize)]
|
|
pub struct TrifidConfig {
|
|
pub database: TrifidConfigDatabase,
|
|
pub server: TrifidConfigServer,
|
|
pub tokens: TrifidConfigTokens,
|
|
pub crypto: TrifidConfigCryptography,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct TrifidConfigDatabase {
|
|
pub url: String,
|
|
#[serde(default = "max_connections_default")]
|
|
pub max_connections: u32,
|
|
#[serde(default = "min_connections_default")]
|
|
pub min_connections: u32,
|
|
#[serde(default = "time_defaults")]
|
|
pub connect_timeout: u64,
|
|
#[serde(default = "time_defaults")]
|
|
pub acquire_timeout: u64,
|
|
#[serde(default = "time_defaults")]
|
|
pub idle_timeout: u64,
|
|
#[serde(default = "time_defaults")]
|
|
pub max_lifetime: u64,
|
|
#[serde(default = "sqlx_logging_default")]
|
|
pub sqlx_logging: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct TrifidConfigServer {
|
|
#[serde(default = "socketaddr_8080")]
|
|
pub bind: SocketAddr,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct TrifidConfigTokens {
|
|
#[serde(default = "magic_link_expiry_time")]
|
|
pub magic_link_expiry_time_seconds: u64,
|
|
#[serde(default = "session_token_expiry_time")]
|
|
pub session_token_expiry_time_seconds: u64,
|
|
#[serde(default = "totp_setup_timeout_time")]
|
|
pub totp_setup_timeout_time_seconds: u64,
|
|
#[serde(default = "mfa_tokens_expiry_time")]
|
|
pub mfa_tokens_expiry_time_seconds: u64,
|
|
#[serde(default = "enrollment_tokens_expiry_time")]
|
|
pub enrollment_tokens_expiry_time: u64,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
pub struct TrifidConfigCryptography {
|
|
pub data_encryption_key: String,
|
|
pub local_keystore_directory: PathBuf,
|
|
#[serde(default = "certs_expiry_time")]
|
|
pub certs_expiry_time: u64,
|
|
}
|
|
|
|
fn max_connections_default() -> u32 {
|
|
100
|
|
}
|
|
fn min_connections_default() -> u32 {
|
|
5
|
|
}
|
|
fn time_defaults() -> u64 {
|
|
8
|
|
}
|
|
fn sqlx_logging_default() -> bool {
|
|
true
|
|
}
|
|
fn socketaddr_8080() -> SocketAddr {
|
|
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from([0, 0, 0, 0]), 8080))
|
|
}
|
|
fn magic_link_expiry_time() -> u64 {
|
|
3600
|
|
} // 1 hour
|
|
fn session_token_expiry_time() -> u64 {
|
|
15780000
|
|
} // 6 months
|
|
fn totp_setup_timeout_time() -> u64 {
|
|
600
|
|
} // 10 minutes
|
|
fn mfa_tokens_expiry_time() -> u64 {
|
|
600
|
|
} // 10 minutes
|
|
fn enrollment_tokens_expiry_time() -> u64 {
|
|
600
|
|
} // 10 minutes
|
|
fn certs_expiry_time() -> u64 {
|
|
3600 * 24 * 31 * 12 // 1 year
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfig {
|
|
pub pki: NebulaConfigPki,
|
|
#[serde(default = "empty_hashmap")]
|
|
#[serde(skip_serializing_if = "is_empty_hashmap")]
|
|
pub static_host_map: HashMap<Ipv4Addr, Vec<SocketAddrV4>>,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub lighthouse: Option<NebulaConfigLighthouse>,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub listen: Option<NebulaConfigListen>,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub punchy: Option<NebulaConfigPunchy>,
|
|
#[serde(default = "cipher_aes")]
|
|
#[serde(skip_serializing_if = "is_cipher_aes")]
|
|
pub cipher: NebulaConfigCipher,
|
|
#[serde(default = "empty_vec")]
|
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
|
pub preferred_ranges: Vec<IpNet>,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub relay: Option<NebulaConfigRelay>,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub tun: Option<NebulaConfigTun>,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub logging: Option<NebulaConfigLogging>,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub sshd: Option<NebulaConfigSshd>,
|
|
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub firewall: Option<NebulaConfigFirewall>,
|
|
|
|
#[serde(default = "u64_1")]
|
|
#[serde(skip_serializing_if = "is_u64_1")]
|
|
pub routines: u64,
|
|
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub stats: Option<NebulaConfigStats>,
|
|
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub local_range: Option<Ipv4Net>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
pub struct NebulaConfigPki {
|
|
pub ca: String,
|
|
pub cert: String,
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub key: Option<String>,
|
|
#[serde(default = "empty_vec")]
|
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
|
pub blocklist: Vec<String>,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub disconnect_invalid: bool,
|
|
}
|
|
|
|
impl PartialEq for NebulaConfigPki {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
if self.ca != other.ca {
|
|
return false;
|
|
}
|
|
if self.key != other.key {
|
|
return false;
|
|
}
|
|
if self.blocklist != other.blocklist {
|
|
return false;
|
|
}
|
|
if self.disconnect_invalid != other.disconnect_invalid {
|
|
return false;
|
|
}
|
|
|
|
// cert logic
|
|
// if the cert is invalid, fallback to just checking equality
|
|
match is_cert_equal_ignoring_expiry(&self.cert, &other.cert) {
|
|
Ok(res) => res,
|
|
Err(_) => self.cert == other.cert,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn is_cert_equal_ignoring_expiry(me: &str, other: &str) -> Result<bool, Box<dyn Error>> {
|
|
// determines if the certificates are equal, ignoring not_before, not_after and the signature
|
|
// exception: if either certificate is expired, not_before and not_after will be checked anyway
|
|
|
|
// parse cert A
|
|
let cert_a = deserialize_nebula_certificate_from_pem(me.as_bytes())?;
|
|
let cert_b = deserialize_nebula_certificate_from_pem(other.as_bytes())?;
|
|
|
|
if cert_a.details.is_ca != cert_b.details.is_ca {
|
|
return Ok(false);
|
|
}
|
|
if cert_a.details.name != cert_b.details.name {
|
|
return Ok(false);
|
|
}
|
|
if cert_a.details.public_key != cert_b.details.public_key {
|
|
return Ok(false);
|
|
}
|
|
if cert_a.details.groups != cert_b.details.groups {
|
|
return Ok(false);
|
|
}
|
|
if cert_a.details.ips != cert_b.details.ips {
|
|
return Ok(false);
|
|
}
|
|
if cert_a.details.issuer != cert_b.details.issuer {
|
|
return Ok(false);
|
|
}
|
|
if cert_a.details.subnets != cert_b.details.subnets {
|
|
return Ok(false);
|
|
}
|
|
|
|
if cert_a.expired(SystemTime::now()) || cert_b.expired(SystemTime::now()) {
|
|
if cert_a.details.not_before != cert_b.details.not_before {
|
|
return Ok(false);
|
|
}
|
|
if cert_a.details.not_after != cert_b.details.not_after {
|
|
return Ok(false);
|
|
}
|
|
if cert_a.signature != cert_b.signature {
|
|
return Ok(false);
|
|
}
|
|
}
|
|
|
|
Ok(true)
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigLighthouse {
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub am_lighthouse: bool,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub serve_dns: bool,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub dns: Option<NebulaConfigLighthouseDns>,
|
|
#[serde(default = "u32_10")]
|
|
#[serde(skip_serializing_if = "is_u32_10")]
|
|
pub interval: u32,
|
|
#[serde(default = "empty_vec")]
|
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
|
pub hosts: Vec<Ipv4Addr>,
|
|
#[serde(default = "empty_hashmap")]
|
|
#[serde(skip_serializing_if = "is_empty_hashmap")]
|
|
pub remote_allow_list: HashMap<Ipv4Net, bool>,
|
|
#[serde(default = "empty_hashmap")]
|
|
#[serde(skip_serializing_if = "is_empty_hashmap")]
|
|
pub local_allow_list: HashMap<Ipv4Net, bool>, // `interfaces` is not supported
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigLighthouseDns {
|
|
#[serde(default = "string_empty")]
|
|
#[serde(skip_serializing_if = "is_string_empty")]
|
|
pub host: String,
|
|
#[serde(default = "u16_53")]
|
|
#[serde(skip_serializing_if = "is_u16_53")]
|
|
pub port: u16,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigListen {
|
|
#[serde(default = "string_empty")]
|
|
#[serde(skip_serializing_if = "is_string_empty")]
|
|
pub host: String,
|
|
#[serde(default = "u16_0")]
|
|
#[serde(skip_serializing_if = "is_u16_0")]
|
|
pub port: u16,
|
|
#[serde(default = "u32_64")]
|
|
#[serde(skip_serializing_if = "is_u32_64")]
|
|
pub batch: u32,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub read_buffer: Option<u32>,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub write_buffer: Option<u32>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigPunchy {
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub punch: bool,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub respond: bool,
|
|
#[serde(default = "string_1s")]
|
|
#[serde(skip_serializing_if = "is_string_1s")]
|
|
pub delay: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub enum NebulaConfigCipher {
|
|
#[serde(rename = "aes")]
|
|
Aes,
|
|
#[serde(rename = "chachapoly")]
|
|
ChaChaPoly,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigRelay {
|
|
#[serde(default = "empty_vec")]
|
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
|
pub relays: Vec<Ipv4Addr>,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub am_relay: bool,
|
|
#[serde(default = "bool_true")]
|
|
#[serde(skip_serializing_if = "is_bool_true")]
|
|
pub use_relays: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigTun {
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub disabled: bool,
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub dev: Option<String>,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub drop_local_broadcast: bool,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub drop_multicast: bool,
|
|
#[serde(default = "u64_500")]
|
|
#[serde(skip_serializing_if = "is_u64_500")]
|
|
pub tx_queue: u64,
|
|
#[serde(default = "u64_1300")]
|
|
#[serde(skip_serializing_if = "is_u64_1300")]
|
|
pub mtu: u64,
|
|
#[serde(default = "empty_vec")]
|
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
|
pub routes: Vec<NebulaConfigTunRouteOverride>,
|
|
#[serde(default = "empty_vec")]
|
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
|
pub unsafe_routes: Vec<NebulaConfigTunUnsafeRoute>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigTunRouteOverride {
|
|
pub mtu: u64,
|
|
pub route: Ipv4Net,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigTunUnsafeRoute {
|
|
pub route: Ipv4Net,
|
|
pub via: Ipv4Addr,
|
|
#[serde(default = "u64_1300")]
|
|
#[serde(skip_serializing_if = "is_u64_1300")]
|
|
pub mtu: u64,
|
|
#[serde(default = "i64_100")]
|
|
#[serde(skip_serializing_if = "is_i64_100")]
|
|
pub metric: i64,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigLogging {
|
|
#[serde(default = "loglevel_info")]
|
|
#[serde(skip_serializing_if = "is_loglevel_info")]
|
|
pub level: NebulaConfigLoggingLevel,
|
|
#[serde(default = "format_text")]
|
|
#[serde(skip_serializing_if = "is_format_text")]
|
|
pub format: NebulaConfigLoggingFormat,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub disable_timestamp: bool,
|
|
#[serde(default = "timestamp")]
|
|
#[serde(skip_serializing_if = "is_timestamp")]
|
|
pub timestamp_format: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub enum NebulaConfigLoggingLevel {
|
|
#[serde(rename = "panic")]
|
|
Panic,
|
|
#[serde(rename = "fatal")]
|
|
Fatal,
|
|
#[serde(rename = "error")]
|
|
Error,
|
|
#[serde(rename = "warning")]
|
|
Warning,
|
|
#[serde(rename = "info")]
|
|
Info,
|
|
#[serde(rename = "debug")]
|
|
Debug,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub enum NebulaConfigLoggingFormat {
|
|
#[serde(rename = "json")]
|
|
Json,
|
|
#[serde(rename = "text")]
|
|
Text,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigSshd {
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub enabled: bool,
|
|
pub listen: SocketAddrV4,
|
|
pub host_key: String,
|
|
#[serde(default = "empty_vec")]
|
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
|
pub authorized_users: Vec<NebulaConfigSshdAuthorizedUser>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigSshdAuthorizedUser {
|
|
pub user: String,
|
|
#[serde(default = "empty_vec")]
|
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
|
pub keys: Vec<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
#[serde(tag = "type")]
|
|
pub enum NebulaConfigStats {
|
|
#[serde(rename = "graphite")]
|
|
Graphite(NebulaConfigStatsGraphite),
|
|
#[serde(rename = "prometheus")]
|
|
Prometheus(NebulaConfigStatsPrometheus),
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigStatsGraphite {
|
|
#[serde(default = "string_nebula")]
|
|
#[serde(skip_serializing_if = "is_string_nebula")]
|
|
pub prefix: String,
|
|
#[serde(default = "protocol_tcp")]
|
|
#[serde(skip_serializing_if = "is_protocol_tcp")]
|
|
pub protocol: NebulaConfigStatsGraphiteProtocol,
|
|
pub host: SocketAddrV4,
|
|
pub interval: String,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub message_metrics: bool,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub lighthouse_metrics: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub enum NebulaConfigStatsGraphiteProtocol {
|
|
#[serde(rename = "tcp")]
|
|
Tcp,
|
|
#[serde(rename = "udp")]
|
|
Udp,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigStatsPrometheus {
|
|
pub listen: String,
|
|
pub path: String,
|
|
#[serde(default = "string_nebula")]
|
|
#[serde(skip_serializing_if = "is_string_nebula")]
|
|
pub namespace: String,
|
|
#[serde(default = "string_nebula")]
|
|
#[serde(skip_serializing_if = "is_string_nebula")]
|
|
pub subsystem: String,
|
|
pub interval: String,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub message_metrics: bool,
|
|
#[serde(default = "bool_false")]
|
|
#[serde(skip_serializing_if = "is_bool_false")]
|
|
pub lighthouse_metrics: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigFirewall {
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub conntrack: Option<NebulaConfigFirewallConntrack>,
|
|
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub inbound: Option<Vec<NebulaConfigFirewallRule>>,
|
|
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub outbound: Option<Vec<NebulaConfigFirewallRule>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigFirewallConntrack {
|
|
#[serde(default = "string_12m")]
|
|
#[serde(skip_serializing_if = "is_string_12m")]
|
|
pub tcp_timeout: String,
|
|
#[serde(default = "string_3m")]
|
|
#[serde(skip_serializing_if = "is_string_3m")]
|
|
pub udp_timeout: String,
|
|
#[serde(default = "string_10m")]
|
|
#[serde(skip_serializing_if = "is_string_10m")]
|
|
pub default_timeout: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
pub struct NebulaConfigFirewallRule {
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub port: Option<String>,
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub proto: Option<String>,
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub ca_name: Option<String>,
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub ca_sha: Option<String>,
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub host: Option<String>,
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub group: Option<String>,
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub groups: Option<Vec<String>>,
|
|
#[serde(default = "none")]
|
|
#[serde(skip_serializing_if = "is_none")]
|
|
pub cidr: Option<String>,
|
|
}
|
|
|
|
// Default values for serde
|
|
|
|
fn string_12m() -> String {
|
|
"12m".to_string()
|
|
}
|
|
fn is_string_12m(s: &str) -> bool {
|
|
s == "12m"
|
|
}
|
|
|
|
fn string_3m() -> String {
|
|
"3m".to_string()
|
|
}
|
|
fn is_string_3m(s: &str) -> bool {
|
|
s == "3m"
|
|
}
|
|
|
|
fn string_10m() -> String {
|
|
"10m".to_string()
|
|
}
|
|
fn is_string_10m(s: &str) -> bool {
|
|
s == "10m"
|
|
}
|
|
|
|
fn empty_vec<T>() -> Vec<T> {
|
|
vec![]
|
|
}
|
|
fn is_empty_vec<T>(v: &Vec<T>) -> bool {
|
|
v.is_empty()
|
|
}
|
|
|
|
fn empty_hashmap<A, B>() -> HashMap<A, B> {
|
|
HashMap::new()
|
|
}
|
|
fn is_empty_hashmap<A, B>(h: &HashMap<A, B>) -> bool {
|
|
h.is_empty()
|
|
}
|
|
|
|
fn bool_false() -> bool {
|
|
false
|
|
}
|
|
fn is_bool_false(b: &bool) -> bool {
|
|
!*b
|
|
}
|
|
|
|
fn bool_true() -> bool {
|
|
true
|
|
}
|
|
fn is_bool_true(b: &bool) -> bool {
|
|
*b
|
|
}
|
|
|
|
fn u16_53() -> u16 {
|
|
53
|
|
}
|
|
fn is_u16_53(u: &u16) -> bool {
|
|
*u == 53
|
|
}
|
|
|
|
fn u32_10() -> u32 {
|
|
10
|
|
}
|
|
fn is_u32_10(u: &u32) -> bool {
|
|
*u == 10
|
|
}
|
|
|
|
fn u16_0() -> u16 {
|
|
0
|
|
}
|
|
fn is_u16_0(u: &u16) -> bool {
|
|
*u == 0
|
|
}
|
|
|
|
fn u32_64() -> u32 {
|
|
64
|
|
}
|
|
fn is_u32_64(u: &u32) -> bool {
|
|
*u == 64
|
|
}
|
|
|
|
fn string_1s() -> String {
|
|
"1s".to_string()
|
|
}
|
|
fn is_string_1s(s: &str) -> bool {
|
|
s == "1s"
|
|
}
|
|
|
|
fn cipher_aes() -> NebulaConfigCipher {
|
|
NebulaConfigCipher::Aes
|
|
}
|
|
fn is_cipher_aes(c: &NebulaConfigCipher) -> bool {
|
|
matches!(c, NebulaConfigCipher::Aes)
|
|
}
|
|
|
|
fn u64_500() -> u64 {
|
|
500
|
|
}
|
|
fn is_u64_500(u: &u64) -> bool {
|
|
*u == 500
|
|
}
|
|
|
|
fn u64_1300() -> u64 {
|
|
1300
|
|
}
|
|
fn is_u64_1300(u: &u64) -> bool {
|
|
*u == 1300
|
|
}
|
|
|
|
fn i64_100() -> i64 {
|
|
100
|
|
}
|
|
fn is_i64_100(i: &i64) -> bool {
|
|
*i == 100
|
|
}
|
|
|
|
fn loglevel_info() -> NebulaConfigLoggingLevel {
|
|
NebulaConfigLoggingLevel::Info
|
|
}
|
|
fn is_loglevel_info(l: &NebulaConfigLoggingLevel) -> bool {
|
|
matches!(l, NebulaConfigLoggingLevel::Info)
|
|
}
|
|
|
|
fn format_text() -> NebulaConfigLoggingFormat {
|
|
NebulaConfigLoggingFormat::Text
|
|
}
|
|
fn is_format_text(f: &NebulaConfigLoggingFormat) -> bool {
|
|
matches!(f, NebulaConfigLoggingFormat::Text)
|
|
}
|
|
|
|
fn timestamp() -> String {
|
|
"2006-01-02T15:04:05Z07:00".to_string()
|
|
}
|
|
fn is_timestamp(s: &str) -> bool {
|
|
s == "2006-01-02T15:04:05Z07:00"
|
|
}
|
|
|
|
fn u64_1() -> u64 {
|
|
1
|
|
}
|
|
fn is_u64_1(u: &u64) -> bool {
|
|
*u == 1
|
|
}
|
|
|
|
fn string_nebula() -> String {
|
|
"nebula".to_string()
|
|
}
|
|
fn is_string_nebula(s: &str) -> bool {
|
|
s == "nebula"
|
|
}
|
|
|
|
fn string_empty() -> String {
|
|
String::new()
|
|
}
|
|
fn is_string_empty(s: &str) -> bool {
|
|
s.is_empty()
|
|
}
|
|
|
|
fn protocol_tcp() -> NebulaConfigStatsGraphiteProtocol {
|
|
NebulaConfigStatsGraphiteProtocol::Tcp
|
|
}
|
|
fn is_protocol_tcp(p: &NebulaConfigStatsGraphiteProtocol) -> bool {
|
|
matches!(p, NebulaConfigStatsGraphiteProtocol::Tcp)
|
|
}
|
|
|
|
fn none<T>() -> Option<T> {
|
|
None
|
|
}
|
|
fn is_none<T>(o: &Option<T>) -> bool {
|
|
o.is_none()
|
|
}
|