VPS Security Audit Checklist: Finding and Fixing Common Server Vulnerabilities

Server security is not a one-time setup. New vulnerabilities emerge daily, configuration drift happens gradually, and an attacker only needs to find one weakness. A systematic security audit helps you identify and fix common vulnerabilities before they are exploited. This checklist covers the essential areas every VPS administrator should review — from SSH hardening and firewall rules to file permissions and kernel parameters.

1. SSH Hardening

SSH is the most common attack vector on any internet-facing server. Review the /etc/ssh/sshd_config file for these settings:

SettingRecommended ValueWhy
PortNon-default (e.g., 2222)Eliminates 99% of automated attacks
PermitRootLoginnoPrevents direct root SSH access
PasswordAuthenticationnoOnly allow key-based authentication
PubkeyAuthenticationyesEnables SSH key login
MaxAuthTries3Limits brute-force attempts
ClientAliveInterval300Drops idle connections after 5 minutes
AllowUsersSpecific usernames onlyWhitelist who can SSH in
# Audit current SSH configuration
sudo sshd -T | grep -E "(port|permitrootlogin|passwordauthentication|pubkeyauthentication|maxauthtries)"

# Check for failed login attempts
sudo grep "Failed password" /var/log/auth.log | tail -20

# Count unique IPs that attempted logins in the last 24 hours
sudo grep "$(date +'%b %e')" /var/log/auth.log | grep "Failed password" | awk '{print $(NF-3)}' | sort -u | wc -l

After making changes to sshd_config, always test the configuration before restarting: sudo sshd -t. Open a second SSH session in tmux before restarting so you can recover if something goes wrong.

2. Firewall Configuration

A properly configured firewall should be the first line of defense. Use UFW on Ubuntu or iptables directly:

# With UFW (Ubuntu/Debian)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh  # Or: sudo ufw allow 2222/tcp if you changed SSH port
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
sudo ufw status verbose

# Audit current firewall rules
sudo iptables -L -n -v

# Check for open ports
sudo ss -tlnp | grep LISTEN

Key audit questions:

  • Are only necessary ports open? (At minimum: SSH, HTTP, HTTPS)
  • Are database ports (3306, 5432, 6379) bound to localhost only?
  • Is there a rate limit on SSH connections? (ufw limit ssh)
  • Are unused services not listening on any port?

3. User and Permission Audits

Unnecessary user accounts or misconfigured permissions are a common source of privilege escalation vulnerabilities:

# List all users with login shells
sudo awk -F: '/bash|sh|zsh/{print $1, $6}' /etc/passwd

# Find users with UID 0 (root-equivalent)
sudo awk -F: '$3 == 0 {print $1}' /etc/passwd

# Check for empty passwords
sudo awk -F: '($2 == "" || $2 == "!") {print $1}' /etc/shadow

# Find world-writable files outside /tmp
sudo find / -xdev -type f -perm -0002 ! -path "/tmp/*" ! -path "/proc/*" 2>/dev/null

# Find SUID/SGID binaries (potential privilege escalation vectors)
sudo find / -xdev -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | xargs ls -la

Checklist items:

  • Remove or disable unused user accounts
  • Ensure only root has UID 0
  • Audit sudoers file: sudo visudo and check /etc/sudoers.d/
  • Review world-writable files and directories (they should not exist outside /tmp)
  • Review SUID/SGID binaries and remove the bit from unnecessary ones

4. Filesystem and Directory Permissions

Critical configuration files should have restricted permissions:

File/DirectoryExpected PermissionsOwner
/etc/shadow640 or 600root:shadow
/etc/ssh/sshd_config644root:root
/etc/ssl/private/700root:root
/var/log/755root:root (or syslog)
/etc/nginx/sites-available/644root:root
Nginx SSL certificates600root:root
# Audit key file permissions
sudo stat -c "%a %U:%G %n" /etc/shadow /etc/ssh/sshd_config /etc/ssl/private

5. Automatic Security Updates

Unpatched software is the number one entry point for attackers. Configure automatic security updates and verify they are working:

# Install unattended-upgrades (Ubuntu/Debian)
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

# Check configuration
sudo cat /etc/apt/apt.conf.d/20auto-upgrades
# Should show:
# APT::Periodic::Update-Package-Lists "1";
# APT::Periodic::Unattended-Upgrade "1";

# Verify it is running
sudo systemctl status unattended-upgrades

# Check the log for recent upgrades
sudo grep -i upgrade /var/log/unattended-upgrades/unattended-upgrades.log | tail -10

# List available security updates that haven't been applied
sudo apt list --upgradable 2>/dev/null | grep -i security

