πŸ” SELinux Workshop

Understand SELinux with hands-on exercises (RHEL 9/10)

SELinux Workshop 03/2026

πŸ‘€ About me

  • RenΓ© Koch
  • Self-employed consultant for:
    • Red Hat Ansible (Automation Platform)
    • Red Hat Enterprise Linux
    • Red Hat Satellite
    • Red Hat Identity Management (IPA)
SELinux Workshop 03/2026

πŸ‘€ About me

SELinux Workshop 03/2026

🎯 Goals for today

  • Understand what SELinux is and why it exists
  • Explain DAC vs MAC with clear examples
  • Read and interpret SELinux AVC logs
  • Use audit2allow, setenforce, and booleans safely
  • Fix real-world web issues (custom webroot, PHP access, outbound connections)
  • Know what we are not doing: writing custom policy modules
SELinux Workshop 03/2026

πŸ’» Target environment

  • RHEL 9 or RHEL 10
  • SELinux in enforcing mode
  • root access to system
SELinux Workshop 03/2026

πŸ—“οΈ Agenda

  • 09:00-09:30 Introduction, SELinux overview
  • 09:30-10:30 DAC vs MAC, SELinux concepts
  • 10:30-10:45 Break
  • 10:45-12:00 SELinux policy, labels, and file contexts
  • 12:00-13:00 Lunch
  • 13:00-14:00 Logs and troubleshooting workflow
  • 14:00-14:15 Break
  • 14:15-14:30 Booleans and common service scenarios
  • 14:30-15:45 Web troubleshooting workshop
  • 15:45-16:00 Wrap-up, Q&A
SELinux Workshop 03/2026

🚩 Ground rules

  • Everyone works on their own VM
  • Ask questions anytime
  • If you get stuck, show your last command and the error
  • We keep SELinux enforcing throughout
SELinux Workshop 03/2026

🧰 Lab setup

  • You need root access
  • Use two terminals:
    • one for commands

    • one for logs:

      $ sudo journalctl -f
      

      or

      $ sudo tail -f /var/log/audit/audit.log
      
SELinux Workshop 03/2026

πŸ§ͺ Exercise

LAB 1: Access and baseline checks

SELinux Workshop 03/2026

πŸ§ͺ LAB 1: Access and baseline checks

  • SSH into your VM

  • Switch to root:

    $ sudo -i
    

    or

    $ su -
    
  • Tail the audit log:

    $ tail -f /var/log/audit/audit.log
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 1: Access and baseline checks

  • Check RHEL version:

    $ cat /etc/redhat-release
    Red Hat Enterprise Linux release 10.1 (Coughlan)
    
  • Verify repos:

    $ dnf repolist
    repo id                               repo name
    rhel-10-for-x86_64-appstream-rpms     Red Hat Enterprise Linux 10 for x86_64 - AppStream (RPMs)
    rhel-10-for-x86_64-baseos-rpms        Red Hat Enterprise Linux 10 for x86_64 - BaseOS (RPMs)
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 1: Access and baseline checks

  • Install Apache:

    $ dnf install httpd
    
  • Start and enable Apache:

    $ systemctl enable --now httpd
    
SELinux Workshop 03/2026

πŸ“‘ Chapter 1

Introduction and SELinux overview

SELinux Workshop 03/2026

πŸ›‘οΈ What is SELinux?

  • SELinux = Security-Enhanced Linux
  • Mandatory Access Control (MAC) on top of DAC
  • Enforces least privilege based on labels and policy
  • Default in RHEL and trusted OS base for security compliance
SELinux Workshop 03/2026

⏳ History of SELinux

  • Developed by the NSA as research into MAC for Linux
  • First released to the open source community in 2000 (GPL)
  • Merged into the mainline Linux kernel in the 2.6 series
  • In RHEL, SELinux is enabled and enforcing by default on install
SELinux Workshop 03/2026

πŸ“‹ Compliance drivers (NIS2, DORA, benchmarks)

  • Regulations like NIS2 and DORA push stronger security controls and auditability
  • Many hardening baselines (CIS, DISA STIGs, internal policies) recommend SELinux enforcing
  • Treat requirements as organization-specific: confirm with your policy or auditors
SELinux Workshop 03/2026

πŸ”’ Why SELinux is useful (1/6)

  • Enforces least privilege with labels and policy rules (deny by default)
  • Confines services so a compromise has limited reach
  • Complements DAC rather than replacing it
SELinux Workshop 03/2026

πŸ”’ Why SELinux is useful (2/6) Impact: Critical

SELinux Workshop 03/2026

