Module 8: Manage Security
RHCSA domain covered: Manage Security (9).
Labs in this kit: Lab 02 (permissions + ACLs), Lab 04 (SELinux).
Security on RHEL is layered, and the exam tests your ability to work each layer *and* to diagnose which layer is blocking you when something fails. The mental model below is the order to think in: a packet has to get through the firewall, then the file's POSIX permissions (and any ACL exceptions) decide who can open it, and finally SELinux decides what the *process* is allowed to do with it. When something is mysteriously "permission denied" and the ownership looks right, the answer is almost always SELinux.
The security layers
┌─────────────────────────────────────────────┐
│ firewalld (what packets reach the host) │
├─────────────────────────────────────────────┤
│ POSIX perms (who can open the file) │
├─────────────────────────────────────────────┤
│ ACLs (exceptions to POSIX) │
├─────────────────────────────────────────────┤
│ SELinux (what the process is allowed) │
└─────────────────────────────────────────────┘
Walk the layers top-to-bottom when troubleshooting. The classic exam scenario: a web server returns 403 even though ls -l shows the files are readable. POSIX permissions are fine — but SELinux thinks the files have the wrong *type label* for httpd to serve. Always check SELinux.
ACLs — exceptions to standard permissions
What it is: Access Control Lists let you grant permissions to *additional* specific users or groups beyond the single owner/group/other that standard POSIX permissions allow.
Why it matters: when a task says "alice needs write access too, but don't change the existing ownership," POSIX can't express that — ACLs can. The exam uses this exact framing.
getfacl /srv/projects # view ACLs
ls -l /srv/projects # a trailing '+' means ACLs exist
setfacl -m u:alice:rwx /srv/projects # grant a USER an ACL
setfacl -m g:engineering:rx /srv/projects # grant a GROUP an ACL
setfacl -d -m g:engineering:rwx /srv/projects # DEFAULT ACL (inherited by new files)
setfacl -x u:alice /srv/projects # remove one entry
setfacl -b /srv/projects # remove ALL ACLs
setfacl -m modifies (adds/changes) an entry: u:alice:rwx for a user, g:engineering:rx for a group. The -d flag sets a *default* ACL — it doesn't affect the directory itself, it dictates what new files created inside inherit. The detail to remember is the mask: it's a ceiling on the effective permissions of named users and groups. If you grant u:alice:rwx but the mask is r-x, alice effectively gets r-x — the mask wins. A + at the end of an ls -l permission string is the visible flag that ACLs are present; don't overlook it when diagnosing access.
SELinux modes
What it is: SELinux is mandatory access control — it confines processes to only the actions its policy permits, independent of file ownership. It runs in Enforcing (block + log), Permissive (allow + log), or Disabled.
Why it matters: SELinux is the layer people disable in frustration and the exam expects you to *leave it enforcing* and fix the actual policy problem.
getenforce # current mode
sestatus # full state
setenforce 0 # to Permissive (RUNTIME only)
setenforce 1 # to Enforcing (RUNTIME only)
# Persistent mode change:
sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
The key distinction: setenforce changes the mode *right now* but doesn't survive reboot; the persistent setting lives in /etc/selinux/config. Permissive mode is a diagnostic tool — it stops blocking but keeps *logging* denials, so you can see everything SELinux *would* have blocked without breaking the system. Use it to gather information, then switch back to Enforcing and fix the root cause. Never set SELINUX=disabled on the exam unless explicitly told — leaving it disabled fails hardening-oriented tasks.
File contexts — the labels SELinux enforces
What it is: every file has an SELinux *context* (notably a type label like httpd_sys_content_t). Policy rules say which process types may access which file types. Relocating files often breaks the labels.
Why it matters: the "web server can't read its files" scenario is a context problem, and fixing it *persistently* (not just with chcon) is the scored skill.
ls -Z /var/www/html/ # view contexts
chcon -t httpd_sys_content_t /data/file # change a context (RUNTIME — wiped by relabel)
restorecon -Rv /data # reset to the policy's defaults
# PERSISTENT: register the path with the policy, then apply
semanage fcontext -a -t httpd_sys_content_t '/data/web(/.*)?'
restorecon -Rv /data/web
semanage fcontext -l | grep /data/web # verify the rule is recorded
This is the most important "persistence" trap in SELinux. chcon changes a label *now*, but it's not recorded in the policy — the next restorecon or filesystem relabel reverts it, so a chcon-only fix fails the post-reboot check. The durable fix is two steps: semanage fcontext -a *records* the desired label for a path in the policy database, then restorecon *applies* that recorded rule to the files on disk. The regex (/.*)? at the end of the path matches both the directory itself and everything beneath it. Think of it as semanage = "remember this rule," restorecon = "make the files match the rules."
Booleans — policy on/off switches
What it is: SELinux booleans are named toggles that turn predefined policy behaviors on or off (e.g., "may httpd make network connections to a database").
Why it matters: many "the app is denied even though contexts are right" problems are solved by flipping one boolean — and forgetting to make it persistent is the #1 SELinux mistake.
getsebool -a | less # list all booleans
getsebool httpd_can_network_connect_db # check one
setsebool -P httpd_can_network_connect_db on # set PERSISTENTLY (the -P)
The entire lesson here is the -P flag. setsebool httpd_can_network_connect_db on works until reboot; setsebool -P ... writes it into the policy so it survives. On an exam graded after reboot, a boolean set without -P reverts and you fail the task even though it "worked" when you tested it. Always -P.
SELinux ports
What it is: SELinux also labels network ports. To let a service listen on a non-standard port, you must label that port for the service's type.
Why it matters: "make httpd listen on 8443" fails silently under SELinux unless you label the port — a subtle, testable detail.
semanage port -a -t http_port_t -p tcp 8443 # allow httpd's type on 8443
semanage port -l | grep http_port_t # verify
Even with the firewall open and httpd configured, SELinux blocks the bind to an unlabeled port. semanage port -a adds the port to the service's type so the bind is permitted. This is the kind of step that's invisible until you check journalctl/ausearch and see the denial.
Troubleshooting SELinux denials
What it is: tools that read the audit log to show *why* SELinux blocked something and suggest a fix.
Why it matters: the fastest path from "it's denied" to "here's the boolean/context to fix" runs through these commands.
ausearch -m AVC -ts recent # recent denials
ausearch -m AVC -ts today | audit2why # WHY each denial happened
audit2allow -a # suggest a policy rule (use with care)
When something is blocked, SELinux logs an AVC (Access Vector Cache) denial. ausearch -m AVC -ts recent pulls the recent ones; piping to audit2why translates the cryptic log line into a plain explanation and often names the exact boolean or context that would fix it. audit2allow can generate a custom policy module to permit the action — but treat that as a last resort. On the exam, the right fix is almost always a boolean flip or an fcontext correction, not a custom module.
SSH key-based authentication
What it is: SSH keys let users authenticate with a cryptographic key pair instead of a password — more secure and scriptable.
Why it matters: "configure key-based login" and "disable password authentication" are standard hardening tasks.
ssh-keygen -t ed25519 -C 'alice@laptop' # generate a key pair
ssh-copy-id alice@server # install the public key on the server
Disable password auth in /etc/ssh/sshd_config:
PasswordAuthentication no
PermitRootLogin prohibit-password
Then reload: systemctl reload sshd. ssh-copy-id is the safe way to install your public key into the server's ~/.ssh/authorized_keys with correct permissions — doing it by hand risks wrong modes (the .ssh directory must be 700 and authorized_keys 600, or sshd ignores them). PermitRootLogin prohibit-password allows root in by key but not by password — a common middle-ground hardening setting.
Common pitfalls
setseboolwithout-Pevaporates on reboot — the single most common SELinux mistake. Always-P.chconis overwritten by the nextrestoreconor relabel. For a durable context change, usesemanage fcontext -athenrestorecon.- A
+afterls -lpermissions means ACLs are set — don't ignore it when something has unexpected access. - Permissive mode still LOGS denials — use it to gather information during troubleshooting, then return to Enforcing.
- Forgetting to label a non-standard port with
semanage port -a→ the service can't bind even with the firewall open. - Setting
SELINUX=disabledto "make it work" → fails hardening tasks and requires a full relabel to re-enable. Fix the policy instead.
Exam tips
- The mental checklist for "permission denied" that shouldn't be: check POSIX (
ls -l), check ACLs (the+), then check SELinux (ls -Z,ausearch -m AVC -ts recent). The third one is the usual culprit. audit2whyturns a denial into an explanation and often hands you the fix — run it before reaching foraudit2allow.- Every SELinux persistence command has a tell:
-Pfor booleans,semanage(notchcon) for contexts and ports. If you usedsetenforce,setseboolwithout-P, orchcon, assume it won't survive reboot.
Next: Module 9 — Containers.