Compare commits

...

17 Commits

Author SHA1 Message Date
Jędrzej Stuczyński a0e1103648 additional logs for stalled deposits 2026-05-01 08:41:21 +01:00
benedettadavico 31b059c0d3 unit test 2026-05-01 08:42:48 +02:00
benedettadavico 4fa9b8b200 version 2026-05-01 08:33:39 +02:00
benedettadavico 8c56739c8d fix? 2026-05-01 08:33:01 +02:00
benedetta davico 5599987d89 Merge pull request #6723 from nymtech/bdq/bump-cred-proxy
Bump cred proxy version
2026-04-30 18:01:50 +02:00
rachyandco a93763d73b Merge pull request #6688 from nymtech/fix/dns-ttl-reduce-cache-and-shuffle-ips
Title: fix(dns): shuffle resolved IPs
2026-04-30 16:21:22 +02:00
benedetta davico 8e8b6f4467 Bump version to 0.3.1 in Cargo.toml 2026-04-30 15:00:53 +02:00
mfahampshire 7feeed41d5 tweak subheading (#6721) 2026-04-30 09:09:49 +00:00
mfahampshire e9a20653b8 Components autogenerate + BYON section in NymVPNCLI docs (#6719) 2026-04-30 08:56:29 +00:00
Jędrzej Stuczyński 9438691506 Merge pull request #6718 from nymtech/chore/ipr-clippy
chore: made sphinx version threshold assertion a compile time check
2026-04-30 09:43:44 +01:00
p17o 84a4924e77 nym-node-setup: plumb HOST_SSH_PORT through tunnel manager, CLI, and env setup (#6633)
* network-tunnel-manager: make SSH port configurable

* Rename SSH_PORT to HOST_SSH_PORT.

* setup: plumb HOST_SSH_PORT through env and CLI

* setup-env-vars: persist HOST_SSH_PORT in env.sh

---------

Co-authored-by: p17o <p17o>
2026-04-30 07:10:21 +00:00
rachyandco 49277310ba Apply suggestion from @jmwample
Co-authored-by: Jack Wampler <jmwample@users.noreply.github.com>
2026-04-30 05:57:09 +02:00
rachyandco 944d2eb7d5 Apply suggestion from @jmwample
Co-authored-by: Jack Wampler <jmwample@users.noreply.github.com>
2026-04-30 05:56:43 +02:00
rachyandco bfaf17540e Apply suggestion from @jmwample
Co-authored-by: Jack Wampler <jmwample@users.noreply.github.com>
2026-04-30 05:56:09 +02:00
rachyandco 6dbc4efbd9 Apply suggestion from @jmwample
Co-authored-by: Jack Wampler <jmwample@users.noreply.github.com>
2026-04-30 05:55:39 +02:00
p17o cabbeaf1bf Handle split IPv4/IPv6 uplinks in network-tunnel-manager (#6640)
* Handle separate IPv4 and IPv6 uplink interfaces in network-tunnel-manager

* check_forward_chain() now checks IPv6 and is less brittle overall; missing IPv6 uplink detection now degrades to a loud warning plus partial IPv4-only setup rather than hard-failing early

* fix typos; fix UDP port 4443 being configured but not tested

---------

Co-authored-by: p17o <p17o>
2026-04-29 15:42:29 +00:00
Rachyandco bf85e9eb79 fix(dns): reduce positive TTL to 60s and shuffle resolved IPs
The 1800s minimum TTL defeated CDN failover mechanisms (e.g. Fastly
  publishes 30–60s A-record TTLs specifically to signal when edge nodes
  are removed). Dead IPs were cached for up to 30 minutes with no
  way for the client to recover without a restart.

  - Drop DEFAULT_POSITIVE_LOOKUP_CACHE_TTL from 1800s to 60s so that
    CDN-signalled failovers take effect within a minute
  - Shuffle resolved IPs on each lookup so retries cycle through all
    available edge nodes rather than hitting the same dead address
  - Add invalidate_preresolve_entry / invalidate_preresolve_for API
    for callers that want targeted per-host cache eviction on hard
    connection failures
2026-04-19 23:03:26 +02:00
27 changed files with 636 additions and 240 deletions
Generated
+1
View File
@@ -6926,6 +6926,7 @@ dependencies = [
"bytes",
"cfg-if",
"encoding_rs",
"fastrand",
"hickory-resolver",
"http 1.3.1",
"inventory",
@@ -13,8 +13,9 @@ use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
use tokio::sync::Mutex as AsyncMutex;
use tokio::time::Instant;
use tokio_util::sync::CancellationToken;
use tracing::{debug, info, instrument, warn};
use tracing::{debug, error, info, instrument, warn};
use uuid::Uuid;
pub use helpers::{BufferedDeposit, PerformedDeposits, make_deposits_request, split_deposits};
@@ -146,9 +147,14 @@ impl DepositsBuffer {
// if we're here, we know we're below the threshold
fn maybe_refill_deposits(&self) {
if let Some(mut guard) = self.inner.deposits_refill_task.try_get_new_task_guard() {
if let Some((mut guard, completion_guard)) =
self.inner.deposits_refill_task.try_get_new_task_guard()
{
let this = self.clone();
*guard = Some(tokio::spawn(async move { this.refill_deposits().await }));
*guard = Some(tokio::spawn(async move {
let _completion_guard = completion_guard;
this.refill_deposits().await
}));
}
}
@@ -179,6 +185,8 @@ impl DepositsBuffer {
requested_on: OffsetDateTime,
client_pubkey: PublicKeyUser,
) -> Result<BufferedDeposit, CredentialProxyError> {
let wait_start = Instant::now();
let mut i = 0;
loop {
tokio::time::sleep(Duration::from_millis(500)).await;
if let Some(buffered_deposit) = self.inner.unused_deposits.lock().await.pop() {
@@ -195,6 +203,15 @@ impl DepositsBuffer {
// make sure there's always a task working in the background in case deposits get used up too quickly
self.maybe_refill_deposits()
}
i += 1;
let elapsed = wait_start.elapsed();
if elapsed > Duration::from_secs(5) && i % 10 == 0 {
warn!("we've been waiting for over 5s to make a deposit - something is wrong!")
} else if elapsed > Duration::from_secs(10) && i % 5 == 0 {
error!(
"we've been waiting for over 10s to make a deposit - something is SERIOUSLY wrong!"
)
}
}
}
@@ -3,12 +3,22 @@
use crate::error::CredentialProxyError;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex as StdMutex, MutexGuard};
use std::sync::{Arc, Mutex as StdMutex, MutexGuard};
use tokio::task::JoinHandle;
use tracing::{debug, error};
pub(super) type RefillTaskResult = Result<(), CredentialProxyError>;
pub(super) struct InProgressGuard {
in_progress: Arc<AtomicBool>,
}
impl Drop for InProgressGuard {
fn drop(&mut self) {
self.in_progress.store(false, Ordering::SeqCst);
}
}
#[derive(Default)]
pub(super) struct RefillTask {
// note that we can only have a single transaction in progress (or it'd mess up with our sequence numbers)
@@ -16,7 +26,7 @@ pub(super) struct RefillTask {
// we'll have to increase the number of deposits per transaction
join_handle: StdMutex<Option<JoinHandle<RefillTaskResult>>>,
in_progress: AtomicBool,
in_progress: Arc<AtomicBool>,
}
impl RefillTask {
@@ -28,9 +38,15 @@ impl RefillTask {
.is_ok()
}
/// Returns `None` if a refill is already in progress. On success, returns the
/// join-handle guard (to store the new `JoinHandle` into) and an [`InProgressGuard`]
/// that **must be moved into the spawned task** — it resets the flag when dropped.
pub(super) fn try_get_new_task_guard(
&self,
) -> Option<MutexGuard<'_, Option<JoinHandle<RefillTaskResult>>>> {
) -> Option<(
MutexGuard<'_, Option<JoinHandle<RefillTaskResult>>>,
InProgressGuard,
)> {
// sanity check for concurrent request
if !self.try_set_in_progress() {
debug!("another task has already started deposit refill request");
@@ -48,7 +64,11 @@ impl RefillTask {
}
}
Some(guard)
let completion_guard = InProgressGuard {
in_progress: Arc::clone(&self.in_progress),
};
Some((guard, completion_guard))
}
pub(super) fn take_task_join_handle(&self) -> Option<JoinHandle<RefillTaskResult>> {
@@ -56,3 +76,34 @@ impl RefillTask {
self.join_handle.lock().expect("mutex got poisoned").take()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn in_progress_resets_after_guard_drop() {
let task = RefillTask::default();
let (guard, completion_guard) = task.try_get_new_task_guard().unwrap();
drop(guard);
assert!(task.try_get_new_task_guard().is_none());
drop(completion_guard);
assert!(task.try_get_new_task_guard().is_some());
}
#[test]
fn in_progress_resets_on_panic() {
let task = RefillTask::default();
let (guard, completion_guard) = task.try_get_new_task_guard().unwrap();
drop(guard);
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _g = completion_guard;
panic!("simulated refill task panic");
}));
assert!(task.try_get_new_task_guard().is_some());
}
}
+1
View File
@@ -36,6 +36,7 @@ thiserror = { workspace = true }
tracing = { workspace = true }
itertools = { workspace = true }
inventory = { workspace = true }
fastrand = { workspace = true }
tokio = { workspace = true, features = ["rt", "macros", "time"] }
rustls = { workspace=true }
# used for decoding text responses (they were already implicitly included)
+5 -16
View File
@@ -56,7 +56,6 @@ use std::{
use hickory_resolver::{
TokioResolver,
config::{NameServerConfig, NameServerConfigGroup, ResolverConfig, ResolverOpts},
lookup_ip::LookupIpIntoIter,
name_server::TokioConnectionProvider,
};
use once_cell::sync::OnceCell;
@@ -227,9 +226,11 @@ async fn resolve(
let primary_err = match resolve_fut.await {
Err(_) => ResolveError::Timeout,
Ok(Ok(lookup)) => {
let addrs: Addrs = Box::new(SocketAddrs {
iter: lookup.into_iter(),
});
// Shuffle so that successive connection attempts cycle through all
// returned IPs rather than always hitting the same first address.
let mut ips: Vec<IpAddr> = lookup.into_iter().collect();
fastrand::shuffle(&mut ips);
let addrs: Addrs = Box::new(ips.into_iter().map(|ip| SocketAddr::new(ip, 0)));
return Ok(addrs);
}
Ok(Err(e)) => {
@@ -256,18 +257,6 @@ async fn resolve(
Err(primary_err)
}
struct SocketAddrs {
iter: LookupIpIntoIter,
}
impl Iterator for SocketAddrs {
type Item = SocketAddr;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
}
}
impl HickoryDnsResolver {
/// Returns an instance of the shared resolver.
pub fn shared() -> Self {
+4 -1
View File
@@ -24,11 +24,14 @@ pub const SPHINX_STREAM_VERSION_THRESHOLD: u8 = v9::VERSION;
mod tests {
use super::*;
const _: () = {
assert!(SPHINX_STREAM_VERSION_THRESHOLD > MAX_NON_STREAM_VERSION);
};
#[test]
fn stream_transport_threshold_is_consistent() {
assert_eq!(MAX_NON_STREAM_VERSION, 8);
assert_eq!(SPHINX_STREAM_VERSION_THRESHOLD, 9);
assert!(SPHINX_STREAM_VERSION_THRESHOLD > MAX_NON_STREAM_VERSION);
}
}
@@ -5,7 +5,7 @@
},
"mixmining_reserve": {
"denom": "unym",
"amount": "168575020719057"
"amount": "166613455567357"
},
"vesting_tokens": {
"denom": "unym",
@@ -13,6 +13,6 @@
},
"circulating_supply": {
"denom": "unym",
"amount": "831424979280943"
"amount": "833386544432643"
}
}
@@ -1,6 +1,6 @@
{
"nodes": 752,
"locations": 76,
"mixnodes": 273,
"exit_gateways": 470
"nodes": 744,
"locations": 77,
"mixnodes": 263,
"exit_gateways": 474
}
@@ -1 +1 @@
831_424_979
833_386_544
@@ -1 +1 @@
61_050_638
61_194_673
@@ -1 +1 @@
61_050_637
61_194_672
@@ -1,7 +1,7 @@
| **Item** | **Description** | **Amount in NYM** |
|:-------------------|:------------------------------------------------------|--------------------:|
| Total Supply | Maximum amount of NYM token in existence | 1_000_000_000 |
| Mixmining Reserve | Tokens releasing for operators rewards | 168_575_020 |
| Mixmining Reserve | Tokens releasing for operators rewards | 166_613_455 |
| Vesting Tokens | Tokens locked outside of circulation for future claim | 0 |
| Circulating Supply | Amount of unlocked tokens | 831_424_979 |
| Stake Saturation | Optimal size of node self-bond + delegation | 254_377 |
| Circulating Supply | Amount of unlocked tokens | 833_386_544 |
| Stake Saturation | Optimal size of node self-bond + delegation | 254_977 |
@@ -1,10 +1,10 @@
{
"interval": {
"reward_pool": "168575020719057.456153922699547282",
"staking_supply": "61050637328128.353993717821521057",
"reward_pool": "166613455567357.391753168447944888",
"staking_supply": "61194672938727.325886595653401629",
"staking_supply_scale_factor": "0.07342892",
"epoch_reward_budget": "4682639464.418262670942297209",
"stake_saturation_point": "254377655533.868141640490923004",
"epoch_reward_budget": "4628151543.537705326476901331",
"stake_saturation_point": "254977803911.363857860815222506",
"sybil_resistance": "0.3",
"active_set_work_factor": "10",
"interval_pool_emission": "0.02"
@@ -1 +1 @@
Thursday, April 9th 2026, 15:33:24 UTC
Thursday, April 30th 2026, 08:30:26 UTC
@@ -6,6 +6,7 @@ usage: nym-node-cli install [-h] [-V] [-d BRANCH] [-v]
[--email EMAIL] [--moniker MONIKER]
[--description DESCRIPTION]
[--public-ip PUBLIC_IP]
[--host-ssh-port HOST_SSH_PORT]
[--nym-node-binary NYM_NODE_BINARY]
[--uplink-dev UPLINK_DEV] [--env KEY=VALUE]
@@ -27,6 +28,8 @@ options:
Short public description of the node
--public-ip PUBLIC_IP
External IPv4 address (autodetected if omitted)
--host-ssh-port HOST_SSH_PORT
Host SSH port to allow in the firewall (default: 22)
--nym-node-binary NYM_NODE_BINARY
URL for nym-node binary (autodetected if omitted)
--uplink-dev UPLINK_DEV
@@ -4,56 +4,115 @@ Start this nym-node
Usage: nym-node run [OPTIONS]
Options:
--id <ID> Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node]
--config-file <CONFIG_FILE> Path to a configuration file of this node [env: NYMNODE_CONFIG=]
--accept-operator-terms-and-conditions Explicitly specify whether you agree with the terms and conditions of a nym node operator as defined at <https://nymtech.net/terms-and-conditions/operators/v1.0.0> [env: NYMNODE_ACCEPT_OPERATOR_TERMS=]
--deny-init Forbid a new node from being initialised if configuration file for the provided specification doesn't already exist [env: NYMNODE_DENY_INIT=]
--init-only If this is a brand new nym-node, specify whether it should only be initialised without actually running the subprocesses [env: NYMNODE_INIT_ONLY=]
--local Flag specifying this node will be running in a local setting [env: NYMNODE_LOCAL=]
--mode [<MODE>...] Specifies the current mode(s) of this nym-node [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway, exit-providers-only]
--modes <MODES> Specifies the current mode(s) of this nym-node as a single flag [env: NYMNODE_MODES=] [possible values: mixnode, entry-gateway, exit-gateway, exit-providers-only]
-w, --write-changes If this node has been initialised before, specify whether to write any new changes to the config file [env: NYMNODE_WRITE_CONFIG_CHANGES=]
--bonding-information-output <BONDING_INFORMATION_OUTPUT> Specify output file for bonding information of this nym-node, i.e. its encoded keys. NOTE: the required bonding information is still a subject to change and this argument should be treated only as a preview of
future features [env: NYMNODE_BONDING_INFORMATION_OUTPUT=]
-o, --output <OUTPUT> Specify the output format of the bonding information (`text` or `json`) [env: NYMNODE_OUTPUT=] [default: text] [possible values: text, json]
--public-ips <PUBLIC_IPS> 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 [env: NYMNODE_PUBLIC_IPS=]
--hostname <HOSTNAME> Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: NYMNODE_HOSTNAME=]
--location <LOCATION> 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 [env: NYMNODE_LOCATION=]
--http-bind-address <HTTP_BIND_ADDRESS> Socket address this node will use for binding its http API. default: `[::]:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=]
--landing-page-assets-path <LANDING_PAGE_ASSETS_PATH> Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=]
--http-access-token <HTTP_ACCESS_TOKEN> An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: NYMNODE_HTTP_ACCESS_TOKEN=]
--expose-system-info <EXPOSE_SYSTEM_INFO> Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] [possible values: true, false]
--expose-system-hardware <EXPOSE_SYSTEM_HARDWARE> Specify whether basic system hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false]
--expose-crypto-hardware <EXPOSE_CRYPTO_HARDWARE> Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false]
--mixnet-bind-address <MIXNET_BIND_ADDRESS> Address this node will bind to for listening for mixnet packets default: `[::]:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=]
--mixnet-announce-port <MIXNET_ANNOUNCE_PORT> 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 [env: NYMNODE_MIXNET_ANNOUNCE_PORT=]
--nym-api-urls <NYM_API_URLS> Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=]
--nyxd-urls <NYXD_URLS> Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=]
--enable-console-logging <ENABLE_CONSOLE_LOGGING> Specify whether running statistics of this node should be logged to the console [env: NYMNODE_ENABLE_CONSOLE_LOGGING=] [possible values: true, false]
--wireguard-enabled <WIREGUARD_ENABLED> Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false]
--wireguard-bind-address <WIREGUARD_BIND_ADDRESS> Socket address this node will use for binding its wireguard interface. default: `[::]:51822` [env: NYMNODE_WG_BIND_ADDRESS=]
--wireguard-tunnel-announced-port <WIREGUARD_TUNNEL_ANNOUNCED_PORT> Tunnel port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=]
--wireguard-private-network-prefix <WIREGUARD_PRIVATE_NETWORK_PREFIX> 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 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=]
--wireguard-userspace <WIREGUARD_USERSPACE> Use userspace implementation of WireGuard (wireguard-go) instead of kernel module. Useful in containerized environments without kernel WireGuard support [env: NYMNODE_WG_USERSPACE=] [possible values: true,
false]
--verloc-bind-address <VERLOC_BIND_ADDRESS> Socket address this node will use for binding its verloc API. default: `[::]:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=]
--verloc-announce-port <VERLOC_ANNOUNCE_PORT> 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 [env: NYMNODE_VERLOC_ANNOUNCE_PORT=]
--entry-bind-address <ENTRY_BIND_ADDRESS> Socket address this node will use for binding its client websocket API. default: `[::]:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=]
--announce-ws-port <ANNOUNCE_WS_PORT> Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will be used instead [env: NYMNODE_ENTRY_ANNOUNCE_WS_PORT=]
--announce-wss-port <ANNOUNCE_WSS_PORT> If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=]
--enforce-zk-nyms <ENFORCE_ZK_NYMS> Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=] [possible values: true, false]
--mnemonic <MNEMONIC> Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be generated [env: NYMNODE_MNEMONIC=]
--upgrade-mode-attestation-url <UPGRADE_MODE_ATTESTATION_URL> Endpoint to query to retrieve current upgrade mode attestation. This argument should never be set outside testnets and local networks [env: NYMNODE_UPGRADE_MODE_ATTESTATION_URL=]
--upgrade-mode-attester-public-key <UPGRADE_MODE_ATTESTER_PUBLIC_KEY> Expected public key of the entity signing the published attestation. This argument should never be set outside testnets and local networks [env: NYMNODE_UPGRADE_MODE_ATTESTER_PUBKEY=]
--upstream-exit-policy-url <UPSTREAM_EXIT_POLICY_URL> Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=]
--open-proxy <OPEN_PROXY> Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it receives [env: NYMNODE_OPEN_PROXY=] [possible values: true, false]
--lp-control-bind-address <LP_CONTROL_BIND_ADDRESS> Bind address for the TCP LP control traffic. default: `[::]:41264` [env: NYMNODE_LP_CONTROL_BIND_ADDRESS=]
--lp-control-announce-port <LP_CONTROL_ANNOUNCE_PORT> Custom announced port for listening for the TCP LP control traffic. If unspecified, the value from the `lp_control_bind_address` will be used instead [env: NYMNODE_LP_CONTROL_ANNOUNCE_PORT=]
--lp-data-bind-address <LP_DATA_BIND_ADDRESS> Bind address for the UDP LP data traffic. default: `[::]:51264` [env: NYMNODE_LP_DATA_BIND_ADDRESS=]
--lp-data-announce-port <LP_DATA_ANNOUNCE_PORT> Custom announced port for listening for the UDP LP data traffic. If unspecified, the value from the `lp_data_bind_address` will be used instead [env: NYMNODE_LP_DATA_ANNOUNCE_PORT=]
--lp-use-mock-ecash <LP_USE_MOCK_ECASH> Use mock ecash manager for LP testing. WARNING: Only use this for local testing! Never enable in production. When enabled, the LP listener will accept any credential without blockchain verification [env:
NYMNODE_LP_USE_MOCK_ECASH=] [possible values: true, false]
-h, --help Print help
--id <ID>
Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node]
--config-file <CONFIG_FILE>
Path to a configuration file of this node [env: NYMNODE_CONFIG=]
--accept-operator-terms-and-conditions
Explicitly specify whether you agree with the terms and conditions of a nym node operator as defined at <https://nymtech.net/terms-and-conditions/operators/v1.0.0> [env:
NYMNODE_ACCEPT_OPERATOR_TERMS=]
--deny-init
Forbid a new node from being initialised if configuration file for the provided specification doesn't already exist [env: NYMNODE_DENY_INIT=]
--init-only
If this is a brand new nym-node, specify whether it should only be initialised without actually running the subprocesses [env: NYMNODE_INIT_ONLY=]
--local
Flag specifying this node will be running in a local setting [env: NYMNODE_LOCAL=]
--mode [<MODE>...]
Specifies the current mode(s) of this nym-node [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway, exit-providers-only]
--modes <MODES>
Specifies the current mode(s) of this nym-node as a single flag [env: NYMNODE_MODES=] [possible values: mixnode, entry-gateway, exit-gateway, exit-providers-only]
-w, --write-changes
If this node has been initialised before, specify whether to write any new changes to the config file [env: NYMNODE_WRITE_CONFIG_CHANGES=]
--bonding-information-output <BONDING_INFORMATION_OUTPUT>
Specify output file for bonding information of this nym-node, i.e. its encoded keys. NOTE: the required bonding information is still a subject to change and this argument
should be treated only as a preview of future features [env: NYMNODE_BONDING_INFORMATION_OUTPUT=]
-o, --output <OUTPUT>
Specify the output format of the bonding information (`text` or `json`) [env: NYMNODE_OUTPUT=] [default: text] [possible values: text, json]
--public-ips <PUBLIC_IPS>
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 [env: NYMNODE_PUBLIC_IPS=]
--hostname <HOSTNAME>
Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: NYMNODE_HOSTNAME=]
--location <LOCATION>
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 [env: NYMNODE_LOCATION=]
--http-bind-address <HTTP_BIND_ADDRESS>
Socket address this node will use for binding its http API. default: `[::]:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=]
--landing-page-assets-path <LANDING_PAGE_ASSETS_PATH>
Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=]
--http-access-token <HTTP_ACCESS_TOKEN>
An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: NYMNODE_HTTP_ACCESS_TOKEN=]
--expose-system-info <EXPOSE_SYSTEM_INFO>
Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] [possible values: true, false]
--expose-system-hardware <EXPOSE_SYSTEM_HARDWARE>
Specify whether basic system hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false]
--expose-crypto-hardware <EXPOSE_CRYPTO_HARDWARE>
Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false]
--mixnet-bind-address <MIXNET_BIND_ADDRESS>
Address this node will bind to for listening for mixnet packets default: `[::]:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=]
--mixnet-announce-port <MIXNET_ANNOUNCE_PORT>
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 [env:
NYMNODE_MIXNET_ANNOUNCE_PORT=]
--nym-api-urls <NYM_API_URLS>
Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=]
--nyxd-urls <NYXD_URLS>
Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=]
--enable-console-logging <ENABLE_CONSOLE_LOGGING>
Specify whether running statistics of this node should be logged to the console [env: NYMNODE_ENABLE_CONSOLE_LOGGING=] [possible values: true, false]
--wireguard-enabled <WIREGUARD_ENABLED>
Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false]
--wireguard-bind-address <WIREGUARD_BIND_ADDRESS>
Socket address this node will use for binding its wireguard interface. default: `[::]:51822` [env: NYMNODE_WG_BIND_ADDRESS=]
--wireguard-tunnel-announced-port <WIREGUARD_TUNNEL_ANNOUNCED_PORT>
Tunnel port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env:
NYMNODE_WG_ANNOUNCED_PORT=]
--wireguard-private-network-prefix <WIREGUARD_PRIVATE_NETWORK_PREFIX>
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 [env:
NYMNODE_WG_PRIVATE_NETWORK_PREFIX=]
--wireguard-userspace <WIREGUARD_USERSPACE>
Use userspace implementation of WireGuard (wireguard-go) instead of kernel module. Useful in containerized environments without kernel WireGuard support [env:
NYMNODE_WG_USERSPACE=] [possible values: true, false]
--verloc-bind-address <VERLOC_BIND_ADDRESS>
Socket address this node will use for binding its verloc API. default: `[::]:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=]
--verloc-announce-port <VERLOC_ANNOUNCE_PORT>
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 [env:
NYMNODE_VERLOC_ANNOUNCE_PORT=]
--entry-bind-address <ENTRY_BIND_ADDRESS>
Socket address this node will use for binding its client websocket API. default: `[::]:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=]
--announce-ws-port <ANNOUNCE_WS_PORT>
Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will be used instead [env:
NYMNODE_ENTRY_ANNOUNCE_WS_PORT=]
--announce-wss-port <ANNOUNCE_WSS_PORT>
If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=]
--enforce-zk-nyms <ENFORCE_ZK_NYMS>
Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=]
[possible values: true, false]
--mnemonic <MNEMONIC>
Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be generated [env: NYMNODE_MNEMONIC=]
--upgrade-mode-attestation-url <UPGRADE_MODE_ATTESTATION_URL>
Endpoint to query to retrieve current upgrade mode attestation. This argument should never be set outside testnets and local networks [env:
NYMNODE_UPGRADE_MODE_ATTESTATION_URL=]
--upgrade-mode-attester-public-key <UPGRADE_MODE_ATTESTER_PUBLIC_KEY>
Expected public key of the entity signing the published attestation. This argument should never be set outside testnets and local networks [env:
NYMNODE_UPGRADE_MODE_ATTESTER_PUBKEY=]
--upstream-exit-policy-url <UPSTREAM_EXIT_POLICY_URL>
Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=]
--open-proxy <OPEN_PROXY>
Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it receives [env: NYMNODE_OPEN_PROXY=] [possible values:
true, false]
--lp-control-bind-address <LP_CONTROL_BIND_ADDRESS>
Bind address for the TCP LP control traffic. default: `[::]:41264` [env: NYMNODE_LP_CONTROL_BIND_ADDRESS=]
--lp-control-announce-port <LP_CONTROL_ANNOUNCE_PORT>
Custom announced port for listening for the TCP LP control traffic. If unspecified, the value from the `lp_control_bind_address` will be used instead [env:
NYMNODE_LP_CONTROL_ANNOUNCE_PORT=]
--lp-data-bind-address <LP_DATA_BIND_ADDRESS>
Bind address for the UDP LP data traffic. default: `[::]:51264` [env: NYMNODE_LP_DATA_BIND_ADDRESS=]
--lp-data-announce-port <LP_DATA_ANNOUNCE_PORT>
Custom announced port for listening for the UDP LP data traffic. If unspecified, the value from the `lp_data_bind_address` will be used instead [env:
NYMNODE_LP_DATA_ANNOUNCE_PORT=]
--lp-use-mock-ecash <LP_USE_MOCK_ECASH>
Use mock ecash manager for LP testing. WARNING: Only use this for local testing! Never enable in production. When enabled, the LP listener will accept any credential
without blockchain verification [env: NYMNODE_LP_USE_MOCK_ECASH=] [possible values: true, false]
-h, --help
Print help
```
@@ -17,9 +17,9 @@ Download and run instructions for the GUIs can be found [here](https://nymvpn.co
## Download & Extract Binary
Check the [release page](https://github.com/nymtech/nym-vpn-client/releases/) page for the latest release version and modify the instructions accordingly. These instructions use the latest as of the time of writing.
```sh
wget -q https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-core-v1.27.0-beta/nym-vpn-core-v1.27.0-beta_<YOUR_OPERATING_SYSTEM>.tar.gz &&
tar -xzf nym-vpn-core-v1.27.0-beta_<YOUR_OPERATING_SYSTEM>.tar.gz &&
cd nym-vpn-core-v1.27.0-beta_<YOUR_OPERATING_SYSTEM>/ &&
wget -q https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-core-v1.29.0/nym-vpn-core-v1.29.0_<YOUR_OPERATING_SYSTEM>.tar.gz &&
tar -xzf nym-vpn-core-v1.29.0_<YOUR_OPERATING_SYSTEM>.tar.gz &&
cd nym-vpn-core-v1.29.0_<YOUR_OPERATING_SYSTEM>/ &&
chmod u+x *
```
@@ -165,6 +165,66 @@ View the current device identity:
nym-vpnc device get
```
## Pay as You Go: Decentralized Access to Nym
You can fund your VPN usage directly from your own wallet instead of going through the NymVPN account system. You deposit `$NYM` into the ticketbook smart contract and receive zk-nym ticketbooks that authenticate you on the network.
<Callout type="warning" emoji="⚠️">
If you already have an account stored in `nym-vpnc`, you must remove it first with `nym-vpnc account forget` before setting a new mnemonic.
</Callout>
### Set Your Mnemonic
Store the recovery phrase for your on-chain wallet address (`n1...`) that holds your `$NYM` tokens:
```sh
nym-vpnc account set "<MNEMONIC>" --location blockchain
```
You must fund this address yourself, for example by transferring `$NYM` from an exchange or another wallet. The `--location blockchain` flag tells `nym-vpnc` to use the on-chain wallet directly rather than the NymVPN account system.
### Obtain Ticketbooks
Deposit `$NYM` into the ticketbook smart contract and receive zk-nym credentials:
```sh
nym-vpnc account obtain-ticketbooks --amount 1 --source smartcontract
```
You can omit `--source` to use the default:
```sh
nym-vpnc account obtain-ticketbooks --amount 1
```
The `--amount` flag specifies how many ticketbooks to obtain **per ticket type**. Each request issues one ticketbook for each of the three types:
- **Mixnet Entry** — used for 5-hop mixnet mode
- **WireGuard Entry** — used for 2-hop WireGuard mode (entry side)
- **WireGuard Exit** — used for 2-hop WireGuard mode (exit side)
So `--amount 1` produces 3 ticketbooks total (one of each type), and `--amount 2` produces 6.
<Callout type="warning" emoji="⚠️">
Each ticketbook costs 75 NYM. Because `--amount` applies per type, `--amount 1` deposits **225 NYM** (75 × 3 types), and `--amount 2` deposits **450 NYM**. If you only use two-hop (WireGuard) mode, the Mixnet Entry ticketbooks will go unused. There is currently no way to obtain ticketbooks for a single type.
</Callout>
This command:
- Deposits `$NYM` into the ticketbook smart contract (plus a small fee buffer per deposit)
- Requests credential issuance from the decentralised Nym API validators
- Stores the resulting zk-nym ticketbooks locally on the device
### Connect
Connect using the locally stored ticketbooks:
```sh
nym-vpnc connect-v2
```
The CLI uses the ticketbooks to authenticate with entry nodes (gateways) and connect to the Nym network. You will see the connection happening in the `nym-vpnd` logs.
## Tunnel Configuration
Print current tunnel configuration:
@@ -1,6 +1,6 @@
[package]
name = "nym-credential-proxy"
version = "0.3.0"
version = "0.3.2-rc"
authors.workspace = true
edition.workspace = true
license.workspace = true
+53 -30
View File
@@ -1258,7 +1258,8 @@ dependencies = [
[[package]]
name = "core-models"
version = "0.0.5"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "657f625ff361906f779745d08375ae3cc9fef87a35fba5f22874cf773010daf4"
dependencies = [
"hax-lib",
"pastey",
@@ -3854,7 +3855,8 @@ checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libcrux-aesgcm"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99f2a019dab4097585a7d4f5b9deebe46cd1e628b16a5bc4cb0ce35e1da334e6"
dependencies = [
"libcrux-intrinsics",
"libcrux-platform",
@@ -3864,8 +3866,9 @@ dependencies = [
[[package]]
name = "libcrux-chacha20poly1305"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc08d044676af21343b32b988411fa98dbb5cf65a03c9df478ced221bbdfdb1b"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -3877,7 +3880,8 @@ dependencies = [
[[package]]
name = "libcrux-curve25519"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1e5fd8476a6ed609d24ef42aee5ab6f99f7c65d054f92412da9f499e423299"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -3888,7 +3892,8 @@ dependencies = [
[[package]]
name = "libcrux-ecdh"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b65f73ce79337c762eb38bbac91e4c9b9e60cf318e8501b812750c640814d45e"
dependencies = [
"libcrux-curve25519",
"libcrux-p256",
@@ -3898,8 +3903,9 @@ dependencies = [
[[package]]
name = "libcrux-ed25519"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835919315b7042fe9e03b6458efe0db94bf2aa7b873934dbee5b5463a8124b43"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -3911,7 +3917,8 @@ dependencies = [
[[package]]
name = "libcrux-hacl-rs"
version = "0.0.4"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2637dc87d158e1f1b550fd9b226443e84153fded4de69028d897b534d16d22e6"
dependencies = [
"libcrux-macros",
]
@@ -3919,7 +3926,8 @@ dependencies = [
[[package]]
name = "libcrux-hkdf"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1a89ca0c89be3a268a921e47105fb7873badf7267f5e3ebf4ea46baedd73ef"
dependencies = [
"libcrux-hacl-rs",
"libcrux-hmac",
@@ -3929,7 +3937,8 @@ dependencies = [
[[package]]
name = "libcrux-hmac"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7a242707d65960770bd7e14e4f18a92bdf0b967777dd404887db8d087a643b"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -3939,7 +3948,8 @@ dependencies = [
[[package]]
name = "libcrux-intrinsics"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1b5db005ff8001e026b73a6842ee81bbef8ec5ff0e1915a67ae65fd2a9fafa5"
dependencies = [
"core-models",
"hax-lib",
@@ -3947,8 +3957,9 @@ dependencies = [
[[package]]
name = "libcrux-kem"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12631592f491d22fd1a176d32b2c6edfb673998fd3987e9d95f8fa79ad2a737b"
dependencies = [
"libcrux-curve25519",
"libcrux-ecdh",
@@ -3963,7 +3974,8 @@ dependencies = [
[[package]]
name = "libcrux-macros"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffd6aa2dcd5be681662001b81d493f1569c6d49a32361f470b0c955465cd0338"
dependencies = [
"quote",
"syn 2.0.100",
@@ -3971,8 +3983,9 @@ dependencies = [
[[package]]
name = "libcrux-ml-dsa"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a72929ed421cc3bf16a946b3e7d2a58d215b0b5c2a12be26b53629f081bf49b2"
dependencies = [
"core-models",
"hax-lib",
@@ -3985,8 +3998,9 @@ dependencies = [
[[package]]
name = "libcrux-ml-kem"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a14ab3e477de9df6ee1273a114018ff62c4996ca9220070c4e5cb1743f94a67d"
dependencies = [
"hax-lib",
"libcrux-intrinsics",
@@ -4001,7 +4015,8 @@ dependencies = [
[[package]]
name = "libcrux-p256"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4778ba25cb08bb8a96bd100e19ed9aecf78337198fd176036e21042b2dd99bc"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4013,15 +4028,17 @@ dependencies = [
[[package]]
name = "libcrux-platform"
version = "0.0.3"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d9e21d7ed31a92ac539bd69a8c970b183ee883872d2d19ce27036e24cb8ecc4"
dependencies = [
"libc",
]
[[package]]
name = "libcrux-poly1305"
version = "0.0.4"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02491808ee5b9db8cb65fad64ae0be812db64beef179d945c00c7787dc7dfcf9"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4029,8 +4046,9 @@ dependencies = [
[[package]]
name = "libcrux-psq"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779ade7aa5e1b4b400c716b313cbf69070988dd005f92e961c2da4c3c42fbea4"
dependencies = [
"libcrux-aesgcm",
"libcrux-chacha20poly1305",
@@ -4050,7 +4068,8 @@ dependencies = [
[[package]]
name = "libcrux-secrets"
version = "0.0.5"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce650f3041b44ba40d4263852347d007cd2cd9d1cc856a6f6c8b2e10c3fd40b"
dependencies = [
"hax-lib",
]
@@ -4058,7 +4077,8 @@ dependencies = [
[[package]]
name = "libcrux-sha2"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9d253473f259fc74a280c43f29c464f7e374abdf28b4942234dc707f529d4b7"
dependencies = [
"libcrux-hacl-rs",
"libcrux-macros",
@@ -4067,8 +4087,9 @@ dependencies = [
[[package]]
name = "libcrux-sha3"
version = "0.0.7"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1ae0b7d0e1cc4793a609fd0ff2ca3b3a3fabae523770c619a3d4bc86417b0d7"
dependencies = [
"hax-lib",
"libcrux-intrinsics",
@@ -4079,7 +4100,8 @@ dependencies = [
[[package]]
name = "libcrux-traits"
version = "0.0.6"
source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e4fa89f3f5e34b47f928b22b1b78395a0d4ec23b1f583db635f128159d65f"
dependencies = [
"libcrux-secrets",
"rand 0.9.2",
@@ -5070,6 +5092,7 @@ dependencies = [
"serde",
"thiserror 2.0.12",
"x25519-dalek",
"zeroize",
]
[[package]]
+241 -99
View File
@@ -1,5 +1,5 @@
#!/bin/bash
# nym tunnel and wireguard exit policy manager
# nym host network firewall, tunnel and wireguard exit policy manager
# run this script as root
set -euo pipefail
@@ -20,6 +20,10 @@ info() {
printf "%b\n" "${YELLOW}[INFO] $*${NC}"
}
warn() {
printf "%b\n" "${YELLOW}[WARN] $*${NC}"
}
ok() {
printf "%b\n" "${GREEN}[OK] $*${NC}"
}
@@ -102,18 +106,44 @@ detect_uplink_interface() {
}
# uplink device detection, can be overridden
# Backward compatibility:
# - NETWORK_DEVICE sets both IPv4 and IPv6 uplinks.
# Preferred overrides:
# - NETWORK_DEVICE_V4
# - NETWORK_DEVICE_V6
NETWORK_DEVICE="${NETWORK_DEVICE:-}"
if [[ -z "$NETWORK_DEVICE" ]]; then
NETWORK_DEVICE="$(detect_uplink_interface "ip -o route show default")"
NETWORK_DEVICE_V4="${NETWORK_DEVICE_V4:-${NETWORK_DEVICE:-}}"
NETWORK_DEVICE_V6="${NETWORK_DEVICE_V6:-${NETWORK_DEVICE:-}}"
if [[ -z "$NETWORK_DEVICE_V4" ]]; then
NETWORK_DEVICE_V4="$(detect_uplink_interface "ip -o route show default")"
fi
if [[ -z "$NETWORK_DEVICE" ]]; then
NETWORK_DEVICE="$(detect_uplink_interface "ip -o route show default table all")"
if [[ -z "$NETWORK_DEVICE_V4" ]]; then
NETWORK_DEVICE_V4="$(detect_uplink_interface "ip -o route show default table all")"
fi
if [[ -z "$NETWORK_DEVICE" ]]; then
error "cannot determine uplink interface. set NETWORK_DEVICE or UPLINK_DEV"
if [[ -z "$NETWORK_DEVICE_V4" ]]; then
error "cannot determine ipv4 uplink interface. set NETWORK_DEVICE_V4 or NETWORK_DEVICE"
exit 1
fi
if [[ -z "$NETWORK_DEVICE_V6" ]]; then
NETWORK_DEVICE_V6="$(detect_uplink_interface "ip -6 -o route show default")"
fi
if [[ -z "$NETWORK_DEVICE_V6" ]]; then
NETWORK_DEVICE_V6="$(detect_uplink_interface "ip -6 -o route show default table all")"
fi
has_ipv6_uplink() {
[[ -n "${NETWORK_DEVICE_V6:-}" ]]
}
info "detected ipv4 uplink: $NETWORK_DEVICE_V4"
if has_ipv6_uplink; then
info "detected ipv6 uplink: $NETWORK_DEVICE_V6"
else
warn "could not determine ipv6 uplink interface. continuing with ipv4-only setup; ipv6-specific setup will be skipped and ipv6 tests may fail"
fi
###############################################################################
# shared helpers
###############################################################################
@@ -168,7 +198,7 @@ EOF
}
save_iptables_rules() {
info "saving iptables rules to /etc/iptables$"
info "saving iptables rules to /etc/iptables"
mkdir -p /etc/iptables
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
@@ -194,11 +224,16 @@ fetch_ipv6_address() {
fetch_and_display_ipv6() {
local ipv6_address
ipv6_address=$(ip -6 addr show "$NETWORK_DEVICE" scope global | awk '/inet6/ {print $2}')
if ! has_ipv6_uplink; then
warn "no ipv6 uplink detected; skipping ipv6 uplink address display"
return 0
fi
ipv6_address=$(ip -6 addr show "$NETWORK_DEVICE_V6" scope global | awk '/inet6/ {print $2}')
if [[ -z "$ipv6_address" ]]; then
error "no global ipv6 address found on $NETWORK_DEVICE"
error "no global ipv6 address found on $NETWORK_DEVICE_V6"
else
ok "ipv6 address on $NETWORK_DEVICE: $ipv6_address"
ok "ipv6 address on $NETWORK_DEVICE_V6: $ipv6_address"
fi
}
@@ -343,35 +378,39 @@ remove_duplicate_rules() {
apply_iptables_rules() {
local interface=$1
info "applying iptables rules for $interface using uplink $NETWORK_DEVICE"
info "applying iptables rules for $interface using ipv4 uplink $NETWORK_DEVICE_V4${NETWORK_DEVICE_V6:+ and ipv6 uplink $NETWORK_DEVICE_V6}"
sleep 1
# ipv4 nat and forwarding
iptables -t nat -C POSTROUTING -o "$NETWORK_DEVICE" -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -o "$NETWORK_DEVICE" -j MASQUERADE
iptables -t nat -C POSTROUTING -o "$NETWORK_DEVICE_V4" -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -o "$NETWORK_DEVICE_V4" -j MASQUERADE
# governed by NYM-EXIT, do not add a broad FORWARD ACCEPT
if ! iptables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then
iptables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null || \
iptables -I FORWARD 1 -i "$interface" -o "$NETWORK_DEVICE" -j ACCEPT
if ! iptables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN" 2>/dev/null; then
iptables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE_V4" -j ACCEPT 2>/dev/null || \
iptables -I FORWARD 1 -i "$interface" -o "$NETWORK_DEVICE_V4" -j ACCEPT
fi
iptables -C FORWARD -i "$NETWORK_DEVICE" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
iptables -I FORWARD 2 -i "$NETWORK_DEVICE" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -C FORWARD -i "$NETWORK_DEVICE_V4" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
iptables -I FORWARD 2 -i "$NETWORK_DEVICE_V4" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
# ipv6 nat and forwarding
ip6tables -t nat -C POSTROUTING -o "$NETWORK_DEVICE" -j MASQUERADE 2>/dev/null || \
ip6tables -t nat -A POSTROUTING -o "$NETWORK_DEVICE" -j MASQUERADE
if has_ipv6_uplink; then
ip6tables -t nat -C POSTROUTING -o "$NETWORK_DEVICE_V6" -j MASQUERADE 2>/dev/null || \
ip6tables -t nat -A POSTROUTING -o "$NETWORK_DEVICE_V6" -j MASQUERADE
# governed by NYM-EXIT, do not add a broad FORWARD ACCEPT
if ! ip6tables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then
ip6tables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null || \
ip6tables -I FORWARD 1 -i "$interface" -o "$NETWORK_DEVICE" -j ACCEPT
# governed by NYM-EXIT, do not add a broad FORWARD ACCEPT
if ! ip6tables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN" 2>/dev/null; then
ip6tables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE_V6" -j ACCEPT 2>/dev/null || \
ip6tables -I FORWARD 1 -i "$interface" -o "$NETWORK_DEVICE_V6" -j ACCEPT
fi
ip6tables -C FORWARD -i "$NETWORK_DEVICE_V6" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
ip6tables -I FORWARD 2 -i "$NETWORK_DEVICE_V6" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
else
warn "no ipv6 uplink detected; skipping ipv6 nat/forwarding rules for $interface"
fi
ip6tables -C FORWARD -i "$NETWORK_DEVICE" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
ip6tables -I FORWARD 2 -i "$NETWORK_DEVICE" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
save_iptables_rules
}
@@ -517,6 +556,34 @@ apply_smtps_465_rate_limit() {
###############################################################################
NETWORK_FIREWALL_COMMENT="NYM-NETWORK-FW"
HOST_SSH_PORT="${HOST_SSH_PORT:-22}"
NETWORK_FIREWALL_TCP_PORTS=("$HOST_SSH_PORT" 80 443 1789 1790 8080 9000 9001 41264)
NETWORK_FIREWALL_UDP_PORTS=(4443 51822 51264)
NETWORK_FIREWALL_WG_TCP_PORT=51830
validate_port_value() {
local name="$1"
local value="$2"
if ! [[ "$value" =~ ^[0-9]+$ ]] || (( 10#$value < 1 || 10#$value > 65535 )); then
error "invalid ${name}='${value}'. expected integer 1-65535"
exit 1
fi
}
validate_port_value "HOST_SSH_PORT" "$HOST_SSH_PORT"
build_network_firewall_status_pattern() {
local patterns=("$NETWORK_FIREWALL_COMMENT")
local port
for port in "${NETWORK_FIREWALL_TCP_PORTS[@]}" "${NETWORK_FIREWALL_UDP_PORTS[@]}" "$NETWORK_FIREWALL_WG_TCP_PORT"; do
patterns+=("dpt:${port}")
done
local IFS='|'
printf '%s' "${patterns[*]}"
}
delete_managed_input_rules() {
local cmd="$1"
@@ -554,22 +621,19 @@ configure_network_firewall() {
delete_managed_input_rules iptables
delete_managed_input_rules ip6tables
local tcp_ports=(22 80 443 1789 1790 8080 9000 9001 41264)
local udp_ports=(4443 51822 51264)
local port
for port in "${tcp_ports[@]}"; do
for port in "${NETWORK_FIREWALL_TCP_PORTS[@]}"; do
add_input_port_rule iptables "$port" tcp
add_input_port_rule ip6tables "$port" tcp
done
for port in "${udp_ports[@]}"; do
for port in "${NETWORK_FIREWALL_UDP_PORTS[@]}"; do
add_input_port_rule iptables "$port" udp
add_input_port_rule ip6tables "$port" udp
done
add_input_port_rule iptables 51830 tcp "$WG_INTERFACE"
add_input_port_rule ip6tables 51830 tcp "$WG_INTERFACE"
add_input_port_rule iptables "$NETWORK_FIREWALL_WG_TCP_PORT" tcp "$WG_INTERFACE"
add_input_port_rule ip6tables "$NETWORK_FIREWALL_WG_TCP_PORT" tcp "$WG_INTERFACE"
save_iptables_rules
ok "host network firewall configuration completed"
@@ -577,23 +641,25 @@ configure_network_firewall() {
show_network_firewall_status() {
info "managed host network firewall rules"
local status_pattern
status_pattern="$(build_network_firewall_status_pattern)"
echo
info "ipv4 input rules:"
iptables -L INPUT -n -v --line-numbers | grep -E "($NETWORK_FIREWALL_COMMENT|dpt:22|dpt:80|dpt:443|dpt:1789|dpt:1790|dpt:8080|dpt:9000|dpt:9001|dpt:51822|dpt:51830|dpt:41264|dpt:51264)" || true
iptables -L INPUT -n -v --line-numbers | grep -E "(${status_pattern})" || true
echo
info "ipv6 input rules:"
ip6tables -L INPUT -n -v --line-numbers | grep -E "($NETWORK_FIREWALL_COMMENT|dpt:22|dpt:80|dpt:443|dpt:1789|dpt:1790|dpt:8080|dpt:9000|dpt:9001|dpt:51822|dpt:51830|dpt:41264|dpt:51264)" || true
ip6tables -L INPUT -n -v --line-numbers | grep -E "(${status_pattern})" || true
}
test_network_firewall_rules() {
info "testing host network firewall rules"
local failures=0
local tcp_ports=(22 80 443 1789 1790 8080 9000 9001 41264)
local udp_ports=(51822 51264)
local port
for port in "${tcp_ports[@]}"; do
for port in "${NETWORK_FIREWALL_TCP_PORTS[@]}"; do
if iptables -C INPUT -p tcp --dport "$port" -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
ok "ipv4 tcp port $port allowed"
else
@@ -609,7 +675,7 @@ test_network_firewall_rules() {
fi
done
for port in "${udp_ports[@]}"; do
for port in "${NETWORK_FIREWALL_UDP_PORTS[@]}"; do
if iptables -C INPUT -p udp --dport "$port" -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
ok "ipv4 udp port $port allowed"
else
@@ -625,17 +691,17 @@ test_network_firewall_rules() {
fi
done
if iptables -C INPUT -i "$WG_INTERFACE" -p tcp --dport 51830 -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
ok "ipv4 tcp port 51830 allowed on $WG_INTERFACE"
if iptables -C INPUT -i "$WG_INTERFACE" -p tcp --dport "$NETWORK_FIREWALL_WG_TCP_PORT" -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
ok "ipv4 tcp port $NETWORK_FIREWALL_WG_TCP_PORT allowed on $WG_INTERFACE"
else
error "ipv4 tcp port 51830 missing on $WG_INTERFACE"
error "ipv4 tcp port $NETWORK_FIREWALL_WG_TCP_PORT missing on $WG_INTERFACE"
((failures++))
fi
if ip6tables -C INPUT -i "$WG_INTERFACE" -p tcp --dport 51830 -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
ok "ipv6 tcp port 51830 allowed on $WG_INTERFACE"
if ip6tables -C INPUT -i "$WG_INTERFACE" -p tcp --dport "$NETWORK_FIREWALL_WG_TCP_PORT" -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
ok "ipv6 tcp port $NETWORK_FIREWALL_WG_TCP_PORT allowed on $WG_INTERFACE"
else
error "ipv6 tcp port 51830 missing on $WG_INTERFACE"
error "ipv6 tcp port $NETWORK_FIREWALL_WG_TCP_PORT missing on $WG_INTERFACE"
((failures++))
fi
@@ -708,38 +774,50 @@ create_nym_chain() {
done < <(ip6tables -S FORWARD | grep -F " -j $NYM_CHAIN" || true)
# remove broad ACCEPT rules for wg + tun outbound so NYM-EXIT is authoritative
iptables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null || true
iptables -D FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null || true
ip6tables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null || true
ip6tables -D FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null || true
iptables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V4" -j ACCEPT 2>/dev/null || true
iptables -D FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V4" -j ACCEPT 2>/dev/null || true
if has_ipv6_uplink; then
ip6tables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V6" -j ACCEPT 2>/dev/null || true
ip6tables -D FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V6" -j ACCEPT 2>/dev/null || true
fi
# install the correct hook for both wg + tun
iptables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN"
iptables -I FORWARD 1 -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN"
iptables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN"
iptables -I FORWARD 1 -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN"
ip6tables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN"
ip6tables -I FORWARD 1 -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN"
ok "NYM-EXIT chain ready + FORWARD hooks installed for $WG_INTERFACE and $TUNNEL_INTERFACE"
if has_ipv6_uplink; then
ip6tables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN"
ip6tables -I FORWARD 1 -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN"
ok "NYM-EXIT chain ready + IPv4/IPv6 FORWARD hooks installed for $WG_INTERFACE and $TUNNEL_INTERFACE"
else
warn "no ipv6 uplink detected; installing only IPv4 FORWARD hooks for $WG_INTERFACE and $TUNNEL_INTERFACE"
ok "NYM-EXIT chain ready + IPv4 FORWARD hooks installed for $WG_INTERFACE and $TUNNEL_INTERFACE"
fi
}
setup_nat_rules() {
info "setting up nat and forwarding rules for $WG_INTERFACE via $NETWORK_DEVICE"
info "setting up nat and forwarding rules for $WG_INTERFACE via ipv4 uplink $NETWORK_DEVICE_V4${NETWORK_DEVICE_V6:+ and ipv6 uplink $NETWORK_DEVICE_V6}"
if ! iptables -t nat -C POSTROUTING -o "$NETWORK_DEVICE" -j MASQUERADE 2>/dev/null; then
iptables -t nat -A POSTROUTING -o "$NETWORK_DEVICE" -j MASQUERADE
if ! iptables -t nat -C POSTROUTING -o "$NETWORK_DEVICE_V4" -j MASQUERADE 2>/dev/null; then
iptables -t nat -A POSTROUTING -o "$NETWORK_DEVICE_V4" -j MASQUERADE
fi
if ! ip6tables -t nat -C POSTROUTING -o "$NETWORK_DEVICE" -j MASQUERADE 2>/dev/null; then
ip6tables -t nat -A POSTROUTING -o "$NETWORK_DEVICE" -j MASQUERADE
if has_ipv6_uplink; then
if ! ip6tables -t nat -C POSTROUTING -o "$NETWORK_DEVICE_V6" -j MASQUERADE 2>/dev/null; then
ip6tables -t nat -A POSTROUTING -o "$NETWORK_DEVICE_V6" -j MASQUERADE
fi
else
warn "no ipv6 uplink detected; skipping ipv6 NAT setup for $WG_INTERFACE"
fi
# keep reverse RELATED,ESTABLISHED in FORWARD for return traffic.
if ! iptables -C FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
iptables -I FORWARD 2 -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT
if ! iptables -C FORWARD -i "$NETWORK_DEVICE_V4" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
iptables -I FORWARD 2 -i "$NETWORK_DEVICE_V4" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT
fi
if ! ip6tables -C FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
ip6tables -I FORWARD 2 -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT
if has_ipv6_uplink; then
if ! ip6tables -C FORWARD -i "$NETWORK_DEVICE_V6" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
ip6tables -I FORWARD 2 -i "$NETWORK_DEVICE_V6" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT
fi
fi
}
@@ -980,10 +1058,12 @@ clear_exit_policy_rules() {
ip6tables -F "$NYM_CHAIN" 2>/dev/null || true
# remove hooks for BOTH wg + tun
iptables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true
iptables -D FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true
ip6tables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true
ip6tables -D FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true
iptables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN" 2>/dev/null || true
iptables -D FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN" 2>/dev/null || true
if has_ipv6_uplink; then
ip6tables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN" 2>/dev/null || true
ip6tables -D FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN" 2>/dev/null || true
fi
iptables -X "$NYM_CHAIN" 2>/dev/null || true
ip6tables -X "$NYM_CHAIN" 2>/dev/null || true
@@ -991,7 +1071,12 @@ clear_exit_policy_rules() {
show_exit_policy_status() {
info "nym exit policy status"
info "network device: $NETWORK_DEVICE"
info "ipv4 network device: $NETWORK_DEVICE_V4"
if has_ipv6_uplink; then
info "ipv6 network device: $NETWORK_DEVICE_V6"
else
warn "ipv6 network device: not detected"
fi
info "wireguard interface: $WG_INTERFACE"
info "tunnel interface: $TUNNEL_INTERFACE"
echo
@@ -1092,21 +1177,71 @@ firewall_rule_line() {
}
check_forward_chain() {
local output
output=$(iptables -L FORWARD -n --line-numbers)
local errors=0
if ! echo "$output" | grep -q "^1[[:space:]]\+$NYM_CHAIN"; then
error "FORWARD rule 1 is not ${NYM_CHAIN}; re-run network-tunnel-manager.sh exit_policy_install"
return 1
info "checking FORWARD hooks and reverse RELATED,ESTABLISHED rules"
if iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv4 FORWARD hook ok (wg): -i $WG_INTERFACE -o $NETWORK_DEVICE_V4 -> $NYM_CHAIN"
else
error "ipv4 FORWARD hook missing (wg): -i $WG_INTERFACE -o $NETWORK_DEVICE_V4 -> $NYM_CHAIN"
errors=1
fi
if ! echo "$output" | grep -q "ACCEPT.*state RELATED,ESTABLISHED"; then
error "FORWARD chain missing RELATED,ESTABLISHED accepts; re-run network-tunnel-manager.sh apply_iptables_rules_wg"
return 1
if iptables -C FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv4 FORWARD hook ok (tun): -i $TUNNEL_INTERFACE -o $NETWORK_DEVICE_V4 -> $NYM_CHAIN"
else
error "ipv4 FORWARD hook missing (tun): -i $TUNNEL_INTERFACE -o $NETWORK_DEVICE_V4 -> $NYM_CHAIN"
errors=1
fi
ok "FORWARD chain ordering looks good"
return 0
if iptables -C FORWARD -i "$NETWORK_DEVICE_V4" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
ok "ipv4 reverse RELATED,ESTABLISHED ok (wg): -i $NETWORK_DEVICE_V4 -o $WG_INTERFACE"
else
error "ipv4 reverse RELATED,ESTABLISHED missing (wg): -i $NETWORK_DEVICE_V4 -o $WG_INTERFACE"
errors=1
fi
if iptables -C FORWARD -i "$NETWORK_DEVICE_V4" -o "$TUNNEL_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
ok "ipv4 reverse RELATED,ESTABLISHED ok (tun): -i $NETWORK_DEVICE_V4 -o $TUNNEL_INTERFACE"
else
error "ipv4 reverse RELATED,ESTABLISHED missing (tun): -i $NETWORK_DEVICE_V4 -o $TUNNEL_INTERFACE"
errors=1
fi
if has_ipv6_uplink; then
if ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv6 FORWARD hook ok (wg): -i $WG_INTERFACE -o $NETWORK_DEVICE_V6 -> $NYM_CHAIN"
else
error "ipv6 FORWARD hook missing (wg): -i $WG_INTERFACE -o $NETWORK_DEVICE_V6 -> $NYM_CHAIN"
errors=1
fi
if ip6tables -C FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv6 FORWARD hook ok (tun): -i $TUNNEL_INTERFACE -o $NETWORK_DEVICE_V6 -> $NYM_CHAIN"
else
error "ipv6 FORWARD hook missing (tun): -i $TUNNEL_INTERFACE -o $NETWORK_DEVICE_V6 -> $NYM_CHAIN"
errors=1
fi
if ip6tables -C FORWARD -i "$NETWORK_DEVICE_V6" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
ok "ipv6 reverse RELATED,ESTABLISHED ok (wg): -i $NETWORK_DEVICE_V6 -o $WG_INTERFACE"
else
error "ipv6 reverse RELATED,ESTABLISHED missing (wg): -i $NETWORK_DEVICE_V6 -o $WG_INTERFACE"
errors=1
fi
if ip6tables -C FORWARD -i "$NETWORK_DEVICE_V6" -o "$TUNNEL_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then
ok "ipv6 reverse RELATED,ESTABLISHED ok (tun): -i $NETWORK_DEVICE_V6 -o $TUNNEL_INTERFACE"
else
error "ipv6 reverse RELATED,ESTABLISHED missing (tun): -i $NETWORK_DEVICE_V6 -o $TUNNEL_INTERFACE"
errors=1
fi
else
warn "no ipv6 uplink detected; skipping ipv6 FORWARD validation"
fi
return $errors
}
check_nym_exit_chain() {
@@ -1288,33 +1423,37 @@ test_forward_chain_hook() {
local failures=0
# verify BOTH interfaces are hooked to NYM-EXIT (IPv4 + IPv6)
if iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv4 forward hook ok (wg): -i $WG_INTERFACE -o $NETWORK_DEVICE -> $NYM_CHAIN"
# verify BOTH interfaces are hooked to NYM-EXIT for IPv4
if iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv4 forward hook ok (wg): -i $WG_INTERFACE -o $NETWORK_DEVICE_V4 -> $NYM_CHAIN"
else
error "ipv4 forward hook missing or wrong (wg)"
((failures++))
fi
if iptables -C FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv4 forward hook ok (tun): -i $TUNNEL_INTERFACE -o $NETWORK_DEVICE -> $NYM_CHAIN"
if iptables -C FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V4" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv4 forward hook ok (tun): -i $TUNNEL_INTERFACE -o $NETWORK_DEVICE_V4 -> $NYM_CHAIN"
else
error "ipv4 forward hook missing or wrong (tun)"
((failures++))
fi
if ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv6 forward hook ok (wg): -i $WG_INTERFACE -o $NETWORK_DEVICE -> $NYM_CHAIN"
else
error "ipv6 forward hook missing or wrong (wg)"
((failures++))
fi
if has_ipv6_uplink; then
if ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv6 forward hook ok (wg): -i $WG_INTERFACE -o $NETWORK_DEVICE_V6 -> $NYM_CHAIN"
else
error "ipv6 forward hook missing or wrong (wg)"
((failures++))
fi
if ip6tables -C FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv6 forward hook ok (tun): -i $TUNNEL_INTERFACE -o $NETWORK_DEVICE -> $NYM_CHAIN"
if ip6tables -C FORWARD -i "$TUNNEL_INTERFACE" -o "$NETWORK_DEVICE_V6" -j "$NYM_CHAIN" 2>/dev/null; then
ok "ipv6 forward hook ok (tun): -i $TUNNEL_INTERFACE -o $NETWORK_DEVICE_V6 -> $NYM_CHAIN"
else
error "ipv6 forward hook missing or wrong (tun)"
((failures++))
fi
else
error "ipv6 forward hook missing or wrong (tun)"
((failures++))
warn "no ipv6 uplink detected; skipping ipv6 forward hook tests"
fi
return "$failures"
@@ -1409,7 +1548,7 @@ nym_tunnel_setup() {
}
exit_policy_install() {
info "installing nym wireguard exit policy for ${WG_INTERFACE} via ${NETWORK_DEVICE}"
info "installing nym wireguard exit policy for ${WG_INTERFACE} via ipv4 uplink ${NETWORK_DEVICE_V4}${NETWORK_DEVICE_V6:+ and ipv6 uplink ${NETWORK_DEVICE_V6}}"
exit_policy_install_deps
adjust_ip_forwarding
create_nym_chain
@@ -1561,7 +1700,7 @@ tunnel and nat helpers:
check_nym_wg_tun Inspect forward chain for ${WG_INTERFACE}
check_nymtun_iptables Inspect forward chain for ${TUNNEL_INTERFACE}
configure_dns_and_icmp_wg Allow ping and dns ports on this host
fetch_and_display_ipv6 Show ipv6 on uplink ${NETWORK_DEVICE}
fetch_and_display_ipv6 Show ipv6 on uplink ${NETWORK_DEVICE_V6:-<none>}
fetch_ipv6_address_nym_tun Show global ipv6 address on ${TUNNEL_INTERFACE}
joke_through_the_mixnet Test via ${TUNNEL_INTERFACE} with joke
joke_through_wg_tunnel Test via ${WG_INTERFACE} with joke
@@ -1578,9 +1717,12 @@ exit policy manager:
Run verification tests on exit policy (options: --skip-default-reject).
environment overrides:
NETWORK_DEVICE Auto-detected uplink (e.g., eth0). Set manually if detection fails.
NETWORK_DEVICE Backward-compatible override that sets both uplinks.
NETWORK_DEVICE_V4 Auto-detected IPv4 uplink (e.g., eth0). Set manually if detection fails.
NETWORK_DEVICE_V6 Auto-detected IPv6 uplink (e.g., eth2). Optional; if unset, IPv6-specific setup is skipped.
TUNNEL_INTERFACE Default: nymtun0. Requires root privileges (sudo) to manage.
WG_INTERFACE Default: nymwg - Must match your WireGuard interface name.
HOST_SSH_PORT Default: 22. Set manually if you connect to your host's SSH daemon through another port.
EOF
status=0
+52 -8
View File
@@ -68,23 +68,60 @@ class NodeSetupCLI:
print("Without confirming the points above, we cannot continue.")
exit(1)
def _coerce_ssh_port(self, value) -> str:
sval = str(value).strip() if value is not None else ""
if not sval:
sval = "22"
if not sval.isdigit():
print(f"Invalid SSH port: {sval!r}. Expected integer 1..65535.")
raise SystemExit(1)
port = int(sval, 10)
if not 1 <= port <= 65535:
print(f"Invalid SSH port: {port}. Expected integer 1..65535.")
raise SystemExit(1)
return str(port)
def _resolve_field(args, existing, arg_name, env_key, prompt, *, default=None, validator=None):
cli_val = getattr(args, arg_name, None)
if cli_val is not None:
value = str(cli_val).strip()
elif existing.get(env_key):
value = str(existing[env_key]).strip()
else:
entered = input(prompt).strip()
value = entered if entered else (default if default is not None else "")
if validator:
value = validator(value)
return value
def ensure_env_values(self, args):
"""Collect env vars from args or prompt interactively, then save to env.sh."""
env_file = Path("env.sh")
fields = [
("hostname", "HOSTNAME", "Enter hostname (if you don't use a DNS, press enter): "),
("location", "LOCATION", "Enter node location (country code or name): "),
("email", "EMAIL", "Enter your email: "),
("moniker", "MONIKER", "Enter node public moniker (visible in explorer & NymVPN app): "),
("description", "DESCRIPTION", "Enter short node public description: "),
("hostname", "HOSTNAME", "Enter hostname (if you don't use a DNS, press enter): ", None, None),
("location", "LOCATION", "Enter node location (country code or name): ", None, None),
("email", "EMAIL", "Enter your email: ", None, None),
("moniker", "MONIKER", "Enter node public moniker (visible in explorer & NymVPN app): ", None, None),
("description", "DESCRIPTION", "Enter short node public description: ", None, None),
("host_ssh_port", "HOST_SSH_PORT", "Enter host SSH port (press enter for default port 22): ", "22", self._coerce_ssh_port),
]
existing = self._read_env_file(env_file)
updated = {}
for arg_name, key, prompt in fields:
cli_val = getattr(args, arg_name, None)
value = cli_val.strip() if cli_val else existing.get(key) or input(prompt).strip()
for arg_name, key, prompt, default, validator in fields:
value = self._resolve_field(
args,
existing,
arg_name,
key,
prompt,
default=default,
validator=validator,
)
updated[key] = value
os.environ[key] = value
@@ -639,6 +676,13 @@ class ArgParser:
install_parser.add_argument("--moniker", help="Public moniker displayed in explorer & NymVPN app")
install_parser.add_argument("--description", help="Short public description of the node")
install_parser.add_argument("--public-ip", help="External IPv4 address (autodetected if omitted)")
install_parser.add_argument(
"--host-ssh-port",
type=int,
help="Host SSH port to allow in the firewall (default: 22)",
)
install_parser.add_argument("--nym-node-binary", help="URL for nym-node binary (autodetected if omitted)")
install_parser.add_argument("--uplink-dev", help="Override uplink interface used for NAT/FORWARD (e.g., 'eth0'; autodetected if omitted)")
+4
View File
@@ -19,6 +19,8 @@ while true; do
read -rp "Enter your email: " EMAIL
read -rp "Enter node public moniker (visible in the explorer and NymVPN app): " MONIKER
read -rp "Enter node public description: " DESCRIPTION
read -rp "Enter host SSH port (press enter for default port 22): " HOST_SSH_PORT
HOST_SSH_PORT="${HOST_SSH_PORT:-22}"
# show summary table
echo -e "\nPlease confirm the values you entered:"
@@ -28,6 +30,7 @@ while true; do
printf "%-20s %s\n" "EMAIL:" "$EMAIL"
printf "%-20s %s\n" "MONIKER:" "$MONIKER"
printf "%-20s %s\n" "DESCRIPTION:" "$DESCRIPTION"
printf "%-20s %s\n" "HOST_SSH_PORT:" "$HOST_SSH_PORT"
echo "---------------------------------------"
read -rp "Are these correct? (y/n): " CONFIRM
@@ -52,6 +55,7 @@ PUBLIC_IP=${PUBLIC_IP:-""}
echo "export MONIKER=\"${MONIKER}\""
echo "export DESCRIPTION=\"${DESCRIPTION}\""
echo "export PUBLIC_IP=\"${PUBLIC_IP}\""
echo "export HOST_SSH_PORT=\"${HOST_SSH_PORT}\""
} > env.sh
echo -e "\nVariables saved to ./env.sh"
@@ -133,8 +133,7 @@ impl VersionedResponse {
ClientVersion::V9 => {
let mut resp = IpPacketResponseV8::try_from(self)?;
resp.version = nym_ip_packet_requests::v9::VERSION;
let bytes = resp.to_bytes();
bytes
resp.to_bytes()
}
}
.map_err(|err| IpPacketRouterError::FailedToSerializeResponsePacket { source: err })