On systems with critical production workloads, keep unattended-upgrades enabled but configure it to automatically reboot only during maintenance windows by updating /etc/apt/apt.conf.d/50unattended-upgrades.

6. Web Server and Application Security

Your web server configuration is a common source of information disclosure and misconfiguration vulnerabilities:

# Check web server version information
curl -I https://your-server.com | grep -i server

# Test HTTP methods
curl -X OPTIONS https://your-server.com/ -i | grep Allow

# Test directory listing
curl https://your-server.com/images/ -I

# Test for common paths
curl -s -o /dev/null -w "%{http_code}" https://your-server.com/.git/config
curl -s -o /dev/null -w "%{http_code}" https://your-server.com/admin/
curl -s -o /dev/null -w "%{http_code}" https://your-server.com/backup/
curl -s -o /dev/null -w "%{http_code}" https://your-server.com/.env

Nginx-specific audit:

  • server_tokens off; — hides Nginx version from error pages and headers
  • autoindex off; — disables directory listing
  • Rate limiting configured: limit_req_zone and limit_conn_zone
  • File upload size limited: client_max_body_size
  • No sensitive files served from web root (.git, .env, backups)

7. Database Security

Databases are a high-value target for attackers. Common misconfigurations to check:

# MySQL/MariaDB
# Check that bind-address is 127.0.0.1
sudo grep "bind-address" /etc/mysql/mysql.conf.d/mysqld.cnf

# List database users and their hosts
sudo mysql -e "SELECT user, host, authentication_string FROM mysql.user;"

# Find users with no password
sudo mysql -e "SELECT user, host FROM mysql.user WHERE authentication_string = '';"

# PostgreSQL
# Check listen_addresses
sudo grep "listen_addresses" /etc/postgresql/*/main/postgresql.conf

# List users and roles
sudo -u postgres psql -c "\du"

Database audit items:

  • Database bound to localhost only, not 0.0.0.0
  • No default or empty passwords on any user account
  • Application database user has least-privilege access (only needed tables/operations)
  • Remote root login disabled
  • SSL/TLS enabled for database connections if not on localhost

8. Intrusion Detection and Logging

Without logging and detection, you will not know you have been compromised until it is too late:

# Install and configure Fail2ban
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

# Check banned IPs
sudo fail2ban-client status sshd

# Check all jails
sudo fail2ban-client status

# Audit log management
# Check logrotate configuration
sudo ls -la /etc/logrotate.d/
sudo cat /etc/logrotate.d/rsyslog

# Verify logs are being written
sudo ls -lh /var/log/syslog /var/log/auth.log /var/log/nginx/access.log

# Check for large or unexpected log files
sudo du -sh /var/log/* | sort -hr | head -10

Consider setting up log forwarding to a centralized logging server or sending security-relevant logs (auth.log, nginx access logs) to an external SIEM for long-term retention and analysis.

9. Kernel and System Hardening

Several sysctl parameters improve security at the network and kernel level:

# /etc/sysctl.d/99-security.conf

# 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

# Ignore source-routed packets
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Disable ICMP echo requests (ping)
net.ipv4.icmp_echo_ignore_all = 1

# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_synack_retries = 2

# Enable kernel ASLR (should be enabled by default)
kernel.randomize_va_space = 2

Apply and verify:

sudo sysctl -p /etc/sysctl.d/99-security.conf

# Verify settings
sudo sysctl kernel.randomize_va_space
sudo sysctl net.ipv4.tcp_syncookies

10. Backup and Recovery Verification

Security is not just about prevention — it is also about recovery. A good backup strategy is the difference between a minor incident and a catastrophic data loss:

  • Test your backups: Actually restore from backup at least quarterly. A backup that has never been tested is not a backup — it is a hope.
  • Off-site storage: Store backups on a different provider or in object storage (S3, Backblaze B2).
  • Encryption: Encrypt backups before uploading off-site using GPG or a tool like restic.
  • Backup critical directories: /etc/, /var/www/, database dumps, SSL certificates.
# Minimal backup test: can you restore from a recent snapshot?
# List your backup files and check dates
ls -lh /backups/

# Verify backup integrity (example with restic)
restic check

# Test database restore
sudo mysql -u root test_restore < /backups/latest-mysql-dump.sql

Automating the Audit

Running through this checklist manually every month is tedious. Automate periodic security audits with tools like lynis or custom scripts:

# Install lynis for automated security auditing
sudo apt install lynis -y
sudo lynis audit system

# Or use a simple audit script that checks the key items
# and emails the results weekly via cron

Security is a continuous process, not a destination. Running through this checklist quarterly — and whenever you make major infrastructure changes — will catch the majority of common vulnerabilities before they become problems. For a VPS with built-in DDoS protection and robust network security features, compare VPS plans from leading providers.

Leave a Reply