From fc5d31093531dc64299cc766397de705fe1deb7e Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:04:09 +0100 Subject: [PATCH 01/83] add QUIC setup script to nym-node-cli --- scripts/nym-node-setup/nym-node-cli.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index ebae150523b..6b001df8d2e 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -33,6 +33,7 @@ def __init__(self, args): self.tunnel_manager_sh = self._check_gwx_mode() and self.fetch_script("network_tunnel_manager.sh") self.wg_ip_tables_manager_sh = self._check_gwx_mode() and self.fetch_script("wireguard-exit-policy-manager.sh") self.wg_ip_tables_test_sh = self._check_gwx_mode() and self.fetch_script("exit-policy-tests.sh") + self.wg_ip_tables_test_sh = self._check_gwx_mode() and self.fetch_script("quic_bridge_deployment.sh") def print_welcome_message(self): """Welcome user, warns for needed pre-reqs and asks for confimation""" @@ -128,7 +129,9 @@ def _return_script_url(self, script_init_name): "network_tunnel_manager.sh": f"https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/network_tunnel_manager.sh", "wireguard-exit-policy-manager.sh": f"https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh", "exit-policy-tests.sh": f"https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/exit-policy-tests.sh", + "quic_bridge_deployment.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/quic_bridge_deployment.sh" } + return scripts_urls[script_init_name] def run_script( @@ -320,6 +323,10 @@ def setup_test_wg_ip_tables(self): self.run_script(self.wg_ip_tables_manager_sh, args=["status"]) self.run_script(self.wg_ip_tables_test_sh) + def quic_bridge_deploy(self): + """Setup QUIC bridge and configuration using external script""" + self.run_script(self.quic_bridge_deployment_sh, args=["full_bridge_setup"], env=run_env) + return def run_nym_node_as_service(self): """Starts /etc/systemd/system/nym-node.service based on prompt using external script""" @@ -521,7 +528,7 @@ def run_node_installation(self,args): self.run_tunnel_manager_setup() if self.check_wg_enabled(): self.setup_test_wg_ip_tables() - self.setup_test_wg_ip_tables() + self.quic_bridge_deploy() From dea8a287e6d2ca000447957405bb2e947890b848 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:42:03 +0100 Subject: [PATCH 02/83] add arguments for env vars --- scripts/nym-node-setup/nym-node-cli.py | 215 ++++++++++++++++++------- 1 file changed, 155 insertions(+), 60 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 6b001df8d2e..b7c59dfc16a 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -22,18 +22,28 @@ class NodeSetupCLI: def __init__(self, args): self.branch = args.dev self.welcome_message = self.print_welcome_message() - self.mode = self.prompt_mode() + self.mode = self._get_or_prompt_mode(args) self.prereqs_install_sh = self.fetch_script("nym-node-prereqs-install.sh") - self.env_vars_install_sh = self.fetch_script("setup-env-vars.sh") + #self.env_vars_install_sh = self.fetch_script("setup-env-vars.sh") self.node_install_sh = self.fetch_script("nym-node-install.sh") self.service_config_sh = self.fetch_script("setup-systemd-service-file.sh") self.start_node_systemd_service_sh = self.fetch_script("start-node-systemd-service.sh") - self.landing_page_html = self._check_gwx_mode() and self.fetch_script("landing-page.html") - self.nginx_proxy_wss_sh = self._check_gwx_mode() and self.fetch_script("nginx_proxy_wss_sh") - self.tunnel_manager_sh = self._check_gwx_mode() and self.fetch_script("network_tunnel_manager.sh") - self.wg_ip_tables_manager_sh = self._check_gwx_mode() and self.fetch_script("wireguard-exit-policy-manager.sh") - self.wg_ip_tables_test_sh = self._check_gwx_mode() and self.fetch_script("exit-policy-tests.sh") - self.wg_ip_tables_test_sh = self._check_gwx_mode() and self.fetch_script("quic_bridge_deployment.sh") + self.is_gwx = self.mode == "exit-gateway" + if self.is_gwx: + self.landing_page_html = self.fetch_script("landing-page.html") + self.nginx_proxy_wss_sh = self.fetch_script("nginx_proxy_wss_sh") + self.tunnel_manager_sh = self.fetch_script("network_tunnel_manager.sh") + self.wg_ip_tables_manager_sh = self.fetch_script("wireguard-exit-policy-manager.sh") + self.wg_ip_tables_test_sh = self.fetch_script("exit-policy-tests.sh") + self.quic_bridge_deployment_sh = self.fetch_script("quic_bridge_deployment.sh") + else: + self.landing_page_html = None + self.nginx_proxy_wss_sh = None + self.tunnel_manager_sh = None + self.wg_ip_tables_manager_sh = None + self.wg_ip_tables_test_sh = None + self.quic_bridge_deployment_sh = None + def print_welcome_message(self): """Welcome user, warns for needed pre-reqs and asks for confimation""" @@ -46,7 +56,7 @@ def print_welcome_message(self): self.print_character("=", 41) msg = \ "Before you begin, make sure that:\n"\ - "1. You run this setup on Debian based Linux (ie Ubuntu)\n"\ + "1. You run this setup on Debian based Linux (ie Ubuntu 22.04 LTS)\n"\ "2. You run this installation program from a root shell\n"\ "3. You meet minimal requirements: https://nym.com/docs/operators/nodes\n"\ "4. You accept Operators Terms & Conditions: https://nym.com/operators-validators-terms\n"\ @@ -60,43 +70,99 @@ def print_welcome_message(self): else: print("Without confirming the points above, we cannot continue.") exit(1) + + 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: "), + ] + + 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() + updated[key] = value + os.environ[key] = value + + # autodetect PUBLIC_IP if not already set + if not os.environ.get("PUBLIC_IP"): + try: + ip = subprocess.run(["curl", "-fsS4", "https://ifconfig.me"], + capture_output=True, text=True, timeout=5) + if ip.returncode == 0 and ip.stdout.strip(): + updated["PUBLIC_IP"] = ip.stdout.strip() + os.environ["PUBLIC_IP"] = ip.stdout.strip() + except Exception: + pass + + # write all collected variables to env.sh in one go + self._upsert_env_vars(updated, env_file) + + print(f"[OK] Updated env.sh with {len(updated)} entries.") + + - def prompt_mode(self): - """Ask user to insert node functionality and save it in python and bash envs""" - mode = input( - "\nEnter the mode you want to run nym-node in: " - "\n1. mixnode " - "\n2. entry-gateway " - "\n3. exit-gateway (works as entry-gateway as well) " - "\nPress 1, 2 or 3 and enter:\n" - ).strip() - - if mode in ("1", "mixnode"): - mode = "mixnode" - elif mode in ("2", "entry-gateway"): - mode = "entry-gateway" - elif mode in ("3", "exit-gateway"): - mode = "exit-gateway" - else: - print("Only numbers 1, 2 or 3 are accepted.") - raise SystemExit(1) - # save mode for this Python instance - self.mode = mode - os.environ["MODE"] = mode + def _upsert_env_vars(self, updates: dict, env_file: Path = Path("env.sh")): + existing = self._read_env_file(env_file) + existing.update(updates) + with env_file.open("w") as f: + for k, v in existing.items(): + f.write(f'export {k}="{v}"\n') + os.environ.update(updates) + + def _read_env_file(self, env_file: Path) -> dict: + env = {} + if env_file.exists(): + for line in env_file.read_text().splitlines(): + if line.startswith("export ") and "=" in line: + k, v = line.replace("export ", "", 1).split("=", 1) + env[k.strip()] = v.strip().strip('"') + return env + + def _get_or_prompt_mode(self, args): + """Resolve MODE from --mode, env.sh, os.environ, or prompt; persist to env.sh.""" - # persist to env.sh so other scripts can source it env_file = Path("env.sh") - with env_file.open("a") as f: - f.write(f'export MODE="{mode}"\n') - # source env.sh so future bash subprocesses see it immediately - subprocess.run("source ./env.sh", shell=True, executable="/bin/bash") + # CLI arg + mode = getattr(args, "mode", None) + if mode: + mode = mode.strip().lower() + self._upsert_env_var("MODE", mode) + print(f"Mode set to '{mode}' from CLI argument.") + return mode + + # env.sh (replaces manual read) + existing = self._read_env_file(env_file) + mode = existing.get("MODE") + if mode: + os.environ["MODE"] = mode + return mode + + # process env + if os.environ.get("MODE"): + return os.environ["MODE"] + + # prompt + mode = input( + "\nEnter node mode (mixnode / entry-gateway / exit-gateway): " + ).strip().lower() + if mode not in ("mixnode", "entry-gateway", "exit-gateway"): + print("Invalid mode. Must be one of: mixnode, entry-gateway, exit-gateway.") + raise SystemExit(1) + self._upsert_env_var("MODE", mode) print(f"Mode set to '{mode}' — stored in env.sh and sourced for immediate use.") return mode - def fetch_script(self, script_name): """Fetches needed scripts according to a defined mode""" # print header only the first time @@ -120,7 +186,7 @@ def _return_script_url(self, script_init_name): github_raw_nymtech_nym_scripts_url = f"https://raw.githubusercontent.com/nymtech/nym/refs/heads/{self.branch}/scripts/" scripts_urls = { "nym-node-prereqs-install.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/nym-node-prereqs-install.sh", - "setup-env-vars.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/setup-env-vars.sh", + #"setup-env-vars.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/setup-env-vars.sh", "nym-node-install.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/nym-node-install.sh", "setup-systemd-service-file.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/setup-systemd-service-file.sh", "start-node-systemd-service.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/start-node-systemd-service.sh", @@ -208,27 +274,30 @@ def _check_gwx_mode(self): else: return False - def check_wg_enabled(self): - """Checks if Wireguard is enabled and if not, prompts user if they want to enable it, stores it to env.sh""" + def check_wg_enabled(self, args=None): + """Determine if WireGuard is enabled; precedence: CLI > env > env.sh > prompt. Persist normalized value.""" + env_file = os.path.join(os.getcwd(), "env.sh") - env_file = os.path.abspath(os.path.join(os.getcwd(), "env.sh")) + def norm(v): + return "true" if str(v).strip().lower() in ("true") else "false" - def norm(v): # -> "true" or "false" - return "true" if str(v).strip().lower() in ("1", "true", "yes", "y") else "false" + val = None - # precedence: process env → env.sh → prompt - val = os.environ.get("WIREGUARD") + # CLI argument + if args and getattr(args, "wireguard", None) is not None: + val = norm(getattr(args, "wireguard")) + print(f"[INFO] WireGuard mode provided via CLI: {val}") - if val is None and os.path.isfile(env_file): - try: - with open(env_file, "r", encoding="utf-8") as f: - m = re.search(r'^\s*export\s+WIREGUARD\s*=\s*"?([^"\n]+)"?', f.read(), re.M) - if m: - val = m.group(1) - except Exception: - pass + # Environment variable + val = val or os.environ.get("WIREGUARD") + # env.sh file + if val is None: + envs = self._read_env_file(Path(env_file)) + val = envs.get("WIREGUARD") + + # Prompt if val is None: ans = input( "\nWireGuard is not configured.\n" @@ -240,25 +309,24 @@ def norm(v): # -> "true" or "false" val = norm(val) os.environ["WIREGUARD"] = val - # persist to env.sh (replace or append) + # Persist to env.sh try: text = "" if os.path.isfile(env_file): - with open(env_file, "r", encoding="utf-8") as f: + with open(env_file, encoding="utf-8") as f: text = f.read() if re.search(r'^\s*export\s+WIREGUARD\s*=.*$', text, re.M): text = re.sub(r'^\s*export\s+WIREGUARD\s*=.*$', f'export WIREGUARD="{val}"', text, flags=re.M) else: - if text and not text.endswith("\n"): - text += "\n" - text += f'export WIREGUARD="{val}"\n' + text = (text.rstrip("\n") + "\n" if text else "") + f'export WIREGUARD="{val}"\n' with open(env_file, "w", encoding="utf-8") as f: f.write(text) print(f'WIREGUARD={val} saved to {env_file}') - except Exception as e: + except OSError as e: print(f"Warning: could not write {env_file}: {e}") - return (val == "true") + return val == "true" + def run_bash_command(self, command, args=None, *, env=None, cwd=None, check=True): """ @@ -517,6 +585,7 @@ def _env_with_envfile(self) -> dict: def run_node_installation(self,args): """Main function called by argparser command install running full node install flow""" + self.ensure_env_values(args) self.run_script(self.prereqs_install_sh) self.run_script(self.env_vars_install_sh) self.run_script(self.node_install_sh) @@ -565,6 +634,32 @@ def parser_main(self): help="Starts nym-node installation setup CLI", aliases=["i", "I"], add_help=True ) + install_parser.add_argument( + "--mode", + choices=["mixnode", "entry-gateway", "exit-gateway"], + help="Node mode: 'mixnode', 'entry-gateway', or 'exit-gateway'", + ) + install_parser.add_argument( + "--wireguard", + choices=["true", "false"], + help="WireGuard functionality switch: true / false" + ) + install_parser.add_argument("--hostname", help="Node domain / hostname") + install_parser.add_argument("--location", help="Node location (country code or name)") + install_parser.add_argument("--email", help="Contact email for the node operator") + 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("--nym-node-binary", help="URL for nym-node binary (autodetected if omitted)") + + # generic fallback + install_parser.add_argument( + "--env", + action="append", + metavar="KEY=VALUE", + help="(Optional) Extra ENV VARS, e.g. --env CUSTOM_KEY=value", + ) + args = parser.parse_args() From cc74d218fcdb8817238f4fa5f5cde0a253bdc741 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Tue, 11 Nov 2025 21:56:15 +0100 Subject: [PATCH 03/83] rm redundant fn --- scripts/nym-node-setup/setup-env-vars.sh | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/scripts/nym-node-setup/setup-env-vars.sh b/scripts/nym-node-setup/setup-env-vars.sh index 3eafd93ba5f..e119a61fd68 100644 --- a/scripts/nym-node-setup/setup-env-vars.sh +++ b/scripts/nym-node-setup/setup-env-vars.sh @@ -39,16 +39,6 @@ while true; do esac done -# try to get the latest binary URL (non-fatal if missing) -LATEST_BINARY=$( - curl -fsSL https://github.com/nymtech/nym/releases/latest \ - | grep -Eo 'href="/nymtech/nym/releases/download/[^"]+/nym-node"' \ - | head -n1 \ - | cut -d'"' -f2 -) -if [[ -z "${LATEST_BINARY:-}" ]]; then - echo "WARNING: Could not determine latest nym-node binary URL right now. The installer will resolve it later." -fi PUBLIC_IP=$(curl -fsS -4 https://ifconfig.me || true) PUBLIC_IP=${PUBLIC_IP:-""} From 9eca9efd823662b15d1769c76fa3425a5a8b5d1e Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Wed, 12 Nov 2025 13:45:33 +0100 Subject: [PATCH 04/83] fix direction and add test --- .../exit-policy-tests.sh | 68 ++++++++++++++++++- .../wireguard-exit-policy-manager.sh | 13 ++-- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/scripts/wireguard-exit-policy/exit-policy-tests.sh b/scripts/wireguard-exit-policy/exit-policy-tests.sh index 5ae10b177c0..e0f86e59693 100644 --- a/scripts/wireguard-exit-policy/exit-policy-tests.sh +++ b/scripts/wireguard-exit-policy/exit-policy-tests.sh @@ -47,7 +47,7 @@ test_port_range_rules() { "8087-8088:tcp:Simplify Media" "8232-8233:tcp:Zcash" "8332-8333:tcp:Bitcoin" - "18080-18081:tcp:Monero" + "18080-18081:tcp:Monero" ) local total_failures=0 @@ -146,6 +146,71 @@ test_critical_services() { return $failures } +# Test that the exit policy chain is correctly wired up to FORWARD chain +test_forward_chain_hook() { + echo -e "${YELLOW}Testing FORWARD Chain Hook...${NC}" + + local failures=0 + NETWORK_DEVICE=$(ip route show default | awk '/default/ {print $5}') + + if [[ -z "$NETWORK_DEVICE" ]]; then + echo -e "${RED}✗ Could not determine network device${NC}" + return 1 + fi + + # (incoming from nymwg, outgoing to network device) + if iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then + echo -e "${GREEN}✓ IPv4 FORWARD hook exists with correct direction: -i $WG_INTERFACE -o $NETWORK_DEVICE${NC}" + else + echo -e "${RED}✗ IPv4 FORWARD hook missing or has wrong direction${NC}" + echo -e "${YELLOW} Expected: -i $WG_INTERFACE -o $NETWORK_DEVICE -j $NYM_CHAIN${NC}" + + # Check if wrong direction exists + if iptables -C FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null; then + echo -e "${RED}✗ WRONG DIRECTION FOUND: -o $WG_INTERFACE (this matches return traffic, not client egress!)${NC}" + fi + + # Show what actually exists - specifically look for jump rules to NYM-EXIT + echo -e "${YELLOW} Current FORWARD rules that jump to $NYM_CHAIN:${NC}" + iptables -L FORWARD -n -v --line-numbers | grep -E "$NYM_CHAIN" || echo " (none found - exit policy chain is not hooked to FORWARD!)" + echo -e "${YELLOW} All FORWARD rules with $WG_INTERFACE (for reference):${NC}" + iptables -L FORWARD -n -v --line-numbers | grep -E "$WG_INTERFACE" | head -5 || echo " (none found)" + ((failures++)) + fi + + # Check IPv6 + if ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then + echo -e "${GREEN}✓ IPv6 FORWARD hook exists with correct direction: -i $WG_INTERFACE -o $NETWORK_DEVICE${NC}" + else + echo -e "${RED}✗ IPv6 FORWARD hook missing or has wrong direction${NC}" + echo -e "${YELLOW} Expected: -i $WG_INTERFACE -o $NETWORK_DEVICE -j $NYM_CHAIN${NC}" + + # Check if wrong direction exists + if ip6tables -C FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null; then + echo -e "${RED}✗ WRONG DIRECTION FOUND: -o $WG_INTERFACE (this matches return traffic, not client egress!)${NC}" + fi + + # Show what actually exists - specifically look for jump rules to NYM-EXIT + echo -e "${YELLOW} Current IPv6 FORWARD rules that jump to $NYM_CHAIN:${NC}" + ip6tables -L FORWARD -n -v --line-numbers | grep -E "$NYM_CHAIN" || echo " (none found - exit policy chain is not hooked to FORWARD!)" + echo -e "${YELLOW} All IPv6 FORWARD rules with $WG_INTERFACE (for reference):${NC}" + ip6tables -L FORWARD -n -v --line-numbers | grep -E "$WG_INTERFACE" | head -5 || echo " (none found)" + ((failures++)) + fi + + # Check rule position (should be before UFW rules) + local rule_num=$(iptables -L FORWARD -n --line-numbers | grep -E "$NYM_CHAIN.*$WG_INTERFACE.*$NETWORK_DEVICE" | awk '{print $1}' | head -1) + if [[ -n "$rule_num" ]]; then + if [[ $rule_num -le 5 ]]; then + echo -e "${GREEN}✓ Rule is early in FORWARD chain (position #$rule_num) - good for UFW compatibility${NC}" + else + echo -e "${YELLOW}⚠ Rule is later in FORWARD chain (position #$rule_num) - may conflict with UFW${NC}" + fi + fi + + return $failures +} + # Verify default reject rule exists test_default_reject_rule() { echo -e "${YELLOW}This test takes some time, do not quit the process${NC}" @@ -197,6 +262,7 @@ run_all_tests() { done local test_functions=( + "test_forward_chain_hook" "test_port_range_rules" "test_critical_services" ) diff --git a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh b/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh index bce3d09711c..b803cd65a32 100644 --- a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh +++ b/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh @@ -11,7 +11,7 @@ # - Groups rules logically for easier management # - Integrates with existing Nym node configuration # -# Usage: ./nym-exit-policy.sh [command] +# Usage: ./wireguard-exit-policy-manager.sh [command] set -e @@ -117,15 +117,15 @@ create_nym_chain() { fi # Link it to the FORWARD chain if not already linked - if ! iptables -C FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null; then + if ! iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then echo -e "${YELLOW}Linking $NYM_CHAIN to FORWARD chain...${NC}" - iptables -A FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" + iptables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" fi # Link IPv6 chain - if ! ip6tables -C FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null; then + if ! ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then echo -e "${YELLOW}Linking $NYM_CHAIN to IPv6 FORWARD chain...${NC}" - ip6tables -A FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" + ip6tables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" fi } @@ -313,6 +313,7 @@ apply_port_allowlist() { ["IMAPOverTLS"]="993" ["POP3OverTLS"]="995" ["OpenVPN"]="1194" + ["WireGuardPeer"]="51820-51822" ["QTServerAdmin"]="1220" ["PKTKRB"]="1293" ["MSSQL"]="1433" @@ -418,7 +419,9 @@ clear_rules() { # Remove the chain from FORWARD if it exists iptables -D FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null || true + iptables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true ip6tables -D FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null || true + ip6tables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true # Delete the chains iptables -X "$NYM_CHAIN" 2>/dev/null || true From bdacc7200378e49eb04236aedf8e749cda4085b4 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:22:26 +0100 Subject: [PATCH 05/83] rm redundant --- scripts/nym-node-setup/nym-node-cli.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index b7c59dfc16a..06b8ef7d5f3 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -24,7 +24,6 @@ def __init__(self, args): self.welcome_message = self.print_welcome_message() self.mode = self._get_or_prompt_mode(args) self.prereqs_install_sh = self.fetch_script("nym-node-prereqs-install.sh") - #self.env_vars_install_sh = self.fetch_script("setup-env-vars.sh") self.node_install_sh = self.fetch_script("nym-node-install.sh") self.service_config_sh = self.fetch_script("setup-systemd-service-file.sh") self.start_node_systemd_service_sh = self.fetch_script("start-node-systemd-service.sh") @@ -186,7 +185,6 @@ def _return_script_url(self, script_init_name): github_raw_nymtech_nym_scripts_url = f"https://raw.githubusercontent.com/nymtech/nym/refs/heads/{self.branch}/scripts/" scripts_urls = { "nym-node-prereqs-install.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/nym-node-prereqs-install.sh", - #"setup-env-vars.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/setup-env-vars.sh", "nym-node-install.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/nym-node-install.sh", "setup-systemd-service-file.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/setup-systemd-service-file.sh", "start-node-systemd-service.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/start-node-systemd-service.sh", From b56d9505e6c954446f8e7eeb5295bb60b8895e37 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:31:08 +0100 Subject: [PATCH 06/83] address comments --- scripts/nym-node-setup/nym-node-cli.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 06b8ef7d5f3..6bbe7bd5e41 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -135,7 +135,7 @@ def _get_or_prompt_mode(self, args): mode = getattr(args, "mode", None) if mode: mode = mode.strip().lower() - self._upsert_env_var("MODE", mode) + self._upsert_env_vars("MODE", mode) print(f"Mode set to '{mode}' from CLI argument.") return mode @@ -158,7 +158,7 @@ def _get_or_prompt_mode(self, args): print("Invalid mode. Must be one of: mixnode, entry-gateway, exit-gateway.") raise SystemExit(1) - self._upsert_env_var("MODE", mode) + self._upsert_env_vars("MODE", mode) print(f"Mode set to '{mode}' — stored in env.sh and sourced for immediate use.") return mode @@ -278,7 +278,7 @@ def check_wg_enabled(self, args=None): env_file = os.path.join(os.getcwd(), "env.sh") def norm(v): - return "true" if str(v).strip().lower() in ("true") else "false" + return "true" if str(v).strip().lower() == ("true") else "false" val = None @@ -391,8 +391,7 @@ def setup_test_wg_ip_tables(self): def quic_bridge_deploy(self): """Setup QUIC bridge and configuration using external script""" - self.run_script(self.quic_bridge_deployment_sh, args=["full_bridge_setup"], env=run_env) - return + self.run_script(self.quic_bridge_deployment_sh, args=["full_bridge_setup"]) def run_nym_node_as_service(self): """Starts /etc/systemd/system/nym-node.service based on prompt using external script""" From 0453345d65e60f828b1fa09af04a99b75f6142c8 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:32:41 +0100 Subject: [PATCH 07/83] address comments --- scripts/nym-node-setup/nym-node-cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 6bbe7bd5e41..c4ef41ca958 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -584,7 +584,6 @@ def run_node_installation(self,args): """Main function called by argparser command install running full node install flow""" self.ensure_env_values(args) self.run_script(self.prereqs_install_sh) - self.run_script(self.env_vars_install_sh) self.run_script(self.node_install_sh) self.run_script(self.service_config_sh) self._check_gwx_mode() and self.run_script(self.nginx_proxy_wss_sh) From a8086675d9268207202f3281c9c036275aba7c49 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:34:00 +0100 Subject: [PATCH 08/83] metadata port inside nymwg --- scripts/nym-node-setup/nym-node-prereqs-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/nym-node-setup/nym-node-prereqs-install.sh b/scripts/nym-node-setup/nym-node-prereqs-install.sh index 63f3264ff01..58b04ecc65c 100644 --- a/scripts/nym-node-setup/nym-node-prereqs-install.sh +++ b/scripts/nym-node-setup/nym-node-prereqs-install.sh @@ -24,6 +24,7 @@ ufw allow 8080/tcp # Nym specific - nym-node-api ufw allow 9000/tcp # Nym Specific - clients port ufw allow 9001/tcp # Nym specific - wss port ufw allow 51822/udp # WireGuard +ufw allow in on nymwg to any port 51830 proto tcp # bandwidth queries/topup - inside the tunnel ufw allow 'Nginx Full' && \ ufw reload && \ ufw status From 73a34935aef7b6acc3ce8dc4ba80a14fae371076 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 12 Nov 2025 16:29:52 +0100 Subject: [PATCH 09/83] trims --- scripts/nym-node-setup/nym-node-cli.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index c4ef41ca958..a56d46e84c2 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -267,10 +267,7 @@ def _write_temp_script(self, script_text: str) -> Path: def _check_gwx_mode(self): """Helper: Several fns run only for GWx - this fn checks this condition""" - if self.mode == "exit-gateway": - return True - else: - return False + return self.mode == "exit-gateway" def check_wg_enabled(self, args=None): """Determine if WireGuard is enabled; precedence: CLI > env > env.sh > prompt. Persist normalized value.""" @@ -609,7 +606,7 @@ def parser_main(self): version=f"nym-node-cli {__version__}" ) parent.add_argument("-d", "--dev", metavar="BRANCH", - help="Define github branch", + help="Define github branch (default: develop)", type=str, default=argparse.SUPPRESS) parent.add_argument("-v", "--verbose", action="store_true", @@ -625,7 +622,7 @@ def parser_main(self): subparsers = parser.add_subparsers(dest="command", help="subcommands") subparsers.required = True - p_install = subparsers.add_parser( + install_parser = subparsers.add_parser( "install", parents=[parent], help="Starts nym-node installation setup CLI", aliases=["i", "I"], add_help=True @@ -636,7 +633,7 @@ def parser_main(self): help="Node mode: 'mixnode', 'entry-gateway', or 'exit-gateway'", ) install_parser.add_argument( - "--wireguard", + "--wireguard-enabled", choices=["true", "false"], help="WireGuard functionality switch: true / false" ) From 5a26fa262e0f39b9d9fd2e9e3822822bff970554 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 12 Nov 2025 16:58:10 +0100 Subject: [PATCH 10/83] add uplink override arg --- scripts/nym-node-setup/nym-node-cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index a56d46e84c2..2732f367561 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -644,6 +644,7 @@ def parser_main(self): 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("--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)") # generic fallback install_parser.add_argument( From 66797efa805b0260901d02ccea0c651638c5e0db Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Wed, 12 Nov 2025 17:18:59 +0100 Subject: [PATCH 11/83] test new order of events.. --- .../wireguard-exit-policy-manager.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh b/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh index b803cd65a32..5a31d8ed540 100644 --- a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh +++ b/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh @@ -282,6 +282,16 @@ add_default_reject_rule() { apply_port_allowlist() { echo -e "${YELLOW}Applying allowed ports...${NC}" + # Insert DNS rules at the very beginning of NYM-EXIT chain, this ensures DNS queries are never blocked by REJECT rules + echo -e "${YELLOW}Ensuring DNS is at the beginning of $NYM_CHAIN...${NC}" + + iptables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT + iptables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT + ip6tables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT + ip6tables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT + + echo -e "${GREEN}✓ DNS rules inserted at beginning of $NYM_CHAIN${NC}" + # Dictionary of services and their ports declare -A PORT_MAPPINGS=( ["FTP"]="20-21" @@ -383,8 +393,6 @@ apply_port_allowlist() { add_port_rules ip6tables "$port" "udp" done - add_default_reject_rule - echo -e "${GREEN}Port allowlist applied successfully.${NC}" } @@ -643,8 +651,11 @@ main() { create_nym_chain setup_nat_rules configure_dns_and_icmp - apply_spamhaus_blocklist + # Apply allowlist first (so DNS and other allowed ports are not blocked) apply_port_allowlist + # Then apply blocklist (aka the REJECT rules) + apply_spamhaus_blocklist + add_default_reject_rule save_rules echo -e "${GREEN}Nym exit policy installed successfully.${NC}" ;; From 58083df0b092f96fce83a58b77917e676feed3da Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 12 Nov 2025 17:22:43 +0100 Subject: [PATCH 12/83] fix QUIC helper script --- .../nym-node-setup/quic_bridge_deployment.sh | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/scripts/nym-node-setup/quic_bridge_deployment.sh b/scripts/nym-node-setup/quic_bridge_deployment.sh index f7dc60e0f9f..1d409585782 100644 --- a/scripts/nym-node-setup/quic_bridge_deployment.sh +++ b/scripts/nym-node-setup/quic_bridge_deployment.sh @@ -32,6 +32,7 @@ log() { # simple redirection that keeps function scope intact add_log_redirection() { + exec 3>&1 4>&2 exec > >(tee -a "$LOG_FILE") 2>&1 } add_log_redirection @@ -47,7 +48,17 @@ NYM_ETC_BRIDGES="$NYM_ETC_DIR/bridges.toml" NYM_ETC_CLIENT_PARAMS_DEFAULT="$NYM_ETC_DIR/client_bridge_params.json" SERVICE_FILE="/etc/systemd/system/nym-bridge.service" -NET_DEV="$(ip route show default 2>/dev/null | awk '/default/ {print $5}' || true)" +NET_DEV="${UPLINK_DEV:-}" +if [[ -z "$NET_DEV" ]]; then + NET_DEV="$(ip -o route show default 2>/dev/null | awk '{print $5}' | head -n1)" + [[ -z "$NET_DEV" ]] && NET_DEV="$(ip -o route show default table all 2>/dev/null | awk '{print $5}' | head -n1)" +fi +if [[ -z "$NET_DEV" ]]; then + echo -e "${RED}Cannot determine uplink interface. Set UPLINK_DEV.${RESET}" | tee -a "$LOG_FILE" + exit 1 +fi +info "Using uplink device: $NET_DEV" + WG_IFACE="nymwg" # Root check @@ -65,6 +76,11 @@ err() { echo -e "${RED}$1${RESET}"; } info() { echo -e "${CYAN}$1${RESET}"; } press_enter() { read -r -p "$1"; } +# Disable pauses for noninteractive mode +if [[ "${NONINTERACTIVE:-0}" == "1" ]]; then + press_enter() { :; } +fi + # Helper: detect dpkg dependency failure for libc6>=2.34 deb_depends_libc_too_old() { local v @@ -377,6 +393,11 @@ User=root ExecStart=$BRIDGE_BIN --config $NYM_ETC_BRIDGES Restart=on-failure RestartSec=5 +LimitNOFILE=65535 +ProtectSystem=full +ProtectHome=yes +PrivateTmp=yes + [Install] WantedBy=multi-user.target @@ -391,12 +412,26 @@ EOF # IPTABLES & helpers apply_bridge_iptables_rules() { title "Applying iptables rules" - iptables -I INPUT -i "$WG_IFACE" -j ACCEPT || true - ip6tables -I INPUT -i "$WG_IFACE" -j ACCEPT || true - iptables -t nat -A POSTROUTING -o "$NET_DEV" -j MASQUERADE || true - ip6tables -t nat -A POSTROUTING -o "$NET_DEV" -j MASQUERADE || true + + # Ensure stateful rules exist + iptables -C FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \ + iptables -I FORWARD 1 -m state --state RELATED,ESTABLISHED -j ACCEPT + ip6tables -C FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \ + ip6tables -I FORWARD 1 -m state --state RELATED,ESTABLISHED -j ACCEPT + + # Allow WG interface input + iptables -C INPUT -i "$WG_IFACE" -j ACCEPT 2>/dev/null || iptables -I INPUT -i "$WG_IFACE" -j ACCEPT + ip6tables -C INPUT -i "$WG_IFACE" -j ACCEPT 2>/dev/null || ip6tables -I INPUT -i "$WG_IFACE" -j ACCEPT + + # NAT (idempotent) + iptables -t nat -C POSTROUTING -o "$NET_DEV" -j MASQUERADE 2>/dev/null || \ + iptables -t nat -I POSTROUTING 1 -o "$NET_DEV" -j MASQUERADE + ip6tables -t nat -C POSTROUTING -o "$NET_DEV" -j MASQUERADE 2>/dev/null || \ + ip6tables -t nat -I POSTROUTING 1 -o "$NET_DEV" -j MASQUERADE + iptables-save > /etc/iptables/rules.v4 ip6tables-save > /etc/iptables/rules.v6 + ok "iptables rules applied." } @@ -404,6 +439,9 @@ configure_dns_and_icmp() { title "Allow ICMP and DNS" iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT || true ip6tables -A INPUT -p ipv6-icmp -j ACCEPT || true + iptables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null || iptables -I INPUT -p udp --dport 53 -j ACCEPT + ip6tables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null || ip6tables -I INPUT -p udp --dport 53 -j ACCEPT + ok "ICMP and DNS rules applied." } @@ -429,6 +467,8 @@ full_bridge_setup() { echo "" echo "Step 2/6: Installing bridge binary..." install_bridge_binary + echo "[Bridge Install] $(date '+%F %T') $( $BRIDGE_BIN --version 2>/dev/null || echo 'nym-bridge (unknown)')" \ + >> /var/log/nym-bridge-version.log press_enter "Press Enter to continue..." echo "" From 781afd35226f3f3d83bf306530687e9537a0fb65 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Wed, 12 Nov 2025 17:29:47 +0100 Subject: [PATCH 13/83] add arg --- scripts/nym-node-setup/nym-node-cli.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 2732f367561..0fe9fa64e0c 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -580,6 +580,9 @@ def _env_with_envfile(self) -> dict: def run_node_installation(self,args): """Main function called by argparser command install running full node install flow""" self.ensure_env_values(args) + # Pass uplink override to all helper scripts if provided + if getattr(args, "uplink_dev", None): + os.environ["UPLINK_DEV"] = args.uplink_dev self.run_script(self.prereqs_install_sh) self.run_script(self.node_install_sh) self.run_script(self.service_config_sh) From c503a5f0e8b0dce88fe0fb84e02b783542be4b73 Mon Sep 17 00:00:00 2001 From: benedettadavico Date: Thu, 13 Nov 2025 10:21:37 +0100 Subject: [PATCH 14/83] few more tweaks --- .../wireguard-exit-policy-manager.sh | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh b/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh index 5a31d8ed540..f3288b6204c 100644 --- a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh +++ b/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh @@ -290,7 +290,12 @@ apply_port_allowlist() { ip6tables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT ip6tables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT - echo -e "${GREEN}✓ DNS rules inserted at beginning of $NYM_CHAIN${NC}" + # ICMP is needed because the probe tests DNS by pinging hostnames + iptables -I "$NYM_CHAIN" 3 -p icmp --icmp-type echo-request -j ACCEPT + iptables -I "$NYM_CHAIN" 4 -p icmp --icmp-type echo-reply -j ACCEPT + ip6tables -I "$NYM_CHAIN" 3 -p ipv6-icmp -j ACCEPT + + echo -e "${GREEN}✓ DNS and ICMP rules inserted at beginning of $NYM_CHAIN${NC}" # Dictionary of services and their ports declare -A PORT_MAPPINGS=( @@ -368,17 +373,18 @@ apply_port_allowlist() { ["Lightning"]="9735" ["NDMP"]="10000" ["OpenPGP"]="11371" - ["Monero"]="18080-18081" - ["MoneroRPC"]="18089" + ["Monero"]="18080-18081" + ["MoneroRPC"]="18089" ["GoogleVoice"]="19294" ["EnsimControlPanel"]="19638" - ["DarkFiTor"]="25551" + ["DarkFiTor"]="25551" ["Minecraft"]="25565" - ["DarkFi"]="26661" + ["DarkFi"]="26661" ["Steam"]="27000-27050" ["ElectrumSSL"]="50002" ["MOSH"]="60000-61000" ["Mumble"]="64738" + ["Metadata"]="51830" ) # Add TCP and UDP rules for each allowed port From 010b013094749db6fb3cbe04c5955b84ca9ffbf4 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 10:17:04 +0000 Subject: [PATCH 15/83] comment fix Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/nym-node-cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 0fe9fa64e0c..590717ab2c9 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -275,7 +275,7 @@ def check_wg_enabled(self, args=None): env_file = os.path.join(os.getcwd(), "env.sh") def norm(v): - return "true" if str(v).strip().lower() == ("true") else "false" + return "true" if str(v).strip().lower() == "true" else "false" val = None From ef52f25564500fe449dea2954c523009585694f2 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 11:20:13 +0100 Subject: [PATCH 16/83] address comment --- scripts/nym-node-setup/nym-node-cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 590717ab2c9..6a3f84540a7 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -158,7 +158,7 @@ def _get_or_prompt_mode(self, args): print("Invalid mode. Must be one of: mixnode, entry-gateway, exit-gateway.") raise SystemExit(1) - self._upsert_env_vars("MODE", mode) + self._upsert_env_vars({"MODE": mode}) print(f"Mode set to '{mode}' — stored in env.sh and sourced for immediate use.") return mode From 34a500d0a28c9013fe77b4a8175c3badf6224938 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:26:49 +0100 Subject: [PATCH 17/83] refactor completely --- scripts/network_tunnel_manager.sh | 290 -------- .../nym-node-setup/network-tunnel-manager.sh | 0 .../exit-policy-tests.sh | 306 -------- .../validate-exit-blocking-test.sh | 40 - .../wireguard-exit-policy-manager.sh | 699 ------------------ 5 files changed, 1335 deletions(-) delete mode 100644 scripts/network_tunnel_manager.sh create mode 100644 scripts/nym-node-setup/network-tunnel-manager.sh delete mode 100644 scripts/wireguard-exit-policy/exit-policy-tests.sh delete mode 100644 scripts/wireguard-exit-policy/validate-exit-blocking-test.sh delete mode 100644 scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh diff --git a/scripts/network_tunnel_manager.sh b/scripts/network_tunnel_manager.sh deleted file mode 100644 index 58eff34565d..00000000000 --- a/scripts/network_tunnel_manager.sh +++ /dev/null @@ -1,290 +0,0 @@ -#!/bin/bash - -network_device=$(ip route show default | awk '/default/ {print $5}') -tunnel_interface="nymtun0" -wg_tunnel_interface="nymwg" - -if ! dpkg -s iptables-persistent >/dev/null 2>&1; then - sudo apt-get update - sudo apt-get install -y iptables-persistent -else - echo "iptables-persistent is already installed." -fi - -fetch_ipv6_address() { - local interface=$1 - ipv6_global_address=$(ip -6 addr show "$interface" scope global | grep inet6 | awk '{print $2}' | head -n 1) - - if [[ -z "$ipv6_global_address" ]]; then - echo "no globally routable IPv6 address found on $interface. Please configure IPv6 or check your network settings." - exit 1 - else - echo "using IPv6 address: $ipv6_global_address" - fi -} - -fetch_and_display_ipv6() { - ipv6_address=$(ip -6 addr show "$network_device" scope global | grep inet6 | awk '{print $2}') - if [[ -z "$ipv6_address" ]]; then - echo "no global IPv6 address found on $network_device." - else - echo "IPv6 address on $network_device: $ipv6_address" - fi -} - -remove_duplicate_rules() { - local interface=$1 - local script_name=$(basename "$0") - - if [[ -z "$interface" ]]; then - echo "error: no interface specified. please enter the interface (nymwg or nymtun0):" - read -r interface - fi - - if [[ "$interface" != "nymwg" && "$interface" != "nymtun0" ]]; then - echo "error: invalid interface '$interface'. allowed values are 'nymwg' or 'nymtun0'." >&2 - exit 1 - fi - - echo "removing duplicate rules for $interface..." - - iptables-save | grep "$interface" | while read -r line; do - sudo iptables -D ${line#-A } || echo "Failed to delete rule: $line" - done - - ip6tables-save | grep "$interface" | while read -r line; do - sudo ip6tables -D ${line#-A } || echo "Failed to delete rule: $line" - done - - echo "duplicates removed for $interface." - echo "!!-important-!! you need to now reapply the iptables rules for $interface." - if [ "$interface" == "nymwg" ]; then - echo "run: ./$script_name apply_iptables_rules_wg" - else - echo "run: ./$script_name apply_iptables_rules" - fi -} - -adjust_ip_forwarding() { - ipv6_forwarding_setting="net.ipv6.conf.all.forwarding=1" - ipv4_forwarding_setting="net.ipv4.ip_forward=1" - - # remove duplicate entries for these settings from the file - sudo sed -i "/^net.ipv6.conf.all.forwarding=/d" /etc/sysctl.conf - sudo sed -i "/^net.ipv4.ip_forward=/d" /etc/sysctl.conf - - echo "$ipv6_forwarding_setting" | sudo tee -a /etc/sysctl.conf - echo "$ipv4_forwarding_setting" | sudo tee -a /etc/sysctl.conf - - sudo sysctl -p /etc/sysctl.conf - -} - -apply_iptables_rules() { - local interface=$1 - echo "applying IPtables rules for $interface..." - sleep 2 - - sudo iptables -t nat -A POSTROUTING -o "$network_device" -j MASQUERADE - sudo iptables -A FORWARD -i "$interface" -o "$network_device" -j ACCEPT - sudo iptables -A FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT - - sudo ip6tables -t nat -A POSTROUTING -o "$network_device" -j MASQUERADE - sudo ip6tables -A FORWARD -i "$interface" -o "$network_device" -j ACCEPT - sudo ip6tables -A FORWARD -i "$network_device" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT - - sudo iptables-save | sudo tee /etc/iptables/rules.v4 - sudo ip6tables-save | sudo tee /etc/iptables/rules.v6 -} - -check_tunnel_iptables() { - local interface=$1 - echo "inspecting IPtables rules for $interface..." - echo "---------------------------------------" - echo "IPv4 rules:" - iptables -L FORWARD -v -n | awk -v dev="$interface" '/^Chain FORWARD/ || $0 ~ dev || $0 ~ "ufw-reject-forward"' - echo "---------------------------------------" - echo "IPv6 rules:" - ip6tables -L FORWARD -v -n | awk -v dev="$interface" '/^Chain FORWARD/ || $0 ~ dev || $0 ~ "ufw6-reject-forward"' -} - -check_ipv6_ipv4_forwarding() { - result_ipv4=$(cat /proc/sys/net/ipv4/ip_forward) - result_ipv6=$(cat /proc/sys/net/ipv6/conf/all/forwarding) - echo "IPv4 forwarding is $([ "$result_ipv4" == "1" ] && echo "enabled" || echo "not enabled")." - echo "IPv6 forwarding is $([ "$result_ipv6" == "1" ] && echo "enabled" || echo "not enabled")." -} - -check_ip_routing() { - echo "IPv4 routing table:" - ip route - echo "---------------------------------------" - echo "IPv6 routing table:" - ip -6 route -} - -perform_pings() { - echo "performing IPv4 ping to google.com..." - ping -c 4 google.com - echo "---------------------------------------" - echo "performing IPv6 ping to google.com..." - ping6 -c 4 google.com -} - -joke_through_tunnel() { - local interface=$1 - local green="\033[0;32m" - local reset="\033[0m" - local red="\033[0;31m" - local yellow="\033[0;33m" - - sleep 1 - echo - echo -e "${yellow}checking tunnel connectivity and fetching a joke for $interface...${reset}" - echo -e "${yellow}if these test succeeds, it confirms your machine can reach the outside world via IPv4 and IPv6.${reset}" - echo -e "${yellow}however, probes and external clients may experience different connectivity to your nym-node.${reset}" - - ipv4_address=$(ip addr show "$interface" | awk '/inet / {print $2}' | cut -d'/' -f1) - ipv6_address=$(ip addr show "$interface" | awk '/inet6 / && $2 !~ /^fe80/ {print $2}' | cut -d'/' -f1) - - if [[ -z "$ipv4_address" && -z "$ipv6_address" ]]; then - echo -e "${red}no IP address found on $interface. unable to fetch a joke.${reset}" - echo -e "${red}please verify your tunnel configuration and ensure the interface is up.${reset}" - return 1 - fi - - if [[ -n "$ipv4_address" ]]; then - echo - echo -e "------------------------------------" - echo -e "detected IPv4 address: $ipv4_address" - echo -e "testing IPv4 connectivity..." - echo - - if ping -c 1 -I "$ipv4_address" google.com >/dev/null 2>&1; then - echo -e "${green}IPv4 connectivity is working. fetching a joke...${reset}" - joke=$(curl -s -H "Accept: application/json" --interface "$ipv4_address" https://icanhazdadjoke.com/ | jq -r .joke) - [[ -n "$joke" && "$joke" != "null" ]] && echo -e "${green}IPv4 joke: $joke${reset}" || echo -e "failed to fetch a joke via IPv4." - else - echo -e "${red}IPv4 connectivity is not working for $interface. verify your routing and NAT settings.${reset}" - fi - else - echo -e "${red}no IPv4 address found on $interface. unable to fetch a joke via IPv4.${reset}" - fi - - if [[ -n "$ipv6_address" ]]; then - echo - echo -e "------------------------------------" - echo -e "detected IPv6 address: $ipv6_address" - echo -e "testing IPv6 connectivity..." - echo - - if ping6 -c 1 -I "$ipv6_address" google.com >/dev/null 2>&1; then - echo -e "${green}IPv6 connectivity is working. fetching a joke...${reset}" - joke=$(curl -s -H "Accept: application/json" --interface "$ipv6_address" https://icanhazdadjoke.com/ | jq -r .joke) - [[ -n "$joke" && "$joke" != "null" ]] && echo -e "${green}IPv6 joke: $joke${reset}" || echo -e "${red}failed to fetch a joke via IPv6.${reset}" - else - echo -e "${red}IPv6 connectivity is not working for $interface. verify your routing and NAT settings.${reset}" - fi - else - echo -e "${red}no IPv6 address found on $interface. unable to fetch a joke via IPv6.${reset}" - fi - - echo -e "${green}joke fetching processes completed for $interface.${reset}" - echo -e "------------------------------------" - - sleep 3 - echo - echo - echo -e "${yellow}### connectivity testing recommendations ###${reset}" - echo -e "${yellow}- use the following command to test WebSocket connectivity from an external client:${reset}" - echo -e "${yellow} wscat -c wss://:9001 ${reset}" - echo -e "${yellow}- test UDP connectivity on port 51822 (commonly used for nym wireguard) ${reset}" - echo -e "${yellow} from another machine, use tools like nc or socat to send UDP packets ${reset}" - echo -e "${yellow} echo 'test message' | nc -u 51822 ${reset}" - echo -e "${yellow}if connectivity issues persist, ensure port forwarding and firewall rules are correctly configured ${reset}" - echo -} - - -configure_dns_and_icmp_wg() { - echo "allowing icmp (ping)..." - sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT - sudo iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT - - echo "allowing dns over udp (port 53)..." - sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT - - echo "allowing dns over tcp (port 53)..." - sudo iptables -A INPUT -p tcp --dport 53 -j ACCEPT - - echo "saving iptables rules..." - sudo iptables-save >/etc/iptables/rules.v4 - - echo "dns and icmp configuration completed." -} - -case "$1" in -fetch_ipv6_address_nym_tun) - fetch_ipv6_address "$tunnel_interface" - ;; -fetch_and_display_ipv6) - fetch_and_display_ipv6 - ;; -apply_iptables_rules) - apply_iptables_rules "$tunnel_interface" - ;; -apply_iptables_rules_wg) - apply_iptables_rules "$wg_tunnel_interface" - ;; -check_nymtun_iptables) - check_tunnel_iptables "$tunnel_interface" - ;; -check_nym_wg_tun) - check_tunnel_iptables "$wg_tunnel_interface" - ;; -check_ipv6_ipv4_forwarding) - check_ipv6_ipv4_forwarding - ;; -check_ip_routing) - check_ip_routing - ;; -perform_pings) - perform_pings - ;; -joke_through_the_mixnet) - joke_through_tunnel "$tunnel_interface" - ;; -joke_through_wg_tunnel) - joke_through_tunnel "$wg_tunnel_interface" - ;; -configure_dns_and_icmp_wg) - configure_dns_and_icmp_wg - ;; -adjust_ip_forwarding) - adjust_ip_forwarding - ;; -remove_duplicate_rules) - remove_duplicate_rules "$2" - ;; -*) - echo "Usage: $0 [command]" - echo "Commands:" - echo " fetch_ipv6_address_nym_tun - Fetch IPv6 for nymtun0." - echo " fetch_and_display_ipv6 - Show IPv6 on default device." - echo " apply_iptables_rules - Apply IPtables rules for nymtun0." - echo " apply_iptables_rules_wg - Apply IPtables rules for nymwg." - echo " check_nymtun_iptables - Check IPtables for nymtun0." - echo " check_nym_wg_tun - Check IPtables for nymwg." - echo " check_ipv6_ipv4_forwarding - Check IPv4 and IPv6 forwarding." - echo " check_ip_routing - Display IP routing tables." - echo " perform_pings - Test IPv4 and IPv6 connectivity." - echo " joke_through_the_mixnet - Fetch a joke via nymtun0." - echo " joke_through_wg_tunnel - Fetch a joke via nymwg." - echo " configure_dns_and_icmp_wg - Allows icmp ping tests for probes alongside configuring dns" - echo " adjust_ip_forwarding - Enable IPV6 and IPV4 forwarding" - echo " remove_duplicate_rules - Remove duplicate iptables rules. Valid interfaces: nymwg, nymtun0" - exit 1 - ;; -esac - -echo "operation $1 completed successfully." diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh new file mode 100644 index 00000000000..e69de29bb2d diff --git a/scripts/wireguard-exit-policy/exit-policy-tests.sh b/scripts/wireguard-exit-policy/exit-policy-tests.sh deleted file mode 100644 index e0f86e59693..00000000000 --- a/scripts/wireguard-exit-policy/exit-policy-tests.sh +++ /dev/null @@ -1,306 +0,0 @@ -#!/bin/bash -# Nym Exit Policy Verification Unit Tests - -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[0;33m' -NC='\033[0m' - -NYM_CHAIN="NYM-EXIT" -WG_INTERFACE="nymwg" - -check_port_range_rules() { - local port_range="$1" - local protocol="${2:-tcp}" - local chain="${3:-$NYM_CHAIN}" - - # Extract start and end ports - local start_port=$(echo "$port_range" | cut -d'-' -f1) - local end_port=$(echo "$port_range" | cut -d'-' -f2) - - if iptables -t filter -C "$chain" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT 2>/dev/null; then - echo -e "${GREEN}✓ Rule exists: $chain $protocol port range $start_port:$end_port${NC}" - return 0 - else - echo -e "${RED}✗ Rule missing: $chain $protocol port range $start_port:$end_port${NC}" - - echo -e "${YELLOW}Dumping all rules in $chain:${NC}" - iptables -L "$chain" -n | grep "$protocol" - - return 1 - fi -} - -# Test port range rules -test_port_range_rules() { - echo -e "${YELLOW}Testing Port Range Rules...${NC}" - - # Select the essential port ranges for testing - local port_ranges=( - "20-21:tcp:FTP" - "80-81:tcp:HTTP" - "2082-2083:tcp:CPanel" - "5222-5223:tcp:XMPP" - "27000-27050:tcp:Steam (sampling)" - "989-990:tcp:FTP over TLS" - "5000-5005:tcp:RTP/VoIP" - "8087-8088:tcp:Simplify Media" - "8232-8233:tcp:Zcash" - "8332-8333:tcp:Bitcoin" - "18080-18081:tcp:Monero" - ) - - local total_failures=0 - - for range in "${port_ranges[@]}"; do - IFS=':' read -r port_range protocol service <<< "$range" - - # Extract start and end ports - local start_port=$(echo "$port_range" | cut -d'-' -f1) - local end_port=$(echo "$port_range" | cut -d'-' -f2) - - echo -e "${YELLOW}Testing $service $protocol port range $port_range${NC}" - - if iptables -t filter -C "$NYM_CHAIN" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT 2>/dev/null; then - echo -e "${GREEN}✓ Rule exists: $NYM_CHAIN $protocol port range $start_port:$end_port${NC}" - else - echo -e "${RED}✗ Rule missing: $NYM_CHAIN $protocol port range $start_port:$end_port${NC}" - ((total_failures++)) - - echo -e "${YELLOW}Existing rules for protocol $protocol:${NC}" - iptables -L "$NYM_CHAIN" -n | grep "$protocol" - fi - done - - if [ $total_failures -eq 0 ]; then - return 0 - else - return 1 - fi -} - -test_critical_services() { - echo -e "${YELLOW}Testing Critical Service Rules...${NC}" - - local tcp_services=( - 22 # SSH - 53 # DNS - 443 # HTTPS - 853 # DNS over TLS - 1194 # OpenVPN - ) - - local udp_services=( - 53 # DNS - 123 # NTP - 1194 # OpenVPN - ) - - local failures=0 - - # Test TCP services - for port in "${tcp_services[@]}"; do - local rule_found=false - - # First check for exact match - if iptables -t filter -C "$NYM_CHAIN" -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then - echo -e "${GREEN}✓ Rule exists: NYM-EXIT tcp port $port${NC}" - rule_found=true - else - # If not found as exact port, search for it in port ranges - # This checks if the port is covered by any range rule - if iptables-save | grep -E "^-A $NYM_CHAIN.*tcp.*dpts:" | grep -qP "dpts:(\d+:)?$port(:|\d+)" || \ - iptables-save | grep -E "^-A $NYM_CHAIN.*tcp.*dpts:" | grep -qP "dpts:$port:"; then - echo -e "${GREEN}✓ Rule exists: NYM-EXIT tcp port $port (covered by a range rule)${NC}" - rule_found=true - else - echo -e "${RED}✗ Rule missing: NYM-EXIT tcp port $port${NC}" - ((failures++)) - fi - fi - done - - # Test UDP services - similar approach - for port in "${udp_services[@]}"; do - local rule_found=false - - if iptables -t filter -C "$NYM_CHAIN" -p udp --dport "$port" -j ACCEPT 2>/dev/null; then - echo -e "${GREEN}✓ Rule exists: NYM-EXIT udp port $port${NC}" - rule_found=true - else - # If not found as exact port, search for it in port ranges - if iptables-save | grep -E "^-A $NYM_CHAIN.*udp.*dpts:" | grep -qP "dpts:(\d+:)?$port(:|\d+)" || \ - iptables-save | grep -E "^-A $NYM_CHAIN.*udp.*dpts:" | grep -qP "dpts:$port:"; then - echo -e "${GREEN}✓ Rule exists: NYM-EXIT udp port $port (covered by a range rule)${NC}" - rule_found=true - else - echo -e "${RED}✗ Rule missing: NYM-EXIT udp port $port${NC}" - ((failures++)) - fi - fi - done - - echo -e "${YELLOW}Relevant existing rules for HTTP (port 80):${NC}" - iptables-save | grep -E "$NYM_CHAIN.*tcp" | grep -E "(dpt|dpts):.*80" - - return $failures -} - -# Test that the exit policy chain is correctly wired up to FORWARD chain -test_forward_chain_hook() { - echo -e "${YELLOW}Testing FORWARD Chain Hook...${NC}" - - local failures=0 - NETWORK_DEVICE=$(ip route show default | awk '/default/ {print $5}') - - if [[ -z "$NETWORK_DEVICE" ]]; then - echo -e "${RED}✗ Could not determine network device${NC}" - return 1 - fi - - # (incoming from nymwg, outgoing to network device) - if iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${GREEN}✓ IPv4 FORWARD hook exists with correct direction: -i $WG_INTERFACE -o $NETWORK_DEVICE${NC}" - else - echo -e "${RED}✗ IPv4 FORWARD hook missing or has wrong direction${NC}" - echo -e "${YELLOW} Expected: -i $WG_INTERFACE -o $NETWORK_DEVICE -j $NYM_CHAIN${NC}" - - # Check if wrong direction exists - if iptables -C FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${RED}✗ WRONG DIRECTION FOUND: -o $WG_INTERFACE (this matches return traffic, not client egress!)${NC}" - fi - - # Show what actually exists - specifically look for jump rules to NYM-EXIT - echo -e "${YELLOW} Current FORWARD rules that jump to $NYM_CHAIN:${NC}" - iptables -L FORWARD -n -v --line-numbers | grep -E "$NYM_CHAIN" || echo " (none found - exit policy chain is not hooked to FORWARD!)" - echo -e "${YELLOW} All FORWARD rules with $WG_INTERFACE (for reference):${NC}" - iptables -L FORWARD -n -v --line-numbers | grep -E "$WG_INTERFACE" | head -5 || echo " (none found)" - ((failures++)) - fi - - # Check IPv6 - if ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${GREEN}✓ IPv6 FORWARD hook exists with correct direction: -i $WG_INTERFACE -o $NETWORK_DEVICE${NC}" - else - echo -e "${RED}✗ IPv6 FORWARD hook missing or has wrong direction${NC}" - echo -e "${YELLOW} Expected: -i $WG_INTERFACE -o $NETWORK_DEVICE -j $NYM_CHAIN${NC}" - - # Check if wrong direction exists - if ip6tables -C FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${RED}✗ WRONG DIRECTION FOUND: -o $WG_INTERFACE (this matches return traffic, not client egress!)${NC}" - fi - - # Show what actually exists - specifically look for jump rules to NYM-EXIT - echo -e "${YELLOW} Current IPv6 FORWARD rules that jump to $NYM_CHAIN:${NC}" - ip6tables -L FORWARD -n -v --line-numbers | grep -E "$NYM_CHAIN" || echo " (none found - exit policy chain is not hooked to FORWARD!)" - echo -e "${YELLOW} All IPv6 FORWARD rules with $WG_INTERFACE (for reference):${NC}" - ip6tables -L FORWARD -n -v --line-numbers | grep -E "$WG_INTERFACE" | head -5 || echo " (none found)" - ((failures++)) - fi - - # Check rule position (should be before UFW rules) - local rule_num=$(iptables -L FORWARD -n --line-numbers | grep -E "$NYM_CHAIN.*$WG_INTERFACE.*$NETWORK_DEVICE" | awk '{print $1}' | head -1) - if [[ -n "$rule_num" ]]; then - if [[ $rule_num -le 5 ]]; then - echo -e "${GREEN}✓ Rule is early in FORWARD chain (position #$rule_num) - good for UFW compatibility${NC}" - else - echo -e "${YELLOW}⚠ Rule is later in FORWARD chain (position #$rule_num) - may conflict with UFW${NC}" - fi - fi - - return $failures -} - -# Verify default reject rule exists -test_default_reject_rule() { - echo -e "${YELLOW}This test takes some time, do not quit the process${NC}" - echo - echo -e "${YELLOW}Testing Default Reject Rule...${NC}" - - # Try different patterns to detect the reject rule - if iptables -L "$NYM_CHAIN" | grep -q "REJECT.*all.*anywhere.*anywhere.*reject-with"; then - echo -e "${GREEN}✓ Default REJECT rule exists${NC}" - return 0 - elif iptables -L "$NYM_CHAIN" | grep -q "REJECT.*all -- .*everywhere.*everywhere"; then - echo -e "${GREEN}✓ Default REJECT rule exists${NC}" - return 0 - elif iptables -L "$NYM_CHAIN" | grep -q "REJECT.*all.*0.0.0.0/0.*0.0.0.0/0"; then - echo -e "${GREEN}✓ Default REJECT rule exists${NC}" - return 0 - elif iptables -n -L "$NYM_CHAIN" | grep -qE "REJECT.*all.*0.0.0.0/0.*0.0.0.0/0"; then - echo -e "${GREEN}✓ Default REJECT rule exists${NC}" - return 0 - elif iptables -L "$NYM_CHAIN" | tail -1 | grep -q "REJECT"; then - echo -e "${GREEN}✓ Default REJECT rule exists at the end of chain${NC}" - return 0 - else - echo -e "${RED}✗ Default REJECT rule missing${NC}" - # Display the last 3 rules in the chain for debugging - echo -e "${YELLOW}Last 3 rules in the chain:${NC}" - iptables -L "$NYM_CHAIN" | tail -3 - return 1 - fi -} - -run_all_tests() { - local total_failures=0 - local total_tests=0 - local skip_default_reject=false - - # Parse arguments - while [[ $# -gt 0 ]]; do - case "$1" in - --skip-default-reject) - skip_default_reject=true - shift - ;; - *) - echo -e "${RED}Unknown argument: $1${NC}" - exit 1 - ;; - esac - done - - local test_functions=( - "test_forward_chain_hook" - "test_port_range_rules" - "test_critical_services" - ) - - if [ "$skip_default_reject" = false ]; then - test_functions+=("test_default_reject_rule") - fi - - echo -e "${YELLOW}Running Nym Exit Policy Verification Tests...${NC}" - - for test_func in "${test_functions[@]}"; do - ((total_tests++)) - $test_func - if [ $? -ne 0 ]; then - ((total_failures++)) - echo -e "${RED}Test $test_func FAILED${NC}" - else - echo -e "${GREEN}Test $test_func PASSED${NC}" - fi - done - - echo -e "\n${YELLOW}Test Summary:${NC}" - echo -e "Total Tests: $total_tests" - echo -e "Failures: $total_failures" - - if [ $total_failures -eq 0 ]; then - echo -e "${GREEN}All Tests Passed Successfully!${NC}" - exit 0 - else - echo -e "${RED}Some Tests Failed. Please review the iptables configuration.${NC}" - exit 1 - fi -} - -if [[ $EUID -ne 0 ]]; then - echo -e "${RED}This script must be run as root${NC}" - exit 1 -fi - -# Run the tests -run_all_tests "$@" diff --git a/scripts/wireguard-exit-policy/validate-exit-blocking-test.sh b/scripts/wireguard-exit-policy/validate-exit-blocking-test.sh deleted file mode 100644 index e93d28a24fe..00000000000 --- a/scripts/wireguard-exit-policy/validate-exit-blocking-test.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -validate_exit_policy() { - echo "=== Nym Exit Policy Blocking Validation ===" - - # Check iptables rules - echo "Checking iptables NYM-EXIT chain:" - sudo iptables -L NYM-EXIT -v -n - - # Test IP ranges and individual IPs - test_ips=( - "5.188.10.0/24" # Blocked network range - "31.132.36.50" # Specific blocked IP - "37.9.42.100" # Another blocked IP - ) - - for target in "${test_ips[@]}"; do - echo -e "\n\e[33mTesting blocking for $target\e[0m" - - # Multiple connection test methods - methods=( - "ping -c 4 -W 2" - "curl -m 5 http://$target" - "nc -z -w 5 $target 80" - "telnet $target 80" - ) - - for method in "${methods[@]}"; do - echo -n "Testing with $method: " - if sudo timeout 5 $method >/dev/null 2>&1; then - echo -e "\e[31mFAILED: Connection succeeded (Blocking ineffective)\e[0m" - else - echo -e "\e[32mBLOCKED\e[0m" - fi - done - done -} - -# Run the test -validate_exit_policy \ No newline at end of file diff --git a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh b/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh deleted file mode 100644 index f3288b6204c..00000000000 --- a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh +++ /dev/null @@ -1,699 +0,0 @@ -#!/bin/bash -# -# Nym Wireguard Exit Policy Manager -# Version: 1.0.0 -# -# This script manages iptables rules for Nym Wireguard exit policies -# Features: -# - Implements the Nym exit policy from official documentation -# - Makes rules persistent across reboots -# - Provides commands to inspect and manage rules -# - Groups rules logically for easier management -# - Integrates with existing Nym node configuration -# -# Usage: ./wireguard-exit-policy-manager.sh [command] - -set -e - -NETWORK_DEVICE=$(ip route show default | awk '/default/ {print $5}') -WG_INTERFACE="nymwg" -NYM_CHAIN="NYM-EXIT" -POLICY_FILE="/etc/nym/exit-policy.txt" -EXIT_POLICY_LOCATION="https://nymtech.net/.wellknown/network-requester/exit-policy.txt" - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -NC='\033[0m' - -add_port_rules() { - local chain="$1" - local port="$2" - local protocol="${3:-tcp}" - - # Check if the port contains a range - if [[ "$port" == *"-"* ]]; then - # Port range handling - add as a single rule with a range - local start_port=$(echo "$port" | cut -d'-' -f1) - local end_port=$(echo "$port" | cut -d'-' -f2) - - if ! $chain -C "$NYM_CHAIN" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT 2>/dev/null; then - $chain -A "$NYM_CHAIN" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT - echo -e " ${GREEN}Added: $NYM_CHAIN $protocol port range $start_port:$end_port${NC}" - fi - else - # Single port handling - if ! $chain -C "$NYM_CHAIN" -p "$protocol" --dport "$port" -j ACCEPT 2>/dev/null; then - $chain -A "$NYM_CHAIN" -p "$protocol" --dport "$port" -j ACCEPT - echo -e " ${GREEN}Added: $NYM_CHAIN $protocol port $port${NC}" - fi - fi -} - -install_dependencies() { - if ! dpkg -s iptables-persistent >/dev/null 2>&1; then - echo -e "${YELLOW}Installing iptables-persistent...${NC}" - apt-get update - DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent - echo -e "${GREEN}iptables-persistent installed.${NC}" - else - echo -e "${GREEN}iptables-persistent is already installed.${NC}" - fi - - # Check for other required dependencies - for cmd in iptables ip6tables ip grep sed awk wget curl; do - if ! command -v "$cmd" &>/dev/null; then - echo -e "${YELLOW}Installing $cmd...${NC}" - apt-get install -y "$cmd" - fi - done -} - -configure_ip_forwarding() { - echo -e "${YELLOW}Configuring IP forwarding...${NC}" - - # Remove any existing forwarding settings to avoid duplicates - sed -i "/^net.ipv6.conf.all.forwarding=/d" /etc/sysctl.conf - sed -i "/^net.ipv4.ip_forward=/d" /etc/sysctl.conf - - # Add forwarding settings - echo "net.ipv6.conf.all.forwarding=1" | tee -a /etc/sysctl.conf - echo "net.ipv4.ip_forward=1" | tee -a /etc/sysctl.conf - - # Apply changes - sysctl -p /etc/sysctl.conf - - # Verify settings - ipv4_forwarding=$(cat /proc/sys/net/ipv4/ip_forward) - ipv6_forwarding=$(cat /proc/sys/net/ipv6/conf/all/forwarding) - - if [ "$ipv4_forwarding" == "1" ] && [ "$ipv6_forwarding" == "1" ]; then - echo -e "${GREEN}IP forwarding configured successfully.${NC}" - else - echo -e "${RED}Failed to configure IP forwarding.${NC}" - exit 1 - fi -} - -create_nym_chain() { - echo -e "${YELLOW}Creating Nym exit policy chain...${NC}" - - # Check if the chain already exists - if iptables -L "$NYM_CHAIN" &>/dev/null; then - echo -e "${YELLOW}Chain $NYM_CHAIN already exists. Flushing it...${NC}" - iptables -F "$NYM_CHAIN" - else - echo -e "${YELLOW}Creating chain $NYM_CHAIN...${NC}" - iptables -N "$NYM_CHAIN" - fi - - # Do the same for IPv6 - if ip6tables -L "$NYM_CHAIN" &>/dev/null; then - echo -e "${YELLOW}Chain $NYM_CHAIN already exists in ip6tables. Flushing it...${NC}" - ip6tables -F "$NYM_CHAIN" - else - echo -e "${YELLOW}Creating chain $NYM_CHAIN in ip6tables...${NC}" - ip6tables -N "$NYM_CHAIN" - fi - - # Link it to the FORWARD chain if not already linked - if ! iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${YELLOW}Linking $NYM_CHAIN to FORWARD chain...${NC}" - iptables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" - fi - - # Link IPv6 chain - if ! ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${YELLOW}Linking $NYM_CHAIN to IPv6 FORWARD chain...${NC}" - ip6tables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" - fi -} - -setup_nat_rules() { - echo -e "${YELLOW}Setting up NAT rules...${NC}" - - # Check if NAT rule for IPv4 exists - 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 - echo -e "${GREEN}Added IPv4 NAT rule.${NC}" - else - echo -e "${GREEN}IPv4 NAT rule already exists.${NC}" - fi - - # Check if NAT rule for IPv6 exists - 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 - echo -e "${GREEN}Added IPv6 NAT rule.${NC}" - else - echo -e "${GREEN}IPv6 NAT rule already exists.${NC}" - fi - - # Setup forwarding rules for Wireguard interface - if ! iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null; then - iptables -A FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT - echo -e "${GREEN}Added IPv4 forwarding rule (WG → Internet).${NC}" - fi - - if ! iptables -C FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then - iptables -A FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT - echo -e "${GREEN}Added IPv4 forwarding rule (Internet → WG for established connections).${NC}" - fi - - # IPv6 forwarding rules - if ! ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null; then - ip6tables -A FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT - echo -e "${GREEN}Added IPv6 forwarding rule (WG → Internet).${NC}" - fi - - if ! ip6tables -C FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then - ip6tables -A FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT - echo -e "${GREEN}Added IPv6 forwarding rule (Internet → WG for established connections).${NC}" - fi -} - -configure_dns_and_icmp() { - echo -e "${YELLOW}Configuring DNS and ICMP rules...${NC}" - - # ICMP rules for ping - if ! iptables -C INPUT -p icmp --icmp-type echo-request -j ACCEPT 2>/dev/null; then - iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT - echo -e "${GREEN}Added IPv4 ICMP rule (allow ping requests).${NC}" - fi - - if ! iptables -C OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT 2>/dev/null; then - iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT - echo -e "${GREEN}Added IPv4 ICMP rule (allow ping replies).${NC}" - fi - - # ICMPv6 rules for ping6 - if ! ip6tables -C INPUT -p ipv6-icmp -j ACCEPT 2>/dev/null; then - ip6tables -A INPUT -p ipv6-icmp -j ACCEPT - echo -e "${GREEN}Added IPv6 ICMP rule (allow ping6).${NC}" - fi - - # DNS rules - if ! iptables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null; then - iptables -A INPUT -p udp --dport 53 -j ACCEPT - echo -e "${GREEN}Added IPv4 DNS rule (UDP).${NC}" - fi - - if ! iptables -C INPUT -p tcp --dport 53 -j ACCEPT 2>/dev/null; then - iptables -A INPUT -p tcp --dport 53 -j ACCEPT - echo -e "${GREEN}Added IPv4 DNS rule (TCP).${NC}" - fi - - # IPv6 DNS rules - if ! ip6tables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null; then - ip6tables -A INPUT -p udp --dport 53 -j ACCEPT - echo -e "${GREEN}Added IPv6 DNS rule (UDP).${NC}" - fi - - if ! ip6tables -C INPUT -p tcp --dport 53 -j ACCEPT 2>/dev/null; then - ip6tables -A INPUT -p tcp --dport 53 -j ACCEPT - echo -e "${GREEN}Added IPv6 DNS rule (TCP).${NC}" - fi -} - -# Apply Spamhaus blocklist from the Nym exit policy -apply_spamhaus_blocklist() { - echo -e "${YELLOW}Applying Spamhaus blocklist...${NC}" - - # Create directory if not exists - mkdir -p "$(dirname "$POLICY_FILE")" - - # Try to download the policy file - echo -e "${YELLOW}Downloading exit policy from $EXIT_POLICY_LOCATION${NC}" - if ! wget -q "$EXIT_POLICY_LOCATION" -O "$POLICY_FILE" 2>/dev/null; then - echo -e "${RED}Failed to download exit policy. Using minimal blocklist.${NC}" - - # Create a minimal policy file with a few entries - cat >"$POLICY_FILE" </dev/null; then - iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT - fi - - # Apply IPv6 rules for IPv6 addresses - if [[ "$ip_range" == *":"* ]] && ! ip6tables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then - ip6tables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT - fi - fi - done - - echo -e "${GREEN}Blocklist applied successfully.${NC}" -} - -add_default_reject_rule() { - echo -e "${YELLOW}Adding default reject rule...${NC}" - - # First remove any existing plain reject rules (without specific destinations) - iptables -D "$NYM_CHAIN" -j REJECT 2>/dev/null || true - iptables -D "$NYM_CHAIN" -j REJECT --reject-with icmp-port-unreachable 2>/dev/null || true - ip6tables -D "$NYM_CHAIN" -j REJECT 2>/dev/null || true - ip6tables -D "$NYM_CHAIN" -j REJECT --reject-with icmp6-port-unreachable 2>/dev/null || true - - # Add the default catch-all reject rule (must be the last rule in the chain) - iptables -A "$NYM_CHAIN" -j REJECT --reject-with icmp-port-unreachable - ip6tables -A "$NYM_CHAIN" -j REJECT --reject-with icmp6-port-unreachable - - echo -e "${GREEN}Default reject rule added successfully.${NC}" -} - -apply_port_allowlist() { - echo -e "${YELLOW}Applying allowed ports...${NC}" - - # Insert DNS rules at the very beginning of NYM-EXIT chain, this ensures DNS queries are never blocked by REJECT rules - echo -e "${YELLOW}Ensuring DNS is at the beginning of $NYM_CHAIN...${NC}" - - iptables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT - iptables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT - ip6tables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT - ip6tables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT - - # ICMP is needed because the probe tests DNS by pinging hostnames - iptables -I "$NYM_CHAIN" 3 -p icmp --icmp-type echo-request -j ACCEPT - iptables -I "$NYM_CHAIN" 4 -p icmp --icmp-type echo-reply -j ACCEPT - ip6tables -I "$NYM_CHAIN" 3 -p ipv6-icmp -j ACCEPT - - echo -e "${GREEN}✓ DNS and ICMP rules inserted at beginning of $NYM_CHAIN${NC}" - - # Dictionary of services and their ports - declare -A PORT_MAPPINGS=( - ["FTP"]="20-21" - ["SSH"]="22" - ["WHOIS"]="43" - ["DNS"]="53" - ["Finger"]="79" - ["HTTP"]="80-81" - ["Kerberos"]="88" - ["POP3"]="110" - ["NTP"]="123" - ["IMAP"]="143" - ["IMAP3"]="220" - ["LDAP"]="389" - ["HTTPS"]="443" - ["SMBWindowsFileShare"]="445" - ["Kpasswd"]="464" - ["RTSP"]="554" - ["LDAPS"]="636" - ["SILC"]="706" - ["KerberosAdmin"]="749" - ["DNSOverTLS"]="853" - ["Rsync"]="873" - ["VMware"]="902-904" - ["RemoteHTTPS"]="981" - ["FTPOverTLS"]="989-990" - ["NetnewsAdmin"]="991" - ["TelnetOverTLS"]="992" - ["IMAPOverTLS"]="993" - ["POP3OverTLS"]="995" - ["OpenVPN"]="1194" - ["WireGuardPeer"]="51820-51822" - ["QTServerAdmin"]="1220" - ["PKTKRB"]="1293" - ["MSSQL"]="1433" - ["VLSILicenseManager"]="1500" - ["OracleDB"]="1521" - ["Sametime"]="1533" - ["GroupWise"]="1677" - ["PPTP"]="1723" - ["RTSPAlt"]="1755" - ["MSNP"]="1863" - ["NFS"]="2049" - ["CPanel"]="2082-2083" - ["GNUnet"]="2086-2087" - ["NBX"]="2095-2096" - ["Zephyr"]="2102-2104" - ["XboxLive"]="3074" - ["MySQL"]="3306" - ["SVN"]="3690" - ["RWHOIS"]="4321" - ["Virtuozzo"]="4643" - ["RTPVOIP"]="5000-5005" - ["MMCC"]="5050" - ["ICQ"]="5190" - ["XMPP"]="5222-5223" - ["AndroidMarket"]="5228" - ["PostgreSQL"]="5432" - ["MongoDBDefault"]="27017" - ["Electrum"]="8082" - ["SimplifyMedia"]="8087-8088" - ["Zcash"]="8232-8233" - ["Bitcoin"]="8332-8333" - ["HTTPSALT"]="8443" - ["TeamSpeak"]="8767" - ["MQTTS"]="8883" - ["HTTPProxy"]="8888" - ["TorORPort"]="9001" - ["TorDirPort"]="9030" - ["Tari"]="9053" - ["Gaming"]="9339" - ["Git"]="9418" - ["HTTPSALT2"]="9443" - ["Lightning"]="9735" - ["NDMP"]="10000" - ["OpenPGP"]="11371" - ["Monero"]="18080-18081" - ["MoneroRPC"]="18089" - ["GoogleVoice"]="19294" - ["EnsimControlPanel"]="19638" - ["DarkFiTor"]="25551" - ["Minecraft"]="25565" - ["DarkFi"]="26661" - ["Steam"]="27000-27050" - ["ElectrumSSL"]="50002" - ["MOSH"]="60000-61000" - ["Mumble"]="64738" - ["Metadata"]="51830" - ) - - # Add TCP and UDP rules for each allowed port - for service in "${!PORT_MAPPINGS[@]}"; do - port="${PORT_MAPPINGS[$service]}" - echo -e "${YELLOW}Adding rules for $service (Port: $port)${NC}" - - # Add both TCP and UDP rules for all services - add_port_rules iptables "$port" "tcp" - add_port_rules ip6tables "$port" "tcp" - add_port_rules iptables "$port" "udp" - add_port_rules ip6tables "$port" "udp" - done - - echo -e "${GREEN}Port allowlist applied successfully.${NC}" -} - -safe_iptables_rule_remove() { - local chain="$1" - local table="${2:-filter}" - local interface="$3" - - # Remove rule if it exists - if iptables -t "$table" -C "$chain" -o "$interface" -j "$NYM_CHAIN" 2>/dev/null; then - iptables -t "$table" -D "$chain" -o "$interface" -j "$NYM_CHAIN" - fi -} - -safe_ip6tables_rule_remove() { - local chain="$1" - local table="${2:-filter}" - local interface="$3" - - # Remove rule if it exists - if ip6tables -t "$table" -C "$chain" -o "$interface" -j "$NYM_CHAIN" 2>/dev/null; then - ip6tables -t "$table" -D "$chain" -o "$interface" -j "$NYM_CHAIN" - fi -} - -clear_rules() { - echo -e "${YELLOW}Clearing Nym exit policy rules...${NC}" - - # Flush all rules in the NYM-EXIT chain - iptables -F "$NYM_CHAIN" 2>/dev/null || true - ip6tables -F "$NYM_CHAIN" 2>/dev/null || true - - # Remove the chain from FORWARD if it exists - iptables -D FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null || true - iptables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true - ip6tables -D FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null || true - ip6tables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true - - # Delete the chains - iptables -X "$NYM_CHAIN" 2>/dev/null || true - ip6tables -X "$NYM_CHAIN" 2>/dev/null || true - - echo -e "${GREEN}Nym exit policy rules cleared successfully.${NC}" -} - -remove_duplicate_rules() { - local interface="$1" - - if [[ -z "$interface" ]]; then - echo -e "${RED}Error: No interface specified. Usage: $0 remove-duplicates ${NC}" >&2 - exit 1 - fi - - echo -e "${YELLOW}Detecting and removing duplicate rules for $interface...${NC}" - - # Verbose duplicate rule detection for IPv4 - echo -e "${YELLOW}Checking IPv4 duplicate rules:${NC}" - iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" | sort | uniq -d && { - echo -e "${RED}Duplicate IPv4 rules found! Removing...${NC}" - # Remove duplicates by saving unique rules - iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" | sort | uniq | while read -r rule; do - # Carefully remove duplicates - full_rule=$(echo "$rule" | sed 's/^-A/iptables -D/') - eval "$full_rule" 2>/dev/null - done - } || echo -e "${GREEN}No duplicate IPv4 rules found.${NC}" - - # Verbose duplicate rule detection for IPv6 - echo -e "${YELLOW}Checking IPv6 duplicate rules:${NC}" - ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" | sort | uniq -d && { - echo -e "${RED}Duplicate IPv6 rules found! Removing...${NC}" - # Remove duplicates by saving unique rules - ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" | sort | uniq | while read -r rule; do - # Carefully remove duplicates - full_rule=$(echo "$rule" | sed 's/^-A/ip6tables -D/') - eval "$full_rule" 2>/dev/null - done - } || echo -e "${GREEN}No duplicate IPv6 rules found.${NC}" - - # Additional verification - echo -e "\n${YELLOW}Rule verification:${NC}" - echo "IPv4 Rules:" - iptables -L FORWARD -v -n | grep "$interface" - echo "IPv6 Rules:" - ip6tables -L FORWARD -v -n | grep "$interface" - - echo -e "${GREEN}Duplicate rule removal process completed.${NC}" -} - -save_rules() { - echo -e "${YELLOW}Saving iptables rules to make them persistent...${NC}" - - if [ -d "/etc/iptables" ]; then - # For Debian/Ubuntu with iptables-persistent - iptables-save >/etc/iptables/rules.v4 - ip6tables-save >/etc/iptables/rules.v6 - echo -e "${GREEN}Rules saved to /etc/iptables/rules.v4 and /etc/iptables/rules.v6${NC}" - else - # Fallback method - iptables-save >/etc/iptables.rules - ip6tables-save >/etc/ip6tables.rules - echo -e "${GREEN}Rules saved to /etc/iptables.rules and /etc/ip6tables.rules${NC}" - - # Add loading script to rc.local if it doesn't exist - if [ ! -f "/etc/network/if-pre-up.d/iptables" ]; then - cat >/etc/network/if-pre-up.d/iptables </dev/null; then - echo -e "${RED}WARNING: Wireguard interface $WG_INTERFACE not found!${NC}" - return 1 - fi - - # Interface details - echo -e "\n${YELLOW}Interface Details:${NC}" - ip link show "$WG_INTERFACE" - - # IP Addresses - echo -e "\n${YELLOW}IP Addresses:${NC}" - ip -4 addr show dev "$WG_INTERFACE" - ip -6 addr show dev "$WG_INTERFACE" - - # Iptables Chain Status - echo -e "\n${YELLOW}Iptables Chains:${NC}" - { - echo "IPv4 Chain:" - iptables -L "$NYM_CHAIN" -n -v - echo -e "\nIPv6 Chain:" - ip6tables -L "$NYM_CHAIN" -n -v - } || echo "One or both chains not found" - - # Forwarding Status - echo -e "\n${YELLOW}IP Forwarding:${NC}" - echo "IPv4: $(cat /proc/sys/net/ipv4/ip_forward)" - echo "IPv6: $(cat /proc/sys/net/ipv6/conf/all/forwarding)" -} - -test_connectivity() { - echo -e "${YELLOW}Testing connectivity through $WG_INTERFACE...${NC}" - - # More comprehensive interface check - interface_info=$(ip link show "$WG_INTERFACE" 2>/dev/null) - - if [ -z "$interface_info" ]; then - echo -e "${RED}Interface $WG_INTERFACE not found!${NC}" - return 1 - fi - - # Check for multiple possible interface states - if ! echo "$interface_info" | grep -qE "state (UP|UNKNOWN|DORMANT)"; then - echo -e "${RED}Interface $WG_INTERFACE is not in an active state!${NC}" - echo "$interface_info" - return 1 - fi - - # Detailed interface information - echo -e "${GREEN}Interface Details:${NC}" - echo "$interface_info" - - # Get IP addresses with more robust method - ipv4_address=$(ip -4 addr show dev "$WG_INTERFACE" | grep -oP '(?<=inet\s)\d+\.\d+\.\d+\.\d+/\d+' | cut -d'/' -f1 | head -n1) - ipv6_address=$(ip -6 addr show dev "$WG_INTERFACE" scope global | grep -oP '(?<=inet6\s)[0-9a-f:]+/\d+' | cut -d'/' -f1 | head -n1) - - echo -e "${GREEN}IPv4 Address:${NC} ${ipv4_address:-Not found}" - echo -e "${GREEN}IPv6 Address:${NC} ${ipv6_address:-Not found}" - - # Connectivity tests - if [[ -n "$ipv4_address" ]]; then - echo -e "${YELLOW}Testing IPv4 connectivity from $ipv4_address...${NC}" - - # Ping test - if timeout 5 ping -c 3 -I "$ipv4_address" 8.8.8.8 >/dev/null 2>&1; then - echo -e "${GREEN}IPv4 connectivity to 8.8.8.8: Success${NC}" - else - echo -e "${RED}IPv4 connectivity to 8.8.8.8: Failed${NC}" - fi - - # DNS resolution test - if timeout 5 ping -c 3 -I "$ipv4_address" google.com >/dev/null 2>&1; then - echo -e "${GREEN}IPv4 DNS resolution: Success${NC}" - else - echo -e "${RED}IPv4 DNS resolution: Failed${NC}" - fi - - # HTTP(S) connectivity test - if command -v curl &>/dev/null; then - if timeout 5 curl -s --interface "$ipv4_address" -o /dev/null -w "%{http_code}" https://www.google.com | grep -q "200"; then - echo -e "${GREEN}IPv4 HTTPS connectivity: Success${NC}" - else - echo -e "${RED}IPv4 HTTPS connectivity: Failed${NC}" - fi - fi - else - echo -e "${RED}No IPv4 address configured on $WG_INTERFACE${NC}" - fi - - # Similar tests for IPv6 if available - if [[ -n "$ipv6_address" ]]; then - echo -e "${YELLOW}Testing IPv6 connectivity from $ipv6_address...${NC}" - - if timeout 5 ping6 -c 3 -I "$ipv6_address" 2001:4860:4860::8888 >/dev/null 2>&1; then - echo -e "${GREEN}IPv6 connectivity to Google DNS: Success${NC}" - else - echo -e "${RED}IPv6 connectivity to Google DNS: Failed${NC}" - fi - - if timeout 5 ping6 -c 3 -I "$ipv6_address" google.com >/dev/null 2>&1; then - echo -e "${GREEN}IPv6 DNS resolution: Success${NC}" - else - echo -e "${RED}IPv6 DNS resolution: Failed${NC}" - fi - - if command -v curl &>/dev/null; then - if timeout 5 curl -s --interface "$ipv6_address" -o /dev/null -w "%{http_code}" https://www.google.com | grep -q "200"; then - echo -e "${GREEN}IPv6 HTTPS connectivity: Success${NC}" - else - echo -e "${RED}IPv6 HTTPS connectivity: Failed${NC}" - fi - fi - else - echo -e "${YELLOW}No IPv6 address configured on $WG_INTERFACE${NC}" - fi - - echo -e "${GREEN}Connectivity tests completed.${NC}" -} - -main() { - # Check for root privileges - if [ "$(id -u)" -ne 0 ]; then - echo -e "${RED}This script must be run as root${NC}" >&2 - exit 1 - fi - - # Parse command-line arguments - case "$1" in - install) - install_dependencies - configure_ip_forwarding - create_nym_chain - setup_nat_rules - configure_dns_and_icmp - # Apply allowlist first (so DNS and other allowed ports are not blocked) - apply_port_allowlist - # Then apply blocklist (aka the REJECT rules) - apply_spamhaus_blocklist - add_default_reject_rule - save_rules - echo -e "${GREEN}Nym exit policy installed successfully.${NC}" - ;; - status) - show_status - ;; - test) - test_connectivity - ;; - clear) - clear_rules - echo -e "${GREEN}Nym exit policy rules cleared.${NC}" - ;; - remove-duplicates) - remove_duplicate_rules "$2" - ;; - help | --help | -h) - echo "Usage: $0 [command]" - echo "" - echo "Commands:" - echo " install Install and configure Nym exit policy" - echo " status Show current Nym exit policy status" - echo " test Test connectivity through Wireguard interface" - echo " clear Remove all Nym exit policy rules" - echo " remove-duplicates Remove duplicate iptables rules for an interface" - echo " help Show this help message" - ;; - *) - echo -e "${RED}Invalid command. Use '$0 help' for usage information.${NC}" >&2 - exit 1 - ;; - esac -} - -main "$@" From f402da8e6091b33750cde1e619d07cd29e042604 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:28:38 +0100 Subject: [PATCH 18/83] add new top manager tool --- .../nym-node-setup/network-tunnel-manager.sh | 965 ++++++++++++++++++ 1 file changed, 965 insertions(+) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index e69de29bb2d..1c869a297a0 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -0,0 +1,965 @@ +#!/bin/bash +# nym tunnel and wireguard exit policy manager +# run this script as root + +set -euo pipefail + +############################################################################### +# safety: must run as root +############################################################################### +if [ "$(id -u)" -ne 0 ]; then + echo "this script must be run as root" + exit 1 +fi + +############################################################################### +# basic config +############################################################################### + +TUNNEL_INTERFACE="${TUNNEL_INTERFACE:-nymtun0}" +WG_INTERFACE="${WG_INTERFACE:-nymwg}" + +# uplink device detection, can be overridden +NETWORK_DEVICE="${NETWORK_DEVICE:-}" +if [[ -z "$NETWORK_DEVICE" ]]; then + NETWORK_DEVICE="$(ip -o route show default 2>/dev/null | awk '{print $5}' | head -n1 || true)" +fi +if [[ -z "$NETWORK_DEVICE" ]]; then + NETWORK_DEVICE="$(ip -o route show default table all 2>/dev/null | awk '{print $5}' | head -n1 || true)" +fi +if [[ -z "$NETWORK_DEVICE" ]]; then + echo "cannot determine uplink interface. set NETWORK_DEVICE or UPLINK_DEV" + exit 1 +fi + +NYM_CHAIN="NYM-EXIT" +POLICY_FILE="/etc/nym/exit-policy.txt" +EXIT_POLICY_LOCATION="https://nymtech.net/.wellknown/network-requester/exit-policy.txt" + +# colors (no emojis) +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[0;33m' +NC='\033[0m' + +############################################################################### +# shared helpers +############################################################################### + +install_iptables_persistent() { + if ! dpkg -s iptables-persistent >/dev/null 2>&1; then + echo -e "${YELLOW}installing iptables-persistent${NC}" + apt-get update -y + DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent + else + echo -e "${GREEN}iptables-persistent is already installed${NC}" + fi +} + +adjust_ip_forwarding() { + echo -e "${YELLOW}configuring ip forwarding via /etc/sysctl.d/99-nym-forwarding.conf${NC}" + install -m 0644 /dev/null /etc/sysctl.d/99-nym-forwarding.conf + cat > /etc/sysctl.d/99-nym-forwarding.conf </dev/null || echo 0) + v6=$(cat /proc/sys/net/ipv6/conf/all/forwarding 2>/dev/null || echo 0) + + if [[ "$v4" == "1" && "$v6" == "1" ]]; then + echo -e "${GREEN}ipv4 and ipv6 forwarding enabled${NC}" + else + echo -e "${RED}warning: ip forwarding not fully enabled (ipv4=$v4 ipv6=$v6)${NC}" + fi +} + +save_iptables_rules() { + echo -e "${YELLOW}saving iptables rules to /etc/iptables${NC}" + mkdir -p /etc/iptables + iptables-save > /etc/iptables/rules.v4 + ip6tables-save > /etc/iptables/rules.v6 + echo -e "${GREEN}iptables rules saved${NC}" +} + +############################################################################### +# part 1: network tunnel manager (nymtun0 + nymwg base nat/forwarding) +############################################################################### + +fetch_ipv6_address() { + local interface=$1 + local ipv6_global_address + ipv6_global_address=$(ip -6 addr show "$interface" scope global | awk '/inet6/ {print $2}' | head -n 1) + + if [[ -z "$ipv6_global_address" ]]; then + echo "no globally routable ipv6 address found on $interface. please configure ipv6 or check your network settings" + exit 1 + else + echo "using ipv6 address: $ipv6_global_address" + fi +} + +fetch_and_display_ipv6() { + local ipv6_address + ipv6_address=$(ip -6 addr show "$NETWORK_DEVICE" scope global | awk '/inet6/ {print $2}') + if [[ -z "$ipv6_address" ]]; then + echo "no global ipv6 address found on $NETWORK_DEVICE" + else + echo "ipv6 address on $NETWORK_DEVICE: $ipv6_address" + fi +} + +# dedupe / clean-up rules for an interface in FORWARD and NYM-EXIT +# keeps a single copy of each rule +remove_duplicate_rules() { + local interface="$1" + + if [[ -z "$interface" ]]; then + echo "error: no interface specified. usage: $0 remove_duplicate_rules " + exit 1 + fi + + echo -e "${YELLOW}detecting and removing duplicate rules for $interface in FORWARD and ${NYM_CHAIN}${NC}" + + # ipv4 + local rules_v4 + rules_v4=$(iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) + if [[ -n "$rules_v4" ]]; then + echo -e "${YELLOW}processing ipv4 rules${NC}" + echo "$rules_v4" | sort | uniq -d | while read -r dup; do + echo "removing duplicate ipv4 rule: $dup" + while iptables -t filter -C ${dup#-A } 2>/dev/null; do + iptables -t filter -D ${dup#-A } || break + done + done + else + echo -e "${GREEN}no ipv4 rules found for $interface to deduplicate${NC}" + fi + + # ipv6 + local rules_v6 + rules_v6=$(ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) + if [[ -n "$rules_v6" ]]; then + echo -e "${YELLOW}processing ipv6 rules${NC}" + echo "$rules_v6" | sort | uniq -d | while read -r dup; do + echo "removing duplicate ipv6 rule: $dup" + while ip6tables -t filter -C ${dup#-A } 2>/dev/null; do + ip6tables -t filter -D ${dup#-A } || break + done + done + else + echo -e "${GREEN}no ipv6 rules found for $interface to deduplicate${NC}" + fi + + echo -e "${GREEN}duplicate rule scan completed for $interface${NC}" +} + +apply_iptables_rules() { + local interface=$1 + echo "applying iptables rules for $interface using uplink $NETWORK_DEVICE" + 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 -C FORWARD -i "$interface" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null || \ + iptables -A FORWARD -i "$interface" -o "$NETWORK_DEVICE" -j ACCEPT + + iptables -C FORWARD -i "$NETWORK_DEVICE" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \ + iptables -A FORWARD -i "$NETWORK_DEVICE" -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 + + ip6tables -C FORWARD -i "$interface" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null || \ + ip6tables -A FORWARD -i "$interface" -o "$NETWORK_DEVICE" -j ACCEPT + + ip6tables -C FORWARD -i "$NETWORK_DEVICE" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \ + ip6tables -A FORWARD -i "$NETWORK_DEVICE" -o "$interface" -m state --state RELATED,ESTABLISHED -j ACCEPT + + save_iptables_rules +} + +check_tunnel_iptables() { + local interface=$1 + echo "inspecting iptables rules for $interface" + echo "ipv4 forward chain:" + iptables -L FORWARD -v -n | awk -v dev="$interface" '/^Chain FORWARD/ || $0 ~ dev || $0 ~ "ufw-reject-forward"' + echo + echo "ipv6 forward chain:" + ip6tables -L FORWARD -v -n | awk -v dev="$interface" '/^Chain FORWARD/ || $0 ~ dev || $0 ~ "ufw6-reject-forward"' +} + +check_ipv6_ipv4_forwarding() { + local result_ipv4 result_ipv6 + result_ipv4=$(cat /proc/sys/net/ipv4/ip_forward) + result_ipv6=$(cat /proc/sys/net/ipv6/conf/all/forwarding) + echo "ipv4 forwarding is $([ "$result_ipv4" == "1" ] && echo enabled || echo not enabled)" + echo "ipv6 forwarding is $([ "$result_ipv6" == "1" ] && echo enabled || echo not enabled)" +} + +check_ip_routing() { + echo "ipv4 routing table:" + ip route + echo "---------------------------" + echo "ipv6 routing table:" + ip -6 route +} + +perform_pings() { + echo "performing ipv4 ping to google.com" + ping -c 4 google.com || echo "ipv4 ping failed" + echo "---------------------------" + echo "performing ipv6 ping to google.com" + ping6 -c 4 google.com || echo "ipv6 ping failed" +} + +joke_through_tunnel() { + local interface=$1 + local green="\033[0;32m" + local reset="\033[0m" + local red="\033[0;31m" + local yellow="\033[0;33m" + + sleep 1 + echo + echo -e "${yellow}checking tunnel connectivity and fetching a joke for $interface${reset}" + echo -e "${yellow}if this test succeeds, it confirms your machine can reach the outside world via ipv4 and ipv6${reset}" + echo -e "${yellow}probes and external clients may still see different connectivity to your nym node${reset}" + + local ipv4_address ipv6_address joke + ipv4_address=$(ip addr show "$interface" | awk '/inet / {print $2}' | cut -d'/' -f1) + ipv6_address=$(ip addr show "$interface" | awk '/inet6 / && $2 !~ /^fe80/ {print $2}' | cut -d'/' -f1) + + if [[ -z "$ipv4_address" && -z "$ipv6_address" ]]; then + echo -e "${red}no ip address found on $interface. unable to fetch a joke${reset}" + echo -e "${red}please verify your tunnel configuration and ensure the interface is up${reset}" + return 1 + fi + + if [[ -n "$ipv4_address" ]]; then + echo + echo "------------------------------------" + echo "detected ipv4 address: $ipv4_address" + echo "testing ipv4 connectivity" + echo + + if ping -c 1 -I "$ipv4_address" google.com >/dev/null 2>&1; then + echo -e "${green}ipv4 connectivity is working. fetching a joke${reset}" + joke=$(curl -s -H "Accept: application/json" --interface "$ipv4_address" https://icanhazdadjoke.com/ | jq -r .joke || true) + [[ -n "$joke" && "$joke" != "null" ]] && echo -e "${green}ipv4 joke: $joke${reset}" || echo "failed to fetch a joke via ipv4" + else + echo -e "${red}ipv4 connectivity is not working for $interface. verify your routing and nat settings${reset}" + fi + else + echo -e "${red}no ipv4 address found on $interface. unable to fetch a joke via ipv4${reset}" + fi + + if [[ -n "$ipv6_address" ]]; then + echo + echo "------------------------------------" + echo "detected ipv6 address: $ipv6_address" + echo "testing ipv6 connectivity" + echo + + if ping6 -c 1 -I "$ipv6_address" google.com >/dev/null 2>&1; then + echo -e "${green}ipv6 connectivity is working. fetching a joke${reset}" + joke=$(curl -s -H "Accept: application/json" --interface "$ipv6_address" https://icanhazdadjoke.com/ | jq -r .joke || true) + [[ -n "$joke" && "$joke" != "null" ]] && echo -e "${green}ipv6 joke: $joke${reset}" || echo -e "${red}failed to fetch a joke via ipv6${reset}" + else + echo -e "${red}ipv6 connectivity is not working for $interface. verify your routing and nat settings${reset}" + fi + else + echo -e "${red}no ipv6 address found on $interface. unable to fetch a joke via ipv6${reset}" + fi + + echo -e "${green}joke fetching processes completed for $interface${reset}" + echo "------------------------------------" + + sleep 3 + echo + echo + echo -e "${yellow}connectivity testing recommendations${reset}" + echo -e "${yellow}- from another machine use wscat to test websocket connectivity on 9001${reset}" + echo -e "${yellow}- test udp connectivity on port 51822 (wireguard)${reset}" + echo -e "${yellow}- example: echo 'test' | nc -u 51822${reset}" +} + +configure_dns_and_icmp_wg() { + echo "allowing ping (icmp) and dns on this host" + iptables -C INPUT -p icmp --icmp-type echo-request -j ACCEPT 2>/dev/null || \ + iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT + iptables -C OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT 2>/dev/null || \ + iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT + + iptables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null || \ + iptables -A INPUT -p udp --dport 53 -j ACCEPT + iptables -C INPUT -p tcp --dport 53 -j ACCEPT 2>/dev/null || \ + iptables -A INPUT -p tcp --dport 53 -j ACCEPT + + save_iptables_rules + echo "dns and icmp configuration completed" +} + +############################################################################### +# part 2: wireguard exit policy manager +############################################################################### + +add_port_rules() { + local cmd="$1" # iptables or ip6tables + local port="$2" + local protocol="${3:-tcp}" + + if [[ "$port" == *"-"* ]]; then + local start_port end_port + start_port=$(echo "$port" | cut -d'-' -f1) + end_port=$(echo "$port" | cut -d'-' -f2) + + if ! $cmd -C "$NYM_CHAIN" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT 2>/dev/null; then + $cmd -A "$NYM_CHAIN" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT + echo "added $cmd $NYM_CHAIN $protocol port range $start_port:$end_port" + fi + else + if ! $cmd -C "$NYM_CHAIN" -p "$protocol" --dport "$port" -j ACCEPT 2>/dev/null; then + $cmd -A "$NYM_CHAIN" -p "$protocol" --dport "$port" -j ACCEPT + echo "added $cmd $NYM_CHAIN $protocol port $port" + fi + fi +} + +exit_policy_install_deps() { + install_iptables_persistent + + for cmd in iptables ip6tables ip grep sed awk wget curl; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "installing dependency: $cmd" + apt-get install -y "$cmd" + fi + done +} + +create_nym_chain() { + echo "creating nym exit policy chain $NYM_CHAIN" + + if iptables -L "$NYM_CHAIN" >/dev/null 2>&1; then + iptables -F "$NYM_CHAIN" + else + iptables -N "$NYM_CHAIN" + fi + + if ip6tables -L "$NYM_CHAIN" >/dev/null 2>&1; then + ip6tables -F "$NYM_CHAIN" + else + ip6tables -N "$NYM_CHAIN" + fi + + if ! iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then + iptables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" + fi + + if ! ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then + ip6tables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" + fi +} + +setup_nat_rules() { + echo "setting up nat and forwarding rules for $WG_INTERFACE via $NETWORK_DEVICE" + + 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 + 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 + fi + + if ! iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null; then + iptables -A FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT + fi + if ! iptables -C FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then + iptables -A FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT + fi + + if ! ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null; then + ip6tables -A FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -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 -A FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT + fi +} + +configure_exit_dns_and_icmp() { + echo "ensuring dns and icmp are allowed inside nym exit chain" + + iptables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT + iptables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT + ip6tables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT + ip6tables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT + + iptables -I "$NYM_CHAIN" 3 -p icmp --icmp-type echo-request -j ACCEPT + iptables -I "$NYM_CHAIN" 4 -p icmp --icmp-type echo-reply -j ACCEPT + ip6tables -I "$NYM_CHAIN" 3 -p ipv6-icmp -j ACCEPT +} + +apply_port_allowlist() { + echo "applying allowed port list into ${NYM_CHAIN}" + + configure_exit_dns_and_icmp + + declare -A PORT_MAPPINGS=( + ["FTP"]="20-21" + ["SSH"]="22" + ["WHOIS"]="43" + ["DNS"]="53" + ["Finger"]="79" + ["HTTP"]="80-81" + ["Kerberos"]="88" + ["POP3"]="110" + ["NTP"]="123" + ["IMAP"]="143" + ["IMAP3"]="220" + ["LDAP"]="389" + ["HTTPS"]="443" + ["SMBWindowsFileShare"]="445" + ["Kpasswd"]="464" + ["RTSP"]="554" + ["LDAPS"]="636" + ["SILC"]="706" + ["KerberosAdmin"]="749" + ["DNSOverTLS"]="853" + ["Rsync"]="873" + ["VMware"]="902-904" + ["RemoteHTTPS"]="981" + ["FTPOverTLS"]="989-990" + ["NetnewsAdmin"]="991" + ["TelnetOverTLS"]="992" + ["IMAPOverTLS"]="993" + ["POP3OverTLS"]="995" + ["OpenVPN"]="1194" + ["WireGuardPeer"]="51820-51822" + ["QTServerAdmin"]="1220" + ["PKTKRB"]="1293" + ["MSSQL"]="1433" + ["VLSILicenseManager"]="1500" + ["OracleDB"]="1521" + ["Sametime"]="1533" + ["GroupWise"]="1677" + ["PPTP"]="1723" + ["RTSPAlt"]="1755" + ["MSNP"]="1863" + ["NFS"]="2049" + ["CPanel"]="2082-2083" + ["GNUnet"]="2086-2087" + ["NBX"]="2095-2096" + ["Zephyr"]="2102-2104" + ["XboxLive"]="3074" + ["MySQL"]="3306" + ["SVN"]="3690" + ["RWHOIS"]="4321" + ["Virtuozzo"]="4643" + ["RTPVOIP"]="5000-5005" + ["MMCC"]="5050" + ["ICQ"]="5190" + ["XMPP"]="5222-5223" + ["AndroidMarket"]="5228" + ["PostgreSQL"]="5432" + ["MongoDBDefault"]="27017" + ["Electrum"]="8082" + ["SimplifyMedia"]="8087-8088" + ["Zcash"]="8232-8233" + ["Bitcoin"]="8332-8333" + ["HTTPSALT"]="8443" + ["TeamSpeak"]="8767" + ["MQTTS"]="8883" + ["HTTPProxy"]="8888" + ["TorORPort"]="9001" + ["TorDirPort"]="9030" + ["Tari"]="9053" + ["Gaming"]="9339" + ["Git"]="9418" + ["HTTPSALT2"]="9443" + ["Lightning"]="9735" + ["NDMP"]="10000" + ["OpenPGP"]="11371" + ["Monero"]="18080-18081" + ["MoneroRPC"]="18089" + ["GoogleVoice"]="19294" + ["EnsimControlPanel"]="19638" + ["DarkFiTor"]="25551" + ["Minecraft"]="25565" + ["DarkFi"]="26661" + ["Steam"]="27000-27050" + ["ElectrumSSL"]="50002" + ["MOSH"]="60000-61000" + ["Mumble"]="64738" + ["Metadata"]="51830" + ) + + for service in "${!PORT_MAPPINGS[@]}"; do + local port="${PORT_MAPPINGS[$service]}" + echo "adding rules for $service (ports $port)" + add_port_rules iptables "$port" "tcp" + add_port_rules ip6tables "$port" "tcp" + add_port_rules iptables "$port" "udp" + add_port_rules ip6tables "$port" "udp" + done +} + +apply_spamhaus_blocklist() { + echo "applying spamhaus-like blocklist from $EXIT_POLICY_LOCATION" + + mkdir -p "$(dirname "$POLICY_FILE")" + + if ! wget -q "$EXIT_POLICY_LOCATION" -O "$POLICY_FILE" 2>/dev/null; then + echo "failed to download exit policy, using minimal blocklist" + cat >"$POLICY_FILE" </dev/null; then + iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT + fi + if [[ "$ip_range" == *":"* ]] && ! ip6tables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then + ip6tables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT + fi + fi + done +} + +add_default_reject_rule() { + echo "ensuring default reject rule at end of ${NYM_CHAIN}" + + iptables -D "$NYM_CHAIN" -j REJECT 2>/dev/null || true + iptables -D "$NYM_CHAIN" -j REJECT --reject-with icmp-port-unreachable 2>/dev/null || true + ip6tables -D "$NYM_CHAIN" -j REJECT 2>/dev/null || true + ip6tables -D "$NYM_CHAIN" -j REJECT --reject-with icmp6-port-unreachable 2>/dev/null || true + + iptables -A "$NYM_CHAIN" -j REJECT --reject-with icmp-port-unreachable + ip6tables -A "$NYM_CHAIN" -j REJECT --reject-with icmp6-port-unreachable +} + +clear_exit_policy_rules() { + echo "clearing nym exit policy rules" + + iptables -F "$NYM_CHAIN" 2>/dev/null || true + ip6tables -F "$NYM_CHAIN" 2>/dev/null || true + + iptables -D FORWARD -i "$WG_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 + + iptables -X "$NYM_CHAIN" 2>/dev/null || true + ip6tables -X "$NYM_CHAIN" 2>/dev/null || true +} + +show_exit_policy_status() { + echo "nym exit policy status" + echo "network device: $NETWORK_DEVICE" + echo "wireguard interface: $WG_INTERFACE" + echo + + if ! ip link show "$WG_INTERFACE" >/dev/null 2>&1; then + echo "warning: wireguard interface $WG_INTERFACE not found" + else + echo "interface details:" + ip link show "$WG_INTERFACE" + echo + echo "ipv4 addresses:" + ip -4 addr show dev "$WG_INTERFACE" + echo + echo "ipv6 addresses:" + ip -6 addr show dev "$WG_INTERFACE" + fi + + echo + echo "iptables chains for ${NYM_CHAIN}:" + iptables -L "$NYM_CHAIN" -n -v 2>/dev/null || echo "ipv4 chain not found" + echo + ip6tables -L "$NYM_CHAIN" -n -v 2>/dev/null || echo "ipv6 chain not found" + echo + echo "ip forwarding:" + echo "ipv4: $(cat /proc/sys/net/ipv4/ip_forward 2>/dev/null || echo 0)" + echo "ipv6: $(cat /proc/sys/net/ipv6/conf/all/forwarding 2>/dev/null || echo 0)" +} + +test_exit_policy_connectivity() { + echo "testing connectivity through $WG_INTERFACE" + + local iface_info + iface_info=$(ip link show "$WG_INTERFACE" 2>/dev/null || true) + if [[ -z "$iface_info" ]]; then + echo "interface $WG_INTERFACE not found" + return 1 + fi + + echo "interface:" + echo "$iface_info" + + local ipv4_address ipv6_address + ipv4_address=$(ip -4 addr show dev "$WG_INTERFACE" | awk '/inet / {print $2}' | cut -d'/' -f1 | head -n1) + ipv6_address=$(ip -6 addr show dev "$WG_INTERFACE" scope global | awk '/inet6/ {print $2}' | cut -d'/' -f1 | head -n1) + + echo "ipv4 address: ${ipv4_address:-none}" + echo "ipv6 address: ${ipv6_address:-none}" + + if [[ -n "$ipv4_address" ]]; then + echo "testing ipv4 ping to 8.8.8.8" + timeout 5 ping -c 3 -I "$ipv4_address" 8.8.8.8 >/dev/null 2>&1 && \ + echo "ipv4 ping ok" || echo "ipv4 ping failed" + + echo "testing ipv4 dns resolution" + timeout 5 ping -c 3 -I "$ipv4_address" google.com >/dev/null 2>&1 && \ + echo "ipv4 dns ok" || echo "ipv4 dns failed" + fi + + if [[ -n "$ipv6_address" ]]; then + echo "testing ipv6 ping to google dns" + timeout 5 ping6 -c 3 -I "$ipv6_address" 2001:4860:4860::8888 >/dev/null 2>&1 && \ + echo "ipv6 ping ok" || echo "ipv6 ping failed" + + echo "testing ipv6 dns resolution" + timeout 5 ping6 -c 3 -I "$ipv6_address" google.com >/dev/null 2>&1 && \ + echo "ipv6 dns ok" || echo "ipv6 dns failed" + fi + + echo "connectivity tests finished" +} + +############################################################################### +# part 3: exit policy verification tests +############################################################################### + +test_port_range_rules() { + echo "testing port range rules in ${NYM_CHAIN}" + + local port_ranges=( + "20-21:tcp:ftp" + "80-81:tcp:http" + "2082-2083:tcp:cpanel" + "5222-5223:tcp:xmpp" + "27000-27050:tcp:steam-sample" + "989-990:tcp:ftp-tls" + "5000-5005:tcp:rtp-voip" + "8087-8088:tcp:simplify-media" + "8232-8233:tcp:zcash" + "8332-8333:tcp:bitcoin" + "18080-18081:tcp:monero" + ) + + local failures=0 + for entry in "${port_ranges[@]}"; do + IFS=':' read -r range proto name <<<"$entry" + local start end + start=$(echo "$range" | cut -d'-' -f1) + end=$(echo "$range" | cut -d'-' -f2) + + if iptables -t filter -C "$NYM_CHAIN" -p "$proto" --dport "$start:$end" -j ACCEPT 2>/dev/null; then + echo "rule ok: $name $proto $range" + else + echo "missing rule: $name $proto $range" + ((failures++)) + fi + done + + return "$failures" +} + +test_critical_services() { + echo "testing critical service rules in ${NYM_CHAIN}" + + local tcp_ports=(22 53 443 853 1194) + local udp_ports=(53 123 1194) + local failures=0 + + for port in "${tcp_ports[@]}"; do + if iptables -t filter -C "$NYM_CHAIN" -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then + echo "tcp port $port allowed" + else + if iptables-save | grep -E "^-A $NYM_CHAIN.*tcp.*dpts:" | grep -q "$port"; then + echo "tcp port $port allowed by range" + else + echo "tcp port $port missing" + ((failures++)) + fi + fi + done + + for port in "${udp_ports[@]}"; do + if iptables -t filter -C "$NYM_CHAIN" -p udp --dport "$port" -j ACCEPT 2>/dev/null; then + echo "udp port $port allowed" + else + if iptables-save | grep -E "^-A $NYM_CHAIN.*udp.*dpts:" | grep -q "$port"; then + echo "udp port $port allowed by range" + else + echo "udp port $port missing" + ((failures++)) + fi + fi + done + + return "$failures" +} + +test_forward_chain_hook() { + echo "testing forward chain hook direction for ${NYM_CHAIN}" + + local failures=0 + + if iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then + echo "ipv4 forward hook ok: -i $WG_INTERFACE -o $NETWORK_DEVICE -> $NYM_CHAIN" + else + echo "ipv4 forward hook missing or wrong" + ((failures++)) + fi + + if ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then + echo "ipv6 forward hook ok: -i $WG_INTERFACE -o $NETWORK_DEVICE -> $NYM_CHAIN" + else + echo "ipv6 forward hook missing or wrong" + ((failures++)) + fi + + return "$failures" +} + +test_default_reject_rule() { + echo "testing default reject rule at end of ${NYM_CHAIN}" + + if iptables -L "$NYM_CHAIN" | grep -q "REJECT"; then + echo "default reject present in ipv4 chain" + else + echo "default reject missing in ipv4 chain" + return 1 + fi +} + +exit_policy_run_tests() { + local skip_default=0 + while [[ $# -gt 0 ]]; do + case "$1" in + --skip-default-reject) skip_default=1; shift ;; + *) echo "unknown test option: $1"; return 1 ;; + esac + done + + local total=0 + local failed=0 + + test_forward_chain_hook || ((failed++)) + ((total++)) + + test_port_range_rules || ((failed++)) + ((total++)) + + test_critical_services || ((failed++)) + ((total++)) + + if [[ $skip_default -eq 0 ]]; then + test_default_reject_rule || ((failed++)) + ((total++)) + fi + + echo "tests run: $total, failures: $failed" + if [[ $failed -eq 0 ]]; then + echo "all exit policy tests passed" + else + echo "some exit policy tests failed" + fi + + return "$failed" +} + +############################################################################### +# part 4: high level workflows +############################################################################### + +full_tunnel_setup() { + # this mirrors your previous chain of calls but inside one script + echo "running full tunnel setup for ${TUNNEL_INTERFACE} and ${WG_INTERFACE}" + + check_tunnel_iptables "$TUNNEL_INTERFACE" + remove_duplicate_rules "$TUNNEL_INTERFACE" + remove_duplicate_rules "$WG_INTERFACE" + check_tunnel_iptables "$TUNNEL_INTERFACE" + + adjust_ip_forwarding + + apply_iptables_rules "$TUNNEL_INTERFACE" + check_tunnel_iptables "$TUNNEL_INTERFACE" + + apply_iptables_rules "$WG_INTERFACE" + + configure_dns_and_icmp_wg + adjust_ip_forwarding + check_ipv6_ipv4_forwarding + + joke_through_tunnel "$TUNNEL_INTERFACE" + joke_through_tunnel "$WG_INTERFACE" + + echo "full tunnel setup completed" +} + +exit_policy_install() { + echo "installing nym wireguard exit policy for ${WG_INTERFACE} via ${NETWORK_DEVICE}" + exit_policy_install_deps + adjust_ip_forwarding + create_nym_chain + setup_nat_rules + apply_port_allowlist + apply_spamhaus_blocklist + add_default_reject_rule + save_iptables_rules + echo "nym exit policy installed" +} + +complete_networking_configuration() { + echo "starting complete networking configuration: tunnels + exit policy" + + full_tunnel_setup + exit_policy_install + exit_policy_run_tests || echo "exit policy tests reported problems, please review output" + + echo "complete networking configuration finished" +} + +############################################################################### +# cli +############################################################################### + +cmd="${1:-help}" + +case "$cmd" in + # high level workflows + full_tunnel_setup) + full_tunnel_setup + ;; + exit_policy_install) + exit_policy_install + ;; + complete_networking_configuration) + complete_networking_configuration + ;; + + # tunnel manager cmds + fetch_ipv6_address_nym_tun) + fetch_ipv6_address "$TUNNEL_INTERFACE" + ;; + fetch_and_display_ipv6) + fetch_and_display_ipv6 + ;; + apply_iptables_rules) + apply_iptables_rules "$TUNNEL_INTERFACE" + ;; + apply_iptables_rules_wg) + apply_iptables_rules "$WG_INTERFACE" + ;; + check_nymtun_iptables) + check_tunnel_iptables "$TUNNEL_INTERFACE" + ;; + check_nym_wg_tun) + check_tunnel_iptables "$WG_INTERFACE" + ;; + check_ipv6_ipv4_forwarding) + check_ipv6_ipv4_forwarding + ;; + check_ip_routing) + check_ip_routing + ;; + perform_pings) + perform_pings + ;; + joke_through_the_mixnet) + joke_through_tunnel "$TUNNEL_INTERFACE" + ;; + joke_through_wg_tunnel) + joke_through_tunnel "$WG_INTERFACE" + ;; + configure_dns_and_icmp_wg) + configure_dns_and_icmp_wg + ;; + adjust_ip_forwarding) + adjust_ip_forwarding + ;; + remove_duplicate_rules) + remove_duplicate_rules "${2:-}" + ;; + + # exit policy manager cmds + exit_policy_status) + show_exit_policy_status + ;; + exit_policy_test_connectivity) + test_exit_policy_connectivity + ;; + exit_policy_clear) + clear_exit_policy_rules + ;; + exit_policy_tests) + shift + exit_policy_run_tests "$@" + ;; + + help|--help|-h) + cat < [args] + +high level workflows: + full_tunnel_setup run tunnel iptables and checks for nymtun0 and nymwg + exit_policy_install install and configure wireguard exit policy + complete_networking_configuration run tunnel setup, exit policy install and tests + +tunnel and nat helpers: + fetch_ipv6_address_nym_tun show global ipv6 address on ${TUNNEL_INTERFACE} + fetch_and_display_ipv6 show ipv6 on uplink ${NETWORK_DEVICE} + apply_iptables_rules apply nat/forward rules for ${TUNNEL_INTERFACE} + apply_iptables_rules_wg apply nat/forward rules for ${WG_INTERFACE} + check_nymtun_iptables inspect forward chain for ${TUNNEL_INTERFACE} + check_nym_wg_tun inspect forward chain for ${WG_INTERFACE} + check_ipv6_ipv4_forwarding show ipv4/ipv6 forwarding flags + check_ip_routing show ipv4 and ipv6 routes + perform_pings test ipv4 and ipv6 pings + joke_through_the_mixnet test via ${TUNNEL_INTERFACE} with joke + joke_through_wg_tunnel test via ${WG_INTERFACE} with joke + configure_dns_and_icmp_wg allow ping and dns on this host + adjust_ip_forwarding enable ipv4/ipv6 forwarding via sysctl.d + remove_duplicate_rules deduplicate rules for interface in FORWARD and ${NYM_CHAIN} + +exit policy manager: + exit_policy_install install exit policy (iptables rules and blocklist) + exit_policy_status show status of exit policy and forwarding + exit_policy_test_connectivity test connectivity via ${WG_INTERFACE} + exit_policy_clear remove ${NYM_CHAIN} chains and hooks + exit_policy_tests [--skip-default-reject] + run verification tests on exit policy + +environment overrides: + TUNNEL_INTERFACE default nymtun0 + WG_INTERFACE default nymwg + NETWORK_DEVICE uplink device, auto-detected if not set + +EOF + ;; + *) + echo "unknown command: $cmd" + echo "run with 'help' for usage" + exit 1 + ;; +esac + +echo "operation ${cmd} completed" From e815f08505762542b82b8b2eea8e3ce58168773b Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:38:15 +0000 Subject: [PATCH 19/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 1c869a297a0..29ed4c802e7 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -143,11 +143,17 @@ remove_duplicate_rules() { rules_v6=$(ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) if [[ -n "$rules_v6" ]]; then echo -e "${YELLOW}processing ipv6 rules${NC}" - echo "$rules_v6" | sort | uniq -d | while read -r dup; do - echo "removing duplicate ipv6 rule: $dup" - while ip6tables -t filter -C ${dup#-A } 2>/dev/null; do - ip6tables -t filter -D ${dup#-A } || break - done + # For each unique rule, count occurrences and remove all but one + echo "$rules_v6" | sort | uniq | while read -r rule; do + count=$(echo "$rules_v6" | grep -Fx "$rule" | wc -l) + if [[ "$count" -gt 1 ]]; then + echo "removing $((count - 1)) duplicate(s) of ipv6 rule: $rule" + for ((i=1; i/dev/null; then + ip6tables -t filter -D ${rule#-A } || break + fi + done + fi done else echo -e "${GREEN}no ipv6 rules found for $interface to deduplicate${NC}" From 694135c81bfebcc9f1c743dd49af7d733647f2f2 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:38:45 +0000 Subject: [PATCH 20/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 29ed4c802e7..b6babd8c651 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -531,7 +531,7 @@ EOF fi local total_rules - total_rules=$(grep -c "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" || true) + total_rules=$(grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" | wc -l || true) echo "processing $total_rules blocklist rules" grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" | while read -r line; do From 1f8144eb119a53b590d8ae4375e450fbd5d56614 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:47:02 +0100 Subject: [PATCH 21/83] add a safeguard --- .../nym-node-setup/network-tunnel-manager.sh | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index b6babd8c651..e50b4f8d5d5 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -5,13 +5,31 @@ set -euo pipefail ############################################################################### -# safety: must run as root +# safety: must run as root, jq ############################################################################### if [ "$(id -u)" -ne 0 ]; then echo "this script must be run as root" exit 1 fi +echo "checking for jq..." + +if command -v jq >/dev/null 2>&1; then + echo "jq is already installed" + return 0 +fi + +echo "jq not found, installing..." +apt-get update -y +DEBIAN_FRONTEND=noninteractive apt-get install -y jq + +if command -v jq >/dev/null 2>&1; then + echo "jq installed successfully" +else + echo "failed to install jq" + return 1 +fi + ############################################################################### # basic config ############################################################################### @@ -966,6 +984,6 @@ EOF echo "run with 'help' for usage" exit 1 ;; + echo "operation ${cmd} completed" + ;; esac - -echo "operation ${cmd} completed" From c617bbb240d4fd16f9f7a32fd5d6e6d05be7a8ab Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:59:36 +0100 Subject: [PATCH 22/83] fix jq --- scripts/nym-node-setup/network-tunnel-manager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index e50b4f8d5d5..0083286aa22 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -16,7 +16,7 @@ echo "checking for jq..." if command -v jq >/dev/null 2>&1; then echo "jq is already installed" - return 0 + exit 0 fi echo "jq not found, installing..." @@ -27,7 +27,7 @@ if command -v jq >/dev/null 2>&1; then echo "jq installed successfully" else echo "failed to install jq" - return 1 + exit 1 fi ############################################################################### From aba6c9d4acc34ac824c77c97b6b65365705dcac2 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:04:17 +0100 Subject: [PATCH 23/83] fix exit message --- .../nym-node-setup/network-tunnel-manager.sh | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 0083286aa22..0a2a4c6f895 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -869,74 +869,94 @@ complete_networking_configuration() { cmd="${1:-help}" case "$cmd" in - # high level workflows full_tunnel_setup) full_tunnel_setup + status=$? ;; exit_policy_install) exit_policy_install + status=$? ;; complete_networking_configuration) complete_networking_configuration + status=$? ;; # tunnel manager cmds fetch_ipv6_address_nym_tun) fetch_ipv6_address "$TUNNEL_INTERFACE" + status=$? ;; fetch_and_display_ipv6) fetch_and_display_ipv6 + status=$? ;; apply_iptables_rules) apply_iptables_rules "$TUNNEL_INTERFACE" + status=$? ;; apply_iptables_rules_wg) apply_iptables_rules "$WG_INTERFACE" + status=$? ;; check_nymtun_iptables) check_tunnel_iptables "$TUNNEL_INTERFACE" + status=$? ;; check_nym_wg_tun) check_tunnel_iptables "$WG_INTERFACE" + status=$? ;; check_ipv6_ipv4_forwarding) check_ipv6_ipv4_forwarding + status=$? ;; check_ip_routing) check_ip_routing + status=$? ;; perform_pings) perform_pings + status=$? ;; joke_through_the_mixnet) joke_through_tunnel "$TUNNEL_INTERFACE" + status=$? ;; joke_through_wg_tunnel) joke_through_tunnel "$WG_INTERFACE" + status=$? ;; configure_dns_and_icmp_wg) configure_dns_and_icmp_wg + status=$? ;; adjust_ip_forwarding) adjust_ip_forwarding + status=$? ;; remove_duplicate_rules) remove_duplicate_rules "${2:-}" + status=$? ;; # exit policy manager cmds exit_policy_status) show_exit_policy_status + status=$? ;; exit_policy_test_connectivity) test_exit_policy_connectivity + status=$? ;; exit_policy_clear) clear_exit_policy_rules + status=$? ;; exit_policy_tests) shift exit_policy_run_tests "$@" + status=$? ;; help|--help|-h) @@ -978,12 +998,17 @@ environment overrides: NETWORK_DEVICE uplink device, auto-detected if not set EOF + status=0 ;; + *) echo "unknown command: $cmd" echo "run with 'help' for usage" exit 1 ;; - echo "operation ${cmd} completed" - ;; esac + +# Only print when operation succeeded +if [ "$status" -eq 0 ]; then + echo "operation ${cmd} completed" +fi \ No newline at end of file From 71301ee0cc0f2fcfa0c2a281065fb82f042c6f91 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:08:34 +0100 Subject: [PATCH 24/83] sync ipv4 w ipv6 --- .../nym-node-setup/network-tunnel-manager.sh | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 0a2a4c6f895..241fc1137db 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -131,6 +131,7 @@ fetch_and_display_ipv6() { # dedupe / clean-up rules for an interface in FORWARD and NYM-EXIT # keeps a single copy of each rule + remove_duplicate_rules() { local interface="$1" @@ -139,29 +140,35 @@ remove_duplicate_rules() { exit 1 fi - echo -e "${YELLOW}detecting and removing duplicate rules for $interface in FORWARD and ${NYM_CHAIN}${NC}" + echo "detecting and removing duplicate rules for $interface in FORWARD and ${NYM_CHAIN}" # ipv4 local rules_v4 rules_v4=$(iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) + if [[ -n "$rules_v4" ]]; then - echo -e "${YELLOW}processing ipv4 rules${NC}" - echo "$rules_v4" | sort | uniq -d | while read -r dup; do - echo "removing duplicate ipv4 rule: $dup" - while iptables -t filter -C ${dup#-A } 2>/dev/null; do - iptables -t filter -D ${dup#-A } || break - done + echo "processing ipv4 rules" + echo "$rules_v4" | sort | uniq | while read -r rule; do + count=$(echo "$rules_v4" | grep -Fx "$rule" | wc -l) + if [[ "$count" -gt 1 ]]; then + echo "removing $((count - 1)) duplicate(s) of ipv4 rule: $rule" + for ((i=1; i/dev/null; then + iptables -t filter -D ${rule#-A } || break + fi + done + fi done else - echo -e "${GREEN}no ipv4 rules found for $interface to deduplicate${NC}" + echo "no ipv4 rules found for $interface to deduplicate" fi # ipv6 local rules_v6 rules_v6=$(ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) + if [[ -n "$rules_v6" ]]; then - echo -e "${YELLOW}processing ipv6 rules${NC}" - # For each unique rule, count occurrences and remove all but one + echo "processing ipv6 rules" echo "$rules_v6" | sort | uniq | while read -r rule; do count=$(echo "$rules_v6" | grep -Fx "$rule" | wc -l) if [[ "$count" -gt 1 ]]; then @@ -174,12 +181,13 @@ remove_duplicate_rules() { fi done else - echo -e "${GREEN}no ipv6 rules found for $interface to deduplicate${NC}" + echo "no ipv6 rules found for $interface to deduplicate" fi - echo -e "${GREEN}duplicate rule scan completed for $interface${NC}" + echo "duplicate rule scan completed for $interface" } + apply_iptables_rules() { local interface=$1 echo "applying iptables rules for $interface using uplink $NETWORK_DEVICE" From e2fe3a60a692b6e95489a4309b212609c4bab279 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:01:23 +0000 Subject: [PATCH 25/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/wireguard-exit-policy/exit-policy-tests.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/wireguard-exit-policy/exit-policy-tests.sh b/scripts/wireguard-exit-policy/exit-policy-tests.sh index e0f86e59693..0734f19318a 100644 --- a/scripts/wireguard-exit-policy/exit-policy-tests.sh +++ b/scripts/wireguard-exit-policy/exit-policy-tests.sh @@ -199,7 +199,9 @@ test_forward_chain_hook() { fi # Check rule position (should be before UFW rules) - local rule_num=$(iptables -L FORWARD -n --line-numbers | grep -E "$NYM_CHAIN.*$WG_INTERFACE.*$NETWORK_DEVICE" | awk '{print $1}' | head -1) + local rule_num=$(iptables -L FORWARD -n --line-numbers -v | awk -v chain="$NYM_CHAIN" -v in="$WG_INTERFACE" -v out="$NETWORK_DEVICE" ' + $0 ~ chain && $0 ~ in && $0 ~ out {print $1; exit} + ') if [[ -n "$rule_num" ]]; then if [[ $rule_num -le 5 ]]; then echo -e "${GREEN}✓ Rule is early in FORWARD chain (position #$rule_num) - good for UFW compatibility${NC}" From 70a119ac587f6cb3aea1d6004221519471e93baa Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:09:38 +0000 Subject: [PATCH 26/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 241fc1137db..021b8cd818b 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -16,7 +16,7 @@ echo "checking for jq..." if command -v jq >/dev/null 2>&1; then echo "jq is already installed" - exit 0 + # continue script execution fi echo "jq not found, installing..." From d04b61a88bbd86bd9da45cccabeb7e9bc5c584e8 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:10:01 +0000 Subject: [PATCH 27/83] spacing Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 021b8cd818b..729dec97678 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -517,7 +517,7 @@ apply_port_allowlist() { ["NDMP"]="10000" ["OpenPGP"]="11371" ["Monero"]="18080-18081" - ["MoneroRPC"]="18089" + ["MoneroRPC"]="18089" ["GoogleVoice"]="19294" ["EnsimControlPanel"]="19638" ["DarkFiTor"]="25551" From c6a0256b0386137fbcd2c73894d491a01c9c3b51 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:12:52 +0100 Subject: [PATCH 28/83] remove wrong stdout --- scripts/nym-node-setup/network-tunnel-manager.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 729dec97678..6d8626078ed 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -142,6 +142,7 @@ remove_duplicate_rules() { echo "detecting and removing duplicate rules for $interface in FORWARD and ${NYM_CHAIN}" + # ipv4 local rules_v4 rules_v4=$(iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) @@ -282,7 +283,7 @@ joke_through_tunnel() { if ping -c 1 -I "$ipv4_address" google.com >/dev/null 2>&1; then echo -e "${green}ipv4 connectivity is working. fetching a joke${reset}" - joke=$(curl -s -H "Accept: application/json" --interface "$ipv4_address" https://icanhazdadjoke.com/ | jq -r .joke || true) + joke=$(curl -s -H "Accept: application/json" --interface "$ipv4_address" https://icanhazdadjoke.com/ | jq -r .joke) [[ -n "$joke" && "$joke" != "null" ]] && echo -e "${green}ipv4 joke: $joke${reset}" || echo "failed to fetch a joke via ipv4" else echo -e "${red}ipv4 connectivity is not working for $interface. verify your routing and nat settings${reset}" @@ -300,7 +301,7 @@ joke_through_tunnel() { if ping6 -c 1 -I "$ipv6_address" google.com >/dev/null 2>&1; then echo -e "${green}ipv6 connectivity is working. fetching a joke${reset}" - joke=$(curl -s -H "Accept: application/json" --interface "$ipv6_address" https://icanhazdadjoke.com/ | jq -r .joke || true) + joke=$(curl -s -H "Accept: application/json" --interface "$ipv6_address" https://icanhazdadjoke.com/ | jq -r .joke) [[ -n "$joke" && "$joke" != "null" ]] && echo -e "${green}ipv6 joke: $joke${reset}" || echo -e "${red}failed to fetch a joke via ipv6${reset}" else echo -e "${red}ipv6 connectivity is not working for $interface. verify your routing and nat settings${reset}" From 06dd74ba349410362d642fe6feb6482ad6c2e81f Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:18:38 +0000 Subject: [PATCH 29/83] address comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../nym-node-setup/network-tunnel-manager.sh | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 6d8626078ed..daf3ed17bda 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -17,17 +17,17 @@ echo "checking for jq..." if command -v jq >/dev/null 2>&1; then echo "jq is already installed" # continue script execution -fi - -echo "jq not found, installing..." -apt-get update -y -DEBIAN_FRONTEND=noninteractive apt-get install -y jq - -if command -v jq >/dev/null 2>&1; then - echo "jq installed successfully" else - echo "failed to install jq" - exit 1 + echo "jq not found, installing..." + apt-get update -y + DEBIAN_FRONTEND=noninteractive apt-get install -y jq + + if command -v jq >/dev/null 2>&1; then + echo "jq installed successfully" + else + echo "failed to install jq" + exit 1 + fi fi ############################################################################### From c5971d0e9d0bb5dc3d8166f18022423cb966f91d Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:18:54 +0000 Subject: [PATCH 30/83] align space Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index daf3ed17bda..22516144708 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -518,7 +518,7 @@ apply_port_allowlist() { ["NDMP"]="10000" ["OpenPGP"]="11371" ["Monero"]="18080-18081" - ["MoneroRPC"]="18089" + ["MoneroRPC"]="18089" ["GoogleVoice"]="19294" ["EnsimControlPanel"]="19638" ["DarkFiTor"]="25551" From a6fe1b1de7713594b65750de42dda0d51a6045bb Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:32:40 +0100 Subject: [PATCH 31/83] fix logic to ensure to more robust --- .../nym-node-setup/network-tunnel-manager.sh | 79 +++++++++++++++++-- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 22516144708..93229176c2c 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -142,45 +142,109 @@ remove_duplicate_rules() { echo "detecting and removing duplicate rules for $interface in FORWARD and ${NYM_CHAIN}" - + # # ipv4 + # local rules_v4 rules_v4=$(iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) if [[ -n "$rules_v4" ]]; then echo "processing ipv4 rules" + echo "$rules_v4" | sort | uniq | while read -r rule; do + local count count=$(echo "$rules_v4" | grep -Fx "$rule" | wc -l) + if [[ "$count" -gt 1 ]]; then echo "removing $((count - 1)) duplicate(s) of ipv4 rule: $rule" + for ((i=1; i/dev/null; then - iptables -t filter -D ${rule#-A } || break + local cleaned="${rule#-A }" + + # try exact match delete first + if iptables -t filter -C $cleaned 2>/dev/null; then + iptables -t filter -D $cleaned && continue + fi + + # fallback: locate rule in iptables -S + local match + match=$(iptables -S | grep -F -- "$cleaned" | head -n1 || true) + + if [[ -n "$match" ]]; then + # match looks like: -A FORWARD ...full rule... + local chain + chain=$(echo "$match" | awk '{print $2}') + + # find the rule index from iptables -L --line-numbers + local index + index=$(iptables -L "$chain" --line-numbers | grep -F "$interface" | awk 'NR==1{print $1}') + + if [[ -n "$index" ]]; then + iptables -D "$chain" "$index" 2>/dev/null || \ + echo "warning: failed to delete ipv4 duplicate via index ($chain $index)" + else + echo "warning: unable to locate ipv4 duplicate index for: $rule" + fi + else + echo "warning: could not match ipv4 duplicate rule reliably: $rule" fi done fi done + else echo "no ipv4 rules found for $interface to deduplicate" fi + # # ipv6 + # local rules_v6 rules_v6=$(ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) if [[ -n "$rules_v6" ]]; then echo "processing ipv6 rules" + echo "$rules_v6" | sort | uniq | while read -r rule; do + local count count=$(echo "$rules_v6" | grep -Fx "$rule" | wc -l) + if [[ "$count" -gt 1 ]]; then echo "removing $((count - 1)) duplicate(s) of ipv6 rule: $rule" + for ((i=1; i/dev/null; then - ip6tables -t filter -D ${rule#-A } || break + local cleaned="${rule#-A }" + + # try exact delete first + if ip6tables -t filter -C $cleaned 2>/dev/null; then + ip6tables -t filter -D $cleaned && continue + fi + + # fallback: find matching rule in ip6tables -S + local match + match=$(ip6tables -S | grep -F -- "$cleaned" | head -n1 || true) + + if [[ -n "$match" ]]; then + local chain + chain=$(echo "$match" | awk '{print $2}') + + local index + index=$(ip6tables -L "$chain" --line-numbers | grep -F "$interface" | awk 'NR==1{print $1}') + + if [[ -n "$index" ]]; then + ip6tables -D "$chain" "$index" 2>/dev/null || \ + echo "warning: failed to delete ipv6 duplicate via index ($chain $index)" + else + echo "warning: unable to locate ipv6 duplicate index for: $rule" + fi + else + echo "warning: could not match ipv6 duplicate rule reliably: $rule" fi + done fi done + else echo "no ipv6 rules found for $interface to deduplicate" fi @@ -1017,7 +1081,6 @@ EOF ;; esac -# Only print when operation succeeded -if [ "$status" -eq 0 ]; then +if [ "${status:-1}" -eq 0 ]; then echo "operation ${cmd} completed" -fi \ No newline at end of file +fi From 04be5624fae8a8fea8b03c9acf9e07a5fd58f35c Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:41:42 +0100 Subject: [PATCH 32/83] ensure cars passing in a shell --- scripts/nym-node-setup/network-tunnel-manager.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 93229176c2c..2f3d9b24249 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -151,7 +151,9 @@ remove_duplicate_rules() { if [[ -n "$rules_v4" ]]; then echo "processing ipv4 rules" - echo "$rules_v4" | sort | uniq | while read -r rule; do + while read -r rule; do + [[ -z "$rule" ]] && continue + local count count=$(echo "$rules_v4" | grep -Fx "$rule" | wc -l) @@ -171,11 +173,9 @@ remove_duplicate_rules() { match=$(iptables -S | grep -F -- "$cleaned" | head -n1 || true) if [[ -n "$match" ]]; then - # match looks like: -A FORWARD ...full rule... local chain chain=$(echo "$match" | awk '{print $2}') - # find the rule index from iptables -L --line-numbers local index index=$(iptables -L "$chain" --line-numbers | grep -F "$interface" | awk 'NR==1{print $1}') @@ -190,7 +190,7 @@ remove_duplicate_rules() { fi done fi - done + done < <(echo "$rules_v4" | sort | uniq) else echo "no ipv4 rules found for $interface to deduplicate" From 4e1228fff0ba7e7db54ec1e148557f50b0263f31 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:43:39 +0100 Subject: [PATCH 33/83] ensure cars passing in a shell --- .../nym-node-setup/network-tunnel-manager.sh | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 2f3d9b24249..eac8a4112a3 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -625,21 +625,36 @@ EOF total_rules=$(grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" | wc -l || true) echo "processing $total_rules blocklist rules" - grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" | while read -r line; do + # + # main loop — no subshell, safe variable handling + # + while read -r line; do + [[ -z "$line" ]] && continue + local ip_range ip_range=$(echo "$line" | sed -E 's/ExitPolicy reject ([^:]+):.*/\1/') if [[ -n "$ip_range" ]]; then + + # ipv4 reject if ! iptables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then - iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT + iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT || \ + echo "warning: failed adding ipv4 reject for $ip_range" fi - if [[ "$ip_range" == *":"* ]] && ! ip6tables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then - ip6tables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT + + # ipv6 reject + if [[ "$ip_range" == *":"* ]]; then + if ! ip6tables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then + ip6tables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT || \ + echo "warning: failed adding ipv6 reject for $ip_range" + fi fi fi - done + + done < <(grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*") } + add_default_reject_rule() { echo "ensuring default reject rule at end of ${NYM_CHAIN}" From 5627ada57e003123f89048f8535502156a71426c Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:49:02 +0000 Subject: [PATCH 34/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index eac8a4112a3..312b7e09131 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -41,9 +41,17 @@ WG_INTERFACE="${WG_INTERFACE:-nymwg}" NETWORK_DEVICE="${NETWORK_DEVICE:-}" if [[ -z "$NETWORK_DEVICE" ]]; then NETWORK_DEVICE="$(ip -o route show default 2>/dev/null | awk '{print $5}' | head -n1 || true)" + # Validate that NETWORK_DEVICE is non-empty and looks like a network interface + if [[ -z "$NETWORK_DEVICE" || ! "$NETWORK_DEVICE" =~ ^[a-zA-Z0-9_-]+$ ]]; then + NETWORK_DEVICE="" + fi fi if [[ -z "$NETWORK_DEVICE" ]]; then NETWORK_DEVICE="$(ip -o route show default table all 2>/dev/null | awk '{print $5}' | head -n1 || true)" + # Validate that NETWORK_DEVICE is non-empty and looks like a network interface + if [[ -z "$NETWORK_DEVICE" || ! "$NETWORK_DEVICE" =~ ^[a-zA-Z0-9_-]+$ ]]; then + NETWORK_DEVICE="" + fi fi if [[ -z "$NETWORK_DEVICE" ]]; then echo "cannot determine uplink interface. set NETWORK_DEVICE or UPLINK_DEV" From 99b28b2b6f5ce146673a8ce21225936a4dad5eaa Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:49:32 +0000 Subject: [PATCH 35/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 312b7e09131..b4b21d65316 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -172,8 +172,8 @@ remove_duplicate_rules() { local cleaned="${rule#-A }" # try exact match delete first - if iptables -t filter -C $cleaned 2>/dev/null; then - iptables -t filter -D $cleaned && continue + if iptables -t filter -C "$cleaned" 2>/dev/null; then + iptables -t filter -D "$cleaned" && continue fi # fallback: locate rule in iptables -S From 91d0b7bdad7f8481b0fde4cb672783c44e2c0f41 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:49:49 +0000 Subject: [PATCH 36/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index b4b21d65316..529788599ce 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -224,8 +224,8 @@ remove_duplicate_rules() { local cleaned="${rule#-A }" # try exact delete first - if ip6tables -t filter -C $cleaned 2>/dev/null; then - ip6tables -t filter -D $cleaned && continue + if ip6tables -t filter -C "$cleaned" 2>/dev/null; then + ip6tables -t filter -D "$cleaned" && continue fi # fallback: find matching rule in ip6tables -S From 239c6c701b83a6bac0cfb295ec8513ba21d07e3e Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:50:19 +0000 Subject: [PATCH 37/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 529788599ce..79e97dd619d 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -636,10 +636,9 @@ EOF # # main loop — no subshell, safe variable handling # + local ip_range while read -r line; do [[ -z "$line" ]] && continue - - local ip_range ip_range=$(echo "$line" | sed -E 's/ExitPolicy reject ([^:]+):.*/\1/') if [[ -n "$ip_range" ]]; then From 71e0c025c6d95641ab44247920f897c4dfdd26ca Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:50:42 +0000 Subject: [PATCH 38/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 79e97dd619d..4609a295454 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -603,8 +603,9 @@ apply_port_allowlist() { ["Metadata"]="51830" ) + local port for service in "${!PORT_MAPPINGS[@]}"; do - local port="${PORT_MAPPINGS[$service]}" + port="${PORT_MAPPINGS[$service]}" echo "adding rules for $service (ports $port)" add_port_rules iptables "$port" "tcp" add_port_rules ip6tables "$port" "tcp" From 943b5fa8bc5d4ea9cf70e08f7c406bb707b07c36 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:51:01 +0000 Subject: [PATCH 39/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 4609a295454..b4f5c6eb260 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -783,9 +783,9 @@ test_port_range_rules() { ) local failures=0 + local start end for entry in "${port_ranges[@]}"; do IFS=':' read -r range proto name <<<"$entry" - local start end start=$(echo "$range" | cut -d'-' -f1) end=$(echo "$range" | cut -d'-' -f2) From 1525aed657cd318482eae5a4617490753ccaf1de Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:56:18 +0000 Subject: [PATCH 40/83] expand pattern to common naming conventions Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index b4f5c6eb260..e2a8b3d5d16 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -42,14 +42,14 @@ NETWORK_DEVICE="${NETWORK_DEVICE:-}" if [[ -z "$NETWORK_DEVICE" ]]; then NETWORK_DEVICE="$(ip -o route show default 2>/dev/null | awk '{print $5}' | head -n1 || true)" # Validate that NETWORK_DEVICE is non-empty and looks like a network interface - if [[ -z "$NETWORK_DEVICE" || ! "$NETWORK_DEVICE" =~ ^[a-zA-Z0-9_-]+$ ]]; then + if [[ -z "$NETWORK_DEVICE" || ! "$NETWORK_DEVICE" =~ ^[a-zA-Z0-9._-]+$ ]]; then NETWORK_DEVICE="" fi fi if [[ -z "$NETWORK_DEVICE" ]]; then NETWORK_DEVICE="$(ip -o route show default table all 2>/dev/null | awk '{print $5}' | head -n1 || true)" # Validate that NETWORK_DEVICE is non-empty and looks like a network interface - if [[ -z "$NETWORK_DEVICE" || ! "$NETWORK_DEVICE" =~ ^[a-zA-Z0-9_-]+$ ]]; then + if [[ -z "$NETWORK_DEVICE" || ! "$NETWORK_DEVICE" =~ ^[a-zA-Z0-9._-]+$ ]]; then NETWORK_DEVICE="" fi fi From aea74425258ea271a12c9a4b258a493d9257e587 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:56:52 +0000 Subject: [PATCH 41/83] add status Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index e2a8b3d5d16..0563510fc2a 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -1107,3 +1107,4 @@ esac if [ "${status:-1}" -eq 0 ]; then echo "operation ${cmd} completed" fi +exit $status From 219f3af9675d4a6798a9dcceaea2013a50a5c83c Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:57:34 +0100 Subject: [PATCH 42/83] remove subshell --- .../nym-node-setup/network-tunnel-manager.sh | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 0563510fc2a..e31051d8259 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -635,34 +635,43 @@ EOF echo "processing $total_rules blocklist rules" # - # main loop — no subshell, safe variable handling + # SAFE non-subshell loop using a temp file # - local ip_range - while read -r line; do + local tmpfile + tmpfile=$(mktemp) + + grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" > "$tmpfile" + + local line ip_range + while IFS= read -r line; do [[ -z "$line" ]] && continue + ip_range=$(echo "$line" | sed -E 's/ExitPolicy reject ([^:]+):.*/\1/') if [[ -n "$ip_range" ]]; then # ipv4 reject if ! iptables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then - iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT || \ - echo "warning: failed adding ipv4 reject for $ip_range" + iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT \ + || echo "warning: failed adding ipv4 reject for $ip_range" fi # ipv6 reject if [[ "$ip_range" == *":"* ]]; then if ! ip6tables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then - ip6tables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT || \ - echo "warning: failed adding ipv6 reject for $ip_range" + ip6tables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT \ + || echo "warning: failed adding ipv6 reject for $ip_range" fi fi + fi + done < "$tmpfile" - done < <(grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*") + rm -f "$tmpfile" } + add_default_reject_rule() { echo "ensuring default reject rule at end of ${NYM_CHAIN}" From 81fd37e5c039abce13d945e50bd26356df808d00 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:02:56 +0000 Subject: [PATCH 43/83] address comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index e31051d8259..6e0a0e4bff1 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -630,18 +630,14 @@ ExitPolicy reject *:* EOF fi - local total_rules - total_rules=$(grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" | wc -l || true) - echo "processing $total_rules blocklist rules" - - # - # SAFE non-subshell loop using a temp file - # local tmpfile tmpfile=$(mktemp) grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" > "$tmpfile" + local total_rules + total_rules=$(wc -l < "$tmpfile") + echo "processing $total_rules blocklist rules" local line ip_range while IFS= read -r line; do [[ -z "$line" ]] && continue From de4fb6291c6d317deed3255db6fcd0696ce79266 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:03:50 +0000 Subject: [PATCH 44/83] address comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 6e0a0e4bff1..a2c160a5873 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -170,10 +170,13 @@ remove_duplicate_rules() { for ((i=1; i/dev/null; then - iptables -t filter -D "$cleaned" && continue + if iptables -t filter -C "$chain" $rest 2>/dev/null; then + iptables -t filter -D "$chain" $rest && continue fi # fallback: locate rule in iptables -S From 8c799b29760c59a3c9d1055116beb009359ad5a7 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:04:34 +0000 Subject: [PATCH 45/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index a2c160a5873..25e8ca69d72 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -225,10 +225,12 @@ remove_duplicate_rules() { for ((i=1; i/dev/null; then - ip6tables -t filter -D "$cleaned" && continue + if ip6tables -t filter -C "$chain" $rule_spec 2>/dev/null; then + ip6tables -t filter -D "$chain" $rule_spec && continue fi # fallback: find matching rule in ip6tables -S From ba0182058648e4aa577afb3d28b72649d27fd6db Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:05:10 +0000 Subject: [PATCH 46/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 25e8ca69d72..c09ce4cacf3 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -653,7 +653,7 @@ EOF # ipv4 reject if ! iptables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then - iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT \ + iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT --reject-with icmp-port-unreachable \ || echo "warning: failed adding ipv4 reject for $ip_range" fi From cf8a399089bb1d09ef2f450a34ede3ccd10a84cb Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:07:20 +0100 Subject: [PATCH 47/83] remove subshell --- .../nym-node-setup/network-tunnel-manager.sh | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index c09ce4cacf3..72c891e7915 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -159,54 +159,60 @@ remove_duplicate_rules() { if [[ -n "$rules_v4" ]]; then echo "processing ipv4 rules" - while read -r rule; do + # temp file to avoid subshell loops + local tmp4 + tmp4=$(mktemp) + + printf "%s\n" "$rules_v4" | sort | uniq > "$tmp4" + + local rule count cleaned chain rest match index + + while IFS= read -r rule; do [[ -z "$rule" ]] && continue - local count - count=$(echo "$rules_v4" | grep -Fx "$rule" | wc -l) + count=$(printf "%s\n" "$rules_v4" | grep -Fx "$rule" | wc -l) if [[ "$count" -gt 1 ]]; then echo "removing $((count - 1)) duplicate(s) of ipv4 rule: $rule" for ((i=1; i/dev/null; then iptables -t filter -D "$chain" $rest && continue fi - # fallback: locate rule in iptables -S - local match + # fallback: find exact match textually in iptables -S match=$(iptables -S | grep -F -- "$cleaned" | head -n1 || true) if [[ -n "$match" ]]; then - local chain chain=$(echo "$match" | awk '{print $2}') - local index index=$(iptables -L "$chain" --line-numbers | grep -F "$interface" | awk 'NR==1{print $1}') if [[ -n "$index" ]]; then iptables -D "$chain" "$index" 2>/dev/null || \ - echo "warning: failed to delete ipv4 duplicate via index ($chain $index)" + echo "warning: failed deleting ipv4 duplicate via index ($chain $index)" else echo "warning: unable to locate ipv4 duplicate index for: $rule" fi else - echo "warning: could not match ipv4 duplicate rule reliably: $rule" + echo "warning: could not reliably match ipv4 duplicate rule: $rule" fi done fi - done < <(echo "$rules_v4" | sort | uniq) + done < "$tmp4" + + rm -f "$tmp4" else echo "no ipv4 rules found for $interface to deduplicate" fi + # # ipv6 # @@ -216,47 +222,54 @@ remove_duplicate_rules() { if [[ -n "$rules_v6" ]]; then echo "processing ipv6 rules" - echo "$rules_v6" | sort | uniq | while read -r rule; do - local count - count=$(echo "$rules_v6" | grep -Fx "$rule" | wc -l) + local tmp6 + tmp6=$(mktemp) + + printf "%s\n" "$rules_v6" | sort | uniq > "$tmp6" + + local rule count cleaned chain rule_spec match index + + while IFS= read -r rule; do + [[ -z "$rule" ]] && continue + + count=$(printf "%s\n" "$rules_v6" | grep -Fx "$rule" | wc -l) if [[ "$count" -gt 1 ]]; then echo "removing $((count - 1)) duplicate(s) of ipv6 rule: $rule" for ((i=1; i/dev/null; then ip6tables -t filter -D "$chain" $rule_spec && continue fi - # fallback: find matching rule in ip6tables -S - local match + # fallback lookup in ip6tables -S match=$(ip6tables -S | grep -F -- "$cleaned" | head -n1 || true) if [[ -n "$match" ]]; then - local chain chain=$(echo "$match" | awk '{print $2}') - local index index=$(ip6tables -L "$chain" --line-numbers | grep -F "$interface" | awk 'NR==1{print $1}') if [[ -n "$index" ]]; then ip6tables -D "$chain" "$index" 2>/dev/null || \ - echo "warning: failed to delete ipv6 duplicate via index ($chain $index)" + echo "warning: failed deleting ipv6 duplicate via index ($chain $index)" else echo "warning: unable to locate ipv6 duplicate index for: $rule" fi else echo "warning: could not match ipv6 duplicate rule reliably: $rule" fi - done fi - done + + done < "$tmp6" + + rm -f "$tmp6" else echo "no ipv6 rules found for $interface to deduplicate" @@ -266,6 +279,7 @@ remove_duplicate_rules() { } + apply_iptables_rules() { local interface=$1 echo "applying iptables rules for $interface using uplink $NETWORK_DEVICE" From a38917cb9bb94c70c56e2996ecb10f3f33cfd5ef Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:22:34 +0000 Subject: [PATCH 48/83] address comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 72c891e7915..0e1cf0062ca 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -181,8 +181,8 @@ remove_duplicate_rules() { rest=$(echo "$cleaned" | cut -d' ' -f2-) # try exact delete first - if iptables -t filter -C "$chain" $rest 2>/dev/null; then - iptables -t filter -D "$chain" $rest && continue + if iptables -t filter -C "$chain" "$rest" 2>/dev/null; then + iptables -t filter -D "$chain" "$rest" && continue fi # fallback: find exact match textually in iptables -S From 45e14a7fb1cf00c0452d404c1993d4c9e3bfc34e Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:22:57 +0000 Subject: [PATCH 49/83] address comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 0e1cf0062ca..b262cf4d861 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -243,8 +243,8 @@ remove_duplicate_rules() { rule_spec="${cleaned#"$chain" }" # try exact delete first - if ip6tables -t filter -C "$chain" $rule_spec 2>/dev/null; then - ip6tables -t filter -D "$chain" $rule_spec && continue + if ip6tables -t filter -C "$chain" "$rule_spec" 2>/dev/null; then + ip6tables -t filter -D "$chain" "$rule_spec" && continue fi # fallback lookup in ip6tables -S From 8ca6af7c867003a94137eb1bb111c5a8fe30fff5 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:23:18 +0000 Subject: [PATCH 50/83] syntax fix Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index b262cf4d861..07ec842fda9 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -144,7 +144,7 @@ remove_duplicate_rules() { local interface="$1" if [[ -z "$interface" ]]; then - echo "error: no interface specified. usage: $0 remove_duplicate_rules " + echo "Error: No interface specified. Usage: $0 remove_duplicate_rules " exit 1 fi From 76fc9f4a90ff6a71b5c370353f8816f4e237a9c7 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:23:38 +0000 Subject: [PATCH 51/83] syntax fix Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/network-tunnel-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 07ec842fda9..f0a100c7e73 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -8,7 +8,7 @@ set -euo pipefail # safety: must run as root, jq ############################################################################### if [ "$(id -u)" -ne 0 ]; then - echo "this script must be run as root" + echo "This script must be run as root" exit 1 fi From 5ba181b11815a13cc8c66d03d099c4acd11fca6d Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:47:58 +0100 Subject: [PATCH 52/83] break into args --- .../nym-node-setup/network-tunnel-manager.sh | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index f0a100c7e73..fa32cf1cdde 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -159,10 +159,8 @@ remove_duplicate_rules() { if [[ -n "$rules_v4" ]]; then echo "processing ipv4 rules" - # temp file to avoid subshell loops local tmp4 tmp4=$(mktemp) - printf "%s\n" "$rules_v4" | sort | uniq > "$tmp4" local rule count cleaned chain rest match index @@ -180,17 +178,19 @@ remove_duplicate_rules() { chain=$(echo "$cleaned" | awk '{print $1}') rest=$(echo "$cleaned" | cut -d' ' -f2-) + # split "rest" into correct argv tokens + read -ra RULE_ARR <<<"$rest" + # try exact delete first - if iptables -t filter -C "$chain" "$rest" 2>/dev/null; then - iptables -t filter -D "$chain" "$rest" && continue + if iptables -t filter -C "$chain" "${RULE_ARR[@]}" 2>/dev/null; then + iptables -t filter -D "$chain" "${RULE_ARR[@]}" && continue fi - # fallback: find exact match textually in iptables -S + # fallback: find textual rule in iptables -S match=$(iptables -S | grep -F -- "$cleaned" | head -n1 || true) if [[ -n "$match" ]]; then chain=$(echo "$match" | awk '{print $2}') - index=$(iptables -L "$chain" --line-numbers | grep -F "$interface" | awk 'NR==1{print $1}') if [[ -n "$index" ]]; then @@ -202,6 +202,7 @@ remove_duplicate_rules() { else echo "warning: could not reliably match ipv4 duplicate rule: $rule" fi + done fi done < "$tmp4" @@ -224,7 +225,6 @@ remove_duplicate_rules() { local tmp6 tmp6=$(mktemp) - printf "%s\n" "$rules_v6" | sort | uniq > "$tmp6" local rule count cleaned chain rule_spec match index @@ -242,17 +242,19 @@ remove_duplicate_rules() { chain="${cleaned%% *}" rule_spec="${cleaned#"$chain" }" - # try exact delete first - if ip6tables -t filter -C "$chain" "$rule_spec" 2>/dev/null; then - ip6tables -t filter -D "$chain" "$rule_spec" && continue + # split rule_spec safely + read -ra RULE6_ARR <<<"$rule_spec" + + # try exact match delete + if ip6tables -t filter -C "$chain" "${RULE6_ARR[@]}" 2>/dev/null; then + ip6tables -t filter -D "$chain" "${RULE6_ARR[@]}" && continue fi - # fallback lookup in ip6tables -S + # fallback: lookup exact text in ip6tables -S match=$(ip6tables -S | grep -F -- "$cleaned" | head -n1 || true) if [[ -n "$match" ]]; then chain=$(echo "$match" | awk '{print $2}') - index=$(ip6tables -L "$chain" --line-numbers | grep -F "$interface" | awk 'NR==1{print $1}') if [[ -n "$index" ]]; then @@ -264,6 +266,7 @@ remove_duplicate_rules() { else echo "warning: could not match ipv6 duplicate rule reliably: $rule" fi + done fi @@ -280,6 +283,7 @@ remove_duplicate_rules() { + apply_iptables_rules() { local interface=$1 echo "applying iptables rules for $interface using uplink $NETWORK_DEVICE" From 766024be277b3bec62dc9b310c1fc297dee2995b Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:51:39 +0100 Subject: [PATCH 53/83] break into args --- .../nym-node-setup/network-tunnel-manager.sh | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index fa32cf1cdde..66180d67d39 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -164,11 +164,11 @@ remove_duplicate_rules() { printf "%s\n" "$rules_v4" | sort | uniq > "$tmp4" local rule count cleaned chain rest match index - while IFS= read -r rule; do [[ -z "$rule" ]] && continue - count=$(printf "%s\n" "$rules_v4" | grep -Fx "$rule" | wc -l) + # FIX: protect grep from rule content becoming flags + count=$(printf "%s\n" "$rules_v4" | grep -F -- "$rule" | wc -l) if [[ "$count" -gt 1 ]]; then echo "removing $((count - 1)) duplicate(s) of ipv4 rule: $rule" @@ -178,15 +178,12 @@ remove_duplicate_rules() { chain=$(echo "$cleaned" | awk '{print $1}') rest=$(echo "$cleaned" | cut -d' ' -f2-) - # split "rest" into correct argv tokens read -ra RULE_ARR <<<"$rest" - # try exact delete first if iptables -t filter -C "$chain" "${RULE_ARR[@]}" 2>/dev/null; then iptables -t filter -D "$chain" "${RULE_ARR[@]}" && continue fi - # fallback: find textual rule in iptables -S match=$(iptables -S | grep -F -- "$cleaned" | head -n1 || true) if [[ -n "$match" ]]; then @@ -202,9 +199,9 @@ remove_duplicate_rules() { else echo "warning: could not reliably match ipv4 duplicate rule: $rule" fi - done fi + done < "$tmp4" rm -f "$tmp4" @@ -214,6 +211,7 @@ remove_duplicate_rules() { fi + # # ipv6 # @@ -228,11 +226,11 @@ remove_duplicate_rules() { printf "%s\n" "$rules_v6" | sort | uniq > "$tmp6" local rule count cleaned chain rule_spec match index - while IFS= read -r rule; do [[ -z "$rule" ]] && continue - count=$(printf "%s\n" "$rules_v6" | grep -Fx "$rule" | wc -l) + # FIX: protect grep from interpreting rule as flags + count=$(printf "%s\n" "$rules_v6" | grep -F -- "$rule" | wc -l) if [[ "$count" -gt 1 ]]; then echo "removing $((count - 1)) duplicate(s) of ipv6 rule: $rule" @@ -242,19 +240,17 @@ remove_duplicate_rules() { chain="${cleaned%% *}" rule_spec="${cleaned#"$chain" }" - # split rule_spec safely read -ra RULE6_ARR <<<"$rule_spec" - # try exact match delete if ip6tables -t filter -C "$chain" "${RULE6_ARR[@]}" 2>/dev/null; then ip6tables -t filter -D "$chain" "${RULE6_ARR[@]}" && continue fi - # fallback: lookup exact text in ip6tables -S match=$(ip6tables -S | grep -F -- "$cleaned" | head -n1 || true) if [[ -n "$match" ]]; then chain=$(echo "$match" | awk '{print $2}') + index=$(ip6tables -L "$chain" --line-numbers | grep -F "$interface" | awk 'NR==1{print $1}') if [[ -n "$index" ]]; then @@ -266,7 +262,6 @@ remove_duplicate_rules() { else echo "warning: could not match ipv6 duplicate rule reliably: $rule" fi - done fi From 1559f6a9126d7e25a9ca7101da99f2d503754290 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:55:57 +0100 Subject: [PATCH 54/83] bugfix --- scripts/nym-node-setup/network-tunnel-manager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 66180d67d39..66e412d6296 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -154,7 +154,7 @@ remove_duplicate_rules() { # ipv4 # local rules_v4 - rules_v4=$(iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) + rules_v4=$(iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep -F -- "$interface" || true) if [[ -n "$rules_v4" ]]; then echo "processing ipv4 rules" @@ -216,7 +216,7 @@ remove_duplicate_rules() { # ipv6 # local rules_v6 - rules_v6=$(ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" || true) + rules_v6=$(ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep -F -- "$interface" || true) if [[ -n "$rules_v6" ]]; then echo "processing ipv6 rules" From 4e5d88f64c702c04b127b4f3b45f4a4d249d059a Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:26:20 +0000 Subject: [PATCH 55/83] deleting to resolve merge confilict --- .../wireguard-exit-policy-manager.sh | 699 ------------------ 1 file changed, 699 deletions(-) delete mode 100644 scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh diff --git a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh b/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh deleted file mode 100644 index f3288b6204c..00000000000 --- a/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh +++ /dev/null @@ -1,699 +0,0 @@ -#!/bin/bash -# -# Nym Wireguard Exit Policy Manager -# Version: 1.0.0 -# -# This script manages iptables rules for Nym Wireguard exit policies -# Features: -# - Implements the Nym exit policy from official documentation -# - Makes rules persistent across reboots -# - Provides commands to inspect and manage rules -# - Groups rules logically for easier management -# - Integrates with existing Nym node configuration -# -# Usage: ./wireguard-exit-policy-manager.sh [command] - -set -e - -NETWORK_DEVICE=$(ip route show default | awk '/default/ {print $5}') -WG_INTERFACE="nymwg" -NYM_CHAIN="NYM-EXIT" -POLICY_FILE="/etc/nym/exit-policy.txt" -EXIT_POLICY_LOCATION="https://nymtech.net/.wellknown/network-requester/exit-policy.txt" - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -NC='\033[0m' - -add_port_rules() { - local chain="$1" - local port="$2" - local protocol="${3:-tcp}" - - # Check if the port contains a range - if [[ "$port" == *"-"* ]]; then - # Port range handling - add as a single rule with a range - local start_port=$(echo "$port" | cut -d'-' -f1) - local end_port=$(echo "$port" | cut -d'-' -f2) - - if ! $chain -C "$NYM_CHAIN" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT 2>/dev/null; then - $chain -A "$NYM_CHAIN" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT - echo -e " ${GREEN}Added: $NYM_CHAIN $protocol port range $start_port:$end_port${NC}" - fi - else - # Single port handling - if ! $chain -C "$NYM_CHAIN" -p "$protocol" --dport "$port" -j ACCEPT 2>/dev/null; then - $chain -A "$NYM_CHAIN" -p "$protocol" --dport "$port" -j ACCEPT - echo -e " ${GREEN}Added: $NYM_CHAIN $protocol port $port${NC}" - fi - fi -} - -install_dependencies() { - if ! dpkg -s iptables-persistent >/dev/null 2>&1; then - echo -e "${YELLOW}Installing iptables-persistent...${NC}" - apt-get update - DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent - echo -e "${GREEN}iptables-persistent installed.${NC}" - else - echo -e "${GREEN}iptables-persistent is already installed.${NC}" - fi - - # Check for other required dependencies - for cmd in iptables ip6tables ip grep sed awk wget curl; do - if ! command -v "$cmd" &>/dev/null; then - echo -e "${YELLOW}Installing $cmd...${NC}" - apt-get install -y "$cmd" - fi - done -} - -configure_ip_forwarding() { - echo -e "${YELLOW}Configuring IP forwarding...${NC}" - - # Remove any existing forwarding settings to avoid duplicates - sed -i "/^net.ipv6.conf.all.forwarding=/d" /etc/sysctl.conf - sed -i "/^net.ipv4.ip_forward=/d" /etc/sysctl.conf - - # Add forwarding settings - echo "net.ipv6.conf.all.forwarding=1" | tee -a /etc/sysctl.conf - echo "net.ipv4.ip_forward=1" | tee -a /etc/sysctl.conf - - # Apply changes - sysctl -p /etc/sysctl.conf - - # Verify settings - ipv4_forwarding=$(cat /proc/sys/net/ipv4/ip_forward) - ipv6_forwarding=$(cat /proc/sys/net/ipv6/conf/all/forwarding) - - if [ "$ipv4_forwarding" == "1" ] && [ "$ipv6_forwarding" == "1" ]; then - echo -e "${GREEN}IP forwarding configured successfully.${NC}" - else - echo -e "${RED}Failed to configure IP forwarding.${NC}" - exit 1 - fi -} - -create_nym_chain() { - echo -e "${YELLOW}Creating Nym exit policy chain...${NC}" - - # Check if the chain already exists - if iptables -L "$NYM_CHAIN" &>/dev/null; then - echo -e "${YELLOW}Chain $NYM_CHAIN already exists. Flushing it...${NC}" - iptables -F "$NYM_CHAIN" - else - echo -e "${YELLOW}Creating chain $NYM_CHAIN...${NC}" - iptables -N "$NYM_CHAIN" - fi - - # Do the same for IPv6 - if ip6tables -L "$NYM_CHAIN" &>/dev/null; then - echo -e "${YELLOW}Chain $NYM_CHAIN already exists in ip6tables. Flushing it...${NC}" - ip6tables -F "$NYM_CHAIN" - else - echo -e "${YELLOW}Creating chain $NYM_CHAIN in ip6tables...${NC}" - ip6tables -N "$NYM_CHAIN" - fi - - # Link it to the FORWARD chain if not already linked - if ! iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${YELLOW}Linking $NYM_CHAIN to FORWARD chain...${NC}" - iptables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" - fi - - # Link IPv6 chain - if ! ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${YELLOW}Linking $NYM_CHAIN to IPv6 FORWARD chain...${NC}" - ip6tables -I FORWARD 1 -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" - fi -} - -setup_nat_rules() { - echo -e "${YELLOW}Setting up NAT rules...${NC}" - - # Check if NAT rule for IPv4 exists - 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 - echo -e "${GREEN}Added IPv4 NAT rule.${NC}" - else - echo -e "${GREEN}IPv4 NAT rule already exists.${NC}" - fi - - # Check if NAT rule for IPv6 exists - 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 - echo -e "${GREEN}Added IPv6 NAT rule.${NC}" - else - echo -e "${GREEN}IPv6 NAT rule already exists.${NC}" - fi - - # Setup forwarding rules for Wireguard interface - if ! iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null; then - iptables -A FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT - echo -e "${GREEN}Added IPv4 forwarding rule (WG → Internet).${NC}" - fi - - if ! iptables -C FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then - iptables -A FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT - echo -e "${GREEN}Added IPv4 forwarding rule (Internet → WG for established connections).${NC}" - fi - - # IPv6 forwarding rules - if ! ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT 2>/dev/null; then - ip6tables -A FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j ACCEPT - echo -e "${GREEN}Added IPv6 forwarding rule (WG → Internet).${NC}" - fi - - if ! ip6tables -C FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then - ip6tables -A FORWARD -i "$NETWORK_DEVICE" -o "$WG_INTERFACE" -m state --state RELATED,ESTABLISHED -j ACCEPT - echo -e "${GREEN}Added IPv6 forwarding rule (Internet → WG for established connections).${NC}" - fi -} - -configure_dns_and_icmp() { - echo -e "${YELLOW}Configuring DNS and ICMP rules...${NC}" - - # ICMP rules for ping - if ! iptables -C INPUT -p icmp --icmp-type echo-request -j ACCEPT 2>/dev/null; then - iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT - echo -e "${GREEN}Added IPv4 ICMP rule (allow ping requests).${NC}" - fi - - if ! iptables -C OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT 2>/dev/null; then - iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT - echo -e "${GREEN}Added IPv4 ICMP rule (allow ping replies).${NC}" - fi - - # ICMPv6 rules for ping6 - if ! ip6tables -C INPUT -p ipv6-icmp -j ACCEPT 2>/dev/null; then - ip6tables -A INPUT -p ipv6-icmp -j ACCEPT - echo -e "${GREEN}Added IPv6 ICMP rule (allow ping6).${NC}" - fi - - # DNS rules - if ! iptables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null; then - iptables -A INPUT -p udp --dport 53 -j ACCEPT - echo -e "${GREEN}Added IPv4 DNS rule (UDP).${NC}" - fi - - if ! iptables -C INPUT -p tcp --dport 53 -j ACCEPT 2>/dev/null; then - iptables -A INPUT -p tcp --dport 53 -j ACCEPT - echo -e "${GREEN}Added IPv4 DNS rule (TCP).${NC}" - fi - - # IPv6 DNS rules - if ! ip6tables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null; then - ip6tables -A INPUT -p udp --dport 53 -j ACCEPT - echo -e "${GREEN}Added IPv6 DNS rule (UDP).${NC}" - fi - - if ! ip6tables -C INPUT -p tcp --dport 53 -j ACCEPT 2>/dev/null; then - ip6tables -A INPUT -p tcp --dport 53 -j ACCEPT - echo -e "${GREEN}Added IPv6 DNS rule (TCP).${NC}" - fi -} - -# Apply Spamhaus blocklist from the Nym exit policy -apply_spamhaus_blocklist() { - echo -e "${YELLOW}Applying Spamhaus blocklist...${NC}" - - # Create directory if not exists - mkdir -p "$(dirname "$POLICY_FILE")" - - # Try to download the policy file - echo -e "${YELLOW}Downloading exit policy from $EXIT_POLICY_LOCATION${NC}" - if ! wget -q "$EXIT_POLICY_LOCATION" -O "$POLICY_FILE" 2>/dev/null; then - echo -e "${RED}Failed to download exit policy. Using minimal blocklist.${NC}" - - # Create a minimal policy file with a few entries - cat >"$POLICY_FILE" </dev/null; then - iptables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT - fi - - # Apply IPv6 rules for IPv6 addresses - if [[ "$ip_range" == *":"* ]] && ! ip6tables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/dev/null; then - ip6tables -A "$NYM_CHAIN" -d "$ip_range" -j REJECT - fi - fi - done - - echo -e "${GREEN}Blocklist applied successfully.${NC}" -} - -add_default_reject_rule() { - echo -e "${YELLOW}Adding default reject rule...${NC}" - - # First remove any existing plain reject rules (without specific destinations) - iptables -D "$NYM_CHAIN" -j REJECT 2>/dev/null || true - iptables -D "$NYM_CHAIN" -j REJECT --reject-with icmp-port-unreachable 2>/dev/null || true - ip6tables -D "$NYM_CHAIN" -j REJECT 2>/dev/null || true - ip6tables -D "$NYM_CHAIN" -j REJECT --reject-with icmp6-port-unreachable 2>/dev/null || true - - # Add the default catch-all reject rule (must be the last rule in the chain) - iptables -A "$NYM_CHAIN" -j REJECT --reject-with icmp-port-unreachable - ip6tables -A "$NYM_CHAIN" -j REJECT --reject-with icmp6-port-unreachable - - echo -e "${GREEN}Default reject rule added successfully.${NC}" -} - -apply_port_allowlist() { - echo -e "${YELLOW}Applying allowed ports...${NC}" - - # Insert DNS rules at the very beginning of NYM-EXIT chain, this ensures DNS queries are never blocked by REJECT rules - echo -e "${YELLOW}Ensuring DNS is at the beginning of $NYM_CHAIN...${NC}" - - iptables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT - iptables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT - ip6tables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT - ip6tables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT - - # ICMP is needed because the probe tests DNS by pinging hostnames - iptables -I "$NYM_CHAIN" 3 -p icmp --icmp-type echo-request -j ACCEPT - iptables -I "$NYM_CHAIN" 4 -p icmp --icmp-type echo-reply -j ACCEPT - ip6tables -I "$NYM_CHAIN" 3 -p ipv6-icmp -j ACCEPT - - echo -e "${GREEN}✓ DNS and ICMP rules inserted at beginning of $NYM_CHAIN${NC}" - - # Dictionary of services and their ports - declare -A PORT_MAPPINGS=( - ["FTP"]="20-21" - ["SSH"]="22" - ["WHOIS"]="43" - ["DNS"]="53" - ["Finger"]="79" - ["HTTP"]="80-81" - ["Kerberos"]="88" - ["POP3"]="110" - ["NTP"]="123" - ["IMAP"]="143" - ["IMAP3"]="220" - ["LDAP"]="389" - ["HTTPS"]="443" - ["SMBWindowsFileShare"]="445" - ["Kpasswd"]="464" - ["RTSP"]="554" - ["LDAPS"]="636" - ["SILC"]="706" - ["KerberosAdmin"]="749" - ["DNSOverTLS"]="853" - ["Rsync"]="873" - ["VMware"]="902-904" - ["RemoteHTTPS"]="981" - ["FTPOverTLS"]="989-990" - ["NetnewsAdmin"]="991" - ["TelnetOverTLS"]="992" - ["IMAPOverTLS"]="993" - ["POP3OverTLS"]="995" - ["OpenVPN"]="1194" - ["WireGuardPeer"]="51820-51822" - ["QTServerAdmin"]="1220" - ["PKTKRB"]="1293" - ["MSSQL"]="1433" - ["VLSILicenseManager"]="1500" - ["OracleDB"]="1521" - ["Sametime"]="1533" - ["GroupWise"]="1677" - ["PPTP"]="1723" - ["RTSPAlt"]="1755" - ["MSNP"]="1863" - ["NFS"]="2049" - ["CPanel"]="2082-2083" - ["GNUnet"]="2086-2087" - ["NBX"]="2095-2096" - ["Zephyr"]="2102-2104" - ["XboxLive"]="3074" - ["MySQL"]="3306" - ["SVN"]="3690" - ["RWHOIS"]="4321" - ["Virtuozzo"]="4643" - ["RTPVOIP"]="5000-5005" - ["MMCC"]="5050" - ["ICQ"]="5190" - ["XMPP"]="5222-5223" - ["AndroidMarket"]="5228" - ["PostgreSQL"]="5432" - ["MongoDBDefault"]="27017" - ["Electrum"]="8082" - ["SimplifyMedia"]="8087-8088" - ["Zcash"]="8232-8233" - ["Bitcoin"]="8332-8333" - ["HTTPSALT"]="8443" - ["TeamSpeak"]="8767" - ["MQTTS"]="8883" - ["HTTPProxy"]="8888" - ["TorORPort"]="9001" - ["TorDirPort"]="9030" - ["Tari"]="9053" - ["Gaming"]="9339" - ["Git"]="9418" - ["HTTPSALT2"]="9443" - ["Lightning"]="9735" - ["NDMP"]="10000" - ["OpenPGP"]="11371" - ["Monero"]="18080-18081" - ["MoneroRPC"]="18089" - ["GoogleVoice"]="19294" - ["EnsimControlPanel"]="19638" - ["DarkFiTor"]="25551" - ["Minecraft"]="25565" - ["DarkFi"]="26661" - ["Steam"]="27000-27050" - ["ElectrumSSL"]="50002" - ["MOSH"]="60000-61000" - ["Mumble"]="64738" - ["Metadata"]="51830" - ) - - # Add TCP and UDP rules for each allowed port - for service in "${!PORT_MAPPINGS[@]}"; do - port="${PORT_MAPPINGS[$service]}" - echo -e "${YELLOW}Adding rules for $service (Port: $port)${NC}" - - # Add both TCP and UDP rules for all services - add_port_rules iptables "$port" "tcp" - add_port_rules ip6tables "$port" "tcp" - add_port_rules iptables "$port" "udp" - add_port_rules ip6tables "$port" "udp" - done - - echo -e "${GREEN}Port allowlist applied successfully.${NC}" -} - -safe_iptables_rule_remove() { - local chain="$1" - local table="${2:-filter}" - local interface="$3" - - # Remove rule if it exists - if iptables -t "$table" -C "$chain" -o "$interface" -j "$NYM_CHAIN" 2>/dev/null; then - iptables -t "$table" -D "$chain" -o "$interface" -j "$NYM_CHAIN" - fi -} - -safe_ip6tables_rule_remove() { - local chain="$1" - local table="${2:-filter}" - local interface="$3" - - # Remove rule if it exists - if ip6tables -t "$table" -C "$chain" -o "$interface" -j "$NYM_CHAIN" 2>/dev/null; then - ip6tables -t "$table" -D "$chain" -o "$interface" -j "$NYM_CHAIN" - fi -} - -clear_rules() { - echo -e "${YELLOW}Clearing Nym exit policy rules...${NC}" - - # Flush all rules in the NYM-EXIT chain - iptables -F "$NYM_CHAIN" 2>/dev/null || true - ip6tables -F "$NYM_CHAIN" 2>/dev/null || true - - # Remove the chain from FORWARD if it exists - iptables -D FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null || true - iptables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true - ip6tables -D FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null || true - ip6tables -D FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null || true - - # Delete the chains - iptables -X "$NYM_CHAIN" 2>/dev/null || true - ip6tables -X "$NYM_CHAIN" 2>/dev/null || true - - echo -e "${GREEN}Nym exit policy rules cleared successfully.${NC}" -} - -remove_duplicate_rules() { - local interface="$1" - - if [[ -z "$interface" ]]; then - echo -e "${RED}Error: No interface specified. Usage: $0 remove-duplicates ${NC}" >&2 - exit 1 - fi - - echo -e "${YELLOW}Detecting and removing duplicate rules for $interface...${NC}" - - # Verbose duplicate rule detection for IPv4 - echo -e "${YELLOW}Checking IPv4 duplicate rules:${NC}" - iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" | sort | uniq -d && { - echo -e "${RED}Duplicate IPv4 rules found! Removing...${NC}" - # Remove duplicates by saving unique rules - iptables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" | sort | uniq | while read -r rule; do - # Carefully remove duplicates - full_rule=$(echo "$rule" | sed 's/^-A/iptables -D/') - eval "$full_rule" 2>/dev/null - done - } || echo -e "${GREEN}No duplicate IPv4 rules found.${NC}" - - # Verbose duplicate rule detection for IPv6 - echo -e "${YELLOW}Checking IPv6 duplicate rules:${NC}" - ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" | sort | uniq -d && { - echo -e "${RED}Duplicate IPv6 rules found! Removing...${NC}" - # Remove duplicates by saving unique rules - ip6tables-save | grep -E "(-A FORWARD|-A $NYM_CHAIN)" | grep "$interface" | sort | uniq | while read -r rule; do - # Carefully remove duplicates - full_rule=$(echo "$rule" | sed 's/^-A/ip6tables -D/') - eval "$full_rule" 2>/dev/null - done - } || echo -e "${GREEN}No duplicate IPv6 rules found.${NC}" - - # Additional verification - echo -e "\n${YELLOW}Rule verification:${NC}" - echo "IPv4 Rules:" - iptables -L FORWARD -v -n | grep "$interface" - echo "IPv6 Rules:" - ip6tables -L FORWARD -v -n | grep "$interface" - - echo -e "${GREEN}Duplicate rule removal process completed.${NC}" -} - -save_rules() { - echo -e "${YELLOW}Saving iptables rules to make them persistent...${NC}" - - if [ -d "/etc/iptables" ]; then - # For Debian/Ubuntu with iptables-persistent - iptables-save >/etc/iptables/rules.v4 - ip6tables-save >/etc/iptables/rules.v6 - echo -e "${GREEN}Rules saved to /etc/iptables/rules.v4 and /etc/iptables/rules.v6${NC}" - else - # Fallback method - iptables-save >/etc/iptables.rules - ip6tables-save >/etc/ip6tables.rules - echo -e "${GREEN}Rules saved to /etc/iptables.rules and /etc/ip6tables.rules${NC}" - - # Add loading script to rc.local if it doesn't exist - if [ ! -f "/etc/network/if-pre-up.d/iptables" ]; then - cat >/etc/network/if-pre-up.d/iptables </dev/null; then - echo -e "${RED}WARNING: Wireguard interface $WG_INTERFACE not found!${NC}" - return 1 - fi - - # Interface details - echo -e "\n${YELLOW}Interface Details:${NC}" - ip link show "$WG_INTERFACE" - - # IP Addresses - echo -e "\n${YELLOW}IP Addresses:${NC}" - ip -4 addr show dev "$WG_INTERFACE" - ip -6 addr show dev "$WG_INTERFACE" - - # Iptables Chain Status - echo -e "\n${YELLOW}Iptables Chains:${NC}" - { - echo "IPv4 Chain:" - iptables -L "$NYM_CHAIN" -n -v - echo -e "\nIPv6 Chain:" - ip6tables -L "$NYM_CHAIN" -n -v - } || echo "One or both chains not found" - - # Forwarding Status - echo -e "\n${YELLOW}IP Forwarding:${NC}" - echo "IPv4: $(cat /proc/sys/net/ipv4/ip_forward)" - echo "IPv6: $(cat /proc/sys/net/ipv6/conf/all/forwarding)" -} - -test_connectivity() { - echo -e "${YELLOW}Testing connectivity through $WG_INTERFACE...${NC}" - - # More comprehensive interface check - interface_info=$(ip link show "$WG_INTERFACE" 2>/dev/null) - - if [ -z "$interface_info" ]; then - echo -e "${RED}Interface $WG_INTERFACE not found!${NC}" - return 1 - fi - - # Check for multiple possible interface states - if ! echo "$interface_info" | grep -qE "state (UP|UNKNOWN|DORMANT)"; then - echo -e "${RED}Interface $WG_INTERFACE is not in an active state!${NC}" - echo "$interface_info" - return 1 - fi - - # Detailed interface information - echo -e "${GREEN}Interface Details:${NC}" - echo "$interface_info" - - # Get IP addresses with more robust method - ipv4_address=$(ip -4 addr show dev "$WG_INTERFACE" | grep -oP '(?<=inet\s)\d+\.\d+\.\d+\.\d+/\d+' | cut -d'/' -f1 | head -n1) - ipv6_address=$(ip -6 addr show dev "$WG_INTERFACE" scope global | grep -oP '(?<=inet6\s)[0-9a-f:]+/\d+' | cut -d'/' -f1 | head -n1) - - echo -e "${GREEN}IPv4 Address:${NC} ${ipv4_address:-Not found}" - echo -e "${GREEN}IPv6 Address:${NC} ${ipv6_address:-Not found}" - - # Connectivity tests - if [[ -n "$ipv4_address" ]]; then - echo -e "${YELLOW}Testing IPv4 connectivity from $ipv4_address...${NC}" - - # Ping test - if timeout 5 ping -c 3 -I "$ipv4_address" 8.8.8.8 >/dev/null 2>&1; then - echo -e "${GREEN}IPv4 connectivity to 8.8.8.8: Success${NC}" - else - echo -e "${RED}IPv4 connectivity to 8.8.8.8: Failed${NC}" - fi - - # DNS resolution test - if timeout 5 ping -c 3 -I "$ipv4_address" google.com >/dev/null 2>&1; then - echo -e "${GREEN}IPv4 DNS resolution: Success${NC}" - else - echo -e "${RED}IPv4 DNS resolution: Failed${NC}" - fi - - # HTTP(S) connectivity test - if command -v curl &>/dev/null; then - if timeout 5 curl -s --interface "$ipv4_address" -o /dev/null -w "%{http_code}" https://www.google.com | grep -q "200"; then - echo -e "${GREEN}IPv4 HTTPS connectivity: Success${NC}" - else - echo -e "${RED}IPv4 HTTPS connectivity: Failed${NC}" - fi - fi - else - echo -e "${RED}No IPv4 address configured on $WG_INTERFACE${NC}" - fi - - # Similar tests for IPv6 if available - if [[ -n "$ipv6_address" ]]; then - echo -e "${YELLOW}Testing IPv6 connectivity from $ipv6_address...${NC}" - - if timeout 5 ping6 -c 3 -I "$ipv6_address" 2001:4860:4860::8888 >/dev/null 2>&1; then - echo -e "${GREEN}IPv6 connectivity to Google DNS: Success${NC}" - else - echo -e "${RED}IPv6 connectivity to Google DNS: Failed${NC}" - fi - - if timeout 5 ping6 -c 3 -I "$ipv6_address" google.com >/dev/null 2>&1; then - echo -e "${GREEN}IPv6 DNS resolution: Success${NC}" - else - echo -e "${RED}IPv6 DNS resolution: Failed${NC}" - fi - - if command -v curl &>/dev/null; then - if timeout 5 curl -s --interface "$ipv6_address" -o /dev/null -w "%{http_code}" https://www.google.com | grep -q "200"; then - echo -e "${GREEN}IPv6 HTTPS connectivity: Success${NC}" - else - echo -e "${RED}IPv6 HTTPS connectivity: Failed${NC}" - fi - fi - else - echo -e "${YELLOW}No IPv6 address configured on $WG_INTERFACE${NC}" - fi - - echo -e "${GREEN}Connectivity tests completed.${NC}" -} - -main() { - # Check for root privileges - if [ "$(id -u)" -ne 0 ]; then - echo -e "${RED}This script must be run as root${NC}" >&2 - exit 1 - fi - - # Parse command-line arguments - case "$1" in - install) - install_dependencies - configure_ip_forwarding - create_nym_chain - setup_nat_rules - configure_dns_and_icmp - # Apply allowlist first (so DNS and other allowed ports are not blocked) - apply_port_allowlist - # Then apply blocklist (aka the REJECT rules) - apply_spamhaus_blocklist - add_default_reject_rule - save_rules - echo -e "${GREEN}Nym exit policy installed successfully.${NC}" - ;; - status) - show_status - ;; - test) - test_connectivity - ;; - clear) - clear_rules - echo -e "${GREEN}Nym exit policy rules cleared.${NC}" - ;; - remove-duplicates) - remove_duplicate_rules "$2" - ;; - help | --help | -h) - echo "Usage: $0 [command]" - echo "" - echo "Commands:" - echo " install Install and configure Nym exit policy" - echo " status Show current Nym exit policy status" - echo " test Test connectivity through Wireguard interface" - echo " clear Remove all Nym exit policy rules" - echo " remove-duplicates Remove duplicate iptables rules for an interface" - echo " help Show this help message" - ;; - *) - echo -e "${RED}Invalid command. Use '$0 help' for usage information.${NC}" >&2 - exit 1 - ;; - esac -} - -main "$@" From 0fe863c8895eb40af855bcc63f123a1233a6530b Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:27:04 +0000 Subject: [PATCH 56/83] delete to resolve merge conflict --- .../exit-policy-tests.sh | 308 ------------------ 1 file changed, 308 deletions(-) delete mode 100644 scripts/wireguard-exit-policy/exit-policy-tests.sh diff --git a/scripts/wireguard-exit-policy/exit-policy-tests.sh b/scripts/wireguard-exit-policy/exit-policy-tests.sh deleted file mode 100644 index 0734f19318a..00000000000 --- a/scripts/wireguard-exit-policy/exit-policy-tests.sh +++ /dev/null @@ -1,308 +0,0 @@ -#!/bin/bash -# Nym Exit Policy Verification Unit Tests - -GREEN='\033[0;32m' -RED='\033[0;31m' -YELLOW='\033[0;33m' -NC='\033[0m' - -NYM_CHAIN="NYM-EXIT" -WG_INTERFACE="nymwg" - -check_port_range_rules() { - local port_range="$1" - local protocol="${2:-tcp}" - local chain="${3:-$NYM_CHAIN}" - - # Extract start and end ports - local start_port=$(echo "$port_range" | cut -d'-' -f1) - local end_port=$(echo "$port_range" | cut -d'-' -f2) - - if iptables -t filter -C "$chain" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT 2>/dev/null; then - echo -e "${GREEN}✓ Rule exists: $chain $protocol port range $start_port:$end_port${NC}" - return 0 - else - echo -e "${RED}✗ Rule missing: $chain $protocol port range $start_port:$end_port${NC}" - - echo -e "${YELLOW}Dumping all rules in $chain:${NC}" - iptables -L "$chain" -n | grep "$protocol" - - return 1 - fi -} - -# Test port range rules -test_port_range_rules() { - echo -e "${YELLOW}Testing Port Range Rules...${NC}" - - # Select the essential port ranges for testing - local port_ranges=( - "20-21:tcp:FTP" - "80-81:tcp:HTTP" - "2082-2083:tcp:CPanel" - "5222-5223:tcp:XMPP" - "27000-27050:tcp:Steam (sampling)" - "989-990:tcp:FTP over TLS" - "5000-5005:tcp:RTP/VoIP" - "8087-8088:tcp:Simplify Media" - "8232-8233:tcp:Zcash" - "8332-8333:tcp:Bitcoin" - "18080-18081:tcp:Monero" - ) - - local total_failures=0 - - for range in "${port_ranges[@]}"; do - IFS=':' read -r port_range protocol service <<< "$range" - - # Extract start and end ports - local start_port=$(echo "$port_range" | cut -d'-' -f1) - local end_port=$(echo "$port_range" | cut -d'-' -f2) - - echo -e "${YELLOW}Testing $service $protocol port range $port_range${NC}" - - if iptables -t filter -C "$NYM_CHAIN" -p "$protocol" --dport "$start_port:$end_port" -j ACCEPT 2>/dev/null; then - echo -e "${GREEN}✓ Rule exists: $NYM_CHAIN $protocol port range $start_port:$end_port${NC}" - else - echo -e "${RED}✗ Rule missing: $NYM_CHAIN $protocol port range $start_port:$end_port${NC}" - ((total_failures++)) - - echo -e "${YELLOW}Existing rules for protocol $protocol:${NC}" - iptables -L "$NYM_CHAIN" -n | grep "$protocol" - fi - done - - if [ $total_failures -eq 0 ]; then - return 0 - else - return 1 - fi -} - -test_critical_services() { - echo -e "${YELLOW}Testing Critical Service Rules...${NC}" - - local tcp_services=( - 22 # SSH - 53 # DNS - 443 # HTTPS - 853 # DNS over TLS - 1194 # OpenVPN - ) - - local udp_services=( - 53 # DNS - 123 # NTP - 1194 # OpenVPN - ) - - local failures=0 - - # Test TCP services - for port in "${tcp_services[@]}"; do - local rule_found=false - - # First check for exact match - if iptables -t filter -C "$NYM_CHAIN" -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then - echo -e "${GREEN}✓ Rule exists: NYM-EXIT tcp port $port${NC}" - rule_found=true - else - # If not found as exact port, search for it in port ranges - # This checks if the port is covered by any range rule - if iptables-save | grep -E "^-A $NYM_CHAIN.*tcp.*dpts:" | grep -qP "dpts:(\d+:)?$port(:|\d+)" || \ - iptables-save | grep -E "^-A $NYM_CHAIN.*tcp.*dpts:" | grep -qP "dpts:$port:"; then - echo -e "${GREEN}✓ Rule exists: NYM-EXIT tcp port $port (covered by a range rule)${NC}" - rule_found=true - else - echo -e "${RED}✗ Rule missing: NYM-EXIT tcp port $port${NC}" - ((failures++)) - fi - fi - done - - # Test UDP services - similar approach - for port in "${udp_services[@]}"; do - local rule_found=false - - if iptables -t filter -C "$NYM_CHAIN" -p udp --dport "$port" -j ACCEPT 2>/dev/null; then - echo -e "${GREEN}✓ Rule exists: NYM-EXIT udp port $port${NC}" - rule_found=true - else - # If not found as exact port, search for it in port ranges - if iptables-save | grep -E "^-A $NYM_CHAIN.*udp.*dpts:" | grep -qP "dpts:(\d+:)?$port(:|\d+)" || \ - iptables-save | grep -E "^-A $NYM_CHAIN.*udp.*dpts:" | grep -qP "dpts:$port:"; then - echo -e "${GREEN}✓ Rule exists: NYM-EXIT udp port $port (covered by a range rule)${NC}" - rule_found=true - else - echo -e "${RED}✗ Rule missing: NYM-EXIT udp port $port${NC}" - ((failures++)) - fi - fi - done - - echo -e "${YELLOW}Relevant existing rules for HTTP (port 80):${NC}" - iptables-save | grep -E "$NYM_CHAIN.*tcp" | grep -E "(dpt|dpts):.*80" - - return $failures -} - -# Test that the exit policy chain is correctly wired up to FORWARD chain -test_forward_chain_hook() { - echo -e "${YELLOW}Testing FORWARD Chain Hook...${NC}" - - local failures=0 - NETWORK_DEVICE=$(ip route show default | awk '/default/ {print $5}') - - if [[ -z "$NETWORK_DEVICE" ]]; then - echo -e "${RED}✗ Could not determine network device${NC}" - return 1 - fi - - # (incoming from nymwg, outgoing to network device) - if iptables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${GREEN}✓ IPv4 FORWARD hook exists with correct direction: -i $WG_INTERFACE -o $NETWORK_DEVICE${NC}" - else - echo -e "${RED}✗ IPv4 FORWARD hook missing or has wrong direction${NC}" - echo -e "${YELLOW} Expected: -i $WG_INTERFACE -o $NETWORK_DEVICE -j $NYM_CHAIN${NC}" - - # Check if wrong direction exists - if iptables -C FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${RED}✗ WRONG DIRECTION FOUND: -o $WG_INTERFACE (this matches return traffic, not client egress!)${NC}" - fi - - # Show what actually exists - specifically look for jump rules to NYM-EXIT - echo -e "${YELLOW} Current FORWARD rules that jump to $NYM_CHAIN:${NC}" - iptables -L FORWARD -n -v --line-numbers | grep -E "$NYM_CHAIN" || echo " (none found - exit policy chain is not hooked to FORWARD!)" - echo -e "${YELLOW} All FORWARD rules with $WG_INTERFACE (for reference):${NC}" - iptables -L FORWARD -n -v --line-numbers | grep -E "$WG_INTERFACE" | head -5 || echo " (none found)" - ((failures++)) - fi - - # Check IPv6 - if ip6tables -C FORWARD -i "$WG_INTERFACE" -o "$NETWORK_DEVICE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${GREEN}✓ IPv6 FORWARD hook exists with correct direction: -i $WG_INTERFACE -o $NETWORK_DEVICE${NC}" - else - echo -e "${RED}✗ IPv6 FORWARD hook missing or has wrong direction${NC}" - echo -e "${YELLOW} Expected: -i $WG_INTERFACE -o $NETWORK_DEVICE -j $NYM_CHAIN${NC}" - - # Check if wrong direction exists - if ip6tables -C FORWARD -o "$WG_INTERFACE" -j "$NYM_CHAIN" 2>/dev/null; then - echo -e "${RED}✗ WRONG DIRECTION FOUND: -o $WG_INTERFACE (this matches return traffic, not client egress!)${NC}" - fi - - # Show what actually exists - specifically look for jump rules to NYM-EXIT - echo -e "${YELLOW} Current IPv6 FORWARD rules that jump to $NYM_CHAIN:${NC}" - ip6tables -L FORWARD -n -v --line-numbers | grep -E "$NYM_CHAIN" || echo " (none found - exit policy chain is not hooked to FORWARD!)" - echo -e "${YELLOW} All IPv6 FORWARD rules with $WG_INTERFACE (for reference):${NC}" - ip6tables -L FORWARD -n -v --line-numbers | grep -E "$WG_INTERFACE" | head -5 || echo " (none found)" - ((failures++)) - fi - - # Check rule position (should be before UFW rules) - local rule_num=$(iptables -L FORWARD -n --line-numbers -v | awk -v chain="$NYM_CHAIN" -v in="$WG_INTERFACE" -v out="$NETWORK_DEVICE" ' - $0 ~ chain && $0 ~ in && $0 ~ out {print $1; exit} - ') - if [[ -n "$rule_num" ]]; then - if [[ $rule_num -le 5 ]]; then - echo -e "${GREEN}✓ Rule is early in FORWARD chain (position #$rule_num) - good for UFW compatibility${NC}" - else - echo -e "${YELLOW}⚠ Rule is later in FORWARD chain (position #$rule_num) - may conflict with UFW${NC}" - fi - fi - - return $failures -} - -# Verify default reject rule exists -test_default_reject_rule() { - echo -e "${YELLOW}This test takes some time, do not quit the process${NC}" - echo - echo -e "${YELLOW}Testing Default Reject Rule...${NC}" - - # Try different patterns to detect the reject rule - if iptables -L "$NYM_CHAIN" | grep -q "REJECT.*all.*anywhere.*anywhere.*reject-with"; then - echo -e "${GREEN}✓ Default REJECT rule exists${NC}" - return 0 - elif iptables -L "$NYM_CHAIN" | grep -q "REJECT.*all -- .*everywhere.*everywhere"; then - echo -e "${GREEN}✓ Default REJECT rule exists${NC}" - return 0 - elif iptables -L "$NYM_CHAIN" | grep -q "REJECT.*all.*0.0.0.0/0.*0.0.0.0/0"; then - echo -e "${GREEN}✓ Default REJECT rule exists${NC}" - return 0 - elif iptables -n -L "$NYM_CHAIN" | grep -qE "REJECT.*all.*0.0.0.0/0.*0.0.0.0/0"; then - echo -e "${GREEN}✓ Default REJECT rule exists${NC}" - return 0 - elif iptables -L "$NYM_CHAIN" | tail -1 | grep -q "REJECT"; then - echo -e "${GREEN}✓ Default REJECT rule exists at the end of chain${NC}" - return 0 - else - echo -e "${RED}✗ Default REJECT rule missing${NC}" - # Display the last 3 rules in the chain for debugging - echo -e "${YELLOW}Last 3 rules in the chain:${NC}" - iptables -L "$NYM_CHAIN" | tail -3 - return 1 - fi -} - -run_all_tests() { - local total_failures=0 - local total_tests=0 - local skip_default_reject=false - - # Parse arguments - while [[ $# -gt 0 ]]; do - case "$1" in - --skip-default-reject) - skip_default_reject=true - shift - ;; - *) - echo -e "${RED}Unknown argument: $1${NC}" - exit 1 - ;; - esac - done - - local test_functions=( - "test_forward_chain_hook" - "test_port_range_rules" - "test_critical_services" - ) - - if [ "$skip_default_reject" = false ]; then - test_functions+=("test_default_reject_rule") - fi - - echo -e "${YELLOW}Running Nym Exit Policy Verification Tests...${NC}" - - for test_func in "${test_functions[@]}"; do - ((total_tests++)) - $test_func - if [ $? -ne 0 ]; then - ((total_failures++)) - echo -e "${RED}Test $test_func FAILED${NC}" - else - echo -e "${GREEN}Test $test_func PASSED${NC}" - fi - done - - echo -e "\n${YELLOW}Test Summary:${NC}" - echo -e "Total Tests: $total_tests" - echo -e "Failures: $total_failures" - - if [ $total_failures -eq 0 ]; then - echo -e "${GREEN}All Tests Passed Successfully!${NC}" - exit 0 - else - echo -e "${RED}Some Tests Failed. Please review the iptables configuration.${NC}" - exit 1 - fi -} - -if [[ $EUID -ne 0 ]]; then - echo -e "${RED}This script must be run as root${NC}" - exit 1 -fi - -# Run the tests -run_all_tests "$@" From 21d52244cb077b5db9e5a7a11038647894aafd5f Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 18:49:53 +0100 Subject: [PATCH 57/83] sync up with new tunnel manager --- scripts/nym-node-setup/nym-node-cli.py | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 6a3f84540a7..2df9657e3f8 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -32,8 +32,6 @@ def __init__(self, args): self.landing_page_html = self.fetch_script("landing-page.html") self.nginx_proxy_wss_sh = self.fetch_script("nginx_proxy_wss_sh") self.tunnel_manager_sh = self.fetch_script("network_tunnel_manager.sh") - self.wg_ip_tables_manager_sh = self.fetch_script("wireguard-exit-policy-manager.sh") - self.wg_ip_tables_test_sh = self.fetch_script("exit-policy-tests.sh") self.quic_bridge_deployment_sh = self.fetch_script("quic_bridge_deployment.sh") else: self.landing_page_html = None @@ -190,9 +188,7 @@ def _return_script_url(self, script_init_name): "start-node-systemd-service.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/start-node-systemd-service.sh", "nginx_proxy_wss_sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/setup-nginx-proxy-wss.sh", "landing-page.html": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/landing-page.html", - "network_tunnel_manager.sh": f"https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/network_tunnel_manager.sh", - "wireguard-exit-policy-manager.sh": f"https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/wireguard-exit-policy-manager.sh", - "exit-policy-tests.sh": f"https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/wireguard-exit-policy/exit-policy-tests.sh", + "network_tunnel_manager.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/network-tunnel-manager.sh", "quic_bridge_deployment.sh": f"{github_raw_nymtech_nym_scripts_url}nym-node-setup/quic_bridge_deployment.sh" } @@ -297,9 +293,9 @@ def norm(v): ans = input( "\nWireGuard is not configured.\n" "Nodes routing WireGuard can be listed as both entry and exit in the app.\n" - "Enable WireGuard support? (y/n): " + "Enable WireGuard support? (Y/n): " ).strip().lower() - val = "true" if ans in ("y", "yes") else "false" + val = "true" if ans in ("", "y", "yes") else "false" val = norm(val) os.environ["WIREGUARD"] = val @@ -382,13 +378,15 @@ def setup_test_wg_ip_tables(self): "Setting up Wireguard IP tables to match Nym exit policy for mixnet, stored at: https://nymtech.net/.wellknown/network-requester/exit-policy.txt" "\nThis may take a while, follow the steps below and don't kill the process..." ) - self.run_script(self.wg_ip_tables_manager_sh, args=["install"]) - self.run_script(self.wg_ip_tables_manager_sh, args=["status"]) - self.run_script(self.wg_ip_tables_test_sh) + self.run_script(self.tunnel_manager_sh, args=["exit_policy_install"]) def quic_bridge_deploy(self): """Setup QUIC bridge and configuration using external script""" - self.run_script(self.quic_bridge_deployment_sh, args=["full_bridge_setup"]) + print("\n* * * Installing and configuring QUIC bridges * * *") + answer = input("\nDo you want to install, setup and run QUIC bridge? (Y/n) ").strip().lower() + + if answer in ("", "y", "yes"): + self.run_script(self.quic_bridge_deployment_sh, args=["full_bridge_setup"]) def run_nym_node_as_service(self): """Starts /etc/systemd/system/nym-node.service based on prompt using external script""" @@ -416,8 +414,8 @@ def run_nym_node_as_service(self): if is_active: while True: - ans = input(f"{service} is already running. Restart it now? (y/n):\n").strip().lower() - if ans == "y": + ans = input(f"{service} is already running. Restart it now? (Y/n):\n").strip().lower() + if ans in ("", "Y", "y"): self.run_script(self.start_node_systemd_service_sh, args=["restart-poll"], env=run_env) return elif ans == "n": @@ -427,8 +425,8 @@ def run_nym_node_as_service(self): print("Invalid input. Please press 'y' or 'n' and press enter.") else: while True: - ans = input(f"{service} is not running. Start it now? (y/n):\n").strip().lower() - if ans == "y": + ans = input(f"{service} is not running. Start it now? (Y/n):\n").strip().lower() + if ans in ("", "Y", "y"): self.run_script(self.start_node_systemd_service_sh, args=["start-poll"], env=run_env) return elif ans == "n": From fe7470ea44fd7539bae5a4946a4cdc6061326fe0 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:54:17 +0000 Subject: [PATCH 58/83] address comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/quic_bridge_deployment.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/nym-node-setup/quic_bridge_deployment.sh b/scripts/nym-node-setup/quic_bridge_deployment.sh index 1d409585782..4aaa7d72df7 100644 --- a/scripts/nym-node-setup/quic_bridge_deployment.sh +++ b/scripts/nym-node-setup/quic_bridge_deployment.sh @@ -32,6 +32,8 @@ log() { # simple redirection that keeps function scope intact add_log_redirection() { + # Preserve original stdout (fd 1) and stderr (fd 2) in file descriptors 3 and 4 + # before redirection, to support potential future use cases where the original streams are needed. exec 3>&1 4>&2 exec > >(tee -a "$LOG_FILE") 2>&1 } From e8ca490db121c1059dff723be62e85b73d55d841 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:54:42 +0000 Subject: [PATCH 59/83] style Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/quic_bridge_deployment.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/nym-node-setup/quic_bridge_deployment.sh b/scripts/nym-node-setup/quic_bridge_deployment.sh index 4aaa7d72df7..f0a3de83df3 100644 --- a/scripts/nym-node-setup/quic_bridge_deployment.sh +++ b/scripts/nym-node-setup/quic_bridge_deployment.sh @@ -433,7 +433,6 @@ apply_bridge_iptables_rules() { iptables-save > /etc/iptables/rules.v4 ip6tables-save > /etc/iptables/rules.v6 - ok "iptables rules applied." } From 94151965bbebb3559bee101d850beeb242188f89 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 17:55:09 +0000 Subject: [PATCH 60/83] string to dict fix Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/nym-node-cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 2df9657e3f8..21eacbac48b 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -133,7 +133,7 @@ def _get_or_prompt_mode(self, args): mode = getattr(args, "mode", None) if mode: mode = mode.strip().lower() - self._upsert_env_vars("MODE", mode) + self._upsert_env_vars({"MODE": mode}) print(f"Mode set to '{mode}' from CLI argument.") return mode From 6b8a6283a428e0677f4753889e3e166181754d3c Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 20:27:43 +0100 Subject: [PATCH 61/83] fix nginx script --- .../nym-node-setup/setup-nginx-proxy-wss.sh | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh index eae4d7165e0..f3a8aa46c06 100644 --- a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh +++ b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh @@ -64,16 +64,16 @@ cert_ok() { } fetch_landing_html() { - local url="https://raw.githubusercontent.com/nymtech/nym/refs/heads/develop/scripts/nym-node-setup/landing-page.html" + local url="https://raw.githubusercontent.com/nymtech/nym/develop/scripts/nym-node-setup/landing-page.html" + mkdir -p "${WEBROOT}" - if command -v curl >/dev/null 2>&1; then - curl -fsSL "$url" -o "${WEBROOT}/index.html" || true - else - wget -qO "${WEBROOT}/index.html" "$url" || true - fi + curl -fsSL "$url" -o "${WEBROOT}/index.html" || \ + wget -qO "${WEBROOT}/index.html" "$url" || true + # fallback HTML if [[ ! -s "${WEBROOT}/index.html" ]]; then + echo "landing page not found upstream, using fallback template" cat > "${WEBROOT}/index.html" <<'HTML' @@ -158,23 +158,36 @@ echo "Landing page at ${WEBROOT}/index.html" # disable default and stale SSL configs [[ -L "${SITES_EN}/default" ]] && unlink "${SITES_EN}/default" || true + +# hard-clean: dangling SSL symlink for this hostname +if [[ -L "${SITES_EN}/${HOSTNAME}-ssl" && ! -f "${SITES_AVAIL}/${HOSTNAME}-ssl" ]]; then + echo "Removing stale SSL symlink for ${HOSTNAME}" + unlink "${SITES_EN}/${HOSTNAME}-ssl" +fi + +# remove SSL vhosts referencing localhost certs for f in "${SITES_EN}"/*; do [[ -L "$f" ]] || continue if grep -q "/etc/letsencrypt/live/localhost" "$f"; then - echo "Disabling vhost referencing localhost cert: $f"; unlink "$f" + echo "Disabling vhost with localhost cert: $f" + unlink "$f" fi done + +# remove SSL vhosts with missing certs for f in "${SITES_EN}"/*; do [[ -L "$f" ]] || continue if grep -qE 'listen\s+.*443' "$f"; then cert=$(awk '/ssl_certificate[ \t]+/ {print $2}' "$f" | tr -d ';' | head -n1) - key=$(awk '/ssl_certificate_key[ \t]+/ {print $2}' "$f" | tr -d ';' | head -n1) - if [[ -n "${cert:-}" && ! -s "$cert" ]] || [[ -n "${key:-}" && ! -s "$key" ]]; then - echo "Disabling SSL vhost with missing cert/key: $f"; unlink "$f" + key=$(awk '/ssl_certificate_key[ \t]+/ {print $2}' "$f" | tr -d ';') + if [[ ! -s "$cert" || ! -s "$key" ]]; then + echo "Disabling SSL vhost with missing cert/key: $f" + unlink "$f" fi fi done + # HTTP :80 vhost (ACME-safe, proxy to :8080) neat_backup "${BASE_HTTP}" cat > "${BASE_HTTP}" < Date: Thu, 13 Nov 2025 20:37:35 +0100 Subject: [PATCH 62/83] flush nginx script anew --- .../nym-node-setup/setup-nginx-proxy-wss.sh | 380 +++++------------- 1 file changed, 106 insertions(+), 274 deletions(-) diff --git a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh index f3a8aa46c06..ed3d3e59fbc 100644 --- a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh +++ b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh @@ -1,365 +1,197 @@ #!/usr/bin/env bash set -euo pipefail -# load env (prefer absolute ENV_FILE injected by Python CLI; fallback to ./env.sh) +# load env if [[ -n "${ENV_FILE:-}" && -f "${ENV_FILE}" ]]; then set -a; . "${ENV_FILE}"; set +a elif [[ -f "./env.sh" ]]; then set -a; . ./env.sh; set +a fi -: "${HOSTNAME:?HOSTNAME not set in env.sh}" -: "${EMAIL:?EMAIL not set in env.sh}" +: "${HOSTNAME:?HOSTNAME not set}" +: "${EMAIL:?EMAIL not set}" -export SYSTEMD_PAGER="" -export SYSTEMD_COLORS="0" -DEBIAN_FRONTEND=noninteractive - -# sanity check -if [[ "${HOSTNAME}" == "localhost" || "${HOSTNAME}" == "127.0.0.1" || "${HOSTNAME}" == "ubuntu" ]]; then - echo "ERROR: HOSTNAME cannot be 'localhost'. Use a public FQDN." >&2 - exit 1 -fi - -echo -e "\n* * * Starting nginx configuration for landing page, reverse proxy and WSS * * *" - -# set paths & ports vars +export DEBIAN_FRONTEND=noninteractive WEBROOT="/var/www/${HOSTNAME}" -LE_ACME_DIR="/var/www/letsencrypt" SITES_AVAIL="/etc/nginx/sites-available" SITES_EN="/etc/nginx/sites-enabled" -BASE_HTTP="${SITES_AVAIL}/${HOSTNAME}" # :80 vhost -BASE_HTTPS="${SITES_AVAIL}/${HOSTNAME}-ssl" # :443 vhost (we’ll write it ourselves) -WSS_AVAIL="${SITES_AVAIL}/wss-config-nym" -BACKUP_DIR="/etc/nginx/sites-backups" - -NYM_PORT_HTTP="${NYM_PORT_HTTP:-8080}" -NYM_PORT_WSS="${NYM_PORT_WSS:-9000}" -WSS_LISTEN_PORT="${WSS_LISTEN_PORT:-9001}" - -mkdir -p "${WEBROOT}" "${LE_ACME_DIR}" "${BACKUP_DIR}" "${SITES_AVAIL}" "${SITES_EN}" - -# helpers -neat_backup() { - local file="$1"; [[ -f "$file" ]] || return 0 - local sha_now; sha_now="$(sha256sum "$file" | awk '{print $1}')" || return 0 - local tag; tag="$(basename "$file")" - local latest="${BACKUP_DIR}/${tag}.latest" - if [[ -f "$latest" ]]; then - local sha_prev; sha_prev="$(awk '{print $1}' "$latest")" - [[ "$sha_now" == "$sha_prev" ]] && return 0 - fi - cp -a "$file" "${BACKUP_DIR}/${tag}.bak.$(date +%s)" - echo "$sha_now ${tag}" > "$latest" - ls -1t "${BACKUP_DIR}/${tag}.bak."* 2>/dev/null | tail -n +6 | xargs -r rm -f -} -ensure_enabled() { - local src="$1"; local name; name="$(basename "$src")" - ln -sf "$src" "${SITES_EN}/${name}" -} +HTTP_CONF="${SITES_AVAIL}/${HOSTNAME}" +HTTPS_CONF="${SITES_AVAIL}/${HOSTNAME}-ssl" +WSS_CONF="${SITES_AVAIL}/wss-config-nym" -cert_ok() { - [[ -s "/etc/letsencrypt/live/${HOSTNAME}/fullchain.pem" && -s "/etc/letsencrypt/live/${HOSTNAME}/privkey.pem" ]] -} +echo +echo "* * * starting clean nginx configuration for landing page, reverse proxy and wss * * *" -fetch_landing_html() { - local url="https://raw.githubusercontent.com/nymtech/nym/develop/scripts/nym-node-setup/landing-page.html" +############################################################################### +# step 1: clean old configs +############################################################################### - mkdir -p "${WEBROOT}" +echo "cleaning existing nginx configuration" - curl -fsSL "$url" -o "${WEBROOT}/index.html" || \ - wget -qO "${WEBROOT}/index.html" "$url" || true +# remove old default symlink +[[ -L ${SITES_EN}/default ]] && unlink ${SITES_EN}/default || true - # fallback HTML - if [[ ! -s "${WEBROOT}/index.html" ]]; then - echo "landing page not found upstream, using fallback template" - cat > "${WEBROOT}/index.html" <<'HTML' - - - - - - Nym Exit Gateway - - - -

Nym Exit Gateway

-

This is a Nym Exit Gateway. The operator of this router has no access to any of the data routing through that due to encryption design.

- - -HTML - fi -} - -inject_email() { - local file="${WEBROOT}/index.html" - [[ -n "${EMAIL:-}" && -s "$file" ]] || return 0 - - # Escape characters that would break sed replacement - local esc_email - esc_email="$(printf '%s' "$EMAIL" | sed -e 's/[&/\]/\\&/g')" - - # try to update existing meta (case-insensitive on the name attr) - if grep -qiE ']+name=["'"'"']contact:email["'"'"']' "$file"; then - sed -i -E \ - "s|(]+name=[\"']contact:email[\"'][^>]*content=\")[^\"]*(\"[^>]*>)|\1${esc_email}\2|I" \ - "$file" || true - return 0 - fi - - # insert before if present (case-insensitive) - if grep -qi '' "$file"; then - awk -v email="$EMAIL" ' - BEGIN{IGNORECASE=1} - /<\/head>/ && !done { - print " " - done=1 - } - { print } - ' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file" - return 0 - fi - - # fallback: append at end - printf '\n\n' "$EMAIL" >> "$file" || true -} +# remove old vhosts for this domain (symlink + config) +rm -f "${SITES_EN}/${HOSTNAME}" || true +rm -f "${SITES_EN}/${HOSTNAME}-ssl" || true +rm -f "${SITES_EN}/wss-config-nym" || true -fetch_logo() { - local logo_url="https://raw.githubusercontent.com/nymtech/websites/refs/heads/main/www/nym.com/public/images/Nym_meta_Image.png?token=GHSAT0AAAAAACEERII7URYRTFACZ4F2OWZ42GMCPBQ" - mkdir -p "${WEBROOT}/images" - if [[ ! -s "${WEBROOT}/images/nym_logo.png" ]]; then - if command -v curl >/dev/null 2>&1; then - curl -fsSL "$logo_url" -o "${WEBROOT}/images/nym_logo.png" || true - else - wget -qO "${WEBROOT}/images/nym_logo.png" "$logo_url" || true - fi - fi -} +rm -f "${HTTP_CONF}" || true +rm -f "${HTTPS_CONF}" || true +rm -f "${WSS_CONF}" || true -reload_nginx() { nginx -t && systemctl reload nginx; } +############################################################################### +# step 2: create landing page +############################################################################### -# landing page (idempotent) -fetch_landing_html -inject_email -fetch_logo -echo "Landing page at ${WEBROOT}/index.html" +mkdir -p "${WEBROOT}" +if ! curl -fsSL \ + https://raw.githubusercontent.com/nymtech/nym/develop/scripts/nym-node-setup/landing-page.html \ + -o "${WEBROOT}/index.html"; then -# disable default and stale SSL configs -[[ -L "${SITES_EN}/default" ]] && unlink "${SITES_EN}/default" || true + cat > "${WEBROOT}/index.html" <<'EOF' + + +nym node + +

nym exit gateway

+

this is a nym exit gateway.

+ + +EOF -# hard-clean: dangling SSL symlink for this hostname -if [[ -L "${SITES_EN}/${HOSTNAME}-ssl" && ! -f "${SITES_AVAIL}/${HOSTNAME}-ssl" ]]; then - echo "Removing stale SSL symlink for ${HOSTNAME}" - unlink "${SITES_EN}/${HOSTNAME}-ssl" fi -# remove SSL vhosts referencing localhost certs -for f in "${SITES_EN}"/*; do - [[ -L "$f" ]] || continue - if grep -q "/etc/letsencrypt/live/localhost" "$f"; then - echo "Disabling vhost with localhost cert: $f" - unlink "$f" - fi -done - -# remove SSL vhosts with missing certs -for f in "${SITES_EN}"/*; do - [[ -L "$f" ]] || continue - if grep -qE 'listen\s+.*443' "$f"; then - cert=$(awk '/ssl_certificate[ \t]+/ {print $2}' "$f" | tr -d ';' | head -n1) - key=$(awk '/ssl_certificate_key[ \t]+/ {print $2}' "$f" | tr -d ';') - if [[ ! -s "$cert" || ! -s "$key" ]]; then - echo "Disabling SSL vhost with missing cert/key: $f" - unlink "$f" - fi - fi -done - - -# HTTP :80 vhost (ACME-safe, proxy to :8080) -neat_backup "${BASE_HTTP}" -cat > "${BASE_HTTP}" < "${HTTP_CONF}" </dev/null; then - echo "WARNING: Can't reach Let's Encrypt directory. We'll still keep HTTP up." >&2 -fi -THIS_IP="$(curl -fsS -4 https://ifconfig.me || true)" -DNS_IP="$(getent ahostsv4 "${HOSTNAME}" 2>/dev/null | awk '{print $1; exit}')" -echo "Public IPv4: ${THIS_IP:-unknown} DNS A(${HOSTNAME}): ${DNS_IP:-unresolved}" -if [[ -n "${THIS_IP:-}" && -n "${DNS_IP:-}" && "${THIS_IP}" != "${DNS_IP}" ]]; then - echo "WARNING: DNS for ${HOSTNAME} does not match this server's public IPv4." -fi -timedatectl show -p NTPSynchronized --value 2>/dev/null | grep -qi yes || timedatectl set-ntp true || true - -# install certbot if missing -if ! command -v certbot >/dev/null 2>&1; then - if command -v snap >/dev/null 2>&1; then - snap install core || true; snap refresh core || true - snap install --classic certbot; ln -sf /snap/bin/certbot /usr/bin/certbot - else - apt-get update -y >/dev/null 2>&1 || true - apt-get install -y certbot >/dev/null 2>&1 || true - fi -fi -# issue/renew via WEBROOT (no nginx auto-edit), non-fatal if it fails -STAGING_FLAG=""; [[ "${CERTBOT_STAGING:-0}" == "1" ]] && STAGING_FLAG="--staging" && echo "Using Let's Encrypt STAGING." -if ! cert_ok; then - certbot certonly --non-interactive --agree-tos -m "${EMAIL}" -d "${HOSTNAME}" \ - --webroot -w "${LE_ACME_DIR}" ${STAGING_FLAG} || true -fi +ln -sf "${HTTP_CONF}" "${SITES_EN}/${HOSTNAME}" -# create own 443 vhost (only if certs exist) -if cert_ok; then - neat_backup "${BASE_HTTPS}" - cat > "${BASE_HTTPS}" </dev/null 2>&1 || true +apt-get install -y certbot python3-certbot-nginx >/dev/null 2>&1 || true + +echo "requesting let's encrypt certificate for ${HOSTNAME}" + +certbot --nginx --non-interactive --agree-tos \ + -m "${EMAIL}" -d "${HOSTNAME}" --redirect || true + +############################################################################### +# step 5: if cert OK, build https and wss configs +############################################################################### + +if [[ -s "/etc/letsencrypt/live/${HOSTNAME}/fullchain.pem" ]]; then + echo "certificate detected, creating https and wss configs" + + cat > "${HTTPS_CONF}" <HTTPS (keeps ACME path in HTTP too via separate small server) - neat_backup "${BASE_HTTP}" - cat > "${BASE_HTTP}" < "${WSS_AVAIL}" < "${WSS_CONF}" < Date: Thu, 13 Nov 2025 20:46:33 +0100 Subject: [PATCH 63/83] replace y to Y and '' --- scripts/nym-node-setup/nym-node-install.sh | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-install.sh b/scripts/nym-node-setup/nym-node-install.sh index 3ea25687f59..3a88577ad9a 100644 --- a/scripts/nym-node-setup/nym-node-install.sh +++ b/scripts/nym-node-setup/nym-node-install.sh @@ -34,8 +34,9 @@ check_existing_config() { if [[ "${resp}" =~ ^([Rr][Ee][Ss][Ee][Tt])$ ]]; then echo - read -r -p "We are going to remove the existing node with configuration $NODE_CONFIG_DIR and replace it with a fresh one, do you want to back up the old one first? (y/n) " backup_ans - if [[ "${backup_ans}" =~ ^[Yy]$ ]]; then + echo "We are going to remove the existing node with configuration $NODE_CONFIG_DIR and replace it with a fresh one." + read -r -p "back up the old one first? (Y/n) " backup_ans + if [[ -z "${backup_ans}" || "${backup_ans}" =~ ^[Yy]$ ]]; then ts="$(date +%Y%m%d-%H%M%S)" backup_dir="$HOME/.nym/backup/$(basename "$NODE_CONFIG_DIR")-$ts" echo "Backing up to: $backup_dir" @@ -185,14 +186,14 @@ NYM_NODE="$HOME/nym-binaries/nym-node" if [[ -e "${NYM_NODE}" ]]; then echo echo -e "\n* * * A nym-node binary already exists at: ${NYM_NODE}" - read -r -p "Overwrite with the latest release? (y/n): " ow_ans - if [[ "${ow_ans}" =~ ^[Yy]$ ]]; then - echo "Removing existing binary to avoid 'text file busy'..." + read -r -p "Overwrite with the latest release? (Y/n): " ow_ans + if [[ -z "${ow_ans}" || "${ow_ans}" =~ ^[Yy]$ ]]; then + echo "Removing existing binary..." rm -f "${NYM_NODE}" + download_nym_node "$LATEST_TAG_URL" "$NYM_NODE" else - echo "Keeping existing binary." + echo "Keeping existing binary. Skipping download." fi -fi download_nym_node "$LATEST_TAG_URL" "$NYM_NODE" @@ -229,13 +230,12 @@ if [[ ( "$MODE" == "entry-gateway" || "$MODE" == "exit-gateway" ) && ( -n "${ASK # show current default in the prompt if present def_hint="" [[ -n "${WIREGUARD}" ]] && def_hint=" [current: ${WIREGUARD}]" - read -r -p "Enable WireGuard support? (y/n)${def_hint}: " answer || true - case "${answer:-}" in - [Yy]* ) WIREGUARD="true" ;; - [Nn]* ) WIREGUARD="false" ;; - * ) : ;; # keep existing value if user just pressed enter - esac -fi + read -r -p "Enable WireGuard support? (Y/n)${def_hint}: " answer || true + if [[ -z "${answer}" || "${answer}" =~ ^[Yy]$ ]]; then + WIREGUARD="true" + elif [[ "${answer}" =~ ^[Nn]$ ]]; then + WIREGUARD="false" + fi # final default only if still empty WIREGUARD="${WIREGUARD:-false}" From 58c0e289c25d67ba72a868629e7624a38b680087 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Thu, 13 Nov 2025 20:56:04 +0100 Subject: [PATCH 64/83] syntax fix --- scripts/nym-node-setup/nym-node-install.sh | 15 ++-- .../nym-node-setup/setup-nginx-proxy-wss.sh | 70 +++++++++++-------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-install.sh b/scripts/nym-node-setup/nym-node-install.sh index 3a88577ad9a..af36c92017b 100644 --- a/scripts/nym-node-setup/nym-node-install.sh +++ b/scripts/nym-node-setup/nym-node-install.sh @@ -182,11 +182,12 @@ fi NYM_NODE="$HOME/nym-binaries/nym-node" -# if binary already exists, ask to overwrite; if yes, remove first +# if binary already exists, ask to overwrite; if yes, remove first; if no, skip download if [[ -e "${NYM_NODE}" ]]; then echo echo -e "\n* * * A nym-node binary already exists at: ${NYM_NODE}" read -r -p "Overwrite with the latest release? (Y/n): " ow_ans + if [[ -z "${ow_ans}" || "${ow_ans}" =~ ^[Yy]$ ]]; then echo "Removing existing binary..." rm -f "${NYM_NODE}" @@ -195,11 +196,13 @@ if [[ -e "${NYM_NODE}" ]]; then echo "Keeping existing binary. Skipping download." fi -download_nym_node "$LATEST_TAG_URL" "$NYM_NODE" +else + # binary does not exist → must download + download_nym_node "$LATEST_TAG_URL" "$NYM_NODE" +fi echo -e "\n * * * Making binary executable * * *" chmod +x "${NYM_NODE}" - echo "---------------------------------------------------" echo "Nym node binary downloaded:" "${NYM_NODE}" --version || true @@ -226,16 +229,18 @@ WIREGUARD="${WIREGUARD:-}" if [[ ( "$MODE" == "entry-gateway" || "$MODE" == "exit-gateway" ) && ( -n "${ASK_WG:-}" || -z "$WIREGUARD" ) ]]; then echo echo "Gateways can also route WireGuard in NymVPN." - echo "Enabling it means your node may be listed as both entry and exit in the app." - # show current default in the prompt if present def_hint="" [[ -n "${WIREGUARD}" ]] && def_hint=" [current: ${WIREGUARD}]" + read -r -p "Enable WireGuard support? (Y/n)${def_hint}: " answer || true + if [[ -z "${answer}" || "${answer}" =~ ^[Yy]$ ]]; then WIREGUARD="true" elif [[ "${answer}" =~ ^[Nn]$ ]]; then WIREGUARD="false" fi +fi + # final default only if still empty WIREGUARD="${WIREGUARD:-false}" diff --git a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh index ed3d3e59fbc..312ea8bf110 100644 --- a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh +++ b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh @@ -12,6 +12,7 @@ fi : "${EMAIL:?EMAIL not set}" export DEBIAN_FRONTEND=noninteractive + WEBROOT="/var/www/${HOSTNAME}" SITES_AVAIL="/etc/nginx/sites-available" SITES_EN="/etc/nginx/sites-enabled" @@ -23,29 +24,31 @@ WSS_CONF="${SITES_AVAIL}/wss-config-nym" echo echo "* * * starting clean nginx configuration for landing page, reverse proxy and wss * * *" -############################################################################### -# step 1: clean old configs -############################################################################### +################################################################################ +# step 1: clean all previous configs +################################################################################ echo "cleaning existing nginx configuration" -# remove old default symlink -[[ -L ${SITES_EN}/default ]] && unlink ${SITES_EN}/default || true +# remove default nginx config +[[ -L "${SITES_EN}/default" ]] && unlink "${SITES_EN}/default" || true -# remove old vhosts for this domain (symlink + config) -rm -f "${SITES_EN}/${HOSTNAME}" || true -rm -f "${SITES_EN}/${HOSTNAME}-ssl" || true -rm -f "${SITES_EN}/wss-config-nym" || true +# remove domain symlinks +rm -f "${SITES_EN}/${HOSTNAME}" || true +rm -f "${SITES_EN}/${HOSTNAME}-ssl" || true +rm -f "${SITES_EN}/wss-config-nym" || true -rm -f "${HTTP_CONF}" || true -rm -f "${HTTPS_CONF}" || true -rm -f "${WSS_CONF}" || true +# remove old configs +rm -f "${HTTP_CONF}" || true +rm -f "${HTTPS_CONF}" || true +rm -f "${WSS_CONF}" || true -############################################################################### -# step 2: create landing page -############################################################################### +################################################################################ +# step 2: landing page +################################################################################ mkdir -p "${WEBROOT}" + if ! curl -fsSL \ https://raw.githubusercontent.com/nymtech/nym/develop/scripts/nym-node-setup/landing-page.html \ -o "${WEBROOT}/index.html"; then @@ -65,9 +68,9 @@ fi echo "landing page at ${WEBROOT}/index.html" -############################################################################### -# step 3: create basic http config (80) -############################################################################### +################################################################################ +# step 3: HTTP :80 config +################################################################################ cat > "${HTTP_CONF}" </dev/null 2>&1 || true apt-get install -y certbot python3-certbot-nginx >/dev/null 2>&1 || true @@ -108,15 +113,17 @@ apt-get install -y certbot python3-certbot-nginx >/dev/null 2>&1 || true echo "requesting let's encrypt certificate for ${HOSTNAME}" certbot --nginx --non-interactive --agree-tos \ + --reuse-key \ -m "${EMAIL}" -d "${HOSTNAME}" --redirect || true -############################################################################### -# step 5: if cert OK, build https and wss configs -############################################################################### +################################################################################ +# step 5: HTTPS and WSS configs +################################################################################ if [[ -s "/etc/letsencrypt/live/${HOSTNAME}/fullchain.pem" ]]; then echo "certificate detected, creating https and wss configs" + # HTTPS 443 cat > "${HTTPS_CONF}" < "${WSS_CONF}" < Date: Fri, 14 Nov 2025 10:31:26 +0000 Subject: [PATCH 65/83] remove redundant detect interface Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../nym-node-setup/network-tunnel-manager.sh | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 66e412d6296..400a75971f2 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -37,21 +37,25 @@ fi TUNNEL_INTERFACE="${TUNNEL_INTERFACE:-nymtun0}" WG_INTERFACE="${WG_INTERFACE:-nymwg}" +# Function to detect and validate uplink interface +detect_uplink_interface() { + local cmd="$1" + local dev + dev="$(eval "$cmd" 2>/dev/null | awk '{print \$5}' | head -n1 || true)" + if [[ -n "$dev" && "$dev" =~ ^[a-zA-Z0-9._-]+$ ]]; then + echo "$dev" + else + echo "" + fi +} + # uplink device detection, can be overridden NETWORK_DEVICE="${NETWORK_DEVICE:-}" if [[ -z "$NETWORK_DEVICE" ]]; then - NETWORK_DEVICE="$(ip -o route show default 2>/dev/null | awk '{print $5}' | head -n1 || true)" - # Validate that NETWORK_DEVICE is non-empty and looks like a network interface - if [[ -z "$NETWORK_DEVICE" || ! "$NETWORK_DEVICE" =~ ^[a-zA-Z0-9._-]+$ ]]; then - NETWORK_DEVICE="" - fi + NETWORK_DEVICE="$(detect_uplink_interface "ip -o route show default")" fi if [[ -z "$NETWORK_DEVICE" ]]; then - NETWORK_DEVICE="$(ip -o route show default table all 2>/dev/null | awk '{print $5}' | head -n1 || true)" - # Validate that NETWORK_DEVICE is non-empty and looks like a network interface - if [[ -z "$NETWORK_DEVICE" || ! "$NETWORK_DEVICE" =~ ^[a-zA-Z0-9._-]+$ ]]; then - NETWORK_DEVICE="" - fi + NETWORK_DEVICE="$(detect_uplink_interface "ip -o route show default table all")" fi if [[ -z "$NETWORK_DEVICE" ]]; then echo "cannot determine uplink interface. set NETWORK_DEVICE or UPLINK_DEV" From f62dbbdae0b81b8e920501164fd61e34c6230a6d Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 10:33:23 +0000 Subject: [PATCH 66/83] ensure idempotency for the iptable rules Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../nym-node-setup/network-tunnel-manager.sh | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 400a75971f2..668b6c55f12 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -521,14 +521,28 @@ setup_nat_rules() { configure_exit_dns_and_icmp() { echo "ensuring dns and icmp are allowed inside nym exit chain" - iptables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT - iptables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT - ip6tables -I "$NYM_CHAIN" 1 -p udp --dport 53 -j ACCEPT - ip6tables -I "$NYM_CHAIN" 2 -p tcp --dport 53 -j ACCEPT - - iptables -I "$NYM_CHAIN" 3 -p icmp --icmp-type echo-request -j ACCEPT - iptables -I "$NYM_CHAIN" 4 -p icmp --icmp-type echo-reply -j ACCEPT - ip6tables -I "$NYM_CHAIN" 3 -p ipv6-icmp -j ACCEPT + if ! iptables -C "$NYM_CHAIN" -p udp --dport 53 -j ACCEPT 2>/dev/null; then + iptables -A "$NYM_CHAIN" -p udp --dport 53 -j ACCEPT + fi + if ! iptables -C "$NYM_CHAIN" -p tcp --dport 53 -j ACCEPT 2>/dev/null; then + iptables -A "$NYM_CHAIN" -p tcp --dport 53 -j ACCEPT + fi + if ! ip6tables -C "$NYM_CHAIN" -p udp --dport 53 -j ACCEPT 2>/dev/null; then + ip6tables -A "$NYM_CHAIN" -p udp --dport 53 -j ACCEPT + fi + if ! ip6tables -C "$NYM_CHAIN" -p tcp --dport 53 -j ACCEPT 2>/dev/null; then + ip6tables -A "$NYM_CHAIN" -p tcp --dport 53 -j ACCEPT + fi + + if ! iptables -C "$NYM_CHAIN" -p icmp --icmp-type echo-request -j ACCEPT 2>/dev/null; then + iptables -A "$NYM_CHAIN" -p icmp --icmp-type echo-request -j ACCEPT + fi + if ! iptables -C "$NYM_CHAIN" -p icmp --icmp-type echo-reply -j ACCEPT 2>/dev/null; then + iptables -A "$NYM_CHAIN" -p icmp --icmp-type echo-reply -j ACCEPT + fi + if ! ip6tables -C "$NYM_CHAIN" -p ipv6-icmp -j ACCEPT 2>/dev/null; then + ip6tables -A "$NYM_CHAIN" -p ipv6-icmp -j ACCEPT + fi } apply_port_allowlist() { From 3f560180b7713428979732fb705151340131aaed Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 10:35:42 +0000 Subject: [PATCH 67/83] remove redundant Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/quic_bridge_deployment.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/nym-node-setup/quic_bridge_deployment.sh b/scripts/nym-node-setup/quic_bridge_deployment.sh index f0a3de83df3..d6fb15cb2e6 100644 --- a/scripts/nym-node-setup/quic_bridge_deployment.sh +++ b/scripts/nym-node-setup/quic_bridge_deployment.sh @@ -32,9 +32,6 @@ log() { # simple redirection that keeps function scope intact add_log_redirection() { - # Preserve original stdout (fd 1) and stderr (fd 2) in file descriptors 3 and 4 - # before redirection, to support potential future use cases where the original streams are needed. - exec 3>&1 4>&2 exec > >(tee -a "$LOG_FILE") 2>&1 } add_log_redirection From 054715a600d503e823dd76324e0ecbde8b19b439 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 10:36:23 +0000 Subject: [PATCH 68/83] robust error handling Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/nym-node-cli.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 21eacbac48b..b6e4bd55a1d 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -96,8 +96,12 @@ def ensure_env_values(self, args): if ip.returncode == 0 and ip.stdout.strip(): updated["PUBLIC_IP"] = ip.stdout.strip() os.environ["PUBLIC_IP"] = ip.stdout.strip() - except Exception: - pass + except subprocess.TimeoutExpired: + print("[WARN] Timeout expired while trying to fetch public IP with curl.") + except FileNotFoundError: + print("[WARN] 'curl' command not found. Please install curl or set PUBLIC_IP manually.") + except subprocess.CalledProcessError as e: + print(f"[WARN] Error while running curl to fetch public IP: {e}") # write all collected variables to env.sh in one go self._upsert_env_vars(updated, env_file) From d820131d2cc539d9eb14bcad966154a7c9b2ecf0 Mon Sep 17 00:00:00 2001 From: import this <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 10:36:45 +0000 Subject: [PATCH 69/83] arg consistency Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/nym-node-setup/nym-node-cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index b6e4bd55a1d..0e9130c1bcb 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -585,6 +585,7 @@ def run_node_installation(self,args): # Pass uplink override to all helper scripts if provided if getattr(args, "uplink_dev", None): os.environ["UPLINK_DEV"] = args.uplink_dev + os.environ["NETWORK_DEVICE"] = args.uplink_dev self.run_script(self.prereqs_install_sh) self.run_script(self.node_install_sh) self.run_script(self.service_config_sh) From 228ef8b158b7772106062ce30d6a6c87bbb36b29 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:40:14 +0100 Subject: [PATCH 70/83] add else --- scripts/nym-node-setup/nym-node-cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 0e9130c1bcb..179ab0e767a 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -391,6 +391,8 @@ def quic_bridge_deploy(self): if answer in ("", "y", "yes"): self.run_script(self.quic_bridge_deployment_sh, args=["full_bridge_setup"]) + else: + print("Skipping QUIC bridge setup.") def run_nym_node_as_service(self): """Starts /etc/systemd/system/nym-node.service based on prompt using external script""" From 9bdd2af14c24bd05ae152718cbb2b01f4fa02570 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:42:05 +0100 Subject: [PATCH 71/83] enforce root --- scripts/nym-node-setup/nym-node-prereqs-install.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/nym-node-setup/nym-node-prereqs-install.sh b/scripts/nym-node-setup/nym-node-prereqs-install.sh index 58b04ecc65c..f359aceb672 100644 --- a/scripts/nym-node-setup/nym-node-prereqs-install.sh +++ b/scripts/nym-node-setup/nym-node-prereqs-install.sh @@ -1,6 +1,11 @@ #!/bin/bash -# update, upgrade & install dependencies +if [[ "$(id -u)" -ne 0 ]]; then + echo "This script must be run as root." + exit 1 +fi + +# update, upgrade and install dependencies echo -e "\n* * * Installing needed prerequisities * * *" apt update -y && apt --fix-broken install @@ -8,11 +13,9 @@ apt upgrade apt install apt ca-certificates jq curl wget ufw jq tmux pkg-config build-essential libssl-dev git ntp ntpdate neovim tree tmux tig nginx -y apt install ufw --fix-missing - - # enable & setup firewall echo -e "\n* * * Setting up firewall using ufw * * * " -echo "Please enable the firewall in the next prompt for node proper routing!" +echo "Please enable the firewall in the next prompt for node proper routing." echo ufw enable ufw allow 22/tcp # SSH - you're in control of these ports From 10707fd2c5be2c354e2340c436c6014c022b7a0a Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:43:54 +0100 Subject: [PATCH 72/83] convention Y/n --- scripts/nym-node-setup/setup-systemd-service-file.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nym-node-setup/setup-systemd-service-file.sh b/scripts/nym-node-setup/setup-systemd-service-file.sh index 4f8d586492e..fca48f7a2f1 100644 --- a/scripts/nym-node-setup/setup-systemd-service-file.sh +++ b/scripts/nym-node-setup/setup-systemd-service-file.sh @@ -83,8 +83,8 @@ fi # interactive path (manual runs) ensure_mode -read -rp "Service file not found. Create it now? (y/n): " create_ans -if [[ "${create_ans:-}" =~ ^[Yy]$ ]]; then +read -rp "Service file not found. Create it now? (Y/n): " create_ans +if [[ -z "${create_ans}" || "${create_ans}" =~ ^[Yy]$ ]]; then create_service_file else echo "Not creating the service file." From e0ff09f323c1fdb8b9873872cb11a932eae171fa Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:44:57 +0100 Subject: [PATCH 73/83] enforce root --- scripts/nym-node-setup/setup-nginx-proxy-wss.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh index 312ea8bf110..7acb56f1204 100644 --- a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh +++ b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh @@ -1,6 +1,11 @@ #!/usr/bin/env bash set -euo pipefail +if [[ "$(id -u)" -ne 0 ]]; then + echo "This script must be run as root." + exit 1 +fi + # load env if [[ -n "${ENV_FILE:-}" && -f "${ENV_FILE}" ]]; then set -a; . "${ENV_FILE}"; set +a From ae47d53f0ce2d9dd7f766884eeb9f2ac8ce9c1d2 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:49:10 +0100 Subject: [PATCH 74/83] enforce root --- scripts/nym-node-setup/setup-nginx-proxy-wss.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh index 7acb56f1204..aa74b9a7753 100644 --- a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh +++ b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh @@ -27,7 +27,7 @@ HTTPS_CONF="${SITES_AVAIL}/${HOSTNAME}-ssl" WSS_CONF="${SITES_AVAIL}/wss-config-nym" echo -echo "* * * starting clean nginx configuration for landing page, reverse proxy and wss * * *" +echo "* * * Starting clean nginx configuration for landing page, reverse proxy and wss * * *" ################################################################################ # step 1: clean all previous configs From cc04a09ed7463d9362aea30a15a11c0a253b5806 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:58:03 +0100 Subject: [PATCH 75/83] remove redundant work --- .../nym-node-setup/quic_bridge_deployment.sh | 93 ++++++++++++------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/scripts/nym-node-setup/quic_bridge_deployment.sh b/scripts/nym-node-setup/quic_bridge_deployment.sh index d6fb15cb2e6..2d6b151febf 100644 --- a/scripts/nym-node-setup/quic_bridge_deployment.sh +++ b/scripts/nym-node-setup/quic_bridge_deployment.sh @@ -56,7 +56,7 @@ if [[ -z "$NET_DEV" ]]; then echo -e "${RED}Cannot determine uplink interface. Set UPLINK_DEV.${RESET}" | tee -a "$LOG_FILE" exit 1 fi -info "Using uplink device: $NET_DEV" +echo "Using uplink device: $NET_DEV" WG_IFACE="nymwg" @@ -191,13 +191,31 @@ verify_bridge_prerequisites() { } adjust_ip_forwarding() { - title "Adjusting IP Forwarding" - sed -i '/^net\.ipv4\.ip_forward=/d' /etc/sysctl.conf - sed -i '/^net\.ipv6\.conf\.all\.forwarding=/d' /etc/sysctl.conf - echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf - echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf - sysctl -p /etc/sysctl.conf - ok "IPv4/IPv6 forwarding enabled." + title "Checking IP forwarding" + local v4 v6 + v4="$(cat /proc/sys/net/ipv4/ip_forward 2>/dev/null || echo 0)" + v6="$(cat /proc/sys/net/ipv6/conf/all/forwarding 2>/dev/null || echo 0)" + + if [[ "$v4" == "1" ]]; then + ok "IPv4 forwarding is enabled." + else + warn "IPv4 forwarding is not enabled." + fi + + if [[ "$v6" == "1" ]]; then + ok "IPv6 forwarding is enabled." + else + warn "IPv6 forwarding is not enabled." + fi + + if [[ "$v4" != "1" || "$v6" != "1" ]]; then + echo + echo "To enable forwarding and routing consistently, run the network tunnel manager script as root." + echo "For example:" + echo " ./network-tunnel-manager.sh complete_networking_configuration" + echo "or:" + echo " ./network-tunnel-manager.sh adjust_ip_forwarding" + fi } # Install nym-bridge @@ -410,37 +428,40 @@ EOF # IPTABLES & helpers apply_bridge_iptables_rules() { - title "Applying iptables rules" - - # Ensure stateful rules exist - iptables -C FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \ - iptables -I FORWARD 1 -m state --state RELATED,ESTABLISHED -j ACCEPT - ip6tables -C FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \ - ip6tables -I FORWARD 1 -m state --state RELATED,ESTABLISHED -j ACCEPT - - # Allow WG interface input - iptables -C INPUT -i "$WG_IFACE" -j ACCEPT 2>/dev/null || iptables -I INPUT -i "$WG_IFACE" -j ACCEPT - ip6tables -C INPUT -i "$WG_IFACE" -j ACCEPT 2>/dev/null || ip6tables -I INPUT -i "$WG_IFACE" -j ACCEPT - - # NAT (idempotent) - iptables -t nat -C POSTROUTING -o "$NET_DEV" -j MASQUERADE 2>/dev/null || \ - iptables -t nat -I POSTROUTING 1 -o "$NET_DEV" -j MASQUERADE - ip6tables -t nat -C POSTROUTING -o "$NET_DEV" -j MASQUERADE 2>/dev/null || \ - ip6tables -t nat -I POSTROUTING 1 -o "$NET_DEV" -j MASQUERADE - - iptables-save > /etc/iptables/rules.v4 - ip6tables-save > /etc/iptables/rules.v6 - ok "iptables rules applied." + title "Checking iptables rules for bridge routing" + + echo "Inspecting current iptables state for interface ${WG_IFACE} and uplink ${NET_DEV}." + echo + + echo "IPv4 FORWARD:" + iptables -L FORWARD -n -v 2>/dev/null | sed -n '1,20p' || echo "iptables not available." + echo + echo "IPv4 NAT POSTROUTING:" + iptables -t nat -L POSTROUTING -n -v 2>/dev/null | sed -n '1,20p' || true + echo + echo "IPv6 FORWARD:" + ip6tables -L FORWARD -n -v 2>/dev/null | sed -n '1,20p' || true + echo + echo "IPv6 NAT POSTROUTING:" + ip6tables -t nat -L POSTROUTING -n -v 2>/dev/null | sed -n '1,20p' || true + + echo + echo "This script no longer changes iptables. To configure routing and NAT for nym, use the network tunnel manager script." + echo "For example (run as root):" + echo " ./network-tunnel-manager.sh complete_networking_configuration" } configure_dns_and_icmp() { - title "Allow ICMP and DNS" - iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT || true - ip6tables -A INPUT -p ipv6-icmp -j ACCEPT || true - iptables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null || iptables -I INPUT -p udp --dport 53 -j ACCEPT - ip6tables -C INPUT -p udp --dport 53 -j ACCEPT 2>/dev/null || ip6tables -I INPUT -p udp --dport 53 -j ACCEPT + title "Checking ICMP and DNS firewall rules" + + echo "IPv4 INPUT rules related to ICMP and DNS:" + iptables -L INPUT -n -v 2>/dev/null | grep -E 'icmp|dpt:53' || echo "no matching IPv4 rules shown." + echo + echo "IPv6 INPUT rules related to ICMP and DNS:" + ip6tables -L INPUT -n -v 2>/dev/null | grep -E 'icmp|dpt:53' || echo "no matching IPv6 rules shown." - ok "ICMP and DNS rules applied." + echo + echo "If ping or DNS are blocked for bridge traffic, adjust your firewall using the network tunnel manager script or your chosen firewall tool." } # Full interactive setup @@ -485,7 +506,7 @@ full_bridge_setup() { press_enter "Press Enter to continue..." echo "" - echo "Step 6/6: Configuring network rules (optional but recommended)..." + echo "Step 6/6: Checking network rules and forwarding status..." adjust_ip_forwarding apply_bridge_iptables_rules configure_dns_and_icmp From cc95358385ba60fe7137b0b954cd03228b1fbc5a Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:08:05 +0100 Subject: [PATCH 76/83] add email to a fallback --- scripts/nym-node-setup/setup-nginx-proxy-wss.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh index aa74b9a7753..4b0fae6bdac 100644 --- a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh +++ b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh @@ -65,6 +65,7 @@ if ! curl -fsSL \

nym exit gateway

this is a nym exit gateway.

+

Operator contact: ${EMAIL}

EOF From ce261059860ff560e7958092624374e0026f354c Mon Sep 17 00:00:00 2001 From: RadekSabacky Date: Fri, 14 Nov 2025 13:24:05 +0100 Subject: [PATCH 77/83] typo --- .../docs/pages/operators/nodes/nym-node/configuration.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx b/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx index 69afc1e41d5..dbe24c5072c 100644 --- a/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx +++ b/documentation/docs/pages/operators/nodes/nym-node/configuration.mdx @@ -375,7 +375,7 @@ operation check_nymtun_iptables completed successfully. ###### 5. Remove old and apply new rules for wireguad routing ```sh -/network_tunnel_manager.sh remove_duplicate_rules nymwg +./network_tunnel_manager.sh remove_duplicate_rules nymwg ./network_tunnel_manager.sh apply_iptables_rules_wg ``` From 842ce93a60a075ee246783d02b965820e3876a54 Mon Sep 17 00:00:00 2001 From: RadekSabacky Date: Fri, 14 Nov 2025 13:24:20 +0100 Subject: [PATCH 78/83] remove duplicate ufw rule --- scripts/nym-node-setup/nym-node-prereqs-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/nym-node-setup/nym-node-prereqs-install.sh b/scripts/nym-node-setup/nym-node-prereqs-install.sh index f359aceb672..e462ecb5c92 100644 --- a/scripts/nym-node-setup/nym-node-prereqs-install.sh +++ b/scripts/nym-node-setup/nym-node-prereqs-install.sh @@ -28,6 +28,5 @@ ufw allow 9000/tcp # Nym Specific - clients port ufw allow 9001/tcp # Nym specific - wss port ufw allow 51822/udp # WireGuard ufw allow in on nymwg to any port 51830 proto tcp # bandwidth queries/topup - inside the tunnel -ufw allow 'Nginx Full' && \ ufw reload && \ ufw status From e09066858c2dcd1c9e8b980f6906942802b80d8f Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:21:50 +0100 Subject: [PATCH 79/83] bump up version --- scripts/nym-node-setup/nym-node-cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/nym-node-cli.py b/scripts/nym-node-setup/nym-node-cli.py index 179ab0e767a..def82b1d178 100755 --- a/scripts/nym-node-setup/nym-node-cli.py +++ b/scripts/nym-node-setup/nym-node-cli.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -__version__ = "1.1.0" +__version__ = "1.2.0" __default_branch__ = "develop" import os From ab6e08dd13175fda82fdd1b2c49b2ff2ffa24282 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:27:38 +0100 Subject: [PATCH 80/83] fix logic of landing-page lookup --- .../nym-node-setup/setup-nginx-proxy-wss.sh | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh index 4b0fae6bdac..b72cf6ed243 100644 --- a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh +++ b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh @@ -48,17 +48,25 @@ rm -f "${HTTP_CONF}" || true rm -f "${HTTPS_CONF}" || true rm -f "${WSS_CONF}" || true -################################################################################ -# step 2: landing page -################################################################################ +############################################################################### +# step 2: create landing page +############################################################################### mkdir -p "${WEBROOT}" -if ! curl -fsSL \ - https://raw.githubusercontent.com/nymtech/nym/develop/scripts/nym-node-setup/landing-page.html \ - -o "${WEBROOT}/index.html"; then +# script directory where Python CLI stores fetched scripts +SCRIPT_DIR="$(dirname "${ENV_FILE:-./env.sh}")" +LOCAL_FETCHED_PAGE="${SCRIPT_DIR}/landing-page.html" - cat > "${WEBROOT}/index.html" <<'EOF' +if [[ -s "${LOCAL_FETCHED_PAGE}" ]]; then + cp "${LOCAL_FETCHED_PAGE}" "${WEBROOT}/index.html" +else + if curl -fsSL \ + https://raw.githubusercontent.com/nymtech/nym/develop/scripts/nym-node-setup/landing-page.html \ + -o "${WEBROOT}/index.html"; then + : + else + cat > "${WEBROOT}/index.html" < nym node @@ -69,11 +77,13 @@ if ! curl -fsSL \ EOF - + fi fi echo "landing page at ${WEBROOT}/index.html" +echo "landing page at ${WEBROOT}/index.html" + ################################################################################ # step 3: HTTP :80 config ################################################################################ From 6acc54d2bcff46fc3f8f070f17a5ab373d2cd51a Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:03:36 +0100 Subject: [PATCH 81/83] syntax fix --- scripts/nym-node-setup/network-tunnel-manager.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/network-tunnel-manager.sh b/scripts/nym-node-setup/network-tunnel-manager.sh index 668b6c55f12..acd01863668 100644 --- a/scripts/nym-node-setup/network-tunnel-manager.sh +++ b/scripts/nym-node-setup/network-tunnel-manager.sh @@ -41,7 +41,9 @@ WG_INTERFACE="${WG_INTERFACE:-nymwg}" detect_uplink_interface() { local cmd="$1" local dev - dev="$(eval "$cmd" 2>/dev/null | awk '{print \$5}' | head -n1 || true)" + + dev="$(eval "$cmd" 2>/dev/null | awk '{print $5}' | head -n1 || true)" + if [[ -n "$dev" && "$dev" =~ ^[a-zA-Z0-9._-]+$ ]]; then echo "$dev" else @@ -49,6 +51,7 @@ detect_uplink_interface() { fi } + # uplink device detection, can be overridden NETWORK_DEVICE="${NETWORK_DEVICE:-}" if [[ -z "$NETWORK_DEVICE" ]]; then From e5aef76256a9fd42957392977a9719464726190d Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:27:44 +0100 Subject: [PATCH 82/83] non-interactive --- scripts/nym-node-setup/quic_bridge_deployment.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/nym-node-setup/quic_bridge_deployment.sh b/scripts/nym-node-setup/quic_bridge_deployment.sh index 2d6b151febf..db14127016b 100644 --- a/scripts/nym-node-setup/quic_bridge_deployment.sh +++ b/scripts/nym-node-setup/quic_bridge_deployment.sh @@ -75,9 +75,15 @@ err() { echo -e "${RED}$1${RESET}"; } info() { echo -e "${CYAN}$1${RESET}"; } press_enter() { read -r -p "$1"; } -# Disable pauses for noninteractive mode +# Disable pauses and interactive prompts for noninteractive mode if [[ "${NONINTERACTIVE:-0}" == "1" ]]; then + # all pauses become no-ops press_enter() { :; } + + # silence any "enter path:" prompts + echo_prompt() { :; } +else + echo_prompt() { echo -n "$1"; } fi # Helper: detect dpkg dependency failure for libc6>=2.34 From 4f991061dde3a87078b3da6868369f583ed59193 Mon Sep 17 00:00:00 2001 From: serinko <97586125+serinko@users.noreply.github.com> Date: Fri, 14 Nov 2025 16:22:58 +0100 Subject: [PATCH 83/83] fix nginx errors --- .../nym-node-setup/setup-nginx-proxy-wss.sh | 164 +++++++----------- 1 file changed, 65 insertions(+), 99 deletions(-) diff --git a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh index b72cf6ed243..cf1378f5b6c 100644 --- a/scripts/nym-node-setup/setup-nginx-proxy-wss.sh +++ b/scripts/nym-node-setup/setup-nginx-proxy-wss.sh @@ -23,50 +23,28 @@ SITES_AVAIL="/etc/nginx/sites-available" SITES_EN="/etc/nginx/sites-enabled" HTTP_CONF="${SITES_AVAIL}/${HOSTNAME}" -HTTPS_CONF="${SITES_AVAIL}/${HOSTNAME}-ssl" WSS_CONF="${SITES_AVAIL}/wss-config-nym" echo -echo "* * * Starting clean nginx configuration for landing page, reverse proxy and wss * * *" - -################################################################################ -# step 1: clean all previous configs -################################################################################ - -echo "cleaning existing nginx configuration" - -# remove default nginx config -[[ -L "${SITES_EN}/default" ]] && unlink "${SITES_EN}/default" || true - -# remove domain symlinks -rm -f "${SITES_EN}/${HOSTNAME}" || true -rm -f "${SITES_EN}/${HOSTNAME}-ssl" || true -rm -f "${SITES_EN}/wss-config-nym" || true - -# remove old configs -rm -f "${HTTP_CONF}" || true -rm -f "${HTTPS_CONF}" || true -rm -f "${WSS_CONF}" || true +echo "* * * Starting nginx configuration for landing page, reverse proxy and WSS * * *" ############################################################################### -# step 2: create landing page +# step 1: ensure landing page exists (local fetch -> github -> template) ############################################################################### mkdir -p "${WEBROOT}" -# script directory where Python CLI stores fetched scripts SCRIPT_DIR="$(dirname "${ENV_FILE:-./env.sh}")" LOCAL_FETCHED_PAGE="${SCRIPT_DIR}/landing-page.html" if [[ -s "${LOCAL_FETCHED_PAGE}" ]]; then cp "${LOCAL_FETCHED_PAGE}" "${WEBROOT}/index.html" +elif curl -fsSL \ + https://raw.githubusercontent.com/nymtech/nym/develop/scripts/nym-node-setup/landing-page.html \ + -o "${WEBROOT}/index.html"; then + : else - if curl -fsSL \ - https://raw.githubusercontent.com/nymtech/nym/develop/scripts/nym-node-setup/landing-page.html \ - -o "${WEBROOT}/index.html"; then - : - else - cat > "${WEBROOT}/index.html" < "${WEBROOT}/index.html" < nym node @@ -77,16 +55,35 @@ else EOF - fi fi -echo "landing page at ${WEBROOT}/index.html" +echo "Landing page at ${WEBROOT}/index.html" + +############################################################################### +# step 2: remove default site and old configs, restart nginx +############################################################################### + +echo "Cleaning existing nginx configuration" + +# remove default nginx site +[[ -L "${SITES_EN}/default" ]] && unlink "${SITES_EN}/default" || true + +# optional: remove default available config if present +rm -f /etc/nginx/sites-available/default || true -echo "landing page at ${WEBROOT}/index.html" +# remove old vhosts for this domain +rm -f "${SITES_EN}/${HOSTNAME}" || true +rm -f "${SITES_EN}/${HOSTNAME}-ssl" || true +rm -f "${SITES_EN}/wss-config-nym" || true -################################################################################ -# step 3: HTTP :80 config -################################################################################ +rm -f "${HTTP_CONF}" || true +rm -f "${WSS_CONF}" || true + +systemctl restart nginx || systemctl start nginx + +############################################################################### +# step 3: create basic HTTP config like manual flow (80 -> 8080) +############################################################################### cat > "${HTTP_CONF}" </dev/null 2>&1 || true apt-get install -y certbot python3-certbot-nginx >/dev/null 2>&1 || true -echo "requesting let's encrypt certificate for ${HOSTNAME}" +echo "Requesting Let's Encrypt certificate for ${HOSTNAME}" -certbot --nginx --non-interactive --agree-tos \ - --reuse-key \ - -m "${EMAIL}" -d "${HOSTNAME}" --redirect || true +certbot --nginx --non-interactive --agree-tos --redirect --reuse-key \ + -m "${EMAIL}" -d "${HOSTNAME}" || true -################################################################################ -# step 5: HTTPS and WSS configs -################################################################################ +############################################################################### +# step 5: create WSS 9001 config using certbot-generated certs +############################################################################### if [[ -s "/etc/letsencrypt/live/${HOSTNAME}/fullchain.pem" ]]; then - echo "certificate detected, creating https and wss configs" - - # HTTPS 443 - cat > "${HTTPS_CONF}" < "${WSS_CONF}" <