πŸ”’ Why SELinux is useful (3/6) Impact: Important

  • Looney Tunables CVE-2023-4911 (https://access.redhat.com/security/cve/CVE-2023-4911)
  • Important issues can enable privilege escalation after an exploit
  • SELinux policies can confine the elevated process and protect critical system components
  • Reference: Red Hat SELinux hardening blog
SELinux Workshop 03/2026

πŸ”’ Why SELinux is useful (4/6) Impact: Moderate

  • Grafana issue CVE-2023-3128 (https://access.redhat.com/security/cve/CVE-2023-3128)
  • Moderate issues are often harder to exploit or require unlikely configurations
  • SELinux adds a defense layer that helps prevent escalation of impact
  • Reference: Red Hat SELinux hardening blog
SELinux Workshop 03/2026

πŸ”’ Why SELinux is useful (5/6) Impact: Low

  • vim crash CVE-2023-48232 (https://access.redhat.com/security/cve/CVE-2023-48232)
  • Low-severity issues usually have limited consequences
  • SELinux may not directly prevent the issue, but consistent enforcement helps contain theoretical attacks
  • Reference: Red Hat SELinux hardening blog
SELinux Workshop 03/2026

πŸ”’ Why SELinux is useful (6/6) Real-world example

  • Scenario: Webserver is affected by vulnerability allowing remote code execution (RCE)
  • Without SELinux: an attacker can execute arbitrary commands and potentially take control of the system
  • With SELinux:
    • web server is confined to its domain (e.g. httpd_t)
    • SELinux policy restricts access to sensitive files (e.g. backups)
    • Network access is limited to the server's expected behavior
  • Reference: Red Hat SELinux hardening blog
SELinux Workshop 03/2026

πŸ“‘ Chapter 2

DAC vs MAC and SELinux concepts

SELinux Workshop 03/2026

πŸ”‘ DAC recap (traditional Linux)

  • Owner/group/other permissions
  • Discretionary: user can change permissions
  • Example: chmod 777 opens everything
  • Root can do almost anything
$ ls -l /etc/passwd
-rw-r--r--. 1 root root 1524 Oct 28 07:13 /etc/passwd
SELinux Workshop 03/2026

βš–οΈ MAC vs DAC

  • DAC: access based on identity (uid/gid)
  • MAC: access based on labels and policy rules
  • SELinux can still deny access even if DAC allows
  • SELinux cannot allow access if DAC denies it
  • Goal: limit damage when a process is compromised
$ ls -lZ /etc/passwd
-rw-r--r--. 1 root root system_u:object_r:passwd_file_t:s0 1524 Oct 28 07:13 /etc/passwd
SELinux Workshop 03/2026

πŸ’‘ SELinux key concepts

  • Subjects (processes) and objects (files, sockets)
  • Security contexts: user:role:type:level
  • Type Enforcement (TE) is the core decision model
  • Policy defines allowed interactions between types
SELinux Workshop 03/2026

🏷️ Context examples

  • system_u:system_r:httpd_t:s0
  • unconfined_u:unconfined_r:unconfined_t:s0
  • system_u:object_r:httpd_sys_content_t:s0
SELinux Workshop 03/2026

βš™οΈ Modes

  • Enforcing: policy is applied
  • Permissive: only logs, no denial
  • Disabled: SELinux off (do not use)
SELinux Workshop 03/2026

πŸ“¦ Policy types in RHEL

  • targeted: default, confines selected services (recommended)
  • minimum: targeted + fewer services, limited policy set
  • mls: Multi-Level Security policy (specialized environments)
  • Support: Red Hat supports targeted; minimum/MLS are typically self-supported
SELinux Workshop 03/2026

🧰 Tools we will use

  • getenforce, setenforce
  • ls -Z, ps -eZ, id -Z
  • semanage fcontext, restorecon
  • ausearch, sealert, audit2allow
  • getsebool, setsebool
SELinux Workshop 03/2026

πŸ” getenforce and setenforce

  • getenforce: show current SELinux mode (Enforcing, Permissive, Disabled)
  • setenforce 0: switch to permissive mode (runtime only)
  • setenforce 1: switch back to enforcing mode (runtime only)
  • setenforce does not persist across reboot
SELinux Workshop 03/2026

πŸ”Ž -Z option: show SELinux contexts

  • Many commands support -Z to display SELinux context labels
  • Use it to inspect files, processes, and users during troubleshooting
  • This is often the first check after seeing an AVC denial
SELinux Workshop 03/2026

πŸ”Ž -Z examples (ls, ps, id)

  • Files:

    $ ls -Z /var/www
    
  • Processes:

    $ ps -eZ | grep httpd
    
  • Current user/session:

    $ id -Z
    
  • Compare expected vs actual labels before changing anything

SELinux Workshop 03/2026

πŸ§ͺ Exercise

LAB 2: Inspect contexts

SELinux Workshop 03/2026

πŸ§ͺ LAB 2: Inspect contexts

  • Check current mode:

    $ getenforce
    
  • Set mode to permissive (temporary):

    $ setenforce 0
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 2: Inspect contexts

  • Set mode to enforcing (permanent):

    $ vi /etc/sysconfig/selinux
    
    SELINUX=enforcing
    
    $ setenforce 1
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 2: Inspect contexts

  • List file contexts:

    $ ls -lZ /var/www
    total 8
    drwxr-xr-x. 2 root root system_u:object_r:httpd_sys_script_exec_t:s0 4096 Dec 12 14:18 cgi-bin
    drwxr-xr-x. 2 root root system_u:object_r:httpd_sys_content_t:s0     4096 Dec 12 14:18 html
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 2: Inspect contexts

  • Inspect process contexts:

    $ ps -efZ | grep http
    system_u:system_r:httpd_t:s0    root       11969       1  0 08:45 ?        00:00:00 /usr/sbin/httpd -DFOREGROUND
    
  • Check your user context:

    $ id -Z
    unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 2: Expected observations

  • httpd runs as httpd_t
  • web content is labeled httpd_sys_content_t
  • your shell is unconfined_t
SELinux Workshop 03/2026
SELinux Workshop 03/2026

πŸ“‘ Chapter 3

Policy, labels, and file contexts

SELinux Workshop 03/2026

πŸ“ File contexts and labels

  • Every file has a label
  • Label determines which domains can access it
  • Label mismatch causes denials even with correct DAC perms


Source: https://wiki.gentoo.org/wiki/SELinux/Type_enforcement

SELinux Workshop 03/2026

πŸ“¦ SELinux tooling packages

  • policycoreutils: core SELinux admin tools (restorecon, setenforce, getenforce, ...)

  • policycoreutils-python-utils: semanage, audit2allow, and other Python-based tools

  • policycoreutils-devel: development helpers (not required in this workshop)

  • Install (if missing):

    $ dnf install policycoreutils policycoreutils-python-utils
    
SELinux Workshop 03/2026

🏷️ Labels: chcon and matchpathcon

  • chcon: change a context immediately on a file/path (quick test, not persistent)
  • restorecon can overwrite chcon changes later
  • matchpathcon shows the expected default context from policy
SELinux Workshop 03/2026

πŸ“Œ Persistent labeling with semanage

  • semanage manages persistent SELinux settings (file contexts, ports, booleans, logins)

  • We will use semanage fcontext for custom web paths

  • List file-context rules: semanage fcontext -l

  • Filter custom paths:

    $ semanage fcontext -l | grep /srv/webroot
    
SELinux Workshop 03/2026

🎁 Bonus: Where to put files?

  • Use semanage fcontext -l to verify where to put certain files per default

  • Example: store certificates

    $ semanage fcontext -l | grep cert_t
    
    /etc/(letsencrypt|certbot)/(live|archive)(/.*)?    all files          system_u:object_r:cert_t:s0 
    /etc/cockpit/ws-certs\.d(/.*)?                     all files          system_u:object_r:cert_t:s0 
    /etc/docker/certs\.d(/.*)?                         all files          system_u:object_r:cert_t:s0 
    /etc/httpd/alias(/.*)?                             all files          system_u:object_r:cert_t:s0 
    /etc/httpd/alias/ipasession.key                    regular file       system_u:object_r:ipa_cert_t:s0 
    /etc/ipa/nssdb(/.*)?                               all files          system_u:object_r:cert_t:s0 
    /etc/openldap/certs(/.*)?                          all files          system_u:object_r:slapd_cert_t:s0 
    /etc/pki(/.*)?                                     all files          system_u:object_r:cert_t:s0 
    
SELinux Workshop 03/2026

πŸ” semanage fcontext + restorecon workflow

  • Define persistent mapping:

    $ semanage fcontext -a -t httpd_sys_content_t '/srv/webroot(/.*)?'
    
  • Apply labels from policy:

    $ restorecon -Rv /srv/webroot
    
  • Verify result:

    $ ls -Zd /srv/webroot /srv/webroot/index.html
    
  • Workflow: matchpathcon -> semanage fcontext -> restorecon -> verify

SELinux Workshop 03/2026

✏️ Changing labels the right way

  • Temporary: chcon
  • Persistent: semanage fcontext + restorecon
  • Always prefer persistent rules
SELinux Workshop 03/2026

πŸ§ͺ Exercise

LAB 3: Fix a mislabeled webroot

SELinux Workshop 03/2026

πŸ§ͺ LAB 3: Fix a mislabeled webroot

  • Create a new directory /srv/webroot
  • Add a test page
  • Configure httpd to use this path
  • Observe denial
  • Fix with proper context
SELinux Workshop 03/2026

πŸ§ͺ LAB 3: Details

  • Create a new directory

    $ mkdir -p /srv/webroot
    
  • Add a test page

    $ echo "SELinux Test" > /srv/webroot/index.html
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 3: Details

  • Configure httpd to use this path:

    $ cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.bak
    $ vi /etc/httpd/conf/httpd.conf
    
  • Edit DocumentRoot and <Directory> to /srv/webroot

    DocumentRoot "/srv/webroot"
    <Directory "/srv/webroot">
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 3: Details

  • Restart Apache:

    $ systemctl restart httpd
    
  • Browse:

    $ curl http://localhost/index.html
    
    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <html><head>
    <title>403 Forbidden</title>
    </head><body>
    <h1>Forbidden</h1>
    <p>You don't have permission to access this resource.</p>
    </body></html>
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 3: Fix context

  • Install SELinux policy coreutils:

    $ dnf install policycoreutils
    
  • Compare SELinux type of directories:

    $ matchpathcon /srv/webroot /var/www
    /srv/webroot	system_u:object_r:var_t:s0
    /var/www	system_u:object_r:httpd_sys_content_t:s0
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 3: Fix context

  • Set SELinux type to httpd_sys_content_t (temp)

    $ chcon -t httpd_sys_content_t -R /srv/webroot
    
  • Retry curl test:

    $ curl http://localhost/index.html
    SELinux Test
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 3: Fix context

  • Set SELinux type to httpd_sys_content_t (permanent)

    $ semanage fcontext -a -t httpd_sys_content_t '/srv/webroot(/.*)?'
    
  • Restore context to match permanent policy:

    $ restorecon -Rv /srv/webroot
    
  • Test SELinux type for new files:

    $ touch /srv/webroot/test
    $ ls -lZ /srv/webroot
    total 4
    -rw-r--r--. 1 root root unconfined_u:object_r:httpd_sys_content_t:s0 13 Feb 11 08:56 index.html
    -rw-r--r--. 1 root root unconfined_u:object_r:httpd_sys_content_t:s0  0 Feb 11 09:16 test
    
SELinux Workshop 03/2026
SELinux Workshop 03/2026

πŸ“‘ Chapter 4

Logs and troubleshooting workflow

SELinux Workshop 03/2026

πŸ“œ SELinux logging basics

  • Denials appear as AVC messages
  • Sources:
    • /var/log/audit/audit.log
    • journalctl -t setroubleshoot (if installed)
    • ausearch -m AVC,USER_AVC
SELinux Workshop 03/2026

πŸ” Interpreting an AVC message

$ grep AVC /var/log/audit/audit.log 
type=AVC msg=audit(1770796878.691:181): avc:  denied  { getattr } for  pid=12187
comm="httpd"
path="/srv/webroot/index.html"
dev="dm-0" ino=132258
scontext=system_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:var_t:s0
tclass=file permissive=0
  • What (class): file, dir, tcp_socket
  • Source: process type (e.g., httpd_t)
  • Target: object type (e.g., var_t)
  • Permission: read, write, name_connect, etc.
SELinux Workshop 03/2026

πŸ” Interpreting an AVC message

$ ausearch -m AVC,USER_AVC -ts recent
----
time->Wed Feb 25 09:08:39 2026
type=PROCTITLE msg=audit(1772006919.882:304): proctitle=2F7573722F7362696E2F6874747064002D44464F524547524F554E44
type=SYSCALL msg=audit(1772006919.882:304): arch=c000003e syscall=262 success=no exit=-13 
a0=ffffff9ca1=7f6be0004870 a2=7f6becbe39f0 a3=0 items=0 ppid=6248 pid=6252 auid=4294967295 uid=48 gid=48 euid=48
suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd"
subj=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1772006919.882:304): avc:  denied  { getattr } for  pid=6252 comm="httpd"
path="/srv/webroot/index.html" dev="dm-0" ino=33685761 scontext=system_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0
SELinux Workshop 03/2026

πŸ” Interpreting an AVC message

$ journalctl -t setroubleshoot
Feb 25 09:08:40 dev02.lan.rk-it.at setroubleshoot[6516]: failed to retrieve rpm info for path '/srv/webroot/index.html':
Feb 25 09:08:41 dev02.lan.rk-it.at setroubleshoot[6516]: SELinux is preventing /usr/sbin/httpd from getattr access on the file /srv/webroot/index.html.
For complete SELinux messages run: sealert -l d18b9a5f-9610-4412-ae55-b45a2904>
Feb 25 09:08:41 dev02.lan.rk-it.at setroubleshoot[6516]: SELinux is preventing /usr/sbin/httpd from getattr access on the file /srv/webroot/index.html.

                                                         *****  Plugin catchall_labels (83.8 confidence) suggests   *******************
                                                         
                                                         If you want to allow httpd to have getattr access on the index.html file
                                                         Then you need to change the label on /srv/webroot/index.html
...
SELinux Workshop 03/2026

πŸ”§ Troubleshooting tools: ausearch

  • ausearch queries audit records from auditd (including SELinux AVC denials)

  • Good for filtering by message type and time range

  • Common command:

    $ ausearch -m AVC,USER_AVC -ts recent
    
  • Use it when you want structured, SELinux-focused log search

SELinux Workshop 03/2026

πŸ”§ Troubleshooting tools: journalctl

  • journalctl reads the systemd journal (system and service logs)

  • Useful for SELinux-related messages from audit, setroubleshoot, and services

  • Examples:

    $ journalctl -t audit -n 50
    $ journalctl -t setroubleshoot
    
  • Use -f to follow logs live during labs

SELinux Workshop 03/2026

πŸ”§ Troubleshooting tools: setroubleshoot

  • setroubleshoot parses SELinux denials and provides human-readable explanations

  • It can suggest likely fixes (relabeling, booleans, package hints)

  • Treat suggestions as guidance, then verify with policy intent and labels

  • Commands/logs you may use:

    $ journalctl -t setroubleshoot
    $ sealert -a /var/log/audit/audit.log
    
SELinux Workshop 03/2026

πŸ§ͺ Exercise

LAB 4: Read AVC logs

SELinux Workshop 03/2026

πŸ§ͺ LAB 4: Read AVC logs

  • Trigger a denial (from LAB 2 before fix)
  • Find it:
    • $ grep AVC /var/log/audit/audit.log
      
    • $ ausearch -m AVC,USER_AVC -ts recent
      
    • $ journalctl -t audit -n 50
      
    • $ journalctl -t setroubleshoot
      
  • Identify source type, target type, and permission
SELinux Workshop 03/2026

🧭 Troubleshooting workflow

    1. Verify mode and context
    1. Check AVC logs
    1. Decide: correct label or boolean?
    1. Use policy change only as last resort
SELinux Workshop 03/2026

⚠️ audit2allow and why we are careful

  • audit2allow suggests policy changes based on logs
  • It does not know your intent
  • Use for diagnostics, not for blind fixes
  • We will not create custom policies in this workshop
SELinux Workshop 03/2026

⚠️ audit2allow and why we are careful

  • Try audit2allow for our issue:
$ grep AVC /var/log/audit/audit.log | audit2allow

#============= httpd_t ==============
allow httpd_t var_t:file getattr;
  • Note: Policy from audit2allow would allow httpd_t to get attributes of all files labeled with var_t, but we want Apache to only use getattr for httpd_sys_content_t files!
SELinux Workshop 03/2026

🏭 audit2allow -M (how custom modules are created)

  • This is how a local custom policy module is commonly generated from AVC logs

  • Example command:

    $ grep AVC /var/log/audit/audit.log | audit2allow -M myhttpd
    
  • -M myhttpd creates a module package named myhttpd

  • Workshop note: we explain the workflow, but custom policy writing is out of scope

SELinux Workshop 03/2026

πŸ“¦ What audit2allow -M myhttpd generates

  • myhttpd.te: Type Enforcement rules (human-readable policy source)

  • myhttpd.pp: compiled policy package (installable module)

  • Sometimes also myhttpd.fc if file-context entries are part of the generated policy

  • Review the .te file before installing anything

  • Note: Policy from audit2allow would allow httpd_t to get attributes of all files labeled with var_t, but we want Apache to only use getattr for httpd_sys_content_t files!

SELinux Workshop 03/2026

βš™οΈ Typical custom module workflow (demo/reference)

    1. Reproduce denial and collect AVCs
    1. Generate module:
    $ grep AVC audit.log | audit2allow -M myhttpd
    
    1. Inspect myhttpd.te for over-broad allows
    1. Install module:
    $ semodule -i myhttpd.pp
    
    1. Retest and keep the change documented
SELinux Workshop 03/2026

β›” Why we usually do NOT use audit2allow -M first

  • It builds rules from observed denials, not from your intended security design
  • It can create broad permissions that hide labeling mistakes
  • In our example, fixing the file label is the correct solution
  • Prefer: labels -> booleans -> supported config -> custom module (last resort)
SELinux Workshop 03/2026

πŸ” How to check what httpd_t is allowed to do

  • Best method on a running RHEL system: query the compiled policy (current active policy)

  • We usually do this with setools tools such as sesearch

    $ dnf install setools-console
    
  • Think in SELinux tuples: source type, target type, class, permission

  • Example question: "Can httpd_t read files labeled httpd_sys_content_t?"

SELinux Workshop 03/2026

πŸ”Ž Querying the current policy with sesearch

  • Show all allow rules for httpd_t:

    $ sesearch --allow -s httpd_t
    
  • Narrow to a specific target/class/permission:

    $ sesearch --allow -s httpd_t -t httpd_sys_content_t -c file -p read
    
  • Repeat with -p getattr, -p open, -p name_connect, etc.

SELinux Workshop 03/2026

πŸ“– Reading httpd_t policy: .te vs active policy

  • On RHEL, the active policy is compiled; you typically do not inspect a local httpd_t.te file directly
  • audit2allow generates local .te files for custom modules, but base policy is usually queried, not edited
  • If you need source-level policy review, use SELinux policy source packages / upstream sources (advanced topic)
  • For operations and support cases, sesearch + AVC logs are usually enough
SELinux Workshop 03/2026

βœ… Practical checks for httpd_t (examples)

  • Can Apache read normal web content?

    $ sesearch --allow -s httpd_t -t httpd_sys_content_t -c file -p read
    
  • Can Apache connect outbound (before/after boolean change)?

    $ sesearch --allow -s httpd_t -c tcp_socket -p name_connect
    
  • Combine with AVC logs to compare "denied" vs "policy allows"

SELinux Workshop 03/2026
SELinux Workshop 03/2026

πŸ“‘ Chapter 5

SELinux booleans

SELinux Workshop 03/2026

↔️ SELinux booleans

  • Tunables for common service behaviors
  • They exist so common policy choices can be enabled/disabled without writing custom policy modules
  • They provide a supported way to adjust behavior while staying within the shipped SELinux policy
  • Examples:
    • httpd_can_network_connect
    • httpd_enable_homedirs
  • Persistent change with -P
SELinux Workshop 03/2026

πŸ”§ Boolean tools: getsebool

  • getsebool displays SELinux boolean values (on / off)

  • Show one boolean:

    $ getsebool httpd_can_network_connect
    
  • Show all booleans:

    $ getsebool -a
    
SELinux Workshop 03/2026

πŸ”§ Boolean tools: setsebool

  • setsebool changes SELinux booleans

  • Runtime only (until reboot):

    $ setsebool httpd_can_network_connect on
    
  • Persistent:

    $ setsebool -P httpd_can_network_connect on
    
  • Verify after change with getsebool

SELinux Workshop 03/2026

πŸ§ͺ Exercise

LAB 5: Boolean discovery

SELinux Workshop 03/2026

πŸ§ͺ LAB 5: Boolean discovery

  • Identify a boolean that enables a needed behavior

    $ getsebool -a | grep httpd
    
SELinux Workshop 03/2026

πŸ“‘ Chapter 6

Web troubleshooting workshop

SELinux Workshop 03/2026

🌐 Practical scenario: Apache connects to PHP-FPM (TCP)

  • Symptom: Apache cannot connect to PHP-FPM listening on 127.0.0.1:9000
  • AVC may show name_connect denied for httpd_t on tcp_socket
  • Fix (TCP backend): enable httpd_can_network_connect
  • Note: Unix socket setups use different SELinux checks (labels on the socket/path)
SELinux Workshop 03/2026

πŸ§ͺ Exercise

LAB 6: Apache to PHP-FPM (TCP)

SELinux Workshop 03/2026

πŸ§ͺ LAB 6: Apache to PHP-FPM (TCP)

  • Install and start php-fpm
  • Configure PHP-FPM to listen on TCP (127.0.0.1:9000)
  • Configure Apache to pass *.php to PHP-FPM
  • Create index.php and observe SELinux denial
  • $ setsebool -P httpd_can_network_connect on
    
  • Verify success
SELinux Workshop 03/2026

πŸ§ͺ LAB 6: Details (PHP-FPM setup)

  • Install and start PHP-FPM:

    $ dnf install -y php php-fpm
    $ systemctl enable --now php-fpm
    
  • Edit /etc/php-fpm.d/www.conf and set:

    listen = 127.0.0.1:9000
    
  • Restart PHP-FPM:

    $ systemctl restart php-fpm
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 6: Details (Apache + test)

  • Create /srv/webroot/index.php:

    <?php phpinfo();
    
  • Adjust Apache PHP-FPM handler (/etc/httpd/conf.d/php.conf):

    <FilesMatch \.(php|phar)$>
        # SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
        SetHandler "proxy:fcgi://127.0.0.1:9000"
    </FilesMatch>
    
  • Restart Apache

SELinux Workshop 03/2026

πŸ§ͺ LAB 6: Details (Apache + test)

  • Test with curl:

    $ curl http://localhost/index.php
    ...
    <title>503 Service Unavailable</title>
    ...
    
  • Check logs and use audit2allow without creating a policy:

    $ grep AVC /var/log/audit/audit.log | grep 9000 | audit2allow
    
    #============= httpd_t ==============
    
    #!!!! This avc can be allowed using one of the these booleans:
    #     httpd_can_network_connect, httpd_graceful_shutdown, httpd_can_network_relay, nis_enabled
    allow httpd_t http_port_t:tcp_socket name_connect;
    
SELinux Workshop 03/2026

πŸ§ͺ LAB 6: Details (Apache + test)

  • Set boolean as audit2allow suggests:

    $ setsebool -P httpd_can_network_connect=on
    
  • Retest with curl:

    $ curl http://localhost/index.php
    ...
    <title>PHP 8.3.29 - phpinfo()</title><meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /></head>
    ...
    
SELinux Workshop 03/2026

πŸ“‚ Practical scenario: Custom webroot with uploads

  • Separate static vs writable content
  • Writable content must be labeled correctly
  • Example: httpd_sys_rw_content_t
SELinux Workshop 03/2026

πŸ§ͺ Exercise

LAB 7: Upload directory

SELinux Workshop 03/2026

πŸ§ͺ LAB 7: Upload directory

  • Create /srv/webroot/upload
  • Set type: httpd_sys_rw_content_t
  • Validate using a simple PHP upload
SELinux Workshop 03/2026

πŸ§ͺ LAB 7: Upload directory (PHP upload test)

  • Save as /srv/webroot/upload.php

  • Create /srv/webroot/upload/

  • Set owner to apache:

    chown apache: /srv/webroot/upload/
    
  • Upload a file via http://localhost/upload.php

SELinux Workshop 03/2026

πŸ§ͺ LAB 7: Upload directory (PHP upload test) - upload.php (1/2)

<?php
$uploadDir = __DIR__ . '/upload/';
if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0755, true);
}

$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    if ($_FILES['file']['error'] === UPLOAD_ERR_OK) {
        $name = basename($_FILES['file']['name']);
        $target = $uploadDir . $name;
        if (move_uploaded_file($_FILES['file']['tmp_name'], $target)) {
            $message = "Upload OK: " . htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
        } else {
            $message = 'Upload failed (move_uploaded_file).';
        }
SELinux Workshop 03/2026

πŸ§ͺ LAB 7: Upload directory (PHP upload test) - upload.php (2/2)

    } else {
        $message = 'Upload error code: ' . (int) $_FILES['file']['error'];
    }
}
?>
<!doctype html>
<html><body>
<h1>SELinux Upload Test</h1>
<?php if ($message): ?><p><?php echo $message; ?></p><?php endif; ?>
<form method="post" enctype="multipart/form-data">
  <input type="file" name="file" required>
  <button type="submit">Upload</button>
</form>
</body></html>
SELinux Workshop 03/2026

πŸ§ͺ LAB 7: Upload directory (PHP upload test)

  • Add temporary write access:

    $ chcon -t httpd_sys_rw_content_t /srv/webroot/upload
    
  • Retry

  • Use semanage fcontext in a production environment for persistent changes.

SELinux Workshop 03/2026

⚠️ Common mistakes

  • Using chcon without semanage
  • Relabeling entire filesystem without understanding
  • Turning SELinux off
  • Copying audit2allow output into production
