Linux Security — SELinux & Firewalld Explained Simply
Posted: Sat Jun 13, 2026 1:20 pm
Linux Security — SELinux & Firewalld Explained Simply
A clear, practical guide to enforcing modes, contexts, booleans, firewall zones and rich rules, with copy-ready commands (AlmaLinux 9 / RHEL 9)
─────────────────────────────────────────
Part 1 — SELinux
SELinux (Security-Enhanced Linux) is a mandatory access control system. In plain terms: even if a hacker takes over a service, SELinux limits the damage by only letting that service touch the exact files and ports it's supposed to. Every file and process carries a label, and a central policy decides which labels may interact.
Tip: The golden rule of SELinux — don't disable it when something breaks. Almost every "SELinux problem" is fixed with the right context or boolean, not by turning the guard off.
─────────────────────────────────────────
1 Enforcing, Permissive & Disabled — the three modes
Set the mode permanently
Tip: When a service misbehaves, flip to Permissive, reproduce the issue, read the logged denials, fix the context/boolean, then return to Enforcing. You troubleshoot without leaving the system exposed for long.
─────────────────────────────────────────
2 Contexts — the labels on everything
Every file, process and port has an SELinux context, written as four parts: user:role:type:level. The part that matters most day-to-day is the type (e.g. httpd_sys_content_t). SELinux allows access by matching types — a web server labelled httpd_t can only read files labelled as web content.
View contexts
The classic problem — files in the wrong place get the wrong label
Make a custom path's label permanent (the right way)
WARNING: chcon is temporary — a relabel or restorecon wipes it. For anything permanent, use semanage fcontext + restorecon so the label survives.
─────────────────────────────────────────
3 Booleans — on/off switches for policy
Booleans are pre-built toggle switches that turn optional SELinux behaviours on or off, without writing any policy yourself. For example, allowing the web server to make outbound network connections.
List and read booleans
Flip a boolean (use -P to make it permanent)
Handy common booleans
─────────────────────────────────────────
4 Troubleshooting SELinux denials
When SELinux blocks something, it logs an AVC (Access Vector Cache) denial. These tools turn the cryptic log into a plain explanation and a suggested fix.
WARNING: audit2allow makes it easy to generate an "allow everything" module that silently defeats SELinux. Read the suggested rule, and prefer fixing the context or boolean first — only build a custom module when there's genuinely no standard fix.
─────────────────────────────────────────
Part 2 — Firewalld
Firewalld is the front-gate guard. It's the default firewall manager on AlmaLinux 9 and organises rules into zones — named trust levels (public, internal, trusted, etc.) that you assign to network interfaces.
See the current state
─────────────────────────────────────────
5 Firewalld basics — services & ports
Two key ideas: --permanent writes the rule to disk (survives reboot), and you must --reload for permanent rules to take effect.
Allow a service or a specific port
Remove access
Tip: Test a rule live without --permanent first. If it works, add it again WITH --permanent. A rule added live but not made permanent disappears on the next reload/reboot.
─────────────────────────────────────────
6 Rich Rules — fine-grained control
Plain rules say "allow this port." Rich rules add conditions: which source IP, which action, whether to log, rate limits, and more. They're how you express precise policy.
Allow SSH only from one trusted network
Block a specific abusive IP
Rate-limit and log new connections to a port
Tip: Rich rules are evaluated with reject/drop taking priority where it matters — build allowlists (accept from trusted sources) rather than trying to block every bad actor one by one.
─────────────────────────────────────────
7 Security Policies — putting it together
Good server security layers these controls so a failure in one is caught by another:
─────────────────────────────────────────
Quick Reference Cheat Sheet
Do you run SELinux in Enforcing on production, or do you switch it off? And what's your firewalld zone strategy? Share your approach below.
A clear, practical guide to enforcing modes, contexts, booleans, firewall zones and rich rules, with copy-ready commands (AlmaLinux 9 / RHEL 9)
─────────────────────────────────────────
─────────────────────────────────────────Two guards stand between attackers and your server.
Firewalld controls which network traffic is even allowed in — like a guard at the building's front gate. SELinux controls what each program is allowed to do once it's inside — like a guard checking that every worker only enters the rooms their badge permits. This guide covers both.
Part 1 — SELinux
SELinux (Security-Enhanced Linux) is a mandatory access control system. In plain terms: even if a hacker takes over a service, SELinux limits the damage by only letting that service touch the exact files and ports it's supposed to. Every file and process carries a label, and a central policy decides which labels may interact.
Tip: The golden rule of SELinux — don't disable it when something breaks. Almost every "SELinux problem" is fixed with the right context or boolean, not by turning the guard off.
─────────────────────────────────────────
1 Enforcing, Permissive & Disabled — the three modes
- Enforcing — the policy is active and blocks violations. This is the secure, recommended mode.
- Permissive — nothing is blocked, but every violation is LOGGED. Perfect for troubleshooting and testing new policy.
- Disabled — SELinux is off entirely. Not recommended.
Code: Select all
getenforce # show current mode
setenforce 0 # switch to Permissive (until reboot)
setenforce 1 # switch back to Enforcing (until reboot)
sestatus # full status overviewCode: Select all
# Edit /etc/selinux/config and set:
SELINUX=enforcing # or permissive
# A change to/from 'disabled' requires a reboot.─────────────────────────────────────────
2 Contexts — the labels on everything
Every file, process and port has an SELinux context, written as four parts: user:role:type:level. The part that matters most day-to-day is the type (e.g. httpd_sys_content_t). SELinux allows access by matching types — a web server labelled httpd_t can only read files labelled as web content.
View contexts
Code: Select all
ls -Z /var/www/html # file contexts
ps -eZ | grep nginx # process contexts
id -Z # your own contextCode: Select all
# Move content into a custom path and the web server gets "Permission denied"
# even though normal permissions look fine. The fix is the LABEL, not chmod.
# Temporarily set a context:
chcon -t httpd_sys_content_t /srv/web/index.html
# Reset a file back to its policy-defined default:
restorecon -Rv /var/www/htmlCode: Select all
# Tell the policy that /srv/web should always be web content:
semanage fcontext -a -t httpd_sys_content_t "/srv/web(/.*)?"
restorecon -Rv /srv/web # apply it─────────────────────────────────────────
3 Booleans — on/off switches for policy
Booleans are pre-built toggle switches that turn optional SELinux behaviours on or off, without writing any policy yourself. For example, allowing the web server to make outbound network connections.
List and read booleans
Code: Select all
getsebool -a # list every boolean
getsebool httpd_can_network_connect # check oneCode: Select all
setsebool httpd_can_network_connect on # this session only
setsebool -P httpd_can_network_connect on # permanent (survives reboot)- httpd_can_network_connect — let the web server reach other services/DBs
- httpd_enable_homedirs — serve files from user home directories
- ftpd_full_access — let FTP write across the filesystem
- samba_enable_home_dirs — share home directories over Samba
─────────────────────────────────────────
4 Troubleshooting SELinux denials
When SELinux blocks something, it logs an AVC (Access Vector Cache) denial. These tools turn the cryptic log into a plain explanation and a suggested fix.
Code: Select all
ausearch -m avc -ts recent # recent denials
audit2why < /var/log/audit/audit.log # explain WHY it was denied
# audit2allow suggests a policy fix - READ it, don't blindly apply:
ausearch -m avc -ts recent | audit2allow -M mypol─────────────────────────────────────────
Part 2 — Firewalld
Firewalld is the front-gate guard. It's the default firewall manager on AlmaLinux 9 and organises rules into zones — named trust levels (public, internal, trusted, etc.) that you assign to network interfaces.
See the current state
Code: Select all
firewall-cmd --state # is it running?
firewall-cmd --get-active-zones # which zones are in use
firewall-cmd --list-all # everything allowed in the default zone5 Firewalld basics — services & ports
Two key ideas: --permanent writes the rule to disk (survives reboot), and you must --reload for permanent rules to take effect.
Allow a service or a specific port
Code: Select all
firewall-cmd --permanent --add-service=https # allow HTTPS
firewall-cmd --permanent --add-port=8080/tcp # allow a custom port
firewall-cmd --reload # apply permanent changesCode: Select all
firewall-cmd --permanent --remove-service=http
firewall-cmd --reload─────────────────────────────────────────
6 Rich Rules — fine-grained control
Plain rules say "allow this port." Rich rules add conditions: which source IP, which action, whether to log, rate limits, and more. They're how you express precise policy.
Allow SSH only from one trusted network
Code: Select all
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.29.0/24" service name="ssh" accept'
firewall-cmd --reloadCode: Select all
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.45" reject'
firewall-cmd --reloadCode: Select all
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port port="8080" protocol="tcp" log prefix="WEB8080 " level="info" limit value="5/m" accept'
firewall-cmd --reload─────────────────────────────────────────
7 Security Policies — putting it together
Good server security layers these controls so a failure in one is caught by another:
- Firewalld — only expose the ports you actually serve; restrict admin ports (SSH) to known source networks via rich rules.
- SELinux in Enforcing — contains any service that does get compromised, so a breached web server can't read your database files or shells.
- Booleans over disabling — grant the minimum extra permission needed, never switch SELinux off.
- Correct contexts — label custom paths properly with semanage fcontext so services work without weakening policy.
- Audit & review — watch denials and firewall logs; they're early warning of both misconfiguration and attack.
─────────────────────────────────────────
Quick Reference Cheat Sheet
- SELinux mode — getenforce ; sestatus
- Temp mode — setenforce 0|1
- Permanent mode — edit /etc/selinux/config
- View contexts — ls -Z ; ps -eZ
- Fix context (temp) — chcon -t TYPE file
- Reset context — restorecon -Rv /path
- Permanent context — semanage fcontext -a -t TYPE "/path(/.*)?" ; restorecon
- List booleans — getsebool -a
- Set boolean — setsebool -P name on
- Explain denial — ausearch -m avc -ts recent | audit2why
- Firewall status — firewall-cmd --list-all
- Allow service — firewall-cmd --permanent --add-service=https ; --reload
- Allow port — firewall-cmd --permanent --add-port=8080/tcp ; --reload
- Rich rule — firewall-cmd --permanent --add-rich-rule='...' ; --reload
Do you run SELinux in Enforcing on production, or do you switch it off? And what's your firewalld zone strategy? Share your approach below.