Table of Contents
- Introduction
- Initial Server Hardening
- SSH Security
- Firewall Configuration
- Fail2Ban Setup
- Automated Subnet Blocking
- KVM-Specific Security
- Monitoring and Maintenance
- 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
- Test your configuration with penetration testing tools
- Set up regular security audits
- Implement centralized logging (ELK stack)
- Consider intrusion detection (CrowdSec or OSSEC)
Resources
Have questions or suggestions? Leave a comment below or contact our security team.