SELinux Workshop 03/2026

πŸ“ Quick reference cheatsheet

  • Check mode: getenforce
  • Inspect labels: ls -Z, ps -eZ
  • Fix labels: semanage fcontext + restorecon
  • Logs: ausearch -m AVC -ts recent
  • Booleans: getsebool -a, setsebool -P
SELinux Workshop 03/2026

πŸ“‘ Chapter 7

Wrap-up and Q&A

SELinux Workshop 03/2026

βœ… Wrap-up

  • SELinux enforces MAC on top of DAC
  • Most issues are labeling or booleans
  • Logs are your truth source
  • Keep enforcing, use least privilege
SELinux Workshop 03/2026

πŸ’¬ Q&A

  • What scenarios do you want to try next?
  • Ideas for advanced follow-up workshops?
SELinux Workshop 03/2026

πŸ”— Link list

SELinux Workshop 03/2026

πŸ”€ Abbreviations (1/2)

  • AVC β€” Access Vector Cache (denial message)
  • CIS β€” Center for Internet Security
  • CVE β€” Common Vulnerabilities and Exposures
  • DAC β€” Discretionary Access Control
  • DISA β€” Defense Information Systems Agency
  • DORA β€” Digital Operational Resilience Act
  • GPL β€” GNU General Public License
  • MAC β€” Mandatory Access Control
  • MLS β€” Multi-Level Security
  • NSA β€” National Security Agency
