Security & Access Control
Untangle Linux security architecture by exploring how independent layers—DAC, ACLs, capabilities, MAC, and firewalls—converge on every process and file operation.
What this chapter builds
Linux security is a set of applicable decisions, not one onion where passing an inner layer defeats an outer layer. This chapter separates DAC, ACLs, capabilities, LSMs, seccomp, network policy, and audit evidence.
By the end, you can
- Identify which control can decide an operation
- Explain capability sets without treating root as magic
- Distinguish SELinux and AppArmor policy models
- Collect evidence for file, network, and authentication denials
Objects to keep straight
credentials and capabilitiesLSM hooksseccomp filterNetfilter hookaudit contextApplied task: Explain a file read where DAC allows but an enforcing LSM denies.
Linux security is a multi-layered gatekeeper; every check must pass.
How many independent security subsystems must approve an operation before the kernel grants access?
Linux does not possess a single "allow" switch. When a process attempts to open a file or execute a syscall, the request traverses a series of completely independent kernel security layers. Discretionary Access Control (DAC) evaluates standard permissions; Linux capabilities determine process superpowers; Linux Security Modules (LSM) enforce mandatory MAC policies; and Seccomp filters syscall vectors. A single "Deny" from any layer immediately aborts the operation with EACCES or EPERM, regardless of what the other layers allow.
Model scope. A UID 0 process often starts with broad capabilities, but its effective capability sets, user namespace, LSM policy, seccomp filters, and other boundaries determine what a particular operation can do.
cat: /etc/shadow: Permission denied
CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 000001ffffffffff
Discretionary Access Control evaluates permissions via a strict first-match hierarchy.
How does the VFS check file permissions, and why doesn't group membership help if you own the file?
Linux Discretionary Access Control operates via the classic 9-bit mode matrix (rwxrwxrwx) stored in the inode. The kernel VFS evaluates identity classes in a strict, non-cumulative hierarchy: if the process Effective UID matches the file owner, the owner permissions apply immediately and evaluation stops. If the owner permissions do not permit reading (e.g. 0044), access is denied immediately—the kernel never checks whether your group has read permissions! Extended ACLs (setfacl/getfacl) add granular per-user and per-group rules on top of the base mode bits.
Model scope. If a file is owned by your user with mode 0000, you cannot read it even if the group permissions are 0777. The owner check matches and halts evaluation.
640 -rw-r----- root:shadow
# file: /var/log/syslog # owner: syslog # group: adm user::rw- group::r-- other::---
Mandatory Access Control confines processes based on security labels and paths.
How do SELinux and AppArmor enforce mandatory access rules even against root processes?
Owners control ordinary file modes. Privileged capability checks can override specific DAC denials, but do not automatically cancel other applicable controls.
-rw-r----- 1 root shadow 1234 Sep 16 10:00 /etc/shadow
Assigns security contexts (user:role:type:level) to every process and file. Strict Type Enforcement policy matrix dictates which process domain types can access which target file types.
system_u:object_r:httpd_config_t:s0 /etc/nginx/nginx.conf
Profiles attach directly to executable paths (/usr/sbin/nginx) and declare allowed filesystem paths, capabilities, and network calls. Default on Debian and Ubuntu.
/usr/sbin/nginx (enforce)
When a confined process attempts an unapproved action, the kernel LSM hook logs an Access Vector Cache (AVC) denial to the system audit stream.
type=AVC msg=audit(1694772600.123:45): avc: denied { read } for pid=421 comm="nginx" name="shadow" dev="sda1" ino=123 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:shadow_t:s0 tclass=file permissive=0Mandatory Access Control (MAC) addresses the critical flaw of traditional UNIX security: that root can do anything, and users control file access. With MAC, the kernel enforces a centralized security policy across the entire machine. Even if an attacker compromises a web daemon running as root, SELinux or AppArmor prevents the process from reading /etc/shadow, accessing home directories, or opening arbitrary network sockets.
Model scope. Disabling SELinux ("setenforce 0") or disabling AppArmor is an operational antipattern. Use "audit2why" or "aa-logprof" to generate surgical policy exceptions instead.
SELinux status: enabled Current mode: enforcing Loaded policy name: targeted
# Audit stream clean; no active confinement violations
Baseline host hardening minimizes the attack surface through 5 core controls.
What baseline security posture must be established on a modern Linux server before exposing it to the network?
Action 01: Disable Passwords & Root SSH
Field Note: Enforces asymmetric Ed25519/RSA cryptographic key authentication and blocks automated brute-force attacks against the root account.
Field Note: Drops all unsolicited incoming network packets by default, opening strictly required listening ports.
Field Note: Installs critical CVE patches automatically from stable security repositories without human delay.
Field Note: Mitigates TCP SYN flood attacks, restricts unprivileged access to dmesg kernel pointers, and prevents symlink race condition exploits.
Field Note: Configures ProtectSystem=strict, NoNewPrivileges=true, and PrivateTmp=true to prevent compromised daemons from gaining setuid privileges or modifying system binaries.
A fresh Linux instance exposed to the internet will experience automated scanning and brute-force attacks within minutes. Host hardening reduces the attack surface: cryptographic SSH keys replace vulnerable passwords, a default-drop host firewall blocks probing, automatic security updates eliminate known CVE windows, and systemd sandboxing confines daemons in restricted namespaces.
Model scope. Cloud provider security groups (AWS SG, GCP VPC firewall) operate at the hypervisor layer outside the VM and should always provide the primary ingress perimeter.
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=710,fd=3))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=750,fd=6))net.ipv4.tcp_syncookies = 1 kernel.dmesg_restrict = 1
Inbound packets cross 4 distinct defense boundaries before reaching an application socket.
Why does an external client fail to connect even though ss -tulpn confirms the service is listening on port 443?
Network security is deeply layered. Even if an application is running and bound to port 443, incoming packets must survive a gauntlet: the cloud provider's hypervisor security group, the host operating system's Netfilter/nftables firewall rules, interface binding restrictions, and kernel socket queue capacity. Troubleshooting connection timeouts requires validating each boundary in order from the outside network inward.
Model scope. Container runtimes and Kubernetes networking components may install Netfilter rules or use other dataplanes. Their hook priority and forwarding behavior can interact unexpectedly with host firewall front ends, so inspect the effective ruleset and actual packet path.
-P INPUT DROP -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 511 0.0.0.0:443 0.0.0.0:*
Authentication, privilege, and audit records provide evidence—if you preserve them.
Where does the system record successful logins, brute-force attempts, sudo commands, and kernel audit events?
Stage 01: /var/log/auth.log (or /var/log/secure on RHEL) records PAM authentication events, public key logins, and rejected password attempts.
Sep 16 10:00:01 devbox sshd[710]: Accepted publickey for darshan from 192.168.1.5 port 54321 ssh2: ED25519 SHA256:... Sep 16 10:01:22 devbox sshd[715]: Invalid user admin from 10.0.0.9 port 43210 Sep 16 10:01:22 devbox sshd[715]: Connection closed by invalid user admin 10.0.0.9 port 43210 [preauth]
/var/log/auth.log (or /var/log/secure on RHEL) records PAM authentication events, public key logins, and rejected password attempts.
Sep 16 10:15:00 devbox sudo[820]: darshan : TTY=pts/0 ; PWD=/home/darshan ; USER=root ; COMMAND=/usr/bin/cat /etc/shadow
Every sudo execution is recorded with full context: invoking user, terminal TTY, working directory, target user, and exact command string.
darshan pts/0 192.168.1.5 Wed Sep 16 10:00:00 2026 still logged in reboot system boot 0.0.0.0 Wed Sep 16 08:00:00 2026 still running
Parses the binary /var/log/wtmp file to display historical user login sessions, logout timestamps, remote IP addresses, and reboot events.
type=USER_CMD msg=audit(1694772600.123:45): pid=820 uid=1000 auid=1000 ses=1 subj=unconfined cmd="cat /etc/shadow" terminal=pts/0 res=success
Queries structured audit events. The login UID (auid) is intended to retain the originating login identity across many privilege changes.
Linux security evidence comes from complementary subsystems: PAM-aware services may record authentication events in the journal or distribution-specific files; sudo records configured command events; wtmp stores login history; and the kernel audit framework emits structured records. The login UID helps attribute activity across many setuid transitions, but local logs are not inherently immutable or tamper-proof.
Model scope. Local log files can be modified or erased if an attacker achieves root access. Production systems must forward logs immediately over TLS to a centralized SIEM or remote syslog server.
# Displays recent failed password attempts from brute-force scanners
darshan pts/0 192.168.1.5 Wed Sep 16 10:00 still logged in reboot system boot 6.1.0-11-amd64 Wed Sep 16 08:00 still running
A systematic chain of evidence to diagnose "Permission denied" errors.
When an application receives EACCES (Permission denied), how do you pinpoint the exact barrier blocking access?
Diagnosing blocked file access requires methodical investigation. Begin by identifying the exact numeric UID and GIDs of the worker process. Next, use "namei -om" to verify that the process possesses execute (+x) permissions on every parent directory in the path. Then inspect the target file's owner, group, and mode bits. Finally, check if the filesystem is mounted read-only (ro) or if SELinux/AppArmor recorded an AVC denial in the audit log.
Model scope. Filesystems mounted with "noexec", "nosuid", or "ro" in /etc/fstab will block execution or write operations regardless of file permissions.
f: /srv/app/config.json drwxr-xr-x root root / drwxr-xr-x root root srv drwxr-x--- root appgroup app -rw-r----- root appgroup config.json
stat: cannot stat '/srv/app/config.json': Permission denied