Table of Contents

  1. Introduction
  2. Initial Server Hardening
  3. SSH Security
  4. Firewall Configuration
  5. Fail2Ban Setup
  6. Automated Subnet Blocking
  7. KVM-Specific Security
  8. Monitoring and Maintenance
  9. Conclusion

Introduction

Running a KVM host server with multiple virtual machines requires robust security. This guide covers everything from basic hardening to advanced automated defense mechanisms against distributed brute force attacks.

What You’ll Learn:

  • Secure SSH configuration
  • Automated firewall rule management
  • Fail2Ban integration with firewalld
  • KVM isolation techniques
  • Monitoring and alerting setup

Initial Server Hardening

System Updates

First, ensure your system is up to date:

bash

# Rocky Linux / RHEL / CentOS
sudo dnf update -y
sudo dnf upgrade -y

# Enable automatic security updates
sudo dnf install dnf-automatic -y
sudo systemctl enable --now dnf-automatic.timer

Disable Unnecessary Services

bash

# Check running services
sudo systemctl list-units --type=service --state=running

# Remove unnecessary packages
sudo dnf remove -y telnet rsh talk yp-talking

Kernel Hardening

Add these lines to /etc/sysctl.conf:

bash

# IP Spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Log Martians
net.ipv4.conf.all.log_martians = 1

# Apply settings
sudo sysctl -p

SSH Security

Configure Secure SSH

Edit /etc/ssh/sshd_config:

bash

# Disable root login with password
PermitRootLogin prohibit-password

# Disable password authentication (use keys only)
PasswordAuthentication no
PubkeyAuthentication yes

# Change default port (optional)
Port 2222

# Limit authentication attempts
MaxAuthTries 3
MaxSessions 5

# Disable empty passwords
PermitEmptyPasswords no

# Use modern key exchange algorithms
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256

# Set idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2

Restart SSH:

bash

sudo systemctl restart sshd

Create SSH Key Authentication

bash

# On your local machine
ssh-keygen -t ed25519 -C "your_email@example.com"

# Copy to server
ssh-copy-id -p 2222 user@your-server-ip

Firewall Configuration

Install and Configure firewalld

bash

# Install firewalld
sudo dnf install firewalld -y
sudo systemctl enable --now firewalld

Basic Firewall Rules

bash

# Set default zones
sudo firewall-cmd --set-default-zone=public

# Allow SSH (adjust port if changed)
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-port=2222/tcp

# Allow essential services
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https

# Allow KVM/libvirt
sudo firewall-cmd --permanent --add-service=libvirt
sudo firewall-cmd --permanent --add-masquerade

# Reload
sudo firewall-cmd --reload

Rate Limiting for SSH

bash

# Limit SSH connections to prevent brute force
sudo firewall-cmd --permanent --add-rich-rule='rule service name="ssh" limit value="4/m" accept'
sudo firewall-cmd --reload

Fail2Ban Setup

Install Fail2Ban

bash

sudo dnf install fail2ban fail2ban-systemd -y
sudo systemctl enable --now fail2ban

Configure Jails

Create /etc/fail2ban/jail.local:

ini

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
destemail = root@localhost
action = %(action_mwl)s

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 3

[sshd-ddos]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 2

[recidive]
enabled = true
logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime = 86400
maxretry = 3

Create Custom Jail for Distributed Attacks

ini

[sshd-distributed]
enabled = true
filter = sshd
logpath = /var/log/secure
maxretry = 1
bantime = 3600
findtime = 60

Automated Subnet Blocking

The Problem with Distributed Attacks

Attackers now use multiple IP addresses from the same /24 subnet to avoid traditional banning. Our solution automatically blocks entire subnets when multiple IPs are detected.

Auto-Block Script

Create /usr/local/bin/auto-block-subnet.sh:

bash

#!/bin/bash
THRESHOLD=2
LOG_FILE="/var/log/auto-block-subnet.log"

# Get banned IPs from all jails
BANNED_IPS=$(sudo fail2ban-client status | grep "Jail list" | \
    sed 's/.*Jail list://' | tr ',' '\n' | while read jail; do 
        sudo fail2ban-client status "$jail" 2>/dev/null | \
            grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"
    done | sort -u)

# Check each /24 subnet
echo "$BANNED_IPS" | cut -d. -f1-3 | sort | uniq -c | while read count subnet; do
    [ -z "$subnet" ] && continue
    if [ $count -ge $THRESHOLD ]; then
        if ! sudo firewall-cmd --list-rich-rules | grep -q "$subnet.0/24"; then
            echo "$(date): Blocking $subnet.0/24 (detected $count IPs)" | tee -a $LOG_FILE
            sudo firewall-cmd --permanent --add-rich-rule="rule family=\"ipv4\" source address=\"$subnet.0/24\" drop"
            sudo firewall-cmd --reload
            
            # Unban individual IPs
            echo "$BANNED_IPS" | grep "$subnet." | while read ip; do
                sudo fail2ban-client unban $ip 2>/dev/null
            done
        fi
    fi