SELinux Workshop 03/2026

πŸ”€ Abbreviations (2/2)

  • NIS2 β€” Network and Information Security Directive 2 (EU)
  • PHP β€” Hypertext Preprocessor
  • PHP-FPM β€” PHP FastCGI Process Manager
  • RCE β€” Remote Code Execution
  • RHEL β€” Red Hat Enterprise Linux
  • SELinux β€” Security-Enhanced Linux
  • SSH β€” Secure Shell
  • TCP β€” Transmission Control Protocol
  • TE β€” Type Enforcement
  • VM β€” Virtual Machine
SELinux Workshop 03/2026

πŸ“š Terminology (1/3)

  • AVC/audit log: denial records stored by auditd
  • Boolean: runtime policy toggle for common behaviors
  • Domain: type assigned to processes (e.g., httpd_t)
  • Enforcing/Permissive: SELinux modes with/without blocking
  • File context: SELinux label on a file or directory object
  • File-context mapping (fcontext): persistent path-to-type rule managed by semanage fcontext
  • Label: context on files, sockets, and other objects
  • Policy: rules that allow or deny type interactions
  • Relabel/restorecon: apply default contexts from policy mappings
SELinux Workshop 03/2026

πŸ“š Terminology (2/3)

  • Security context: label in the form user:role:type:level
  • Type: TE label used for access decisions
  • Subject / Object: process requesting access vs target resource (file, dir, socket, port)
  • SELinux class (tclass): object class in the decision (e.g., file, dir, tcp_socket)
  • Permission: requested operation (e.g., read, getattr, write, name_connect)
  • Policy type: SELinux policy flavor (targeted, minimum, mls)
