Skip to content
Initial Server Setup Guide — Linux Hardening Basics

Initial Server Setup Guide — Linux Hardening Basics

DodaTech Updated Jun 7, 2026 9 min read

A freshly installed Linux server is like a house with the front door unlocked. This guide walks through the essential hardening steps every Linux server needs before going into production — SSH key authentication, firewall configuration, fail2ban, and non-root user management.

What You’ll Learn

By the end of this tutorial, you’ll be able to provision a new Linux server, create a non-root sudo user, configure SSH key-based authentication, set up UFW firewall rules, install and configure fail2ban for brute-force protection, and apply basic security hardening.

Why Server Hardening Matters

Unsecured servers are scanned by bots within minutes of going online. According to industry data, an unprotected SSH server on the public internet receives thousands of login attempts per day. A single compromised server can lead to data breaches, ransomware, or becoming part of a botnet. At DodaTech, Durga Antivirus Pro uses hardened Linux servers to process threat intelligence — a compromised server would expose sensitive security data.

Server Setup Learning Path

    flowchart LR
  A[Linux Basics] --> B[Server Setup]
  B --> C[Essential Commands]
  C --> D[System Administration]
  D --> E[Package Management]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff
  
Prerequisites: A Linux server (Ubuntu 24.04 LTS recommended) with root SSH access. You should be comfortable with the terminal. Review our Linux Basics tutorial if needed.

Step 1: Connect to Your Server

When you first provision a server from a cloud provider (AWS, Linode, DigitalOcean), you’ll receive an IP address and a root password or initial SSH key.

# Connect as root using password
ssh root@203.0.113.10

If using an SSH key already configured by your provider:

# Connect using the private key file
ssh -i ~/.ssh/id_rsa root@203.0.113.10

Step 2: Create a Non-Root User

Running commands as root is dangerous. Create a regular user with sudo privileges:

# Create a new user
adduser alice

# Add the user to the sudo group (Ubuntu/Debian)
usermod -aG sudo alice

# On RHEL/Fedora/CentOS, use wheel instead
# usermod -aG wheel alice

Expected output:

Adding user `alice' ...
Adding new group `alice' (1001) ...
Adding new user `alice' (1001) with group `alice' ...
Creating home directory `/home/alice' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

Now disconnect and test the new user:

# Exit root session, then reconnect as new user
ssh alice@203.0.113.10

Step 3: Configure SSH Key Authentication

Password-based SSH is vulnerable to brute-force attacks. SSH keys are cryptographically secure and more convenient.

# On your LOCAL machine, generate an SSH key pair (if you don't have one)
ssh-keygen -t ed25519 -C "alice@laptop"

Expected output:

Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/you/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/you/.ssh/id_ed25519
Your public key has been saved in /home/you/.ssh/id_ed25519.pub

Copy the public key to the server:

# Using ssh-copy-id (simplest method)
ssh-copy-id alice@203.0.113.10

# Alternative: manual copy
# cat ~/.ssh/id_ed25519.pub | ssh alice@203.0.113.10 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Test that key authentication works:

ssh alice@203.0.113.10

If it connects without asking for a password, key authentication is working.

Step 4: Disable Password Authentication and Root Login

Edit the SSH daemon configuration:

sudo nano /etc/ssh/sshd_config

Find and change these lines:

# Disable root login
PermitRootLogin no

# Disable password authentication
PasswordAuthentication no

# Only allow key authentication
PubkeyAuthentication yes

# Optional: change SSH port (security through obscurity)
Port 2222

Apply the changes:

sudo systemctl restart sshd

Warning: Keep your current SSH session open while testing a new connection. If you make a mistake (like disabling keys before testing them), you’ll lock yourself out. Always verify access in a second terminal before closing the first.

Step 5: Configure the Firewall (UFW)

UFW (Uncomplicated Firewall) provides a simple interface to iptables.

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

# Allow SSH (change port if you modified sshd_config)
sudo ufw allow 22/tcp

# If you changed SSH to port 2222:
# sudo ufw allow 2222/tcp

# Allow web traffic if running a web server
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable the firewall
sudo ufw enable

Expected output:

Firewall is active and enabled on system startup

Check the status:

sudo ufw status verbose

Expected output:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Step 6: Install and Configure fail2ban

fail2ban scans log files for repeated failed login attempts and temporarily bans the offending IP.

sudo apt update && sudo apt install fail2ban -y

Create a local configuration file:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Add or modify these settings:

[DEFAULT]
# Ban IP after 5 failed attempts
maxretry = 5
# Ban for 10 minutes
bantime = 10m
# Find failures within 10 minute window
findtime = 10m

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
# Ban for 1 hour for SSH specifically
bantime = 1h

Start and enable fail2ban:

sudo systemctl enable --now fail2ban

Check its status:

sudo fail2ban-client status sshd

Expected output:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 0
   |- Total banned:     0
   `- Banned IP list:

Step 7: Automatic Security Updates

Enable automatic security updates to receive critical patches without manual intervention:

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

Select “Yes” when prompted. This configures the system to install security updates automatically.

Common Server Setup Mistakes

1. Locking Yourself Out Before Testing