done

Make it executable and add to cron:

bash

sudo chmod +x /usr/local/bin/auto-block-subnet.sh
sudo crontab -e
# Add: * * * * * /usr/local/bin/auto-block-subnet.sh

Why This Works

  • Before: 100 IPs from /24 subnet → 100 individual bans
  • After: 2 IPs from /24 subnet → entire /24 blocked at firewall
  • Result: 7,168 IPs blocked instantly

KVM-Specific Security

VM Isolation

bash

# Create separate bridge for VMs
sudo nmcli connection add type bridge ifname br0
sudo nmcli connection modify br0 bridge.stp no

# Assign IP to bridge
sudo nmcli connection modify br0 ipv4.addresses 192.168.122.1/24
sudo nmcli connection modify br0 ipv4.method manual

Limit VM Resources

bash

# Set CPU limits in VM XML
virsh edit vm-name

Add to XML:

xml

<cputune>
    <vcpupin vcpu='0' cpuset='0'/>
    <shares>1024</shares>
    <period>100000</period>
    <quota>-1</quota>
</cputune>

Network Security for VMs

bash

# Create VM-specific firewall zone
sudo firewall-cmd --permanent --new-zone=vms
sudo firewall-cmd --permanent --zone=vms --add-interface=virbr0
sudo firewall-cmd --permanent --zone=vms --add-rich-rule='rule family="ipv4" source address="192.168.122.0/24" accept'
sudo firewall-cmd --reload

AppArmor/SELinux for KVM

bash

# Enforce SELinux for VMs
sudo setsebool -P virt_use_nfs on
sudo setsebool -P virt_use_samba on

# Check SELinux context for VM images
ls -Z /var/lib/libvirt/images/

Monitoring and Maintenance

Security Status Script

Create /usr/local/bin/security-status.sh:

bash

#!/bin/bash
echo "=========================================="
echo "SECURITY STATUS - $(hostname)"
echo "Time: $(date)"
echo "=========================================="
echo ""
echo "=== FIREWALL ==="
echo "Total blocks: $(sudo firewall-cmd --list-rich-rules 2>/dev/null | grep -c 'source address')"
echo ""
echo "=== FAIL2BAN ==="
sudo fail2ban-client status sshd 2>/dev/null | grep "Banned IP list"
echo ""
echo "=== AUTO-BLOCK ==="
echo "Last block: $(sudo tail -1 /var/log/auto-block-subnet.log 2>/dev/null)"
echo ""
echo "=== RECENT ATTACKS ==="
echo "SSH attempts (last hour): $(sudo journalctl -u sshd --since '1 hour ago' 2>/dev/null | grep -c 'Failed password')"
echo ""
echo "=========================================="

Daily Cron Job

bash

# Add to /etc/cron.daily/security-report
#!/bin/bash
/usr/local/bin/security-status.sh | mail -s "Daily Security Report" admin@example.com

Log Monitoring

bash

# Install logwatch
sudo dnf install logwatch -y

# Configure daily report
sudo logwatch --output mail --mailto admin@example.com --detail High

KVM Guest Hardening Checklist

For Each VM:

  • Change default SSH port
  • Disable password authentication
  • Set up fail2ban
  • Configure firewall (firewalld/iptables)
  • Limit resources (CPU, RAM, disk)
  • Enable audit logging
  • Regular backup schedule

Backup Strategy

bash

#!/bin/bash
# Daily VM backup script
DATE=$(date +%Y%m%d)
BACKUP_DIR=/backup/vms

for vm in $(virsh list --name --all); do
    virsh dumpxml $vm > $BACKUP_DIR/$vm.xml
    virsh snapshot-create-as $vm $DATE-snapshot
done

# Sync to remote location
rsync -avz $BACKUP_DIR/ backup@remote-server:/backup/

Conclusion

Your KVM host server is now protected with:

  • ✅ Hardened SSH configuration
  • ✅ Automated firewall rules
  • ✅ Distributed attack mitigation
  • ✅ VM isolation and resource limits
  • ✅ Comprehensive monitoring

Next Steps

  1. Test your configuration with penetration testing tools
  2. Set up regular security audits
  3. Implement centralized logging (ELK stack)
  4. Consider intrusion detection (CrowdSec or OSSEC)

Resources


Have questions or suggestions? Leave a comment below or contact our security team.