Linux hardening cheatsheet.
SSH
# /etc/ssh/sshd_config
Port 22
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
PrintMotd no
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 3
AllowUsers alice bob
sshd -t
systemctl reload sshd
Firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
fail2ban
apt install fail2ban
/etc/fail2ban/jail.d/sshd.conf:
[sshd]
enabled = true
port = 22
maxretry = 5
bantime = 1h
findtime = 10m
systemctl enable --now fail2ban
fail2ban-client status sshd
fail2ban-client unban 1.2.3.4
Kernel sysctl
# /etc/sysctl.d/99-hardening.conf
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.unprivileged_bpf_disabled = 1
net.core.bpf_jit_harden = 2
kernel.yama.ptrace_scope = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.tcp_syncookies = 1
net.ipv6.conf.all.accept_redirects = 0
sysctl -p /etc/sysctl.d/99-hardening.conf
Filesystem
/etc/fstab mount options:
/tmp /tmp ext4 defaults,nodev,nosuid,noexec 0 2
/var /var ext4 defaults,nodev 0 2
Drop privileges in services
systemd:
[Service]
User=myapp
Group=myapp
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
RestrictRealtime=yes
SystemCallArchitectures=native
ReadWritePaths=/var/lib/myapp
systemd-analyze security myapp rates.
SELinux (RHEL family)
sestatus
getenforce # Enforcing, Permissive, Disabled
setenforce 1 # enforcing
restorecon -Rv /var/www # fix labels
chcon -t httpd_sys_content_t /var/www/x
audit2allow -a # generate policy from denied audits
# Find issues
ausearch -m avc -ts recent
AppArmor (Ubuntu/Debian)
apparmor_status
aa-enforce /etc/apparmor.d/usr.sbin.nginx
aa-complain /etc/apparmor.d/usr.sbin.nginx
auditd
apt install auditd
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/shadow -p wa -k shadow
auditctl -l # list
ausearch -k passwd_changes
aureport
Persistent: /etc/audit/rules.d/audit.rules.
Unattended upgrades
apt install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
Or dnf-automatic on RHEL.
Lynis (audit)
apt install lynis
lynis audit system
Reports hardening gaps.
Clamav (malware scan)
apt install clamav clamav-daemon
freshclam
clamscan -r /home
chkrootkit / rkhunter
apt install rkhunter chkrootkit
rkhunter --update
rkhunter --check
chkrootkit
tripwire / aide (integrity)
apt install aide
aide --init
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
aide --check
Detects file changes against known baseline.
Cron security
# /etc/cron.allow / /etc/cron.deny
# Allow only specified users
sudo logging
# /etc/sudoers
Defaults log_input, log_output
Defaults logfile=/var/log/sudo.log
Defaults iolog_dir=/var/log/sudo-io
Disable unused services
systemctl list-unit-files --state=enabled
systemctl disable cups bluetooth
CIS / DISA STIG
Run pre-built scripts to apply CIS Benchmark hardening:
cis-catOpenSCAP- Ansible roles:
dev-sec/ansible-collection-hardening
TLS / certificates
Auto-renewal via certbot. Monitor expiration:
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
Container hardening
See Docker / K8s cheatsheets. Drop caps, non-root, read-only FS.
Backups + recovery
Hardening means nothing without backups. Test restores.
Common mistakes
- SSH on port 22 + password auth → brute force.
root:rooteverywhere.- Disabled SELinux / AppArmor “to make it work.”
- No fail2ban.
- Open services on public interfaces.
- No log monitoring for auth.
Read this next
If you want my hardening Ansible playbook, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .