Skip to content
ALB Wiki

Linux Security

Linux security is a discipline of subtraction: every running service is an attack surface, every world-writable file a liability, every setuid binary a privilege. This chapter covers the layers — permissions and least privilege, SSH hardening, the firewall, update hygiene, the audit commands that show you your own exposure, and the SELinux/AppArmor machinery underneath.

The foundation — least privilege

# every service runs as its own user — never root
id nginx                    # nginx's uid — not root
ps -eo user,comm | grep -v ' root ' | sort -u   # who runs what

Principles: run daemons as dedicated users, grant only the rights they need, NOPASSWD: only for specific commands, sudo over su, key-only ssh. The model from chapter 12 — owner/group/other — is the wall; this chapter is keeping the wall maintained.

SSH — the exposed door

# /etc/ssh/sshd_config — the three lines that matter
PermitRootLogin no
PasswordAuthentication no
Port 2222                    # obscurity tax, not security — but reduces noise

sshd -t validates before reload. Key-only auth (PasswordAuthentication no) is the single largest hardening win — brute force becomes impossible. fail2ban rate-limits attempts; AllowUsers whitelists.

The firewall — default deny

nft list ruleset                    # the current policy
# the canonical minimal policy:
iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

Default-deny is the posture; allow what's needed. ss -tlnp tells you what's actually listening — the firewall audit always starts there.

The audit commands — your own exposure

ss -tlnp                     # every listener — attack surface inventory
find / -perm -4000 -type f   # every setuid binary
find / -perm -0002 -type d   # world-writable dirs
find / -nouser -o -nogroup   # orphaned files
ls -la /etc/sudoers.d/       # the delegation surface
cat /etc/passwd | awk -F: '$3==0'   # everyone with uid 0 — only root
last -20                     # login history
grep 'Failed password' /var/log/auth.log | tail   # brute-force traffic

ss -tlnp, the setuid inventory, last, and the auth log — the four commands that see a system's real exposure.

Updates — the unglamorous hardening

apt update && apt upgrade          # Debian
dnf upgrade                        # Fedora
slackpkg update && slackpkg upgrade-all   # Slackware
unattended-upgrades                # auto security updates — Debian
needs-restarting -r                # does it need a reboot?

Most real-world compromises are unpatched software, not clever attacks — the update habit is the highest-value security control.

The capability grammar — root's powers unbundled

getcap /usr/bin/ping               # what does it carry?
# /usr/bin/ping cap_net_raw=ep    — raw sockets, no setuid needed
setcap cap_net_bind_service=+ep /usr/bin/myd   # bind low ports, not root
setcap -r /path                    # strip them
getcap -r / 2>/dev/null            # every capability file on the system

Capabilities split root into ~40 named powers — cap_net_bind_service (bind <1024), cap_net_raw (raw sockets), cap_sys_admin (the dangerous one — mounts, namespaces), cap_chown, cap_dac_override, cap_kill, cap_setuid, cap_sys_time. Modern daemons carry the one they need instead of full setuid — ping carrying cap_net_raw alone is the proof.

The PAM layer — where auth actually happens

/etc/pam.d/sshd — sshd's auth stack:
auth     required   pam_env.so
auth     required   pam_unix.so        — the password check
auth     required   pam_nologin.so
account  required   pam_unix.so        — expiry, policy
password required   pam_unix.so        — password changes
session  optional   pam_motd.so
session  required   pam_limits.so
session  optional   pam_systemd.so

Four phases — auth/account/password/session; four controls — required/requisite/sufficient/optional. PAM stacks modules — add pam_google_authenticator for TOTP, pam_faillock for lockout — without touching sshd's code.

The hardening checklist — the working sequence

# 1. Accounts
passwd -l root                          # or keep root-key-only
awk -F: '$2 ~ /^[!*]/ {print $1}' /etc/shadow   # locked/no-login accounts
find / -nouser -o -nogroup 2>/dev/null  # orphans

# 2. SSH — the door (in /etc/ssh/sshd_config):
#   PermitRootLogin no · PasswordAuthentication no · AllowUsers me
sshd -t && systemctl reload sshd
# fail2ban or sshguard                  # rate-limit attempts
ssh-keygen -t ed25519; ssh-copy-id

# 3. Firewall — default deny
iptables -P INPUT DROP; iptables -P FORWARD DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -j LOG --log-prefix "DROP: "   # watch what hits the wall

# 4. Services — kill the surface
ss -tlnp                              # inventory
systemctl disable unused-svc          # off
apt remove unused                     # gone

# 5. Updates — the unglamorous wall
apt update && apt upgrade             # the habit
unattended-upgrades                   # auto security

# 6. The audit
find / -perm -4000 -type f            # setuid inventory
find / -perm -0002 -type d            # world-writable
last -20; faillog -a                  # who's tried in

The service hardening — the per-daemon grammar

# systemd's sandboxing — the modern confinement:
[Service]
ProtectSystem=strict       # / read-only — services can't touch it
ProtectHome=true           # /home invisible
PrivateTmp=true            # own /tmp
NoNewPrivileges=true       # no privilege escalation
PrivateDevices=true        # no /dev
ProtectKernelTunables/Modules/Logs=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE  # only this cap
RestrictAddressFamilies=AF_INET AF_INET6    # only these sockets
SystemCallFilter=@system-service            # syscall allowlist
ReadWritePaths=/var/lib/myapp               # the one writable dir
MemoryMax=512M; CPUQuota=50%                # resource caps

The kernel-level machinery

  • SELinux — mandatory access control — policies that confine processes beyond file permissions (sestatus, setenforce, ausearch) — Fedora's default
  • AppArmor — the simpler MAC — profiles per program (aa-status) — Debian/Ubuntu's
  • seccomp — syscall filtering — restricts which syscalls a process may make
  • capabilities — root unbundled (chapter 12)
  • cgroups + namespaces — the isolation primitives containers ride

Crypto at rest — LUKS + the secrets files

cryptsetup luksFormat /dev/sda3    # encrypt the partition
cryptsetup luksOpen /dev/sda3 crypt
mkfs.ext4 /dev/mapper/crypt

Full-disk encryption (LUKS) protects at rest — a stolen disk yields nothing without the passphrase. /etc/shadow's 600 perms, ~/.ssh's 700, private keys' 600 — the permission layer is the crypto's keeper.

The operational hygiene

umask 027                  # default-private for a server
passwd -l dormantacct      # lock unused accounts
chmod 600 ~/.ssh/id_*      # private keys private
sudo -l                    # YOUR sudo rights — know them
find . -name '*.key' -perm -o+r   # world-readable private keys — fix now

Where security goes wrong

SymptomCause
Password auth still on after "hardening"sshd -t passed, systemctl reload didn't happen
Permission denied on a legit fileSELinux context — restorecon or chcon
Root login still openPermitRootLogin not actually no
A daemon runs as root "for simplicity"The classic — dedicated user now
sudo -l shows (ALL) NOPASSWD: ALLThe sudoers gift that defeats sudo
A world-writable setuid binaryfind it — the audit commands above

Exercises

  1. ss -tlnp your box — justify every listener.
  2. find / -perm -4000 — inventory setuid; research one you don't recognize.
  3. sshd -t a hardened sshd_config — verify before reload.
  4. ausearch -m avc or aa-status — see your MAC layer.
  5. Write the minimal iptables policy from memory.

See also

Categories: Handbook · Linux · Linux Handbook · Security
This page was last edited 2026-10-26 13:32:06 UTC by ALB Team. What links here