SELinux Workshop 03/2026

πŸ“š Terminology (3/3)

  • Policy module: installable policy component (e.g., generated .pp package)
  • .te file: Type Enforcement policy source (human-readable rules)
  • Compiled policy: active policy loaded on the system and queried with tools like sesearch
SELinux Workshop 03/2026

_notes: Welcome the group, confirm schedule and labs, explain this is hands-on.

_notes: Very short intro; emphasize practical focus and RHEL background.

_notes: Very short intro; emphasize practical focus and RHEL background.

_notes: Set expectations and remind that we will not write custom policy modules.

_notes: Confirm versions and packages; ask participants to verify SELinux enforcing.

_notes: Walk through timing, highlight breaks and lab-heavy afternoon.

_notes: Encourage questions and sharing errors; enforcing mode stays on.

_notes: Ask everyone to open a log terminal now.

_notes: Introduce LAB 1: Access and baseline checks (10 min) and expected outcome.

_notes: Make sure everyone can log in, become root, and confirm RHEL version and repos.

_notes: Start with the motivation and high-level view of SELinux.

_notes: High-level definition and why it matters for containment.

_notes: Keep this short: origin, open source release, kernel integration, and default RHEL posture.

_notes: Keep this non-legal; emphasize that compliance frameworks typically favor MAC/SELinux.

