VPS Hardening
Fail2Ban Configuration
Make a backup
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.localEdit the file
sudo nano /etc/fail2ban/jail.localEnable it
sudo systemctl enable --now fail2banTest it
sudo fail2ban-client status sshdConfiguration Template
# [sshd]
enabled = true
port = 2222 # Match your custom SSH port
filter = sshd # Use default SSH filter
logpath = /var/log/auth.log # Debian/Ubuntu (use /var/log/secure for RHEL)
maxretry = 3 # Ban after 3 failed attempts
findtime = 10m # Track failures within 10 minutes
bantime = 4w # Ban for 4 weeks (adjust as needed)
ignoreip = 127.0.0.1/8 # Whitelist localhost
banaction = ufw # Use UFW if installed (better than iptables)
# [mysqld-auth]
enabled = true
port = 3306,33060
filter = mysqld-auth
logpath = /var/log/mysql/error.log
maxretry = 3
bantime = 1dEdit OpenSSH config file
Make a backup
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bakEdit the file
sudo nano /etc/ssh/sshd_configTest config syntax
sudo sshd -t Apply changes
sudo systemctl restart sshd Configuration Template
# ====== SSH Protocol & Encryption ======
Protocol 2 # Only use SSHv2 (v1 is insecure)
Port 2222 # Custom port to reduce bot scans
HostKey /etc/ssh/ssh_host_ed25519_key # Prefer Ed25519 keys
KexAlgorithms curve25519-sha256@libssh.org # Strong key exchange
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com # Modern encryption
MACs hmac-sha2-512-etm@openssh.com # Secure message integrity
# ====== Authentication Hardening ======
PermitRootLogin no # Disable direct root login
PasswordAuthentication no # Disable password logins (keys only)
PermitEmptyPasswords no # Explicitly block empty passwords
HostbasedAuthentication no # Disable trust-based auth
MaxAuthTries 3 # Allow 3 auth attempts per session
AuthenticationMethods publickey,keyboard-interactive # Enforce SSH + 2FA
ChallengeResponseAuthentication yes # Required for 2FA (e.g., TOTP)
UsePAM yes
# ====== Session & Connection Limits ======
ClientAliveInterval 300 # Check alive every 5 mins
ClientAliveCountMax 0 # Terminate idle sessions immediately (no grace)
MaxSessions 2 # Allow max 2 concurrent sessions per connection
LoginGraceTime 30 # Disconnect if auth takes >30s
# ====== Forwarding & Sandboxing ======
X11Forwarding no # Disable GUI forwarding
AllowAgentForwarding no # Block SSH agent forwarding
AllowTcpForwarding no # Disable port forwarding (unless needed)
# ====== Logging & Information Hiding ======
DebianBanner no # Hide OS/version in pre-auth banner
PrintMotd no # Disable post-login MOTD (potential info leak)
LogLevel VERBOSE # Detailed logs for auditing
Banner /etc/issue.net # Static pre-login banner (optional)
# ====== User Access Control ======
AllowUsers user1 user2 # Whitelist allowed users
DenyUsers * # Explicitly block others (redundant but clear)For
keyboard-interactiveto work, configure PAM.Update firewall rules.
Pair with
Fail2banto ban IPs afterMaxAuthTriesviolations.
Automate UFW hardening
#!/bin/bash
# =============================================
# UFW HARDENING SCRIPT FOR VPS
# =============================================
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if running as root
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}Error: This script must be run as root. Use sudo.${NC}" >&2
exit 1
fi
# ---- Step 1: Verify SSH Port ----
CURRENT_SSH_PORT=$(grep -oP '^Port \K\d+' /etc/ssh/sshd_config 2>/dev/null || echo "22")
echo -e "${YELLOW}[?] Detected SSH port: ${CURRENT_SSH_PORT} (from sshd_config)${NC}"
read -rp "Confirm SSH port to allow in UFW [Press Enter for ${CURRENT_SSH_PORT}]: " SSH_PORT
SSH_PORT=${SSH_PORT:-$CURRENT_SSH_PORT}
# ---- Step 2: Whitelist Current IP ----
CURRENT_IP=$(curl -4 -s ifconfig.co)
if [ -z "$CURRENT_IP" ]; then
echo -e "${RED}Error: Could not detect your public IP. Manually add it later.${NC}"
else
echo -e "${YELLOW}[+] Your current public IP: ${CURRENT_IP}${NC}"
read -rp "Whitelist this IP for SSH? [y/N]: " WHITELIST_IP
if [[ $WHITELIST_IP =~ ^[Yy]$ ]]; then
IP_WHITELIST="from $CURRENT_IP"
fi
fi
# ---- Step 3: Apply UFW Rules ----
echo -e "\n${GREEN}[+] Applying UFW Rules...${NC}"
# Reset UFW (uncomment if needed)
# echo -e "${YELLOW}Resetting UFW...${NC}"
# ufw --force reset
# Default policies
ufw default deny incoming
ufw default allow outgoing
# SSH rules
ufw allow "$SSH_PORT/tcp" comment 'SSH port'
ufw limit "$SSH_PORT/tcp" comment 'Rate-limit SSH' # 6 connections/minute/IP
[ -n "$IP_WHITELIST" ] && ufw allow "$IP_WHITELIST to any port $SSH_PORT" comment 'Trusted IP SSH'
# MySQL rules
read -rp "Is MySQL/MariaDB installed and needed? [y/N]: " NEED_MYSQL
if [[ $NEED_MYSQL =~ ^[Yy]$ ]]; then
read -rp "Allow MySQL remotely? (Only if apps need it from another server) [y/N]: " MYSQL_REMOTE
if [[ $MYSQL_REMOTE =~ ^[Yy]$ ]]; then
read -rp "Enter trusted IP for MySQL access: " MYSQL_IP
ufw allow from "$MYSQL_IP" to any port 3306 comment 'Allow remote MySQL'
else
ufw allow from 127.0.0.1 to any port 3306 comment 'Allow local MySQL'
fi
else
ufw deny 3306/tcp comment 'Block MySQL'
fi
# Common service blocks
ufw deny 3389/tcp comment 'Block RDP'
ufw deny 23/tcp comment 'Block Telnet'
# Optional: Block ICMP (ping)
read -rp "Block ICMP (ping) requests? [y/N]: " BLOCK_ICMP
[[ $BLOCK_ICMP =~ ^[Yy]$ ]] && ufw deny icmp comment 'Block Ping'
# Enable logging
ufw logging on
ufw logging medium
# Enable UFW
echo -e "\n${YELLOW}[!] Enabling UFW in 10 seconds...${NC}"
echo -e "${RED}>>> OPEN A SECOND SSH CONNECTION NOW TO TEST <<<${NC}"
echo -e "${YELLOW}>>> If you get locked out, reboot the VPS to disable UFW <<<${NC}"
sleep 10
ufw enable
# Final status
echo -e "\n${GREEN}[+] UFW Rules Applied:${NC}"
ufw status numbered
# ---- Step 4: Verify SSH Access ----
echo -e "\n${YELLOW}[!] Test SSH access from another terminal before closing this session!${NC}"
echo -e "Command to test: ${GREEN}ssh -p $SSH_PORT $(whoami)@$(hostname -I | awk '{print $1}')${NC}"Add more rules if hosting services
sudo ufw allow 80/tcpsudo ufw allow 443/tcpMySQL hardening
Change MySQL’s port in /etc/mysql/my.cnf
[mysqld]
port = 33060Forbid root login from remote
UPDATE mysql.user SET Host='localhost' WHERE User='root';
FLUSH PRIVILEGES;Use strong passwords and restrict users
CREATE USER 'user'@'192.168.1.100' IDENTIFIED BY 'YourComplexPassword!123';
GRANT ALL PRIVILEGES ON odoo_db.* TO 'user'@'192.168.1.100';CrowdSec Configuration
Add SSH Parsing/Detection
sudo cscli parsers install crowdsecurity/sshd-logs
sudo cscli scenarios install crowdsecurity/ssh-bf
sudo cscli collections install crowdsecurity/sshdVerify Setup
sudo cscli hub listTailor to Your SSH Port
Edit the SSH log path in
/etc/crowdsec/acquis.yaml:
filenames:
- /var/log/auth.log # Debian/Ubuntu
# - /var/log/secure # RHEL/CentOS
labels:
type: syslog
filter: "programname=='sshd'"If using a custom SSH port, modify the SSH detection rule:
sudo sed -i 's/port: 22/port: 2222/' /etc/crowdsec/scenarios/ssh-bf.yamlStart CrowdSec
sudo systemctl enable --now crowdsecCheck Bans
sudo cscli decisions listIntegrate with UFW
Install UFW Bouncer
sudo pacman -S crowdsec-firewall-bouncer-ufw # Debian/UbuntuEdit /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
mode: ufw
deny_action: deny
deny_log: trueRestart Services
sudo systemctl restart crowdsec
sudo systemctl restart crowdsec-firewall-bouncerBan Duration & Sensitivity
Edit /etc/crowdsec/profiles.yaml
name: default
decisions:
- type: ban
duration: 24h # Increase from default 4h
scenarios:
- ssh-bf
# Add other scenarios (e.g., http-bf)Community Blocklists
sudo cscli collections install crowdsecurity/linux
sudo cscli collections install crowdsecurity/http-cveShow attack statistics
sudo cscli metricsList active bans
sudo cscli decisions listView all detected threats
sudo cscli alerts listUpdate threat intelligence
sudo cscli hub upgradeLive debug logs
sudo tail -f /var/log/crowdsec.logLast updated