Checklist · Linux · Hardening self-audit

Linux Hardening
Checklist

A working self-audit that mirrors the Command & File Reference primer, section for section. Check each item as you verify it — the running total and your progress are saved in this browser. Expand any item for the command that confirms it and what a good result looks like. Print it for a paper worksheet.

Self-audit Maps to the primer 43 checks Progress saved locally
Contents
0 / 43 checks complete
Read-only by design. Every command here verifies a setting; none of them change your system. Make the change in the primer or a guided lab, then come back and confirm it. Run destructive changes only from a snapshot, with a break-glass account in place.

1. Accounts & identity

Who exists on the system, and whether any of them should not.

  • No unauthorized accounts in /etc/passwd
    Verify
    cut -d: -f1 /etc/passwd | sort

    Good: only the accounts on your approved roster.

  • No account other than root has UID 0
    Verify
    awk -F: '($3==0){print $1}' /etc/passwd

    Good: prints only root. Any other name is a backdoor.

  • No account has an empty password
    Verify
    sudo awk -F: '($2==""){print $1}' /etc/shadow

    Good: prints nothing.

  • The sudo group holds only intended admins
    Verify
    getent group sudo

    Good: only authorized administrators are listed.

  • Service / system accounts cannot log in
    Verify
    awk -F: '$3<1000{print $1": "$7}' /etc/passwd

    Good: system accounts use /usr/sbin/nologin or /bin/false.

  • Accounts map to their expected UID ranges
    Verify
    awk -F: '{print $3": "$1" ("$7")"}' /etc/passwd | sort -n

    Good: only root at UID 0; system accounts below 1000 use nologin/false; every UID 1000+ is a person who belongs on the box.

2. Password & PAM policy

Aging, strength, reuse, and lockout — the rules that make a credential expensive to guess.

  • Password maximum age is set
    Verify
    grep ^PASS_MAX_DAYS /etc/login.defs

    Good: a sane maximum such as 90 — never 99999.

  • Minimum password length enforced
    Verify
    grep pam_unix /etc/pam.d/common-password

    Good: the line includes minlen=10 (or higher).

  • Recent password reuse is blocked
    Verify
    grep pam_unix /etc/pam.d/common-password

    Good: the line includes remember=5 (or higher).

  • Account lockout after failed logins
    Verify
    grep pam_faillock /etc/pam.d/common-auth

    Good: deny=5 (or fewer) with an unlock_time set.

  • Existing users have aging applied
    Verify
    sudo chage -l user1

    Good: "Maximum number of days" is a real value, not 99999.

3. sudo & privilege

The rules that decide who can become root, and whether any of them are too generous.

  • No NOPASSWD grants in sudoers
    Verify
    sudo grep -R NOPASSWD /etc/sudoers /etc/sudoers.d

    Good: nothing — or only a single, documented, justified entry.

  • sudoers syntax is valid
    Verify
    sudo visudo -c

    Good: reports "parsed OK".

  • /etc/sudoers.d drop-ins reviewed
    Verify
    sudo ls -la /etc/sudoers.d

    Good: only files you recognize; no stray rules.

4. Permissions & special bits

World-writable files and unexpected SUID/SGID binaries are classic footholds.

  • SUID / SGID binaries match a known baseline
    Verify
    sudo find / -perm -4000 -type f 2>/dev/null

    Good: only expected system binaries; nothing in home directories.

  • No unexpected world-writable files
    Verify
    sudo find / -xdev -type f -perm -0002 2>/dev/null

    Good: nothing outside scratch areas like /tmp.

  • World-writable directories keep the sticky bit
    Verify
    sudo find / -xdev -type d -perm -0002 ! -perm -1000 2>/dev/null

    Good: prints nothing.

  • /etc/shadow is not world-readable
    Verify
    sudo ls -l /etc/shadow

    Good: -rw-r-----, owned by root:shadow.

  • File capabilities match a known baseline
    Verify
    sudo getcap -r / 2>/dev/null

    Good: only expected entries (e.g. ping); no capability on a shell or on any file in a home directory.

5. Software & patching

