Skip to content

Local Security

The Linux security model is built on the principle of least privilege: every user, process, and daemon should have only the minimum permissions required to do its job - nothing more.

Account TypeUIDPrivilegesTypical Use
root0Unlimited - no DAC restrictionsSystem administration only
System accounts1-99Very limited; no loginCore OS services (sshd, mail)
Service accounts100-999Limited to specific directoriesApplication services (nginx, postgres)
Normal users1000+Access to own files and permitted devicesHuman users

The last utility shows login history - useful for spotting dormant accounts:

Terminal window
last # full login history
last -n 10 # last 10 logins
lastb # failed login attempts (root only)
Requires rootDoes NOT require root
Create/remove user accountsRun network clients (browsers, SSH clients)
Install/remove system packagesPrint using permitted printers
Modify files in /etc, /lib, /bootAccess files with proper permissions
Restart system servicesRun SUID programs (e.g., passwd)
Load kernel modulesUse devices accessible to your group

The goal: root should be invoked only when strictly necessary, and every root action should be auditable.


Featuresusudo
AuthenticationRoot’s passwordYour own password
PersistenceFull root shell until exit (unlimited time)Per-command; re-prompts after timeout (5-15 min)
GranularityAll-or-nothingPer-command, per-user, per-host control
LoggingMinimalEvery command logged with user identity
RiskRoot password must be sharedRoot password stays secret
Audit trailPoor/var/log/auth.log or /var/log/secure

Best practice: Disable direct root login. Use sudo for all elevated operations.

Terminal window
su - # login shell as root (bad practice)
sudo command # run ONE command as root (preferred)
sudo -i # root login shell via sudo (audited)
sudo -u alice command # run as a different user
sudo -l # list what you're allowed to do

LocationPurpose
/etc/sudoersMaster configuration file
/etc/sudoers.d/Drop-in directory; one file per user or group (preferred)
who where = (as_whom) what
Terminal window
# Examples in /etc/sudoers
owl ALL=(ALL) ALL # full unrestricted sudo
alice ALL=(root) /usr/bin/apt # only apt, only as root
bob ALL=(ALL) NOPASSWD: /usr/bin/rsync # rsync without password prompt
%devs ALL=(ALL) ALL # group devs gets full sudo
carol ALL=(www-data) /usr/sbin/apachectl # switch to www-data only
# Restrict to specific hosts
dave webserver=(root) /usr/bin/systemctl restart nginx

Key syntax:

  • ALL in the where field = any host
  • ALL in (as_whom) = any user (including root)
  • ALL in what = any command
  • %groupname = apply rule to a group
  • NOPASSWD: = skip password prompt for that command

By default, all sudo executions and failures are logged:

Distribution familyLog location
Debian/Ubuntu/var/log/auth.log
RHEL/Fedora/CentOS/var/log/secure or /var/log/messages

Each log entry includes: calling user, terminal, working directory, target user, command + arguments.

A failed sudo attempt looks like:

bjmoose : user NOT in sudoers ; TTY=pts/4 ; PWD=/var/log ; USER=root ; COMMAND=/usr/bin/tail secure

Historical problem: /etc/passwd is world-readable (needed by system programs). Early UNIX stored password hashes there - attackers could copy the file and run offline cracking tools.

Solution: /etc/shadow with permissions 000 to 400 (root-read-only). Passwords are stored here as one-way hashes.

Modern Linux uses SHA-512 for password hashing. The stored format in /etc/shadow:

$6$<salt>$<hash>
  • $6$ = SHA-512 algorithm identifier
  • <salt> = 8-character random value (prevents rainbow table attacks)
  • <hash> = 88-character SHA-512 hash of salt + password

The salt means two users with the same password have completely different hash values. Even if an attacker gets the shadow file, they must brute-force each account separately.

PAM is the authentication abstraction layer in Linux. Applications (login, sshd, su, sudo) delegate all authentication decisions to PAM rather than implementing their own.