_notes: Frame SELinux as containment and damage reduction, not a silver bullet.

_notes: A critical vulnerability, such as CVE-2021-44228 (Log4Shell), often allows remote unauthenticated attackers to execute arbitrary code. SELinux can mitigate the impact by confining vulnerable services within restrictive policies, limiting the attack’s reach. For instance, if the exploited process is running under a confined SELinux domain, it may not have the privileges to access sensitive files or execute further payloads.

_notes: For important CVEs, such as CVE-2023-4911 (Looney Tunables), SELinux plays a vital role in restricting the scope of privilege escalation. Even if an attacker successfully exploits a buffer overflow to gain elevated privileges, SELinux policies can confine their activities, protecting critical system components.

_notes: Moderate CVEs, like CVE-2023-3128 (Grafana issue), often rely on unlikely configurations or are difficult to exploit. SELinux policies act as an additional layer of defense, preventing such vulnerabilities from escalating into significant threats.

_notes: Low-severity issues, such as CVE-2023-48232 (vim crash), usually result in minimal consequences. While SELinux may not directly prevent such issues, its consistent policy enforcement helps contain even theoretical attacks.

_notes: Contrast DAC and MAC, then move into labels and types.

_notes: Quick reminder; show why DAC alone is insufficient.

_notes: Emphasize MAC can deny even when DAC allows.