Every installed package is attack surface; every unpatched one is an open door.

  • System is fully patched
    Verify
    sudo apt update && apt list --upgradable

    Good: nothing left to upgrade.

  • No suspicious applications installed
    Verify
    dpkg -l | grep -Ei 'nmap|zenmap|netcat|ncat|steghide|hydra|john|aircrack|telnetd'

    Good: only tools with a clear reason to be on this box; scanners, reverse-shell tools, and stego utilities are gone.

  • Automatic security updates enabled
    Verify
    systemctl is-enabled unattended-upgrades

    Good: enabled.

  • Installed package files are unmodified
    Verify
    sudo debsums -c

    Good: no system binaries flagged (config files under /etc may differ and are expected). Install first if missing: sudo apt install debsums.

6. Services & startup

Fewer running services means fewer doors to try.

  • Only necessary services start at boot
    Verify
    systemctl list-unit-files --type=service --state=enabled

    Good: every enabled service can be justified.

  • No unexpected listening ports
    Verify
    sudo ss -tlnp

    Good: every listener maps to a service you intend to run.

  • Unneeded network daemons disabled
    Verify
    systemctl status cups avahi-daemon

    Good: disabled unless the role actually needs them.

  • Init system is what's expected (PID 1 not substituted)
    Verify
    ls -l /proc/1/exe

    Good: points at systemd (or your distro's expected init). A mismatch is worth investigating immediately.

  • Journal reviewed for boot and service errors
    Verify
    journalctl -b -p err

    Good: no unexplained failures at the current boot.

  • No services masked or unmasked without a known reason and location
    Verify
    systemctl list-unit-files --type=service --state=masked
    systemctl status <name>   # "Loaded:" line shows whether the mask is in /etc or /run

    Good: every masked entry can be justified, and you know whether it's a persistent admin mask (/etc/), a temporary one (/run/, gone at reboot), or vendor-shipped. "Masked" alone isn't enough — locate it.

  • Preset policy reviewed for security-relevant defaults
    Verify
    ls /usr/lib/systemd/system-preset/*.preset /etc/systemd/system-preset/*.preset 2>/dev/null

    Good: preset files reviewed at least once; nothing security-relevant is enabled by policy that you didn't intend. Never run systemctl preset-all without diffing enabled units before and after — it silently resets manual hardening back to policy defaults.

7. Scheduled tasks

A job that recreates a backdoor on a timer is a favorite persistence trick.

  • System cron reviewed
    Verify
    cat /etc/crontab; ls -la /etc/cron.d /etc/cron.*

    Good: no jobs you did not put there.

  • Per-user crontabs reviewed
    Verify
    for u in $(cut -d: -f1 /etc/passwd); do echo "== $u"; sudo crontab -l -u "$u" 2>/dev/null; done

    Good: nothing unexpected under any user.

  • systemd timers reviewed
    Verify
    systemctl list-timers --all

    Good: only legitimate timers — the modern equivalent of cron jobs.

8. Network & firewall

Exposure is the sum of what listens and what you let in.

  • Firewall active, default-deny inbound
    Verify
    sudo ufw status verbose

    Good: Status: active, default deny (incoming).

  • Only required ports are allowed
    Verify
    sudo ufw status numbered

    Good: the allow-list matches exactly the services you run.

  • IP forwarding is off (unless a router)
    Verify
    sysctl net.ipv4.ip_forward

    Good: = 0 on a workstation.

  • SYN-cookie protection is on
    Verify
    sysctl net.ipv4.tcp_syncookies

    Good: = 1.

9. Remote access (SSH)

The front door from the network — confirm the daemon's effective settings, not just the file.

  • Root SSH login disabled
    Verify
    sudo sshd -T | grep -i permitrootlogin

    Good: permitrootlogin no.

  • Password auth disabled (keys only)
    Verify
    sudo sshd -T | grep -i passwordauthentication

    Good: passwordauthentication no.

  • Empty passwords rejected
    Verify
    sudo sshd -T | grep -i permitemptypasswords

    Good: permitemptypasswords no.

  • SSH access is allow-listed
    Verify
    sudo sshd -T | grep -iE 'allowusers|allowgroups'

    Good: only named users or groups may connect.

10. Logging & audit

You cannot audit the past — turn recording on before you need it.

  • Audit daemon installed and enabled
    Verify
    systemctl is-enabled auditd

    Good: enabled (install auditd if it is missing).

  • Logs are being recorded
    Verify
    journalctl -p err -b | tail

    Good: the journal is populated and readable.