wireguard exit policy rules
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
#!/bin/bash
|
||||
# Nym Exit Policy Verification Unit Tests
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[0;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
NYM_CHAIN="NYM-EXIT"
|
||||
WG_INTERFACE="nymwg"
|
||||
|
||||
# Function to check port range rules - with sampling for large ranges
|
||||
check_port_range_rules() {
|
||||
local port_range="$1"
|
||||
local protocol="${2:-tcp}"
|
||||
local chain="${3:-$NYM_CHAIN}"
|
||||
local start_port=$(echo "$port_range" | cut -d'-' -f1)
|
||||
local end_port=$(echo "$port_range" | cut -d'-' -f2)
|
||||
local range_size=$((end_port - start_port + 1))
|
||||
local failures=0
|
||||
local sample_size=5
|
||||
local step=1
|
||||
|
||||
# For large ranges, test only a sample of ports
|
||||
if [ $range_size -gt $sample_size ]; then
|
||||
step=$((range_size / sample_size))
|
||||
# Ensure we test the start and end ports
|
||||
local sample_ports=($start_port $end_port)
|
||||
|
||||
for ((i=1; i<sample_size-1; i++)); do
|
||||
sample_ports+=($((start_port + i*step)))
|
||||
done
|
||||
|
||||
for port in "${sample_ports[@]}"; do
|
||||
if [ $port -le $end_port ]; then # Ensure we don't go out of range
|
||||
if ! iptables -t filter -C "$chain" -p "$protocol" --dport "$port" -j ACCEPT 2>/dev/null; then
|
||||
echo -e "${RED}✗ Rule missing: $chain $protocol port $port${NC}"
|
||||
((failures++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
for ((port=start_port; port<=end_port; port++)); do
|
||||
if ! iptables -t filter -C "$chain" -p "$protocol" --dport "$port" -j ACCEPT 2>/dev/null; then
|
||||
echo -e "${RED}✗ Rule missing: $chain $protocol port $port${NC}"
|
||||
((failures++))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ $failures -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Rule exists: $chain $protocol port range $port_range${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗ Some rules missing in port range $port_range${NC}"
|
||||
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"
|
||||
)
|
||||
|
||||
local total_failures=0
|
||||
|
||||
for range in "${port_ranges[@]}"; do
|
||||
IFS=':' read -r port_range protocol service <<< "$range"
|
||||
|
||||
echo -e "${YELLOW}Testing $service $protocol port range $port_range${NC}"
|
||||
|
||||
check_port_range_rules "$port_range" "$protocol"
|
||||
failures=$?
|
||||
|
||||
total_failures=$((total_failures + failures))
|
||||
done
|
||||
|
||||
return $total_failures
|
||||
}
|
||||
|
||||
# Test critical services
|
||||
test_critical_services() {
|
||||
echo -e "${YELLOW}Testing Critical Service Rules...${NC}"
|
||||
|
||||
local tcp_services=(
|
||||
22 # SSH
|
||||
53 # DNS
|
||||
80 # HTTP
|
||||
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
|
||||
if ! iptables -t filter -C "$NYM_CHAIN" -p tcp --dport "$port" -j ACCEPT 2>/dev/null; then
|
||||
echo -e "${RED}✗ Rule missing: NYM-EXIT tcp port $port${NC}"
|
||||
((failures++))
|
||||
else
|
||||
echo -e "${GREEN}✓ Rule exists: NYM-EXIT tcp port $port${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Test UDP services
|
||||
for port in "${udp_services[@]}"; do
|
||||
if ! iptables -t filter -C "$NYM_CHAIN" -p udp --dport "$port" -j ACCEPT 2>/dev/null; then
|
||||
echo -e "${RED}✗ Rule missing: NYM-EXIT udp port $port${NC}"
|
||||
((failures++))
|
||||
else
|
||||
echo -e "${GREEN}✓ Rule exists: NYM-EXIT udp port $port${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
return $failures
|
||||
}
|
||||
|
||||
# Verify default reject rule exists - with improved detection
|
||||
test_default_reject_rule() {
|
||||
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_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 "$@"
|
||||
@@ -0,0 +1,676 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Nym Wireguard Exit Policy Manager
|
||||
# Version: 1.1.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: ./nym-exit-policy.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' # No Color
|
||||
|
||||
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)
|
||||
|
||||
# Add as a single rule with a port range
|
||||
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 -o "$WG_INTERFACE" -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"
|
||||
fi
|
||||
|
||||
# Link IPv6 chain
|
||||
if ! ip6tables -C FORWARD -o "$WG_INTERFACE" -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"
|
||||
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" <<EOF
|
||||
ExitPolicy reject 5.188.10.0/23:*
|
||||
ExitPolicy reject 31.132.36.0/22:*
|
||||
ExitPolicy reject 37.9.42.0/24:*
|
||||
ExitPolicy reject 45.43.128.0/18:*
|
||||
ExitPolicy reject *:*
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Count and process rules
|
||||
total_rules=$(grep -c "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*")
|
||||
echo -e "${YELLOW}Processing $total_rules blocklist rules...${NC}"
|
||||
|
||||
# Extract and apply IP block rules
|
||||
grep "^ExitPolicy reject" "$POLICY_FILE" | grep -v "\*:\*" |
|
||||
while read -r line; do
|
||||
# Extract IP range
|
||||
ip_range=$(echo "$line" | sed -E 's/ExitPolicy reject ([^:]+):.*/\1/')
|
||||
|
||||
# Apply rule if it's a valid IP range
|
||||
if [[ -n "$ip_range" ]]; then
|
||||
# Skip if the rule already exists
|
||||
if ! iptables -C "$NYM_CHAIN" -d "$ip_range" -j REJECT 2>/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}"
|
||||
|
||||
# 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"
|
||||
["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"
|
||||
["GoogleVoice"]="19294"
|
||||
["EnsimControlPanel"]="19638"
|
||||
["Minecraft"]="25565"
|
||||
["Steam"]="27000-27050"
|
||||
["ElectrumSSL"]="50002"
|
||||
["MOSH"]="60000-61000"
|
||||
["Mumble"]="64738"
|
||||
)
|
||||
|
||||
# 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
|
||||
|
||||
add_default_reject_rule
|
||||
|
||||
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
|
||||
ip6tables -D FORWARD -o "$WG_INTERFACE" -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 <interface>${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 <<EOF
|
||||
#!/bin/sh
|
||||
iptables-restore < /etc/iptables.rules
|
||||
ip6tables-restore < /etc/ip6tables.rules
|
||||
EOF
|
||||
chmod +x /etc/network/if-pre-up.d/iptables
|
||||
echo -e "${GREEN}Created pre-up script to load rules at boot${NC}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
show_status() {
|
||||
echo -e "${YELLOW}Nym Exit Policy Status:${NC}"
|
||||
echo -e "${YELLOW}----------------------${NC}"
|
||||
|
||||
# Network information
|
||||
echo -e "${GREEN}Network Device:${NC} $NETWORK_DEVICE"
|
||||
echo -e "${GREEN}Wireguard Interface:${NC} $WG_INTERFACE"
|
||||
|
||||
# Interface check
|
||||
if ! ip link show "$WG_INTERFACE" &>/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_spamhaus_blocklist
|
||||
apply_port_allowlist
|
||||
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 <interface> 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 "$@"
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user