arvandor/network/port-forward.sh.example
2026-01-26 00:44:31 -05:00

231 lines
7.9 KiB
Bash

#!/bin/bash
set -euo pipefail
# =============================================================================
# Arvandor Port Forwarding Script
# =============================================================================
# Configures NAT (DNAT/SNAT) and FORWARD rules for Proxmox host.
# Uses a custom chain (ARVANDOR-FORWARD) to avoid conflicts with PVE firewall.
#
# Usage:
# ./port-forward.sh # Apply rules
# ./port-forward.sh --dry-run # Show what would be done
# ./port-forward.sh --restore # Restore backup
# ./port-forward.sh --status # Show current rules
# =============================================================================
# -----------------------------------------------------------------------------
# Configuration - UPDATE THESE FOR YOUR ENVIRONMENT
# -----------------------------------------------------------------------------
NETWORK_INTERFACE="vmbr0"
INTERNAL_NETWORK="192.168.100.0/24"
PUBLIC_IP="203.0.113.10" # Your public IP
CUSTOM_CHAIN="ARVANDOR-FORWARD"
BACKUP_FILE="/root/network/iptables.backup"
# Nebula Lighthouse
NEBULA_IP="192.168.100.10"
NEBULA_PORT="4242"
# Caddy (Reverse Proxy)
CADDY_IP="192.168.100.12"
CADDY_HTTP_PORT="80"
CADDY_HTTPS_PORT="443"
# Gitea (Optional)
GITEA_IP="192.168.100.23"
GITEA_SSH_PORT="2222"
# Security - restrict SSH to specific IP
ALLOWED_SSH_IP="203.0.113.20" # Your home IP
# -----------------------------------------------------------------------------
# Functions
# -----------------------------------------------------------------------------
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
error() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2
}
die() {
error "$*"
exit 1
}
check_root() {
[[ $EUID -eq 0 ]] || die "This script must be run as root"
}
check_interface() {
local iface=$1
ip link show "$iface" &>/dev/null || die "Interface $iface does not exist"
}
backup_rules() {
log "Backing up current iptables rules to $BACKUP_FILE"
mkdir -p "$(dirname "$BACKUP_FILE")"
iptables-save > "$BACKUP_FILE"
}
restore_rules() {
[[ -f "$BACKUP_FILE" ]] || die "Backup file $BACKUP_FILE not found"
log "Restoring iptables rules from $BACKUP_FILE"
iptables-restore < "$BACKUP_FILE"
log "Rules restored successfully"
}
setup_custom_chain() {
# Create custom chain if it doesn't exist
if ! iptables -L "$CUSTOM_CHAIN" -n &>/dev/null; then
log "Creating custom chain: $CUSTOM_CHAIN"
iptables -N "$CUSTOM_CHAIN"
fi
# Ensure chain is jumped to from FORWARD (only once)
if ! iptables -C FORWARD -j "$CUSTOM_CHAIN" &>/dev/null; then
log "Inserting jump to $CUSTOM_CHAIN in FORWARD chain"
iptables -I FORWARD 1 -j "$CUSTOM_CHAIN"
fi
# Flush the custom chain
log "Flushing custom chain: $CUSTOM_CHAIN"
iptables -F "$CUSTOM_CHAIN"
}
apply_rules() {
local dry_run=${1:-false}
if [[ "$dry_run" == "true" ]]; then
log "=== DRY RUN MODE - No changes will be made ==="
echo ""
echo "Would apply the following rules:"
echo ""
echo "NAT PREROUTING (DNAT):"
echo " - UDP $NEBULA_PORT$NEBULA_IP:$NEBULA_PORT (Nebula)"
echo " - TCP $CADDY_HTTP_PORT$CADDY_IP:$CADDY_HTTP_PORT (HTTP)"
echo " - TCP $CADDY_HTTPS_PORT$CADDY_IP:$CADDY_HTTPS_PORT (HTTPS)"
echo " - TCP $GITEA_SSH_PORT$GITEA_IP:$GITEA_SSH_PORT (Gitea SSH)"
echo ""
echo "FORWARD chain ($CUSTOM_CHAIN):"
echo " - Allow traffic to all above destinations"
echo ""
echo "INPUT:"
echo " - Allow Nebula (nebula1 interface)"
echo " - Allow SSH from $ALLOWED_SSH_IP"
echo " - Drop SSH from all others"
echo " - Block Proxmox UI from $NETWORK_INTERFACE"
return
fi
# --- NAT Rules ---
log "Flushing NAT rules..."
iptables -t nat -F PREROUTING
iptables -t nat -F POSTROUTING
log "Setting up NAT masquerading..."
iptables -t nat -A POSTROUTING -s "$INTERNAL_NETWORK" -o "$NETWORK_INTERFACE" -j MASQUERADE
log "Setting up hairpin NAT for Nebula..."
iptables -t nat -A PREROUTING -s "$INTERNAL_NETWORK" -d "$PUBLIC_IP" -p udp --dport "$NEBULA_PORT" -j DNAT --to-destination "$NEBULA_IP:$NEBULA_PORT"
iptables -t nat -A POSTROUTING -s "$INTERNAL_NETWORK" -d "$NEBULA_IP" -p udp --dport "$NEBULA_PORT" -j SNAT --to-source "$PUBLIC_IP"
log "Setting up hairpin NAT for Gitea SSH..."
iptables -t nat -A PREROUTING -s "$INTERNAL_NETWORK" -d "$PUBLIC_IP" -p tcp --dport "$GITEA_SSH_PORT" -j DNAT --to-destination "$GITEA_IP:$GITEA_SSH_PORT"
iptables -t nat -A POSTROUTING -s "$INTERNAL_NETWORK" -d "$GITEA_IP" -p tcp --dport "$GITEA_SSH_PORT" -j SNAT --to-source "$PUBLIC_IP"
log "Setting up DNAT rules..."
# Nebula
iptables -t nat -A PREROUTING -i "$NETWORK_INTERFACE" -p udp --dport "$NEBULA_PORT" -j DNAT --to-destination "$NEBULA_IP:$NEBULA_PORT"
# Caddy
iptables -t nat -A PREROUTING -i "$NETWORK_INTERFACE" -p tcp --dport "$CADDY_HTTP_PORT" -j DNAT --to-destination "$CADDY_IP:$CADDY_HTTP_PORT"
iptables -t nat -A PREROUTING -i "$NETWORK_INTERFACE" -p tcp --dport "$CADDY_HTTPS_PORT" -j DNAT --to-destination "$CADDY_IP:$CADDY_HTTPS_PORT"
# Gitea SSH
iptables -t nat -A PREROUTING -i "$NETWORK_INTERFACE" -p tcp --dport "$GITEA_SSH_PORT" -j DNAT --to-destination "$GITEA_IP:$GITEA_SSH_PORT"
# --- FORWARD Rules (custom chain) ---
setup_custom_chain
log "Adding FORWARD rules to $CUSTOM_CHAIN..."
iptables -A "$CUSTOM_CHAIN" -d "$CADDY_IP" -p tcp --dport "$CADDY_HTTP_PORT" -j ACCEPT
iptables -A "$CUSTOM_CHAIN" -d "$CADDY_IP" -p tcp --dport "$CADDY_HTTPS_PORT" -j ACCEPT
iptables -A "$CUSTOM_CHAIN" -d "$NEBULA_IP" -p udp --dport "$NEBULA_PORT" -j ACCEPT
iptables -A "$CUSTOM_CHAIN" -d "$GITEA_IP" -p tcp --dport "$GITEA_SSH_PORT" -j ACCEPT
# --- INPUT Rules ---
log "Flushing INPUT rules..."
iptables -F INPUT
log "Setting up INPUT rules..."
iptables -A INPUT -i nebula1 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s "$ALLOWED_SSH_IP" -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
iptables -I INPUT -i "$NETWORK_INTERFACE" -p tcp --dport 8006 -j DROP
iptables -I INPUT -i vmbr1 -p tcp --dport 8006 -j ACCEPT
}
save_rules() {
log "Saving iptables rules persistently..."
if command -v netfilter-persistent &>/dev/null; then
netfilter-persistent save
log "Rules saved via netfilter-persistent"
else
die "netfilter-persistent not found. Install with: apt install iptables-persistent"
fi
}
show_status() {
echo ""
echo "=== Port Forwarding Status ==="
echo ""
echo "NAT PREROUTING rules:"
iptables -t nat -L PREROUTING -n --line-numbers 2>/dev/null | head -20
echo ""
echo "FORWARD chain ($CUSTOM_CHAIN):"
iptables -L "$CUSTOM_CHAIN" -n --line-numbers 2>/dev/null || echo "Chain not found"
echo ""
echo "=== Services ==="
echo " HTTP/HTTPS: 80,443 → Caddy ($CADDY_IP)"
echo " Nebula: $NEBULA_PORT → Lighthouse ($NEBULA_IP)"
echo " Gitea SSH: $GITEA_SSH_PORT$GITEA_IP"
}
# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
main() {
local action="${1:-apply}"
case "$action" in
--dry-run|-n)
check_root
check_interface "$NETWORK_INTERFACE"
apply_rules true
;;
--restore|-r)
check_root
restore_rules
;;
--status|-s)
show_status
;;
apply|"")
check_root
check_interface "$NETWORK_INTERFACE"
backup_rules
apply_rules false
save_rules
log "Setup complete!"
show_status
;;
*)
echo "Usage: $0 [--dry-run|--restore|--status]"
exit 1
;;
esac
}
main "$@"