_notes: Define subject/object/types; stress TE as core model.

_notes: Point out the type field is most important for decisions.

_notes: Explain when permissive is acceptable and why disabled is not.

_notes: Keep this brief and practical; advise targeted for most environments.

_notes: Preview the toolchain; mention we will practice each one.

_notes: Clarify runtime vs persistent changes. Persistent mode is configured in /etc/selinux/config (or /etc/sysconfig/selinux).

_notes: Participants should treat -Z output as a core troubleshooting signal.

_notes: Show one quick live example for each command before starting the lab.

_notes: Introduce LAB 2: Inspect contexts (15 min) and expected outcome.

_notes: Give 10–12 minutes, then regroup for observations.

_notes: Verify everyone sees httpd_t and httpd_sys_content_t.

_notes: Time-box the break and announce restart time.

_notes: Transition into labeling and persistent contexts.

_notes: Reinforce labels drive access, not just permissions.

_notes: Explain the package split on RHEL. We use core plus python-utils; devel stays out of scope.

_notes: Make the distinction: semanage defines policy mappings, it does not directly relabel existing files.

_notes: This is the main operational pattern participants should remember for real systems.

_notes: Explain why chcon is temporary; semanage is persistent.

_notes: Introduce LAB 3: Fix a mislabeled webroot (30 min) and expected outcome.

_notes: Let them break it first; troubleshooting starts here.

_notes: Offer as guidance, but let them edit config themselves.

_notes: Show semanage + restorecon pattern.

_notes: Confirm return time and remind to keep terminals open.

_notes: Set the troubleshooting mindset before showing logs.

_notes: Show where AVCs appear; mention auditd and setroubleshoot.

_notes: Walk line by line: source, target, permission.

_notes: Highlight that ausearch reads audit logs and is usually the fastest way to find AVC denials.

_notes: Explain that journalctl complements ausearch and is often easier for broad troubleshooting context.

_notes: Set expectations: helpful for learning and triage, but do not apply fixes blindly.

_notes: Introduce LAB 4: Read AVC logs (20 min) and expected outcome.

_notes: Have them identify source/target/perms from one AVC.

_notes: Stress label or boolean first; policy last.

_notes: Position as learning tool, not production fix.

_notes: Participants asked about this often. Show the mechanics, but reinforce that labels/booleans come first.

_notes: The key teaching point is review the generated policy source (.te), not only the binary package.

_notes: Emphasize this is an escalation path after labels, booleans, and supported settings are exhausted.

_notes: Tie this back to the httpd/var_t example shown on the previous slide.

_notes: This is the bridge from AVC troubleshooting to policy inspection. Emphasize querying the active policy, not guessing.

_notes: Start broad, then narrow. This mirrors how we investigate AVC denials.

_notes: Answer the common misconception directly: "Where is httpd_t.te?" Usually not on the system in a convenient editable form.

_notes: Use this to connect policy inspection to later boolean and PHP network-connect labs.

_notes: Time-box the break and announce restart time.

_notes: Explain booleans as safe, supported toggles.

_notes: Start with discovery: participants should learn to inspect before changing anything.

_notes: Reinforce runtime vs persistent behavior again. Prefer explicit verification after changes.

_notes: Introduce LAB 5: Boolean discovery (20 min) and expected outcome.

_notes: Let them search and discuss which boolean fits.

_notes: Move into real-world web app fixes.

_notes: Use this as a realistic web stack example. The boolean is relevant for TCP-to-PHP-FPM, not all PHP-FPM setups.

_notes: Introduce LAB 6: Apache to PHP-FPM (TCP) (20 min) and expected outcome.

_notes: Use a TCP listener intentionally so participants see the boolean effect clearly.

_notes: We force TCP here for teaching purposes. Mention that default RHEL config often uses a Unix socket.

_notes: If mod_proxy_fcgi is not loaded, note the package/module requirement. Focus is SELinux denial and boolean fix.

_notes: Explain rw label split between static and writable.

_notes: Introduce LAB 7: Upload directory (25 min) and expected outcome.

_notes: Warn about over-labeling; keep scope tight.

_notes: Simple demo only. The teaching goal is the SELinux label on uploads/, not secure upload handling.

_notes: Share real-world pitfalls and quick fixes.

_notes: Tell them to keep this for after class.

_notes: Summarize and invite questions.

_notes: Reinforce key takeaways: labels, booleans, logs.

_notes: Invite follow-up topics and advanced ideas.

_notes: Point participants to the notebook and upstream docs for follow-up reading.

_notes: These match abbreviations used throughout the labs.

_notes: Keep definitions brief; expand only if time permits.

_notes: These terms help participants connect AVC output, sesearch results and policy troubleshooting.