A fresh Linux server is not secure by default. Whether you're running AlmaLinux (RHEL-based) or Ubuntu (Debian-based), these steps will lock things down for production.
This guide shows commands for both distros side by side.
Initial Setup
Update Everything First
# AlmaLinux
sudo dnf update -y
# Ubuntu
sudo apt update && sudo apt upgrade -yEnable automatic security updates:
# AlmaLinux
sudo dnf install -y dnf-automatic
sudo systemctl enable --now dnf-automatic-install.timer
# Ubuntu
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgradesSet the Hostname and Timezone
sudo hostnamectl set-hostname web-prod-01
sudo timedatectl set-timezone UTCAlways use UTC on servers. Convert to local time in your application.
User Management
Create a Non-Root User
Never run services or log in as root.
# Create user
sudo useradd -m -s /bin/bash deploy
sudo passwd deploy
# Add to sudo group
# AlmaLinux
sudo usermod -aG wheel deploy
# Ubuntu
sudo usermod -aG sudo deployDisable Root Login
sudo passwd -l rootEnforce Strong Password Policy
# AlmaLinux
sudo dnf install -y libpwquality
sudo authselect select sssd with-pwquality --force
# Ubuntu
sudo apt install -y libpam-pwqualityEdit /etc/security/pwquality.conf:
minlen = 14
minclass = 3
maxrepeat = 3
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1Set Password Expiry
# Force password change every 90 days
sudo chage -M 90 deploy
# Require 7 days minimum between changes
sudo chage -m 7 deploy
# Warn 14 days before expiry
sudo chage -W 14 deploy
# Check settings
sudo chage -l deploySSH Hardening
SSH is the most attacked service on any server. Lock it down.
Generate a Key Pair (on your local machine)
ssh-keygen -t ed25519 -C "deploy@web-prod-01"
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@your-server-ipHarden sshd_config
Edit /etc/ssh/sshd_config:
# Disable root login
PermitRootLogin no
# Disable password auth (key-only)
PasswordAuthentication no
PubkeyAuthentication yes
# Disable empty passwords
PermitEmptyPasswords no
# Use SSH protocol 2 only
Protocol 2
# Limit login attempts
MaxAuthTries 3
MaxSessions 3
# Disable X11 forwarding
X11Forwarding no
# Disable TCP forwarding unless needed
AllowTcpForwarding no
# Set idle timeout (5 minutes)
ClientAliveInterval 300
ClientAliveCountMax 2
# Restrict to specific users
AllowUsers deploy
# Change default port (optional but reduces noise)
Port 2222
# Disable unused auth methods
ChallengeResponseAuthentication no
KerberosAuthentication no
GSSAPIAuthentication noRestart SSH:
sudo systemctl restart sshdImportant: Always test the new config in a separate terminal before closing your current session.
Install Fail2ban
Automatically ban IPs after failed login attempts.
# AlmaLinux
sudo dnf install -y epel-release
sudo dnf install -y fail2ban
# Ubuntu
sudo apt install -y fail2banCreate /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
banaction = iptables-multiport
[sshd]
enabled = true
port = 2222
logpath = %(sshd_log)s
maxretry = 3
bantime = 86400sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshdFirewall Configuration
UFW (Ubuntu)
sudo apt install -y ufw
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (use your custom port)
sudo ufw allow 2222/tcp comment "SSH"
# Allow web traffic
sudo ufw allow 80/tcp comment "HTTP"
sudo ufw allow 443/tcp comment "HTTPS"
# Enable
sudo ufw enable
sudo ufw status verbosefirewalld (AlmaLinux)
sudo systemctl enable --now firewalld
# Remove default SSH and add custom port
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-port=2222/tcp
# Allow web traffic
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Reload
sudo firewall-cmd --reload
sudo firewall-cmd --list-allRate Limiting
Limit connection rates to prevent brute force:
# UFW (Ubuntu)
sudo ufw limit 2222/tcp
# iptables (both)
sudo iptables -A INPUT -p tcp --dport 2222 -m conntrack --ctstate NEW -m recent --set
sudo iptables -A INPUT -p tcp --dport 2222 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 -j DROPDisable Unnecessary Services
List running services and disable what you don't need:
# List all enabled services
systemctl list-unit-files --state=enabled
# Common services to disable
sudo systemctl disable --now bluetooth.service
sudo systemctl disable --now cups.service
sudo systemctl disable --now avahi-daemon.service
sudo systemctl disable --now rpcbind.service
sudo systemctl disable --now postfix.serviceRule: If you don't know what a service does and your app doesn't need it, disable it.
Kernel Hardening
sysctl Settings
Create /etc/sysctl.d/99-hardening.conf:
# Prevent IP spoofing
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.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
# Log suspicious packets
net.ipv4.conf.all.log_martians = 1
# Ignore ICMP broadcasts
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Disable IPv6 if not needed
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# Restrict kernel pointer exposure
kernel.kptr_restrict = 2
# Restrict dmesg access
kernel.dmesg_restrict = 1
# Harden BPF
kernel.unprivileged_bpf_disabled = 1
# Disable core dumps
fs.suid_dumpable = 0Apply:
sudo sysctl --systemDisable USB Storage (if not needed)
echo "blacklist usb-storage" | sudo tee /etc/modprobe.d/blacklist-usb.confFile System Security
Set Proper Permissions
# Restrict cron access
sudo chmod 700 /etc/crontab
sudo chmod 700 /etc/cron.d
sudo chmod 700 /etc/cron.daily
sudo chmod 700 /etc/cron.hourly
# Restrict SSH directory
sudo chmod 700 /etc/ssh
sudo chmod 600 /etc/ssh/sshd_config
# Restrict passwd and shadow
sudo chmod 644 /etc/passwd
sudo chmod 600 /etc/shadowMount Options
Edit /etc/fstab to add restrictive mount options:
# /tmp - noexec prevents running binaries from /tmp
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
# /var/tmp
tmpfs /var/tmp tmpfs defaults,noexec,nosuid,nodev 0 0File Integrity Monitoring
Install AIDE to detect unauthorized file changes:
# AlmaLinux
sudo dnf install -y aide
sudo aide --init
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
# Ubuntu
sudo apt install -y aide
sudo aideinit
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.dbRun daily checks via cron:
echo "0 3 * * * root /usr/sbin/aide --check | mail -s 'AIDE Report' admin@example.com" | sudo tee /etc/cron.d/aide-checkSELinux / AppArmor
SELinux (AlmaLinux)
SELinux should be enforcing, never disabled:
# Check status
getenforce
# Set to enforcing
sudo setenforce 1
sudo sed -i 's/SELINUX=permissive/SELINUX=enforcing/' /etc/selinux/config
# Troubleshoot denials
sudo ausearch -m avc -ts recent
sudo sealert -a /var/log/audit/audit.logAppArmor (Ubuntu)
# Check status
sudo aa-status
# Install extra profiles
sudo apt install -y apparmor-utils apparmor-profiles apparmor-profiles-extra
# Enforce a profile
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginxAudit Logging
auditd
Track security-relevant events:
# AlmaLinux (usually pre-installed)
sudo dnf install -y audit
# Ubuntu
sudo apt install -y auditdAdd rules to /etc/audit/rules.d/hardening.rules:
# Monitor user/group changes
-w /etc/passwd -p wa -k user_modification
-w /etc/shadow -p wa -k shadow_modification
-w /etc/group -p wa -k group_modification
-w /etc/sudoers -p wa -k sudoers_modification
# Monitor SSH config
-w /etc/ssh/sshd_config -p wa -k sshd_config
# Monitor cron
-w /etc/crontab -p wa -k crontab_modification
-w /var/spool/cron -p wa -k cron_modification
# Monitor login/logout
-w /var/log/lastlog -p wa -k login_tracking
-w /var/log/faillog -p wa -k login_tracking
# Monitor commands run as root
-a always,exit -F arch=b64 -F euid=0 -S execve -k root_commandssudo systemctl restart auditd
sudo auditctl -l # List active rulesSearch audit logs:
# Find all sudo usage
sudo ausearch -k sudoers_modification
# Find failed logins
sudo ausearch -m USER_LOGIN --success noNetwork Security
Restrict Outbound Traffic
Most servers only need HTTP/S and DNS outbound:
# UFW (Ubuntu)
sudo ufw default deny outgoing
sudo ufw allow out 53 comment "DNS"
sudo ufw allow out 80/tcp comment "HTTP"
sudo ufw allow out 443/tcp comment "HTTPS"
sudo ufw allow out 123/udp comment "NTP"TCP Wrappers
Restrict which hosts can connect. Edit /etc/hosts.deny:
ALL: ALLEdit /etc/hosts.allow:
sshd: 10.0.0.0/8 192.168.0.0/16Disable ICMP Ping (optional)
# via sysctl
echo "net.ipv4.icmp_echo_ignore_all = 1" | sudo tee -a /etc/sysctl.d/99-hardening.conf
sudo sysctl --systemTLS and Certificates
Install Certbot
# AlmaLinux
sudo dnf install -y certbot python3-certbot-nginx
# Ubuntu
sudo apt install -y certbot python3-certbot-nginxsudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-runHarden Nginx TLS
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;
# Modern TLS only
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}Monitoring and Alerting
Log Monitoring with Logwatch
# AlmaLinux
sudo dnf install -y logwatch
# Ubuntu
sudo apt install -y logwatch# Daily email summary
echo "0 2 * * * root /usr/sbin/logwatch --output mail --mailto admin@example.com --detail high" | sudo tee /etc/cron.d/logwatchProcess Monitoring
# Install htop for quick checks
# AlmaLinux
sudo dnf install -y htop
# Ubuntu
sudo apt install -y htopRootkit Detection
# Install rkhunter
# AlmaLinux
sudo dnf install -y rkhunter
# Ubuntu
sudo apt install -y rkhunter
# Update and scan
sudo rkhunter --update
sudo rkhunter --check --skip-keypressLynis Security Audit
Comprehensive security audit tool:
# Both distros
sudo dnf install -y lynis # AlmaLinux
sudo apt install -y lynis # Ubuntu
# Run full audit
sudo lynis audit system
# Check the hardening index
sudo lynis show detailsLynis gives you a hardening score and specific recommendations. Run it after every change and aim for 80+.
Hardening Checklist
| Task | Status |
|---|---|
| System updated with auto-updates enabled | |
| Non-root user created, root login disabled | |
| SSH key-only auth, custom port, limited users | |
| Fail2ban installed and configured | |
| Firewall configured (UFW/firewalld) | |
| Unnecessary services disabled | |
| Kernel hardened via sysctl | |
| File permissions tightened | |
| /tmp mounted with noexec | |
| SELinux enforcing / AppArmor enabled | |
| Audit logging configured | |
| TLS configured with modern ciphers | |
| Log monitoring set up | |
| Rootkit scanner installed | |
| Lynis audit score 80+ |
Quick Reference: AlmaLinux vs Ubuntu
| Task | AlmaLinux | Ubuntu |
|---|---|---|
| Package manager | dnf | apt |
| Firewall | firewalld | ufw |
| MAC system | SELinux | AppArmor |
| Auto updates | dnf-automatic | unattended-upgrades |
| Sudo group | wheel | sudo |
| Extra repos | epel-release | Built-in |
Summary
Server hardening is not a one-time task. It's a process:
- Start with the basics — updates, users, SSH, firewall
- Layer defenses — kernel hardening, MAC, audit logging
- Monitor continuously — log analysis, file integrity, rootkit scans
- Audit regularly — run Lynis monthly, review firewall rules quarterly
- Stay updated — subscribe to security mailing lists for your distro
No server is 100% secure, but following these steps puts you ahead of most targets. Attackers go for the easy wins — don't be one.
For the bigger picture on security architecture, read Zero Trust Architecture: Beyond the Buzzword. And to understand what happens when open-source security fails, see The XZ Backdoor: How One Fake Developer Nearly Hacked Every Server on Earth.