PAM policy is configured in /etc/pam.d/:

Terminal window
ls /etc/pam.d/ # per-application PAM configs
cat /etc/pam.d/passwd # policy for the passwd command

PAM enforces password quality through modules:

  • pam_cracklib.so (older) / pam_pwquality.so (newer): Enforces minimum length, complexity requirements, dictionary checks
  • pam_faillock.so: Locks accounts after N failed attempts (brute-force protection)
  • pam_unix.so: Standard Unix password verification
  • pam_securetty.so: Restricts root login to terminals listed in /etc/securetty
  • pam_limits.so: Enforces resource limits from /etc/security/limits.conf
PracticeMechanism
Password agingchage (force rotation after N days)
Complexity requirementsPAM (pam_pwquality.so)
Account lockout on failuresPAM (pam_faillock.so) or faillock
Multi-factor authenticationPAM (pam_google_authenticator.so, etc.)
Audit credential usagesudo logging + auditd
Terminal window
# Check who has empty passwords (security audit)
sudo awk -F: '$2=="" {print $1}' /etc/shadow
# Check for accounts with no expiry policy
sudo chage -l username
# Force immediate password change at next login
sudo chage -d 0 username

Linux security starts with fundamental kernel-level isolation:

  • Separate address spaces: Each process has its own virtual memory - a process can’t read another’s memory without explicit mechanisms (shared memory, pipes)
  • Separate credentials: Each process runs with a UID/GID; kernel enforces DAC on every syscall
  • No implicit trust: Processes can’t access each other’s files, sockets, or descriptors without kernel-mediated sharing

Additional isolation mechanisms that extend the baseline:

MechanismWhat it isolatesNotes
cgroupsCPU, memory, I/O, processesLimits resource use per group of processes
NamespacesPID, network, mount, user, UTS, IPCFoundation of containers
ContainersFull OS tree per serviceUses namespaces + cgroups; no hypervisor
VirtualizationEntire hardware environmentFull isolation; performance overhead
seccompSystem callsRestricts which syscalls a process can invoke
capabilitiesFine-grained root privilegesSplit root’s power into ~40 separate capabilities

Linux enforces device security through the /dev filesystem - devices are special files with owner:group:permissions just like regular files:

Terminal window
ls -l /dev/sda
# brw-rw---- 1 root disk 8, 0 ... /dev/sda
# disk group - users in 'disk' group can access
ls -l /dev/tty
# crw-rw-rw- 1 root tty 5, 0 ... /dev/tty

To give a user direct access to a device, add them to the appropriate group:

Terminal window
sudo usermod -aG disk alice # disk access
sudo usermod -aG audio alice # audio devices
sudo usermod -aG video alice # video devices

GRUB 2 can require a password to:

  • Modify boot parameters (prevent passing init=/bin/sh to get a root shell)
  • Boot specific menu entries
Terminal window
# Generate a hashed GRUB password
grub2-mkpasswd-pbkdf2
# Enter your password at the prompt
# Outputs: grub.pbkdf2.sha512.10000.LONG_HASH_STRING
# Add to GRUB configuration in /etc/grub.d/40_custom:
# set superusers="admin"
# password_pbkdf2 admin grub.pbkdf2.sha512.10000.LONG_HASH_STRING
# Regenerate grub config
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL/Fedora
sudo update-grub # Debian/Ubuntu

When hardware is physically accessible:

ThreatPrevention
Keyboard sniffingPhysically secure keyboard access; use full-disk encryption
Boot from live USB/DVDBIOS password + boot order lock
Cold boot attack (RAM)Full-disk encryption with memory locking
Remove disk and mount elsewhereFull-disk encryption (LUKS)
Network packet sniffingTLS everywhere; encrypted VPNs; network segmentation

Guidelines:

  • Lock workstations and servers physically
  • Encrypt sensitive disks with LUKS (Linux Unified Key Setup)
  • Disable unused network interfaces and ports
  • Keep BIOS/UEFI firmware updated