231 lines
7.9 KiB
Bash
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 "$@"
|