Ubuntu Hardening Standards

Server hardening reduces the attack surface by disabling unnecessary features, enforcing strong authentication, and restricting what processes can do. A freshly installed Ubuntu server has many defaults that are convenient for desktop use but inappropriate for a production server exposed to the internet. Hardening is not a one-time task — it is a baseline configuration that every server in your fleet should share, enforced via Ansible to prevent drift.

SSH hardening

sudo nano /etc/ssh/sshd_config

/etc/ssh/sshd_config — hardened configuration

# === Authentication ===
PasswordAuthentication no         # Key-only authentication
PermitRootLogin no                # No direct root SSH
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

# === Protocol and Security ===
Protocol 2                        # SSHv2 only
Port 22                           # Change to non-standard port in high-risk environments
MaxAuthTries 3                    # Limit brute force attempts
LoginGraceTime 30                 # 30 seconds to authenticate
MaxSessions 4                     # Max concurrent sessions

# === Connection settings ===
ClientAliveInterval 300           # Disconnect idle sessions after 5 minutes
ClientAliveCountMax 2
X11Forwarding no                  # No X11 (not needed on servers)
AllowAgentForwarding no           # Only if SSH agent forwarding is needed

# === Allowed users/groups ===
AllowGroups sshusers              # Only members of sshusers group can SSH
# Validate config before restarting:
sudo sshd -t

# Reload (not restart — avoid locking yourself out):
sudo systemctl reload sshd

# Create sshusers group and add admin users:
sudo groupadd sshusers
sudo usermod -aG sshusers irfan

⚠️ WARNING: Test SSH access in a new session before closing your existing connection after making sshd_config changes. A syntax error or misconfiguration can lock you out of the server.

Kernel hardening

sudo nano /etc/sysctl.d/99-hardening.conf

/etc/sysctl.d/99-hardening.conf

# Kernel self-protection:
kernel.randomize_va_space = 2        # Full ASLR
kernel.dmesg_restrict = 1            # Non-root cannot read kernel messages
kernel.kptr_restrict = 2             # Hide kernel pointers from non-root
kernel.sysrq = 0                     # Disable SysRq key
kernel.yama.ptrace_scope = 1         # Restrict ptrace (debugging) to child processes

# Network hardening:
net.ipv4.conf.all.rp_filter = 1      # Reverse path filtering
net.ipv4.conf.all.accept_source_route = 0  # No source routing
net.ipv4.conf.all.log_martians = 1   # Log impossible addresses
net.ipv4.icmp_echo_ignore_broadcasts = 1   # Ignore broadcast pings
net.ipv4.tcp_syncookies = 1          # SYN flood protection
net.ipv6.conf.all.disable_ipv6 = 0   # Leave IPv6 enabled unless specifically unneeded
net.ipv6.conf.all.accept_ra = 0      # Disable IPv6 router advertisements
sudo sysctl -p /etc/sysctl.d/99-hardening.conf
# Verify settings applied:
sysctl kernel.randomize_va_space

Account and authentication security

# Password policy (PAM):
sudo apt install -y libpam-pwquality
sudo nano /etc/security/pwquality.conf

/etc/security/pwquality.conf

minlen = 14           # Minimum 14 character passwords
dcredit = -1          # Require at least 1 digit
ucredit = -1          # Require at least 1 uppercase
lcredit = -1          # Require at least 1 lowercase
ocredit = -1          # Require at least 1 special character
# Account lockout (PAM) — lock after 5 failed attempts:
sudo nano /etc/pam.d/common-auth
# Add before pam_unix.so line:
# auth required pam_faillock.so preauth deny=5 unlock_time=900

# Remove accounts with no password (security risk):
sudo awk -F: '($2 == "" ) { print $1 }' /etc/shadow

# Lock all system accounts (should not be logged into):
sudo usermod -L -e 1 bin daemon adm lp sync shutdown halt mail news operator games ftp

# Set password expiry policy:
sudo nano /etc/login.defs

/etc/login.defs — password aging

PASS_MAX_DAYS   90      # Force password change every 90 days
PASS_MIN_DAYS   1       # Minimum 1 day before changing again
PASS_WARN_AGE   14      # Warn 14 days before expiry

AppArmor profiles

# AppArmor provides mandatory access control — limits what processes can do:
sudo systemctl status apparmor

# Check profile status:
sudo aa-status

aa-status output

apparmor module is loaded.
31 profiles are loaded.
24 profiles are in enforce mode.
7 profiles are in complain mode.
# Enforce = violations blocked; Complain = violations logged but allowed
# Enable enforcement for a profile (from complain to enforce):
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx

# Check AppArmor logs for denials:
sudo journalctl -t kernel | grep DENIED | tail -20

# Install additional profiles:
sudo apt install -y apparmor-profiles apparmor-profiles-extra

Conclusion

Encode these hardening settings into an Ansible playbook so every new server starts with the same security baseline automatically. The critical SSH hardening settings are PasswordAuthentication no and PermitRootLogin no — without these, servers exposed to the internet will receive constant brute force attacks. Always install and enable fail2ban alongside SSH hardening to block IPs that exceed failed login thresholds, providing an additional layer of defense.

FAQ

Is Ubuntu Hardening Standards important for Ubuntu administrators?+

Yes. It supports practical Ubuntu administration because it connects directly to server reliability, security, troubleshooting, or daily operations.

Should I practice this on a live server?+

Use a lab VM first. After you understand the command output and rollback path, apply the workflow carefully on real systems.

What should I do after reading this article?+

Run the practice commands, write down what each one shows, and continue to the next article in the Ubuntu roadmap.

Need help with Ubuntu administration?

Work directly with Muhammad Irfan Aslam for Ubuntu Server, Linux, cloud, Docker, DevOps, CI/CD, or infrastructure troubleshooting support.

Hire Me for Support