Skip to main content
Back to Category

10 Essential Steps for Server Security

10 essential steps to secure a Linux server: SSH key authentication, firewall, Fail2ban, automatic updates, DDoS protection and more. A guide with commands and explanations.

Read time: 12 min Security
server securitylinux securitysshfirewallfail2banddos protection

Table of Contents

10 Essential Steps for Server Security

When you set up a new Linux server, the default configuration is not optimized for security. Every server exposed to the internet becomes a target for automated scanning tools within minutes. This guide explains the 10 essential steps you need to implement to secure your server, complete with commands.

Be careful not to lose access to your server while applying these steps. Especially when changing SSH configuration, keep your current session open and test with a new terminal window.

Step 1: SSH Key Authentication and Disabling Password Login

Password-based SSH login is vulnerable to brute-force attacks. Using an SSH key pair is much more secure.

Creating an SSH Key Pair (On Local Machine)

hljs bash
# Create an Ed25519 key
ssh-keygen -t ed25519 -C "server-name@rexe"

# Or if you want to use RSA
ssh-keygen -t rsa -b 4096 -C "server-name@rexe"

Copying the Public Key to the Server

hljs bash
# Run from local machine
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip

# Manual method
cat ~/.ssh/id_ed25519.pub | ssh user@server-ip \
  "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Disabling Password Login

hljs bash
# Edit /etc/ssh/sshd_config
sudo nano /etc/ssh/sshd_config

# Change these lines:
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
X11Forwarding no

# Restart SSH service
sudo systemctl restart sshd

Changing the SSH port from the default 22 to a different port (e.g., 2222) blocks the vast majority of automated scanning tools. Add the Port 2222 line to sshd_config.

Step 2: Firewall Configuration (UFW)

UFW (Uncomplicated Firewall) is an easy-to-use interface for iptables.

hljs bash
# Install UFW
sudo apt install ufw -y

# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (update if you changed the port)
sudo ufw allow 22/tcp

# Web server ports
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable UFW
sudo ufw enable

# Check status
sudo ufw status verbose

Step 3: Fail2ban Installation and Configuration

Fail2ban monitors failed login attempts and automatically blocks IP addresses that exceed a certain threshold.

hljs bash
# Install Fail2ban
sudo apt install fail2ban -y

# Create configuration file
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
hljs ini
# /etc/fail2ban/jail.local
[DEFAULT]
bantime  = 3600        # 1 hour ban
findtime = 600         # Within 10 minutes
maxretry = 5           # 5 failed attempts
ignoreip = 127.0.0.1/8 ::1  # Ignore local IPs

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
maxretry = 3
bantime  = 86400       # 24 hour ban
hljs bash
# Start Fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd

Step 4: Automatic Security Updates

Outdated software is one of the most common sources of security vulnerabilities.

hljs bash
# Install unattended-upgrades
sudo apt install unattended-upgrades apt-listchanges -y

# Configure
sudo dpkg-reconfigure -plow unattended-upgrades

# Edit configuration file
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
// Automatically apply security updates
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};

// Automatic reboot (if needed)
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";

Step 5: Disabling Unused Services

Every running service is a potential attack surface.

hljs bash
# List running services
sudo systemctl list-units --type=service --state=running

# Check open ports
sudo ss -tlnp

# Stop and disable unnecessary services
sudo systemctl stop bluetooth
sudo systemctl disable bluetooth

# Remove unnecessary packages
sudo apt remove --purge telnet rsh-client

Step 6: Strong Passwords and Two-Factor Authentication

hljs bash
# Strengthen password policy
sudo apt install libpam-pwquality -y
sudo nano /etc/security/pwquality.conf
# Minimum 12 characters
minlen = 12
# At least 1 uppercase letter
ucredit = -1
# At least 1 lowercase letter
lcredit = -1
# At least 1 digit
dcredit = -1
# At least 1 special character
ocredit = -1

2FA with Google Authenticator

hljs bash
# Install Google Authenticator PAM module
sudo apt install libpam-google-authenticator -y

# Configure for user
google-authenticator

# Update PAM configuration
sudo nano /etc/pam.d/sshd
# Add this line:
# auth required pam_google_authenticator.so

# Enable ChallengeResponseAuthentication in sshd_config
# ChallengeResponseAuthentication yes
# AuthenticationMethods publickey,keyboard-interactive

sudo systemctl restart sshd

Step 7: Regular Backups

hljs bash
# Local backup with rsync
rsync -avz --delete /var/www/ /backup/www/

# Backup to remote server
rsync -avz -e ssh /var/www/ user@backup-server:/backup/www/

# Cron job for automatic backup
crontab -e
# Backup every night at 02:00
0 2 * * * rsync -avz /var/www/ /backup/www/ >> /var/log/backup.log 2>&1

# Database backup
mysqldump -u root -p --all-databases | gzip > /backup/db_$(date +%Y%m%d).sql.gz

Apply the 3-2-1 backup rule: 3 copies, 2 different media, 1 different location.

Step 8: Log Monitoring

hljs bash
# Important log files
tail -f /var/log/auth.log          # Authentication logs
tail -f /var/log/syslog            # System logs
tail -f /var/log/nginx/access.log  # Nginx access logs

# Monitor failed SSH logins
grep "Failed password" /var/log/auth.log | tail -20

# Daily report with logwatch
sudo apt install logwatch -y
sudo logwatch --output mail --mailto admin@example.com --detail high

Step 9: DDoS Protection

hljs bash
# Basic DDoS protection with iptables

# SYN flood protection
sudo iptables -A INPUT -p tcp --syn -m limit \
  --limit 1/s --limit-burst 3 -j ACCEPT
sudo iptables -A INPUT -p tcp --syn -j DROP

# Connection count limit (per IP)
sudo iptables -A INPUT -p tcp --dport 80 -m connlimit \
  --connlimit-above 50 -j REJECT

# Nginx rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

Software-based DDoS protection may be insufficient against large-scale attacks. REXE's Path.net infrastructure provides network-level DDoS filtering, cleaning attack traffic before it reaches your server.

Step 10: SSL/TLS Configuration

hljs bash
# Free SSL certificate with Let's Encrypt
sudo apt install certbot python3-certbot-nginx -y

# Get certificate
sudo certbot --nginx -d example.com -d www.example.com

# Automatic renewal
sudo certbot renew --dry-run

# Automatic renewal with cron
0 12 * * * /usr/bin/certbot renew --quiet

Nginx SSL Configuration

hljs nginx
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Secure SSL settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000" always;

    # Security headers
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

Security Checklist

  • SSH key authentication active, password login disabled
  • Root SSH login disabled
  • Firewall (UFW/iptables) configured
  • Fail2ban installed and running
  • Automatic security updates active
  • Unused services disabled
  • Strong password policy applied
  • 2FA enabled
  • Regular backup plan created
  • Log monitoring configured
  • DDoS protection active
  • SSL/TLS certificate installed and HTTPS enforced