Skip to content

Back to the catalog

How to Secure a Linux Server
github.comrepo

How to Secure a Linux Server

A crowd-sourced, evolving checklist-style guide for locking down a Linux server, covering SSH hardening, firewall/iptables configuration, fail2ban, user/permission hygiene, and includes separate deep-dive documents on Linux kernel sysctl hardening and nginx hardening. It's one of the most widely referenced community Linux hardening playbooks on GitHub.

TL;DR Popular community-maintained step-by-step guide (31.4k stars) for hardening a Linux server, including kernel sysctl and nginx hardening docs.
Document

What it is

A community-maintained how-to by imthenachoman (CC-BY-SA), written and tested on Debian, with copy-paste shell snippets. Each section has Why, How It Works, Goals, Steps and References. It targets at-home servers and says larger environments need more specialised configuration. It recommends running it before the CIS Benchmarks, so that CIS's recommendations take precedence. A separate Ansible port by moltenbit is linked.

What to do

The SSH section is the most concrete part. The order matters: create the group first, then edit sshd_config.

  1. Keep a second terminal open before any SSH change.
  2. Create the sshusers group and add accounts, then set AllowGroups sshusers.
  3. Back up and strip comments from the config, then apply the Mozilla OpenSSH 6.7+ baseline (KexAlgorithms, Ciphers, MACs, LogLevel VERBOSE). The page labels it "as of 2019-01-01".
  4. Set PermitRootLogin no, PasswordAuthentication no, AllowTcpForwarding no, AllowAgentForwarding no, X11Forwarding no, PermitEmptyPasswords no, HostbasedAuthentication no and PermitUserEnvironment no.
  5. Tune MaxAuthTries 2, LoginGraceTime 30, MaxSessions 2, MaxStartups 10:30:60 and ClientAliveInterval 15 with ClientAliveCountMax 3.
  6. Check for contradicting duplicates (sshd honours the first), validate, then restart:
awk 'NF && $1!~/^(#|HostKey)/{print $1}' /etc/ssh/sshd_config | sort | uniq -c | grep -v ' 1 '
sudo sshd -t
sudo service ssh restart

Other procedures with commands:

  • Drop Diffie-Hellman moduli under 3072 bits: sudo awk '$5 >= 3071' /etc/ssh/moduli | sudo tee /etc/ssh/moduli.tmp, then mv it over /etc/ssh/moduli.
  • Restrict su to a group: sudo dpkg-statoverride --update --add root suusers 4750 /bin/su.
  • Restrict sudo with %sudousers ALL=(ALL:ALL) ALL, edited through visudo.
  • Hide other users' processes by adding proc /proc proc defaults,hidepid=2 0 0 to /etc/fstab. Apply without a reboot via sudo mount -o remount,hidepid=2 /proc.
  • TOTP 2FA for SSH uses libpam-google-authenticator. Add auth required pam_google_authenticator.so nullok to /etc/pam.d/sshd and set KbdInteractiveAuthentication yes.
  • On Debian 13 (Trixie) the ntp package is gone. Use systemd-timesyncd (timedatectl set-ntp true), which listens on no port.

The rest of the page's table of contents covers UFW (including Docker interaction), PSAD, Fail2Ban, CrowdSec, AIDE, ClamAV, rkhunter, chkrootkit, logwatch, ss, Lynis, OSSEC, FireJail and MSMTP/Exim4 mail alerts.

Caveats

  • RequiredRSASize 3072 is for OpenSSH 9.1 or later only. On older versions, leaving it uncommented stops sshd from starting.
  • The 2FA setup is weaker than it looks. Key logins skip the TOTP prompt unless AuthenticationMethods requires keyboard-interactive after publickey. nullok lets users without a ~/.google_authenticator file in without a code. The page also warns that losing the authenticator app locks you out.
  • hidepid=2 may break some systemd systems (the page links issue #37).
  • The snippet sets Compression yes, with a comment saying the old compression-oracle risk no longer exists on supported OpenSSH. It also sets UseDNS yes. Review both rather than pasting.
  • AllowTcpForwarding no and MaxSessions 2 will break tunnelling and multiplexed workflows.
  • The Ansible route needs PermitRootLogin yes set temporarily before the playbooks run. Later runs need the new SSH port and key.
  • The su restriction uses dpkg-statoverride, so it is Debian-family only. Package commands throughout are apt. Several auditing sections are marked WIP, and disk encryption, MAC (SELinux/AppArmor) and log shipping are listed as to-do.
  • The fetched text was truncated at 60,000 of 160,226 characters, ending in the password-quality section. I did not see the firewall, Fail2Ban, CrowdSec or auditing steps, or the linked sysctl and nginx documents. This summary does not cover them.