nym-node-setup: plumb HOST_SSH_PORT through tunnel manager, CLI, and env setup (#6633)
* network-tunnel-manager: make SSH port configurable * Rename SSH_PORT to HOST_SSH_PORT. * setup: plumb HOST_SSH_PORT through env and CLI * setup-env-vars: persist HOST_SSH_PORT in env.sh --------- Co-authored-by: p17o <p17o>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# nym tunnel and wireguard exit policy manager
|
||||
# nym host network firewall, tunnel and wireguard exit policy manager
|
||||
# run this script as root
|
||||
|
||||
set -euo pipefail
|
||||
@@ -556,6 +556,34 @@ apply_smtps_465_rate_limit() {
|
||||
###############################################################################
|
||||
|
||||
NETWORK_FIREWALL_COMMENT="NYM-NETWORK-FW"
|
||||
HOST_SSH_PORT="${HOST_SSH_PORT:-22}"
|
||||
NETWORK_FIREWALL_TCP_PORTS=("$HOST_SSH_PORT" 80 443 1789 1790 8080 9000 9001 41264)
|
||||
NETWORK_FIREWALL_UDP_PORTS=(4443 51822 51264)
|
||||
NETWORK_FIREWALL_WG_TCP_PORT=51830
|
||||
|
||||
validate_port_value() {
|
||||
local name="$1"
|
||||
local value="$2"
|
||||
|
||||
if ! [[ "$value" =~ ^[0-9]+$ ]] || (( 10#$value < 1 || 10#$value > 65535 )); then
|
||||
error "invalid ${name}='${value}'. expected integer 1-65535"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_port_value "HOST_SSH_PORT" "$HOST_SSH_PORT"
|
||||
|
||||
build_network_firewall_status_pattern() {
|
||||
local patterns=("$NETWORK_FIREWALL_COMMENT")
|
||||
local port
|
||||
|
||||
for port in "${NETWORK_FIREWALL_TCP_PORTS[@]}" "${NETWORK_FIREWALL_UDP_PORTS[@]}" "$NETWORK_FIREWALL_WG_TCP_PORT"; do
|
||||
patterns+=("dpt:${port}")
|
||||
done
|
||||
|
||||
local IFS='|'
|
||||
printf '%s' "${patterns[*]}"
|
||||
}
|
||||
|
||||
delete_managed_input_rules() {
|
||||
local cmd="$1"
|
||||
@@ -593,22 +621,19 @@ configure_network_firewall() {
|
||||
delete_managed_input_rules iptables
|
||||
delete_managed_input_rules ip6tables
|
||||
|
||||
local tcp_ports=(22 80 443 1789 1790 8080 9000 9001 41264)
|
||||
local udp_ports=(4443 51822 51264)
|
||||
|
||||
local port
|
||||
for port in "${tcp_ports[@]}"; do
|
||||
for port in "${NETWORK_FIREWALL_TCP_PORTS[@]}"; do
|
||||
add_input_port_rule iptables "$port" tcp
|
||||
add_input_port_rule ip6tables "$port" tcp
|
||||
done
|
||||
|
||||
for port in "${udp_ports[@]}"; do
|
||||
for port in "${NETWORK_FIREWALL_UDP_PORTS[@]}"; do
|
||||
add_input_port_rule iptables "$port" udp
|
||||
add_input_port_rule ip6tables "$port" udp
|
||||
done
|
||||
|
||||
add_input_port_rule iptables 51830 tcp "$WG_INTERFACE"
|
||||
add_input_port_rule ip6tables 51830 tcp "$WG_INTERFACE"
|
||||
add_input_port_rule iptables "$NETWORK_FIREWALL_WG_TCP_PORT" tcp "$WG_INTERFACE"
|
||||
add_input_port_rule ip6tables "$NETWORK_FIREWALL_WG_TCP_PORT" tcp "$WG_INTERFACE"
|
||||
|
||||
save_iptables_rules
|
||||
ok "host network firewall configuration completed"
|
||||
@@ -616,23 +641,25 @@ configure_network_firewall() {
|
||||
|
||||
show_network_firewall_status() {
|
||||
info "managed host network firewall rules"
|
||||
local status_pattern
|
||||
|
||||
status_pattern="$(build_network_firewall_status_pattern)"
|
||||
|
||||
echo
|
||||
info "ipv4 input rules:"
|
||||
iptables -L INPUT -n -v --line-numbers | grep -E "($NETWORK_FIREWALL_COMMENT|dpt:22|dpt:80|dpt:443|dpt:1789|dpt:1790|dpt:8080|dpt:9000|dpt:9001|dpt:51822|dpt:51830|dpt:41264|dpt:51264)" || true
|
||||
iptables -L INPUT -n -v --line-numbers | grep -E "(${status_pattern})" || true
|
||||
echo
|
||||
info "ipv6 input rules:"
|
||||
ip6tables -L INPUT -n -v --line-numbers | grep -E "($NETWORK_FIREWALL_COMMENT|dpt:22|dpt:80|dpt:443|dpt:1789|dpt:1790|dpt:8080|dpt:9000|dpt:9001|dpt:51822|dpt:51830|dpt:41264|dpt:51264)" || true
|
||||
ip6tables -L INPUT -n -v --line-numbers | grep -E "(${status_pattern})" || true
|
||||
}
|
||||
|
||||
test_network_firewall_rules() {
|
||||
info "testing host network firewall rules"
|
||||
|
||||
local failures=0
|
||||
local tcp_ports=(22 80 443 1789 1790 8080 9000 9001 41264)
|
||||
local udp_ports=(4443 51822 51264)
|
||||
local port
|
||||
|
||||
for port in "${tcp_ports[@]}"; do
|
||||
for port in "${NETWORK_FIREWALL_TCP_PORTS[@]}"; do
|
||||
if iptables -C INPUT -p tcp --dport "$port" -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
|
||||
ok "ipv4 tcp port $port allowed"
|
||||
else
|
||||
@@ -648,7 +675,7 @@ test_network_firewall_rules() {
|
||||
fi
|
||||
done
|
||||
|
||||
for port in "${udp_ports[@]}"; do
|
||||
for port in "${NETWORK_FIREWALL_UDP_PORTS[@]}"; do
|
||||
if iptables -C INPUT -p udp --dport "$port" -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
|
||||
ok "ipv4 udp port $port allowed"
|
||||
else
|
||||
@@ -664,17 +691,17 @@ test_network_firewall_rules() {
|
||||
fi
|
||||
done
|
||||
|
||||
if iptables -C INPUT -i "$WG_INTERFACE" -p tcp --dport 51830 -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
|
||||
ok "ipv4 tcp port 51830 allowed on $WG_INTERFACE"
|
||||
if iptables -C INPUT -i "$WG_INTERFACE" -p tcp --dport "$NETWORK_FIREWALL_WG_TCP_PORT" -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
|
||||
ok "ipv4 tcp port $NETWORK_FIREWALL_WG_TCP_PORT allowed on $WG_INTERFACE"
|
||||
else
|
||||
error "ipv4 tcp port 51830 missing on $WG_INTERFACE"
|
||||
error "ipv4 tcp port $NETWORK_FIREWALL_WG_TCP_PORT missing on $WG_INTERFACE"
|
||||
((failures++))
|
||||
fi
|
||||
|
||||
if ip6tables -C INPUT -i "$WG_INTERFACE" -p tcp --dport 51830 -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
|
||||
ok "ipv6 tcp port 51830 allowed on $WG_INTERFACE"
|
||||
if ip6tables -C INPUT -i "$WG_INTERFACE" -p tcp --dport "$NETWORK_FIREWALL_WG_TCP_PORT" -m conntrack --ctstate NEW -m comment --comment "$NETWORK_FIREWALL_COMMENT" -j ACCEPT 2>/dev/null; then
|
||||
ok "ipv6 tcp port $NETWORK_FIREWALL_WG_TCP_PORT allowed on $WG_INTERFACE"
|
||||
else
|
||||
error "ipv6 tcp port 51830 missing on $WG_INTERFACE"
|
||||
error "ipv6 tcp port $NETWORK_FIREWALL_WG_TCP_PORT missing on $WG_INTERFACE"
|
||||
((failures++))
|
||||
fi
|
||||
|
||||
@@ -1695,6 +1722,7 @@ environment overrides:
|
||||
NETWORK_DEVICE_V6 Auto-detected IPv6 uplink (e.g., eth2). Optional; if unset, IPv6-specific setup is skipped.
|
||||
TUNNEL_INTERFACE Default: nymtun0. Requires root privileges (sudo) to manage.
|
||||
WG_INTERFACE Default: nymwg - Must match your WireGuard interface name.
|
||||
HOST_SSH_PORT Default: 22. Set manually if you connect to your host's SSH daemon through another port.
|
||||
|
||||
EOF
|
||||
status=0
|
||||
|
||||
@@ -68,23 +68,60 @@ class NodeSetupCLI:
|
||||
print("Without confirming the points above, we cannot continue.")
|
||||
exit(1)
|
||||
|
||||
def _coerce_ssh_port(self, value) -> str:
|
||||
sval = str(value).strip() if value is not None else ""
|
||||
if not sval:
|
||||
sval = "22"
|
||||
if not sval.isdigit():
|
||||
print(f"Invalid SSH port: {sval!r}. Expected integer 1..65535.")
|
||||
raise SystemExit(1)
|
||||
port = int(sval, 10)
|
||||
if not 1 <= port <= 65535:
|
||||
print(f"Invalid SSH port: {port}. Expected integer 1..65535.")
|
||||
raise SystemExit(1)
|
||||
return str(port)
|
||||
|
||||
def _resolve_field(args, existing, arg_name, env_key, prompt, *, default=None, validator=None):
|
||||
cli_val = getattr(args, arg_name, None)
|
||||
|
||||
if cli_val is not None:
|
||||
value = str(cli_val).strip()
|
||||
elif existing.get(env_key):
|
||||
value = str(existing[env_key]).strip()
|
||||
else:
|
||||
entered = input(prompt).strip()
|
||||
value = entered if entered else (default if default is not None else "")
|
||||
|
||||
if validator:
|
||||
value = validator(value)
|
||||
|
||||
return value
|
||||
|
||||
def ensure_env_values(self, args):
|
||||
"""Collect env vars from args or prompt interactively, then save to env.sh."""
|
||||
env_file = Path("env.sh")
|
||||
fields = [
|
||||
("hostname", "HOSTNAME", "Enter hostname (if you don't use a DNS, press enter): "),
|
||||
("location", "LOCATION", "Enter node location (country code or name): "),
|
||||
("email", "EMAIL", "Enter your email: "),
|
||||
("moniker", "MONIKER", "Enter node public moniker (visible in explorer & NymVPN app): "),
|
||||
("description", "DESCRIPTION", "Enter short node public description: "),
|
||||
("hostname", "HOSTNAME", "Enter hostname (if you don't use a DNS, press enter): ", None, None),
|
||||
("location", "LOCATION", "Enter node location (country code or name): ", None, None),
|
||||
("email", "EMAIL", "Enter your email: ", None, None),
|
||||
("moniker", "MONIKER", "Enter node public moniker (visible in explorer & NymVPN app): ", None, None),
|
||||
("description", "DESCRIPTION", "Enter short node public description: ", None, None),
|
||||
("host_ssh_port", "HOST_SSH_PORT", "Enter host SSH port (press enter for default port 22): ", "22", self._coerce_ssh_port),
|
||||
]
|
||||
|
||||
existing = self._read_env_file(env_file)
|
||||
updated = {}
|
||||
|
||||
for arg_name, key, prompt in fields:
|
||||
cli_val = getattr(args, arg_name, None)
|
||||
value = cli_val.strip() if cli_val else existing.get(key) or input(prompt).strip()
|
||||
for arg_name, key, prompt, default, validator in fields:
|
||||
value = self._resolve_field(
|
||||
args,
|
||||
existing,
|
||||
arg_name,
|
||||
key,
|
||||
prompt,
|
||||
default=default,
|
||||
validator=validator,
|
||||
)
|
||||
updated[key] = value
|
||||
os.environ[key] = value
|
||||
|
||||
@@ -639,6 +676,13 @@ class ArgParser:
|
||||
install_parser.add_argument("--moniker", help="Public moniker displayed in explorer & NymVPN app")
|
||||
install_parser.add_argument("--description", help="Short public description of the node")
|
||||
install_parser.add_argument("--public-ip", help="External IPv4 address (autodetected if omitted)")
|
||||
|
||||
install_parser.add_argument(
|
||||
"--host-ssh-port",
|
||||
type=int,
|
||||
help="Host SSH port to allow in the firewall (default: 22)",
|
||||
)
|
||||
|
||||
install_parser.add_argument("--nym-node-binary", help="URL for nym-node binary (autodetected if omitted)")
|
||||
install_parser.add_argument("--uplink-dev", help="Override uplink interface used for NAT/FORWARD (e.g., 'eth0'; autodetected if omitted)")
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ while true; do
|
||||
read -rp "Enter your email: " EMAIL
|
||||
read -rp "Enter node public moniker (visible in the explorer and NymVPN app): " MONIKER
|
||||
read -rp "Enter node public description: " DESCRIPTION
|
||||
read -rp "Enter host SSH port (press enter for default port 22): " HOST_SSH_PORT
|
||||
HOST_SSH_PORT="${HOST_SSH_PORT:-22}"
|
||||
|
||||
# show summary table
|
||||
echo -e "\nPlease confirm the values you entered:"
|
||||
@@ -28,6 +30,7 @@ while true; do
|
||||
printf "%-20s %s\n" "EMAIL:" "$EMAIL"
|
||||
printf "%-20s %s\n" "MONIKER:" "$MONIKER"
|
||||
printf "%-20s %s\n" "DESCRIPTION:" "$DESCRIPTION"
|
||||
printf "%-20s %s\n" "HOST_SSH_PORT:" "$HOST_SSH_PORT"
|
||||
echo "---------------------------------------"
|
||||
|
||||
read -rp "Are these correct? (y/n): " CONFIRM
|
||||
@@ -52,6 +55,7 @@ PUBLIC_IP=${PUBLIC_IP:-""}
|
||||
echo "export MONIKER=\"${MONIKER}\""
|
||||
echo "export DESCRIPTION=\"${DESCRIPTION}\""
|
||||
echo "export PUBLIC_IP=\"${PUBLIC_IP}\""
|
||||
echo "export HOST_SSH_PORT=\"${HOST_SSH_PORT}\""
|
||||
} > env.sh
|
||||
|
||||
echo -e "\nVariables saved to ./env.sh"
|
||||
|
||||
Reference in New Issue
Block a user