// Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only use super::DEFAULT_NYMNODE_ID; use crate::config; use crate::config::default_config_filepath; use crate::env::vars::*; use celes::Country; use clap::builder::ArgPredicate; use clap::Args; use std::net::{IpAddr, SocketAddr}; use std::path::{Path, PathBuf}; use url::Url; use zeroize::{Zeroize, ZeroizeOnDrop}; #[derive(Args, Debug)] pub(crate) struct ConfigArgs { /// Id of the nym-node to use #[clap( long, default_value = DEFAULT_NYMNODE_ID, default_value_if("config_file", ArgPredicate::IsPresent, None), env = NYMNODE_ID_ARG, group = "config" )] id: Option, /// Path to a configuration file of this node. #[clap( long, env = NYMNODE_CONFIG_PATH_ARG, group = "config" )] config_file: Option, } impl ConfigArgs { pub(crate) fn id(&self) -> &Option { &self.id } pub(crate) fn config_path(&self) -> PathBuf { // SAFETY: // if `config_file` hasn't been specified, `id` will default to "DEFAULT_NYMNODE_ID", // so some value will always be available to use #[allow(clippy::unwrap_used)] self.config_file .clone() .unwrap_or_else(|| default_config_filepath(self.id.as_ref().unwrap())) } } #[derive(clap::Args, Debug)] pub(crate) struct HostArgs { /// Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. /// In nearly all circumstances, it's going to be identical to the address you're going to use for bonding. #[clap( long, value_delimiter = ',', env = NYMNODE_PUBLIC_IPS_ARG )] pub(crate) public_ips: Option>, /// Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients #[clap( long, env = NYMNODE_HOSTNAME_ARG )] pub(crate) hostname: Option, /// Optional **physical** location of this node's server. /// Either full country name (e.g. 'Poland'), two-letter alpha2 (e.g. 'PL'), /// three-letter alpha3 (e.g. 'POL') or three-digit numeric-3 (e.g. '616') can be provided. #[clap( long, env = NYMNODE_LOCATION_ARG )] pub(crate) location: Option, } impl HostArgs { // TODO: could we perhaps make a clap error here and call `safe_exit` instead? pub(crate) fn build_config_section(self) -> config::Host { self.override_config_section(config::Host::default()) } pub(crate) fn override_config_section(self, mut section: config::Host) -> config::Host { if let Some(public_ips) = self.public_ips { section.public_ips = public_ips } if let Some(hostname) = self.hostname { section.hostname = Some(hostname) } if let Some(location) = self.location { section.location = Some(location) } section } } #[derive(clap::Args, Debug)] pub(crate) struct HttpArgs { /// Socket address this node will use for binding its http API. /// default: `[::]:8080` #[clap( long, env = NYMNODE_HTTP_BIND_ADDRESS_ARG )] pub(crate) http_bind_address: Option, /// Path to assets directory of custom landing page of this node. #[clap( long, env = NYMNODE_HTTP_LANDING_ASSETS_ARG )] pub(crate) landing_page_assets_path: Option, /// An optional bearer token for accessing certain http endpoints. /// Currently only used for prometheus metrics. #[clap( long, env = NYMNODE_HTTP_ACCESS_TOKEN_ARG, alias = "http-bearer-token" )] pub(crate) http_access_token: Option, /// Specify whether basic system information should be exposed. /// default: true #[clap( long, env = NYMNODE_HTTP_EXPOSE_SYSTEM_INFO_ARG, )] pub(crate) expose_system_info: Option, /// Specify whether basic system hardware information should be exposed. /// default: true #[clap( long, env = NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE_ARG )] pub(crate) expose_system_hardware: Option, /// Specify whether detailed system crypto hardware information should be exposed. /// default: true #[clap( long, env = NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE_ARG )] pub(crate) expose_crypto_hardware: Option, } impl HttpArgs { // TODO: could we perhaps make a clap error here and call `safe_exit` instead? pub(crate) fn build_config_section(self) -> config::Http { self.override_config_section(config::Http::default()) } pub(crate) fn override_config_section(self, mut section: config::Http) -> config::Http { if let Some(bind_address) = self.http_bind_address { section.bind_address = bind_address } if let Some(landing_page_assets_path) = self.landing_page_assets_path { section.landing_page_assets_path = Some(landing_page_assets_path) } if let Some(access_token) = self.http_access_token { section.access_token = Some(access_token) } if let Some(expose_system_info) = self.expose_system_info { section.expose_system_info = expose_system_info } if let Some(expose_hardware_info) = self.expose_system_hardware { section.expose_system_hardware = expose_hardware_info } if let Some(expose_crypto_hardware) = self.expose_crypto_hardware { section.expose_crypto_hardware = expose_crypto_hardware } section } } #[derive(clap::Args, Debug)] pub(crate) struct MixnetArgs { /// Address this node will bind to for listening for mixnet packets /// default: `[::]:1789` #[clap( long, env = NYMNODE_MIXNET_BIND_ADDRESS_ARG )] pub(crate) mixnet_bind_address: Option, /// If applicable, custom port announced in the self-described API that other clients and nodes /// will use. /// Useful when the node is behind a proxy. #[clap( long, env = NYMNODE_MIXNET_ANNOUNCE_PORT_ARG )] pub(crate) mixnet_announce_port: Option, /// Addresses to nym APIs from which the node gets the view of the network. #[clap( long, value_delimiter = ',', env = NYMNODE_NYM_APIS_ARG )] pub(crate) nym_api_urls: Option>, /// Addresses to nyxd chain endpoint which the node will use for chain interactions. #[clap( long, value_delimiter = ',', env = NYMNODE_NYXD_URLS_ARG )] pub(crate) nyxd_urls: Option>, /// Specifies whether this node should **NOT** use noise protocol in the connections (currently not implemented) #[clap( hide = true, long, env = NYMNODE_UNSAFE_DISABLE_NOISE )] pub(crate) unsafe_disable_noise: bool, } impl MixnetArgs { // TODO: could we perhaps make a clap error here and call `safe_exit` instead? pub(crate) fn build_config_section(self) -> config::Mixnet { self.override_config_section(config::Mixnet::default()) } pub(crate) fn override_config_section(self, mut section: config::Mixnet) -> config::Mixnet { if let Some(bind_address) = self.mixnet_bind_address { section.bind_address = bind_address } if let Some(mixnet_announce_port) = self.mixnet_announce_port { section.announce_port = Some(mixnet_announce_port) } if let Some(nym_api_urls) = self.nym_api_urls { section.nym_api_urls = nym_api_urls } if let Some(nyxd_urls) = self.nyxd_urls { section.nyxd_urls = nyxd_urls } if self.unsafe_disable_noise { section.debug.unsafe_disable_noise = true } section } } #[derive(clap::Args, Debug)] pub(crate) struct WireguardArgs { /// Specifies whether the wireguard service is enabled on this node. #[clap( long, env = NYMNODE_WG_ENABLED_ARG )] pub(crate) wireguard_enabled: Option, /// Socket address this node will use for binding its wireguard interface. /// default: `[::]:51822` #[clap( long, env = NYMNODE_WG_BIND_ADDRESS_ARG )] pub(crate) wireguard_bind_address: Option, /// Port announced to external clients wishing to connect to the wireguard interface. /// Useful in the instances where the node is behind a proxy. #[clap( long, env = NYMNODE_WG_ANNOUNCED_PORT_ARG )] pub(crate) wireguard_announced_port: Option, /// The prefix denoting the maximum number of the clients that can be connected via Wireguard. /// The maximum value for IPv4 is 32 and for IPv6 is 128 #[clap( long, env = NYMNODE_WG_PRIVATE_NETWORK_PREFIX_ARG )] pub(crate) wireguard_private_network_prefix: Option, } impl WireguardArgs { // TODO: could we perhaps make a clap error here and call `safe_exit` instead? pub(crate) fn build_config_section>(self, data_dir: P) -> config::Wireguard { self.override_config_section(config::Wireguard::new_default(data_dir)) } pub(crate) fn override_config_section( self, mut section: config::Wireguard, ) -> config::Wireguard { if let Some(enabled) = self.wireguard_enabled { section.enabled = enabled } if let Some(bind_address) = self.wireguard_bind_address { section.bind_address = bind_address } if let Some(announced_port) = self.wireguard_announced_port { section.announced_port = announced_port } if let Some(private_network_prefix) = self.wireguard_private_network_prefix { section.private_network_prefix_v4 = private_network_prefix } section } } #[derive(clap::Args, Debug)] pub(crate) struct VerlocArgs { /// Socket address this node will use for binding its verloc API. /// default: `[::]:1790` #[clap( long, env = NYMNODE_VERLOC_BIND_ADDRESS_ARG )] pub(crate) verloc_bind_address: Option, /// If applicable, custom port announced in the self-described API that other clients and nodes /// will use. /// Useful when the node is behind a proxy. #[clap( long, env = NYMNODE_VERLOC_ANNOUNCE_PORT_ARG )] pub(crate) verloc_announce_port: Option, } impl VerlocArgs { pub(crate) fn build_config_section(self) -> config::Verloc { self.override_config_section(config::Verloc::default()) } pub(crate) fn override_config_section(self, mut section: config::Verloc) -> config::Verloc { if let Some(bind_address) = self.verloc_bind_address { section.bind_address = bind_address } if let Some(announce_port) = self.verloc_announce_port { section.announce_port = Some(announce_port) } section } } #[derive(clap::Args, Debug)] pub(crate) struct MetricsArgs { /// Specify whether running statistics of this node should be logged to the console. #[clap( long, env = NYMNODE_ENABLE_CONSOLE_LOGGING )] enable_console_logging: Option, } impl MetricsArgs { pub(crate) fn build_config_section(self) -> config::MetricsConfig { self.override_config_section(config::MetricsConfig::default()) } pub(crate) fn override_config_section( self, mut section: config::MetricsConfig, ) -> config::MetricsConfig { if let Some(enable_console_logging) = self.enable_console_logging { section.debug.log_stats_to_console = enable_console_logging; } section } } #[derive(clap::Args, Debug, Zeroize, ZeroizeOnDrop)] pub(crate) struct EntryGatewayArgs { /// Socket address this node will use for binding its client websocket API. /// default: `[::]:9000` #[clap( long, env = NYMNODE_ENTRY_BIND_ADDRESS_ARG )] #[zeroize(skip)] pub(crate) entry_bind_address: Option, /// Custom announced port for listening for websocket client traffic. /// If unspecified, the value from the `bind_address` will be used instead #[clap( long, env = NYMNODE_ENTRY_ANNOUNCE_WS_PORT_ARG )] pub(crate) announce_ws_port: Option, /// If applicable, announced port for listening for secure websocket client traffic. #[clap( long, env = NYMNODE_ENTRY_ANNOUNCE_WSS_PORT_ARG )] pub(crate) announce_wss_port: Option, /// Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet /// or if it also accepts non-paying clients #[clap( long, env = NYMNODE_ENFORCE_ZK_NYMS_ARG )] pub(crate) enforce_zk_nyms: Option, /// Custom cosmos wallet mnemonic used for zk-nym redemption. /// If no value is provided, a fresh mnemonic is going to be generated. #[clap( long, env = NYMNODE_MNEMONIC_ARG )] pub(crate) mnemonic: Option, } impl EntryGatewayArgs { // TODO: could we perhaps make a clap error here and call `safe_exit` instead? pub(crate) fn build_config_section>( self, data_dir: P, ) -> config::GatewayTasksConfig { self.override_config_section(config::GatewayTasksConfig::new_default(data_dir)) } pub(crate) fn override_config_section( self, mut section: config::GatewayTasksConfig, ) -> config::GatewayTasksConfig { if let Some(bind_address) = self.entry_bind_address { section.ws_bind_address = bind_address } if let Some(ws_port) = self.announce_ws_port { section.announce_ws_port = Some(ws_port) } if let Some(wss_port) = self.announce_wss_port { section.announce_wss_port = Some(wss_port) } if let Some(enforce_zk_nyms) = self.enforce_zk_nyms { section.enforce_zk_nyms = enforce_zk_nyms } section } } #[derive(clap::Args, Debug)] pub(crate) struct ExitGatewayArgs { /// Specifies the url for an upstream source of the exit policy used by this node. #[clap( long, env = NYMNODE_UPSTREAM_EXIT_POLICY_ARG, )] pub(crate) upstream_exit_policy_url: Option, /// Specifies whether this exit node should run in 'open-proxy' mode /// and thus would attempt to resolve **ANY** request it receives. #[clap( long, env = NYMNODE_OPEN_PROXY_ARG, )] pub(crate) open_proxy: Option, } impl ExitGatewayArgs { // TODO: could we perhaps make a clap error here and call `safe_exit` instead? pub(crate) fn build_config_section>( self, data_dir: P, ) -> config::ServiceProvidersConfig { self.override_config_section(config::ServiceProvidersConfig::new_default(data_dir)) } pub(crate) fn override_config_section( self, mut section: config::ServiceProvidersConfig, ) -> config::ServiceProvidersConfig { if let Some(upstream_exit_policy) = self.upstream_exit_policy_url { section.upstream_exit_policy_url = upstream_exit_policy } if let Some(open_proxy) = self.open_proxy { section.open_proxy = open_proxy } section } }