SELinux & RHEL Hardening

SELinux : La Forteresse Invisible
Security-Enhanced Linux (SELinux) est un système de contrôle d'accès obligatoire (MAC) développé par la NSA. Sur RHEL/CentOS/Rocky, il est activé par défaut et protège le système même si un processus est compromis.
flowchart LR
A[Processus<br/>Apache] -->|1. Requête<br/>Accès| B{SELinux<br/>Policy}
B -->|2. Vérification<br/>Contexte| C[Fichier<br/>/var/www/html/index.html]
B -->|✅ Allow| D[Accès Autorisé]
B -->|❌ Deny| E[AVC Denial<br/>Log dans audit.log]
style A fill:#2196F3,stroke:#005a9e,color:#fff
style B fill:#FF9800800800,stroke:#a52a00,color:#fff
style C fill:#4CAF50,stroke:#0b5a0d,color:#fff
style E fill:#f44336,stroke:#b00016,color:#fff
Modes SELinux
Vérifier l'État Actuel
# Afficher le mode courant
getenforce
# Résultat : Enforcing | Permissive | Disabled
# Statut détaillé
sestatus
# Mode, Policy (targeted), Contexts
# Vérifier le contexte d'un fichier
ls -Z /var/www/html/
# system_u:object_r:httpd_sys_content_t:s0 index.html
Changer de Mode (Runtime)
# Passer en mode Permissive (pour débugger)
setenforce 0 # Ou : setenforce Permissive
# Repasser en mode Enforcing
setenforce 1 # Ou : setenforce Enforcing
Mode Permissive ≠ Désactivé
En Permissive, SELinux log les violations mais ne les bloque pas. Utile pour diagnostiquer, mais ne jamais laisser en production.
Configuration Persistante
# Éditer la config persistante
vim /etc/selinux/config
# Options :
SELINUX=enforcing # Mode strict (production)
SELINUX=permissive # Log seulement (debug)
SELINUX=disabled # ⚠️ Désactivé (reboot requis)
SELINUXTYPE=targeted # Policy par défaut RHEL
Réactivation après Disabled
Si SELinux était disabled, le passer à enforcing nécessite un relabel complet (long, peut échouer au boot). Procédure sécurisée :
Dépannage : Survival Kit
1. Lire les Logs AVC (Access Vector Cache)
# Chercher les denials récents
ausearch -m AVC -ts recent
# -ts recent = dernière 10min
# -ts today = aujourd'hui
# Avec des détails lisibles
ausearch -m AVC -ts recent | audit2why
# Exemple de sortie :
# type=AVC msg=audit(1234567890.123:456): avc: denied { write } for \
# pid=1234 comm="httpd" path="/var/www/html/upload.php" \
# scontext=system_u:system_r:httpd_t:s0 \
# tcontext=system_u:object_r:admin_home_t:s0 tclass=file permissive=0
2. Générer une Règle de Policy (Audit2allow)
flowchart LR
A[🚫 Denial log dans audit.log] --> B{ausearch -m AVC<br/>audit2allow -w}
B --> C[💡 Explication & Suggestion<br/>(Booléen ou Type)]
C --> D{setsebool -P<br/>ou semanage fcontext}
C --> E{audit2allow -M<br/>my_fix}
E --> F[📦 my_fix.te + my_fix.pp]
F --> G{semodule -i my_fix.pp}
G --> H[✅ Accès Autorisé]
Workflow de débogage des denials :
- Reproduire le problème (lancer l'application/service qui échoue).
- Lister les denials :
ausearch -m AVC -ts recent. - Obtenir l'explication et la suggestion :
ausearch -m AVC -ts recent | audit2allow -w.- Si un booléen est suggéré (ex:
httpd_can_network_connect), c'est la solution la plus propre. - Si c'est un contexte de fichier,
semanage fcontextest la solution. - Si rien, ou si c'est trop complexe,
audit2allow -Mest la solution "rapide et sale" (à éviter en production si possible).
- Si un booléen est suggéré (ex:
- Implémenter la solution.
# Analyser les denials et proposer des solutions
ausearch -m AVC -ts recent | audit2allow -w
# -w = why (explication)
# Exemple de sortie :
# type=AVC msg=audit(1234567890.123:456): avc: denied { write } for \
# pid=1234 comm="httpd" path="/var/www/html/upload.php" \
# scontext=system_u:system_r:httpd_t:s0 \
# tcontext=system_u:object_r:admin_home_t:s0 tclass=file permissive=0
# Possible suggestion : "If you want to allow httpd to write to user home directories, use the boolean httpd_enable_homedirs"
# Générer un module de policy (solution générique si pas de booléen ou contexte)
ausearch -m AVC -ts recent | audit2allow -M my_httpd_fix
# Crée : my_httpd_fix.te (source) et my_httpd_fix.pp (compilé)
# Installer le module
semodule -i my_httpd_fix.pp
# Lister les modules chargés
semodule -l | grep my_httpd
audit2allow : Dernier Recours ou Debug Rapide
audit2allow génère des règles permissives qui peuvent réduire la sécurité. Préférez toujours corriger le contexte ou activer un booléen avant de créer un module custom. Utilisez-le pour le débogage et ensuite cherchez une solution plus propre.
3. Gestion des Contextes SELinux
# Afficher le contexte d'un fichier
ls -Z /var/www/html/index.html
# system_u:object_r:httpd_sys_content_t:s0 index.html
# Afficher le contexte d'un processus
ps auxZ | grep httpd
# system_u:system_r:httpd_t:s0 apache 1234 ...
# Changer TEMPORAIREMENT le contexte (jusqu'au relabel)
chcon -t httpd_sys_content_t /var/www/html/newfile.html
# Restaurer le contexte par défaut (selon la policy)
restorecon -v /var/www/html/newfile.html
# Restaurer récursivement
restorecon -Rv /var/www/
# Définir un contexte PERSISTANT (survit au relabel)
semanage fcontext -a -t httpd_sys_content_t "/srv/website(/.*)?"
restorecon -Rv /srv/website/
4. Gestion des Ports
# Lister les ports autorisés pour un service
semanage port -l | grep http
# http_port_t: tcp 80, 443, 488, 8008, 8009, 8443
# Autoriser un port custom
semanage port -a -t http_port_t -p tcp 8080
# Supprimer une autorisation
semanage port -d -t http_port_t -p tcp 8080
Auditd : Surveillance SecNumCloud
Auditd est le démon de logging de sécurité de Linux (requis par les normes PCI-DSS, SecNumCloud).
Installation & Activation
# Installer auditd (déjà présent sur RHEL minimal)
dnf install audit -y
# Activer au boot
systemctl enable --now auditd
# ⚠️ Auditd ne se gère PAS avec systemctl stop !
# Utiliser :
service auditd stop
service auditd start
Configuration de Base pour SecNumCloud
# Éditer les règles d'audit
vim /etc/audit/rules.d/audit.rules
# === Surveillance des fichiers critiques ===
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config_changes
# === Surveillance des commandes root ===
-a always,exit -F arch=b64 -F uid=0 -S execve -k root_commands
# === Surveillance des modifications de users/groups ===
-w /usr/sbin/useradd -p x -k user_modification
-w /usr/sbin/userdel -p x -k user_modification
-w /usr/sbin/usermod -p x -k user_modification
-w /usr/sbin/groupadd -p x -k group_modification
# === Surveillance des tentatives de changement d'heure ===
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time_change
-a always,exit -F arch=b64 -S clock_settime -k time_change
# === Surveillance des modifications de SELinux ===
-w /etc/selinux/ -p wa -k selinux_changes
# Charger les règles
augenrules --load
# Vérifier les règles actives
auditctl -l
Recherche dans les Logs
# Chercher les modifications de /etc/passwd
ausearch -k passwd_changes
# Chercher les commandes root d'un utilisateur spécifique
ausearch -k root_commands -ui 0 -ts today
# Chercher les échecs d'authentification
ausearch -m USER_LOGIN -sv no -ts today
# Rapport formaté
aureport --summary
aureport --failed --summary
Aide-Mémoire : Booléens SELinux Courants
Les booléens sont des interrupteurs pour activer/désactiver des fonctionnalités sans recompiler la policy.
# Lister TOUS les booléens
getsebool -a
# Chercher un booléen spécifique
getsebool -a | grep httpd
# Activer un booléen (runtime)
setsebool httpd_can_network_connect on
# Activer de façon PERSISTANTE
setsebool -P httpd_can_network_connect on
Tableau des Booléens Essentiels
| Booléen | Description | Use Case |
|---|---|---|
httpd_can_network_connect |
Apache peut se connecter à des sockets réseau | Reverse proxy vers backend |
httpd_can_network_connect_db |
Apache peut se connecter à MySQL/PostgreSQL | App web avec BDD externe |
httpd_enable_homedirs |
Apache peut servir ~/public_html |
Hébergement users |
httpd_execmem |
Apache peut utiliser mémoire exécutable | Modules comme mod_security |
httpd_unified |
Apache peut lire/écrire dans tous ses répertoires | Simplification (⚠️ moins sécurisé) |
ssh_sysadm_login |
SSH peut se connecter en tant que sysadm_r | Accès root SSH |
allow_ftpd_full_access |
FTP peut accéder à tout le filesystem | ⚠️ Dangereux |
named_write_master_zones |
Bind peut écrire dans zones DNS | DNSSEC, dynamic updates |
Exemple Concret : Nginx Reverse Proxy
# Problème : Nginx ne peut pas se connecter au backend
curl http://localhost
# 502 Bad Gateway
# Vérifier les logs SELinux
ausearch -m AVC -ts recent | grep nginx
# avc: denied { name_connect } for comm="nginx" dest=8080 \
# scontext=system_u:system_r:httpd_t:s0 tclass=tcp_socket
# Solution : Activer le booléen
setsebool -P httpd_can_network_connect on
# Vérifier
curl http://localhost
# 200 OK
Hardening RHEL : Checklist Production
# 1. SELinux en mode Enforcing
getenforce # Doit retourner : Enforcing
# 2. Auditd activé et configuré
systemctl is-active auditd # active
auditctl -l | wc -l # > 10 règles
# 3. Désactiver les services inutiles
systemctl list-unit-files --state=enabled | grep -v '@'
# 4. Firewalld actif (voir guide rhel-networking.md)
systemctl is-active firewalld
# 5. Mises à jour automatiques de sécurité (RHEL 8+)
dnf install dnf-automatic -y
vim /etc/dnf/automatic.conf
# apply_updates = yes # Pour auto-install
# download_updates = yes # Pour télécharger seulement
systemctl enable --now dnf-automatic.timer
# 6. Désactiver les comptes système
passwd -l bin # Verrouiller les comptes bin, daemon, etc.
# 7. Vérifier les permissions critiques
stat -c "%a %n" /etc/passwd # Doit être 644
stat -c "%a %n" /etc/shadow # Doit être 000 ou 400
# 8. Fail2Ban ou firewalld rich rules contre brute-force SSH
dnf install fail2ban -y
# Ou firewalld rich rules (voir rhel-networking.md)