Deploying firewall to XCP-NG with rescue
-
Hi, just wanted to share this script I am using to deploy firewall to xcp-ng.
I'm rather a UFW person or MikroTik with safe mode, but I don't have that on xcp-ng, so meddling with the firewall was always increasing my anxiety levels. So I was using the Hetzner built in vSwitch firewall which is a pain and allows only 10 rules.So I made (actually I just told Claude to create this) script that will take the local file called
iptablesand push that to one or all of my servers I have with hetzner. The script is intended to be called from you local machine, not on the server.To be safe that I didn't break anything, it schedules an automatic rollback in 2 minutes, applies the new firewall, checks that ssh is still accessible, then removes the planed rollback.
#!/bin/bash # XCP-ng Firewall Deployment Script # Deploys the unified firewall configuration to all hosts # Usage: ./deploy-firewall.sh [test|x1|x2|x3|all] set -e # Host definitions X1_IP="167.235.xx.xx" X2_IP="167.235.xx.xx" X3_IP="167.235.xx.xx" FIREWALL_FILE="iptables" BACKUP_SUFFIX="backup.$(date +%Y%m%d_%H%M%S)" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color print_header() { echo -e "${GREEN}================================${NC}" echo -e "${GREEN}$1${NC}" echo -e "${GREEN}================================${NC}" } print_warning() { echo -e "${YELLOW}WARNING: $1${NC}" } print_error() { echo -e "${RED}ERROR: $1${NC}" } print_success() { echo -e "${GREEN}SUCCESS: $1${NC}" } deploy_to_host() { local HOST_IP=$1 local HOST_NAME=$2 print_header "Deploying to $HOST_NAME ($HOST_IP)" echo "1. Checking if 'at' command is available..." ssh root@$HOST_IP "command -v at > /dev/null" || { print_error "'at' command not found on $HOST_NAME" print_warning "Installing 'at' package..." ssh root@$HOST_IP "yum install -y at && systemctl enable --now atd" || { print_error "Failed to install 'at' on $HOST_NAME" return 1 } } echo "2. Ensuring atd service is running..." ssh root@$HOST_IP "systemctl is-active atd > /dev/null || systemctl start atd" || { print_error "Failed to start atd service on $HOST_NAME" return 1 } echo "3. Copying firewall configuration..." scp $FIREWALL_FILE root@$HOST_IP:/root/iptables.new || { print_error "Failed to copy file to $HOST_NAME" return 1 } echo "4. Backing up current configuration..." ssh root@$HOST_IP "cp /etc/sysconfig/iptables /etc/sysconfig/iptables.$BACKUP_SUFFIX" || { print_error "Failed to backup on $HOST_NAME" return 1 } echo "5. Creating rollback script..." ssh root@$HOST_IP "cat > /root/firewall-rollback.sh << 'ROLLBACK_EOF' #!/bin/bash # Automatic firewall rollback script echo \"[\$(date)] FIREWALL ROLLBACK: Restoring previous configuration\" >> /var/log/firewall-rollback.log cp /etc/sysconfig/iptables.$BACKUP_SUFFIX /etc/sysconfig/iptables systemctl restart iptables echo \"[\$(date)] FIREWALL ROLLBACK: Completed\" >> /var/log/firewall-rollback.log ROLLBACK_EOF chmod +x /root/firewall-rollback.sh" || { print_error "Failed to create rollback script on $HOST_NAME" return 1 } echo "6. Scheduling automatic rollback in 2 minutes..." local AT_JOB_ID=$(ssh root@$HOST_IP "echo '/root/firewall-rollback.sh' | at now + 5 minutes 2>&1 | grep 'job' | awk '{print \$2}'") if [ -z "$AT_JOB_ID" ]; then print_error "Failed to schedule rollback on $HOST_NAME" return 1 fi print_warning "Rollback scheduled with job ID: $AT_JOB_ID" print_warning "If SSH connection is lost, firewall will auto-rollback in 2 minutes!" echo "7. Installing new configuration..." ssh root@$HOST_IP "cp /root/iptables.new /etc/sysconfig/iptables" || { print_error "Failed to install new config on $HOST_NAME" return 1 } echo "8. Restarting iptables service..." ssh root@$HOST_IP "systemctl restart iptables" || { print_error "Failed to restart iptables on $HOST_NAME" print_warning "Manual rollback will occur in 2 minutes..." return 1 } echo "9. Waiting 5 seconds before testing SSH..." sleep 5 echo "10. Verifying SSH access..." if ssh root@$HOST_IP "echo 'SSH test successful'" 2>/dev/null; then print_success "SSH verification successful!" else print_error "SSH verification failed on $HOST_NAME" print_warning "Automatic rollback will occur in ~2 minutes" print_warning "You can also use console access to verify or manually rollback" return 1 fi echo "11. Canceling automatic rollback..." ssh root@$HOST_IP "atrm $AT_JOB_ID" || { print_warning "Failed to cancel rollback job - but SSH works, so manual cancellation recommended" } print_success "Automatic rollback cancelled - firewall is stable!" echo "12. Displaying active rules..." ssh root@$HOST_IP "iptables -L RH-Firewall-1-INPUT -n | head -20" print_success "Deployment to $HOST_NAME completed!" echo "" return 0 } test_connectivity() { local HOST_IP=$1 local HOST_NAME=$2 echo "Testing connectivity to $HOST_NAME..." if ping -c 2 $HOST_IP > /dev/null 2>&1; then print_success "$HOST_NAME is reachable" else print_error "$HOST_NAME is not reachable" return 1 fi if ssh -o ConnectTimeout=5 root@$HOST_IP "echo 'SSH OK'" > /dev/null 2>&1; then print_success "SSH to $HOST_NAME is working" else print_error "SSH to $HOST_NAME failed" return 1 fi return 0 } show_usage() { echo "Usage: $0 [test|x1|x2|x3|all|rollback]" echo "" echo "Commands:" echo " test - Test connectivity to all hosts without deploying" echo " x1 - Deploy to x1 only ($X1_IP)" echo " x2 - Deploy to x2 only ($X2_IP)" echo " x3 - Deploy to x3 only ($X3_IP)" echo " all - Deploy to all hosts (x1, x2, x3)" echo " rollback - Manually rollback firewall on specified host" echo "" echo "Examples:" echo " $0 x3 # Test deployment on x3 first" echo " $0 all # Deploy to all hosts" echo " $0 rollback x3 # Manually rollback x3" echo "" echo "Safety Features:" echo " - Automatic rollback scheduled for 2 minutes after deployment" echo " - Rollback is cancelled only if SSH verification succeeds" echo " - If you lose SSH access, firewall auto-reverts in 2 minutes" exit 1 } manual_rollback() { local HOST_IP=$1 local HOST_NAME=$2 if [ -z "$HOST_IP" ]; then print_error "Please specify host: x1, x2, or x3" echo "Example: $0 rollback x3" exit 1 fi print_header "Manual Rollback on $HOST_NAME ($HOST_IP)" echo "1. Finding latest backup..." local BACKUP_FILE=$(ssh root@$HOST_IP "ls -t /etc/sysconfig/iptables.backup.* 2>/dev/null | head -1") if [ -z "$BACKUP_FILE" ]; then print_error "No backup file found on $HOST_NAME" echo "Available files:" ssh root@$HOST_IP "ls -la /etc/sysconfig/iptables*" return 1 fi echo "Latest backup: $BACKUP_FILE" echo "2. Restoring backup..." ssh root@$HOST_IP "cp $BACKUP_FILE /etc/sysconfig/iptables" || { print_error "Failed to restore backup" return 1 } echo "3. Restarting iptables..." ssh root@$HOST_IP "systemctl restart iptables" || { print_error "Failed to restart iptables" return 1 } echo "4. Verifying SSH access..." sleep 2 ssh root@$HOST_IP "echo 'SSH test successful'" || { print_error "SSH verification failed - you may need console access" return 1 } print_success "Rollback completed successfully on $HOST_NAME!" echo "5. Current rules:" ssh root@$HOST_IP "iptables -L RH-Firewall-1-INPUT -n | head -10" } # Main script if [ ! -f "$FIREWALL_FILE" ]; then print_error "Firewall configuration file '$FIREWALL_FILE' not found!" exit 1 fi case "$1" in test) print_header "Testing Connectivity" test_connectivity $X1_IP "x1" || echo "" test_connectivity $X2_IP "x2" || echo "" test_connectivity $X3_IP "x3" || echo "" ;; x1) deploy_to_host $X1_IP "x1" ;; x2) deploy_to_host $X2_IP "x2" ;; x3) deploy_to_host $X3_IP "x3" ;; rollback) case "$2" in x1) manual_rollback $X1_IP "x1" ;; x2) manual_rollback $X2_IP "x2" ;; x3) manual_rollback $X3_IP "x3" ;; *) manual_rollback "" "" ;; esac ;; all) print_warning "This will deploy to ALL hosts!" read -p "Are you sure? (yes/no): " confirm if [ "$confirm" != "yes" ]; then echo "Deployment cancelled." exit 0 fi deploy_to_host $X3_IP "x3" || exit 1 sleep 2 deploy_to_host $X1_IP "x1" || exit 1 sleep 2 deploy_to_host $X2_IP "x2" || exit 1 print_header "Deployment Summary" print_success "All hosts deployed successfully!" echo "" echo "Next steps:" echo "1. Test VM internet connectivity on all hosts" echo "2. Test VM migration between hosts" echo "3. Disable Hetzner vSwitch firewall" ;; *) show_usage ;; esac -
@ditzy-olive Hello, be careful with that, your script installs it to /etc/sysconfig/iptables which is part of the
iptables-servicespackage, so it could be overwritten by a package update.Granted, I don't think we ever updated it, but an upgrade to a newer version (when v9.0 comes) will for sure replace it.
-
@bleader thanks for the tip. You see my knowledge in redhat based systems is almost non existant. Think I'm almost 99% debian and 1% ubuntu (and that not by choice)
So I will take care of redeploying the script as soon as I upgrade, should be easily noticeable since monitoring will loose access and yell at me.
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login