Always keep your original SSH session active while testing a new connection. If the new connection fails, you can fix the configuration without needing physical console access.

2. Using Weak SSH Keys

RSA keys shorter than 2048 bits are vulnerable. Use Ed25519 keys (ssh-keygen -t ed25519) — they’re more secure, faster, and produce shorter key strings than RSA.

3. Opening Too Many Firewall Ports

Only expose the ports your application needs. Every open port is an attack surface. If you’re not running a web server, don’t open port 80 or 443.

4. Skipping fail2ban Configuration

Many admins install fail2ban but use default settings. The default bantime is often too short (10 minutes). For SSH, consider a 1-hour or permanent ban after repeated failures.

5. Not Configuring Automatic Updates

Manual patching is unreliable. Systems running unpatched software are the #1 target for automated exploits. Use unattended-upgrades or equivalent.

6. Using Port Knocking Instead of Proper Security

Port knocking (hiding SSH behind a sequence of connection attempts) adds complexity without real security. Focus on key auth, firewall rules, and fail2ban instead.

7. Forgetting to Secure the Root User Even After Disabling Root Login

Even with PermitRootLogin no, the root account exists. Ensure root has a strong password and that /etc/ssh/sshd_config has AllowUsers alice to restrict SSH access to specific users.

Practice Questions

1. Why should you disable root SSH login?

Root has unlimited privileges. If an attacker compromises the root account, they have full control. By logging in as a regular user and using sudo, you add an audit trail and limit the damage from a compromised credential.

2. What does the command ssh-keygen -t ed25519 do?

It generates a new SSH key pair using the Ed25519 algorithm. Ed25519 offers strong security, excellent performance, and compact key sizes compared to RSA.

3. What is the difference between ufw allow 22/tcp and ufw enable?

ufw allow 22/tcp creates a rule allowing SSH traffic on port 22. ufw enable activates the firewall and applies all rules. Rules can be defined before or after enabling the firewall.

4. How does fail2ban protect your server?

fail2ban monitors log files (like /var/log/auth.log) for repeated failed authentication attempts. When a threshold is exceeded, it adds a temporary iptables rule to block the attacker’s IP address.

5. Challenge: Write a script that checks whether fail2ban is running and, if not, starts it and sends a notification.

Answer: systemctl is-active --quiet fail2ban || sudo systemctl start fail2ban && echo "fail2ban started" | mail -s "Alert" admin@example.com

Mini Project: Server Hardening Audit Script

Create a script that checks the hardening status of your server:

#!/bin/bash
# hardening_audit.sh — Check server security posture

echo "=== Server Hardening Audit ==="

# Check SSH root login
if grep -q "^PermitRootLogin no" /etc/ssh/sshd_config 2>/dev/null; then
    echo "[PASS] Root SSH login is disabled"
else
    echo "[FAIL] Root SSH login is enabled or not explicitly denied"
fi

# Check password authentication
if grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config 2>/dev/null; then
    echo "[PASS] Password authentication is disabled"
else
    echo "[FAIL] Password authentication is enabled"
fi

# Check UFW status
if ufw status | grep -q "Status: active"; then
    echo "[PASS] UFW firewall is active"
else
    echo "[FAIL] UFW firewall is not active"
fi

# Check fail2ban
if systemctl is-active --quiet fail2ban; then
    echo "[PASS] fail2ban is running"
else
    echo "[FAIL] fail2ban is not running"
fi

# Check unattended-upgrades
if dpkg -l unattended-upgrades 2>/dev/null | grep -q "^ii"; then
    echo "[PASS] unattended-upgrades is installed"
else
    echo "[FAIL] unattended-upgrades is not installed"
fi

echo "=== Audit Complete ==="

Save as hardening_audit.sh, make it executable (chmod +x hardening_audit.sh), and run it with sudo ./hardening_audit.sh.

Expected output:

=== Server Hardening Audit ===
[PASS] Root SSH login is disabled
[PASS] Password authentication is disabled
[PASS] UFW firewall is active
[PASS] fail2ban is running
[PASS] unattended-upgrades is installed
=== Audit Complete ===

FAQ

Do I need a firewall if I’m using a cloud provider’s security group?
Cloud security groups act as a firewall at the provider level, but adding UFW (or iptables/nftables) on the server itself provides defense-in-depth. If the security group is misconfigured, the host firewall is a backup.
Should I change the default SSH port?
Changing from port 22 to a non-standard port reduces log noise from automated scanners, but it’s not a replacement for proper security (key auth + fail2ban). Real attackers scan all ports. Do it if you want, but don’t rely on it.
What’s the difference between UFW and iptables?
UFW is a frontend to iptables. UFW simplifies common firewall tasks with readable syntax. iptables is more powerful but has a steeper learning curve. For most servers, UFW is sufficient.
How do I recover if I lock myself out?
If you still have console access (via your cloud provider’s web console), log in directly to fix the configuration. If not, you’ll need to rebuild the server. Always test changes in a second session before closing the first.
Can I automate the entire setup?
Yes. Tools like Ansible, Puppet, and shell scripts can automate server hardening. Bash scripts are the simplest starting point for automation. For production, infrastructure-as-code tools are recommended.

What’s Next

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro