Sommaire
- Les fichiers de configuration
- Activation par socket
- Confiner les services avec systemd
- Instances de services systemd
- Considérations de sécurité
- Références
Dans une installation Linux-nginx-PHP classique, on a:
- systemd qui doit orchestrer les services et s'exécute en root (inévitable)
- nginx qui reçoit les les requêtes web et les répartit, notamment vers php-fpm. Il fonctionne avec un processus maître qui fonctionne en root pour se mettre en écoute sur le port 443 et des workers, non privilégiés, qui traitent les requêtes
- php-fpm qui tourne sous root, reçoit les requêtes vers des scripts PHP de la part de nginx et les répartit vers des workers moins privilégiés (qui, sous Debian, s'exécutent sous l'utilisateur
www-data
).
Ça fait beaucoup trop de choses qui tournent avec les droits root. Systemd, c'est inévitable. Mais:
- php-fpm il n'y a aucune raison
- nginx a simplement besoin de pouvoir écouter sur le port 443 (port privilégié). On peut confier cette tâche à systemd et ainsi réduire les droits de nginx.
On peut donc configurer tout ça de façon :
- à exécuter php-fpm et nginx sans les droits root grâce à l'activation par socket
- à isoler les différentes applications PHP qui s'exécutent grâce aux template units de systemd
- à bloquer certaines escalades de privilège vers
root
(Baron Samedit, PwnKit, PHP-FPM local root vulnerability) et empêcher l'exploitation de failles de type SSRF
Les fichiers de configuration
Je reprends ici la configuration correspondant à Debian bullseye (stable en février 2022). Il faudra sans doute adapter les chemins/exécutables ou versions à votre système.
PHP
-
/etc/systemd/system/php-fpm@.service
:[Unit] Description=The PHP 7.4 FastCGI Process Manager for %i Documentation=man:php-fpm7.4(8) After=network.target php-fpm@%i.socket # On a besoin de php-fpm@%i.socket pour ouvrir les ports et les envoyer à PHP-FPM Requires=php-fpm@%i.socket [Service] Type=notify Environment="FPM_SOCKETS=/run/php/%i.socket=3" ExecStart=/usr/sbin/php-fpm7.4 --nodaemonize --fpm-config /etc/php/7.4/fpm/%i.conf ExecReload=/bin/kill -USR2 $MAINPID StateDirectory=%i RuntimeDirectory=%i LogsDirectory=%i SupplementaryGroups=%i # Options de durcissement DynamicUser=true PrivateUsers=true ProtectSystem=strict PrivateTmp=true PrivateNetwork=true NoNewPrivileges=true RestrictAddressFamilies=AF_UNIX IPAddressDeny=any SystemCallFilter=@system-service SystemCallFilter=~@resources @privileged CapabilityBoundingSet= MemoryDenyWriteExecute=true UMask=0077 ProtectHome=true PrivateDevices=true ProtectControlGroups=true ProtectKernelModules=true ProtectKernelTunables=true ProtectKernelLogs=true SystemCallArchitectures=native RestrictNamespaces=true LockPersonality=true RestrictRealtime=true RemoveIPC=true ProtectHostname=true ProtectClock=true ProtectProc=invisible ProcSubset=pid RestrictSUIDSGID=true
-
/etc/systemd/system/php-fpm@.socket
:[Unit] PartOf=php-fpm@.service Documentation=https://freedesktop.org/wiki/Software/systemd/DaemonSocketActivation/#php-fpm [Socket] ListenStream=/run/php/%i.socket SocketMode=0660 SocketUser=php SocketGroup=www-data [Install] WantedBy=sockets.target
-
/etc/php/7.4/fpm/mon-application.conf
(pour chaque application PHP que vous souhaitez isoler) :[global] pid = /run/mon-application/pid error_log = /var/log/mon-application/error.log [www] listen = /run/php/mon-application.socket access.log = /var/log/mon-application/access.log php_admin_value[session.save_path] = /var/lib/mon-application php_admin_value[pcre.jit] = 0 ; Ci-dessous sont les options proposées par Debian par défaut pour un worker php ; Je vous renvoie à la documentation pour plus d'info : https://www.php.net/manual/fr/install.fpm.configuration.php pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3
Nginx
-
/etc/systemd/system/nginx.service
:[Unit] Description=A high performance web server and a reverse proxy server Documentation=man:nginx(8) After=network.target nss-lookup.target nginx.socket # On a besoin de nginx.socket pour ouvrir les ports et les envoyer à nginx Requires=nginx.socket [Service] PIDFile=/run/nginx/pid ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;' ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx/pid TimeoutStopSec=5 Environment=NGINX=3:4:5:6: NonBlocking=true # dans la ligne suivante : pensez à ajouter toute application pour laquelle vous auriez créé un groupe SupplementaryGroups=ssl-cert mon-application # Options de durcissement User=www-data PrivateUsers=true LogsDirectory=nginx ProtectSystem=strict RuntimeDirectory=nginx ReadOnlyPaths=/etc/certificats/actif/ PrivateTmp=true NoNewPrivileges=true RestrictAddressFamilies=AF_UNIX IPAddressDeny=any PrivateNetwork=true SystemCallFilter=@system-service SystemCallFilter=~@resources @privileged CapabilityBoundingSet= MemoryDenyWriteExecute=true UMask=0077 ProtectHome=true PrivateDevices=true ProtectControlGroups=true ProtectKernelModules=true ProtectKernelTunables=true ProtectKernelLogs=true SystemCallArchitectures=native RestrictNamespaces=true LockPersonality=true RestrictRealtime=true RemoveIPC=true ProtectHostname=true ProtectClock=true ProtectProc=invisible ProcSubset=pid RestrictSUIDSGID=true [Install] WantedBy=multi-user.target
-
/etc/systemd/system/nginx.socket
:[Unit] PartOf=nginx.service Documentation=https://freedesktop.org/wiki/Software/systemd/DaemonSocketActivation/ [Socket] ListenStream=80 ListenStream=0.0.0.0:80 ListenStream=443 ListenStream=0.0.0.0:443 BindIPv6Only=ipv6-only
-
Dans
/etc/nginx/nginx.conf
, il faut :- retirer la directive
user
- modifier la directive
pid
pour indiquer un fichier sous/run/nginx
- adapter les directives
fastcgi_pass
pour pointer sur les adresses du typeunix:/run/php/mon-application.socket
Exemple d'un fichier de configuration minimal :
pid /run/nginx/pid; events { } http { include mime.types; # Serveur HTTP (sans chiffrement) server { listen [::]:80 default_server; listen 80 default_server; server_name _; return 404; } # Afficher les statistiques nginx pour les requêtes depuis localhost et ciblant 127.0.1.1 : server { listen 127.0.1.1:80; server_name _; stub_status; } # Serveur HTTP redirigeant vers HTTPS server { listen [::]:80; listen 80; server_name mon-application.example.com; # On redirige vers la version https location / { return 302 https://$host$request_uri; } } # Serveur HTTPS (avec chiffrement TLS) server { listen [::]:443 ssl default_server; listen 443 ssl default_server; server_name mon-application.example.com; ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; ssl_protocols TLSv1.3; index index.php index.html; root /var/www/mon-application/public/; location ~ \.php$ { include fastcgi.conf; fastcgi_pass unix:/run/php/mon-application.socket; } } }
- retirer la directive
Installation
Les commandes suivantes sont à lancer avec root
ou via sudo
systemctl disable --now php7.4-fpm.service # Désactivation des anciens processus si nécessaire
systemctl daemon-reload # Prise en compte des nouveaux fichiers systemd
# pour chaque application PHP installée :
groupadd --system mon-application
chown -R root:mon-application /var/www/mon-application
find /var/www/mon-application -type f -exec chmod 640 {} + -o -type d -exec echo chmod 750 {} +
systemctl enable --now php-fpm@mon-application.socket
# Pour chaque clé/certificat TLS utilisé par nginx :
chgrp ssl-cert /etc/xxx/yyyy/mes-certificats.key
chmod 640 /etc/xxx/yyyy/mes-certificats.key
systemctl restart nginx
Activation par socket
Systemd peut se placer en écoute sur des ports réseau, des sockets ou des files FIFO puis transférer les flux entrants aux programmes correspondants, pour peu que ceux-ci soient configurés pour : ils reçoivent alors ces sockets sous forme de file descriptors
numérotés à partir de 3 (après STDIN, STDOUT et STDERR). Il est possible de ne démarrer le service voulu que lorsqu'un premier message arrive sur la socket en question, c'est même la motivation initiale de cet outil.
Nginx peut être configuré pour récupérer ces sockets grâce à la variable d'environnement (non documentée) NGINX
. La seule façon dont j'ai réussi à faire fonctionner nginx de cette façon a été d'indiquer la valeur 3:4:5:6:
(info glanée sur la page DaemonSocketActivation for nginx and php-fpm).
PHP-FPM peut aussi être configuré pour récupérer ces sockets grâce à une variable d'environnement non documentée : FPM_SOCKETS
. Elle est de la forme : /run/php/app.socket=3
ou /run/php/pool1.socket=3,/run/php/pool2.socket=4,/run/php/pool3.socket=5
si on a plusieurs pool
de workers PHP orchestrés par FPM (mais s'il s'agit de séparer plusieurs applications, il vaut mieux utiliser des services systemd différents, cf. plus bas).
Confiner les services avec systemd
Directives de confinement
Systemd met à disposition de nombreuses options pour confiner les services :
-
User, Group : le service est lancé avec les privilèges de l'utilisateur et/ou du groupe indiqué. Cela évite de lancer avec les droits de l'utilisateur
root
tout-puissant (ce qui est fait par défaut pour nginx et php-fpm). Il faut alors s'assurer que l'utilisateur indiqué a bien accès aux ressources nécessaires (fichiers PHP pour php-fpm, fichiers statiques servis par nginx, etc.) - DynamicUser : le service est lancé sous un utilisateur créé pour ça, et détruit lorsque le service s'éteint. Il est possible de donner des droit sur des répertoires particuliers via l'option SupplementaryGroups
- PrivateNetwork, PrivateUsers, PrivateIPC, ProtectHostname, PrivateMounts : place le service dans des namespaces isolés du reste du système, reprenant ainsi les outils d'isolation utilisés par les techologies de conteneurisation (Docker, LXC). Si on souhaite ajouter une cage "chroot" pour se rapprocher davantage d'un conteneur type docker, on peut utiliser les directives RootDirectory ou RootImage
-
PrivateTmp, ProtectHome, ProtectSystem : isole
/tmp
des autres applications, rend/home
et/root
inaccessibles et place l'ensemble du système en lecture seule. On peut relâcher ces contraintes en indiquant des répertoires qui doivent rester accessibles en lecture ou en écriture (ReadOnlyPaths,ReadWritePaths) ou plus simplement en indiquant quels répertoires sous/run
,/var/lib
,/var/cache
ou/var/log
sont utilisés (RuntimeDirectory, StateDirectory, CacheDirectory et LogsDirectory) - SystemCallFilter, CapabilityBoundingSet : limitent les appels système et les capabilities atteignables par le processus ou ses enfants.
- IPAddressAllow, IPAddressDeny : filtrer les flux réseaux autorisés en entrée et en sortie. Les flux établis via les sockets associés au service ne sont pas concernés par ces restrictions.
… et tout un tas d'autres qu'il est fastidieux de lister ici. Je vous renvoie à Mastering systemd: Securing and sandboxing applications and services qui présente les options introduites dans RHEL 7 et 8. De nouvelles directives sont ajoutées au fil des versions de systemd.
Évaluer l'efficacité de ces mesures
La commande systemd-analyze security nginx.service
permet de lister les directives appliquées ou non et de calculer un score entre 0 (service fortement durci, dont la compromission aura un impact minimal sur le système en terme de sécurité) et 10 (service non durci, dont la compromission peut entraîner la compromission de tout le système). Sur une Debian stable, passer du fichier nginx.service
proposé par défaut à celui poposé ci-dessus fait descendre le score de 9,6 à 0,2.
Pour évaluer plus concrètement le confinement mis en place, on peut déployer un web shell PHP. Quelques résultats :
-
ls /home
: Error in Code Execution --> ls: cannot open directory '/home': Permission denied -
ls -alh /run
: on constate que tous les fichiers sont détenus soit parroot
, soit parnobody
(on ne "voit pas" les autres utilisateurs). Cependant,getent passwd
permet de retrouver la liste des comptes installés. -
ls /tmp
: vide -
ls /proc -alh
: on ne voit que les processus exécutés par php-mon-application. -
nslookup linuxfr.org
: Error in Code Execution --> ;; connection timed out; no servers could be reached -
sudo /bin/true
: Error in Code Execution --> sudo: effective uid is not 0, is sudo installed setuid root?
Relâcher les mesures de confinement
Certaines de ces mesures peuvent être trop drastiques pour les applications hébergées. Le blocage du réseau, par exemple, empêche nginx de récupérer ses réponses OCSP pour agrafage, ou peut l'empêcher de faire reverse proxy vers des applications sur d'autres machines ou sur la même machine mais accessibles uniquement via TCP/IP (directives proxy_pass http://127.0.0.1:xxxx/;
). Si votre base de données est hébergée sur le même serveur, l'application PHP peut sans doute s'y connecter par socket Unix, mais si ça n'est pas le cas elle aura besoin d'accéder au réseau. Elle peut aussi avoir besoin d'écrire dans tel ou tel répertoire. Enfin, il est possible que telle ou telle bibliothèque ait besoin d'appels systèmes indûment bloqués par le confinement trop strict imposé (c'est ainsi qu'on a dû désactiver le JIT pcre dans la configuration car incompatible avec la directive MemoryDenyWriteExecute).
Pour cela, vous pouvez modifier directement les fichiers .service
ci-dessus ou créer le dossier /etc/systemd/system/php-fpm@mon-application.service.d/
(par exemple) et y ajouter un fichier relachement.conf
:
[Service]
# Pour rendre le dossier 'upload' accessible en écriture, il faut ̀ chmod 660` le dossier et ajouter la ligne :
ReadWritePaths=/var/www/mon-application/upload
# Pour autoriser les flux réseau :
PrivateNetwork=false
RestrictAddressFamilies=
# Si on veut ouvrir à des requêtes sur localhost uniquement :
RestrictAddressFamilies=AF_UNIX AF_INET
IPAddressAllow=localhost
# Si on veut ouvrir à des requêtes sur tout internet :
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
IPAddressAllow=any
Instances de services systemd
Les fichiers PHP de systemd ci-dessus contiennent un @
et on a indiqué %i
à plusieurs reprises : cela permet de créer plusieurs instances de services similaires.
Ainsi, si vous avez 3 applications différentes, vous pouvez créer autant de fichiers de configuration sous /etc/php/7.4/fpm/
et activer ces services indépendamment avec systemctl start|stop|reload|status php-fpm@applicationX.service
. Chaque service proposera sa propre socket sous /run/php
, il faudra configurer nginx en conséquence.
En restreignant les droits d'accès à /var/www/applicationX/
comme proposé ci-dessus (section installation) vous empêchez un attaquant qui compromettrait une application d'accéder aux informations liées à une autre des applications.
Considérations de sécurité
Sauf faille de sécurité supplémentaire, une fois ces mesures mises en place la compromission de nginx ou php-fpm ne pourra plus aboutir aux conséquences suivantes :
- installation d'applications arbitraires / rootkit
- accès aux clés SSH du serveur
- escalade de privilège vers
root
- exfiltration de données ou exécution de code via Server-Side Request Forgery
- accès direct aux données stockées sur le serveur
Néanmoins :
- l'attaquant qui compromet une application PHP déployée sur le serveur pourra toujours :
- voler les accès et les données d'un autre utilisateur
- accéder à la base de données sans filtre ou à toute donnée à laquelle l'application donne accès (il ne pourra pas accéder au code ou aux données d'une autre application hébergée sur la même machine)
- l'attaquant qui compromet nginx pourra inévitablement accéder aux clés TLS et potentiellement déchiffrer le traffic des autres usagers de l'application
Pour sécuriser davantage, il faut configurer un Linux Security Module comme apparmor ou SELinux.
Références
- Socket activation part 1 et Socket activation part 2
- DaemonSocketActivation for nginx and php-fpm
- Mastering systemd: Securing and sandboxing applications and services
- man systemd.socket
- man systemd.exec
- man systemd.resource-control
- man systemd.unit
- documentation: configuration FasctCGI Process Manager
- nginx ticket#237: Add optional systemd socket activation support
- traitement de l'option FPM_SOCKETS dans le code source de PHP
- What is SSRF ?
- PHP-FPM local root vulnerability
- PwnKit: Local Privilege Escalation Vulnerability Discovered in polkit’s pkexec (CVE-2021-4034)
- CVE-2021-3156: Heap-Based Buffer Overflow in Sudo (Baron Samedit)
# Confiner les applications avec systemd
Posté par pepp . Évalué à 6.
Merci pour le journal.
Comme je n'utilise pas nginx et php, j'ai cherché à utiliser ces possibilités pour des applications classiques (= pas des services).
C'est possible en utilisant
systemd-run
.Par exemple, pour lancer un shell qui n'aura pas d'accès au réseau, et pour lequel le répertoire Home sera en lecture seule:
Ou encore pour lancer gedit sans accès réseau ni accès au Home:
(
PrivateUsers=true
semble être nécessaire pour pouvoir utiliserPrivateNetwork=true
sans droits particuliers)En utilisant un alias, ça donne un moyen simple de lancer des applis avec des droits limités.
[^] # Re: Confiner les applications avec systemd
Posté par Samuel (site web personnel) . Évalué à 2.
Pour des applications graphiques, on peut installer des applications depuis flathub avec flatpak (via "Logiciels" sous Gnome) et restreindre les droits avec flatseal.
Mais on se rapproche des conteneurs, avec leur principal défaut : ils embarquent leurs dépendances, donc si une faille touche une librairie partagée, type openssl, il faut attendre que l'empaqueteur de chaque application qui l'embarque mette à jour sa librairie (ou, s'agissant de flatpak, la version de la plate-forme sur laquelle il repose).
[^] # Re: Confiner les applications avec systemd
Posté par Misc (site web personnel) . Évalué à 2.
C'est pas spécialement propre à des conteneurs, tu as le même souci avec n'importe quoi, c'est la difficulté de comprendre ce qui est supporté et/ou maintenu qui pose souci.
On voit pas le probléme pour les distributions car si le logiciel est dispo, alors il est maintenu (à minima). Mais par exemple, sous Ubuntu, tu n'as pas les mêmes garanties entre le dépôt main et universe.
Techniquement, il n'y a rien qui empêcherais d'avoir un dépôt de conteneur supporté par un des projets de distributions, et certains essayent (exemple, Fedora).
Mais ce que veulent les gens, c'est pas d'avoir des paquets plus facile à utiliser (c'est relativement facile) ou à faire (c'est surtout dur à cause des guidelines), mais bien de sortir des restrictions du à la distribution unifié et synchronisé de logiciels.
Sauf que du coup, c'est la foire. Mais c'est la foire parce que c'est ce qu'on cherche.
# Commentaire supprimé
Posté par Anonyme . Évalué à 8.
Ce commentaire a été supprimé par l’équipe de modération.
[^] # Re: Et les pools php-fpm ?
Posté par Samuel (site web personnel) . Évalué à 6.
Je cherche à minimiser le nombre d'applications qui tournent sous root : le moins j'en ai, le mieux je me porte.
En l'occurrence, ce qui a initialement motivé l'écriture de cet article, c'est la faille PHP-FPM local root vulnerability publiée en octobre 2021 : les workers FPM peuvent contaminer le processus maître et, si le maître est root, gagner les pleins pouvoirs sur la machine. Déléguer à FPM le rôle de créer les workers en tant qu'utilisateurs distincts nécessite de donner à FPM les droits root : je passe donc par systemd pour remplir cette fonction (systemd ayant déjà ces droits par nature) et je retire les droits root à PHP-FPM.
[^] # Commentaire supprimé
Posté par Anonyme . Évalué à 3.
Ce commentaire a été supprimé par l’équipe de modération.
# CAP_NET_BIND_SERVICE
Posté par Joris Dedieu (site web personnel) . Évalué à 6.
Avec la
capabilitécapacité CAP_NET_BIND_SERVICE nginx n'a pas besoin d'être root pout binder le 443 et le 80. Elle peut être ajoutée directement dans l'unit systemd.[^] # Re: CAP_NET_BIND_SERVICE
Posté par claudex . Évalué à 6.
Mais ça peut lui permettre d'écouter sur un autre port (surtout s'il est malveillant). Donc je préfère l'autre méthode.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: CAP_NET_BIND_SERVICE
Posté par Joris Dedieu (site web personnel) . Évalué à 2.
L'avantage du privatenetwork.
[^] # Re: CAP_NET_BIND_SERVICE
Posté par millman . Évalué à 3.
Cependant, cela ne va pas fonctionner avec PrivateNetwork=true.
[^] # Re: CAP_NET_BIND_SERVICE
Posté par Joris Dedieu (site web personnel) . Évalué à 4.
Oui PrivateNetwork=true c'est vraiment intéressant. Mais comment se comporte un wordpress de base lorsqu'il est isolé du réseau ?
Par ailleurs les socket systemd ont un overhead non négligeable. Pour ma part, j'ai mesuré ~ 15% d’écart
[^] # Re: CAP_NET_BIND_SERVICE
Posté par GG (site web personnel) . Évalué à 2.
Comment vous feriez pour gérer plusieurs configurations?
Actuellement, tout ce qui arrive sur le 80 (j'ai un reverse proxy en amont) est renvoyé vers un port en particulier selon le domaine et le sous-domaine. Je trouve que c'est pratique.
Donc, j'ai un nginx qui écoute les ports 80 à 100.
Si je dois limiter l'écoute des ports 80 (et 443 le cas échéant) pour nginx, je vis mal comment faire autrement et avoir plusieurs configurations.
Pourquoi bloquer la publicité et les traqueurs : https://greboca.com/Pourquoi-bloquer-la-publicite-et-les-traqueurs.html
[^] # Re: CAP_NET_BIND_SERVICE
Posté par Moonz . Évalué à 3.
Utilise des sockets unix pour la communication entre le reverse proxy et les instances nginx.
[^] # Re: CAP_NET_BIND_SERVICE
Posté par Samuel (site web personnel) . Évalué à 1.
Nginx peut servir plusieurs domaines sur le même port et la même adresse IP.
Par exemple, la configuration suivante est valide et fait bien ce qu'on attend :
nginx va se baser sur l'hôte demandé dans la requête HTTP pour sélectionner le bon bloc
server
.Référence : Server names (doc nginx).
[^] # Re: CAP_NET_BIND_SERVICE
Posté par GG (site web personnel) . Évalué à 2.
Oui, c'est ce que je fais, pour le port 80 qui me sert de triage, puis je sépare ensuite les configuration avec un port différent.
Parfois la configuration peut être assez longue, ou bien je veux avoir plusieurs versions. Tout dans un seul fichier, ce ne serait pas supportable.
Pourquoi bloquer la publicité et les traqueurs : https://greboca.com/Pourquoi-bloquer-la-publicite-et-les-traqueurs.html
[^] # Re: CAP_NET_BIND_SERVICE
Posté par Samuel (site web personnel) . Évalué à 4. Dernière modification le 04 février 2022 à 12:02.
La configuration nginx par défaut, pour Debian, résout ce problème avec :
Avec l'idée que chaque bloc serveur dispose d'un fichier correspondant dans
/etc/nginx/sites-available/
et activé par un lien depuis/etc/nginx/sites-enabled/
.[^] # Re: CAP_NET_BIND_SERVICE
Posté par GG (site web personnel) . Évalué à 2. Dernière modification le 04 février 2022 à 13:51.
Et dire que je me sers de ces include pour tout ce qui est générique… ça m'était sorti de la tête…
Du coup, je vais même pouvoir faire un test sans trop changer mon infra, et réduire aussi le nombre de ports en écoute par Nginx au fur et à mesure… et je ne serai plus obligé de garder une liste des ports utilisés et utilisables…
Merci pour le rappel!
Pourquoi bloquer la publicité et les traqueurs : https://greboca.com/Pourquoi-bloquer-la-publicite-et-les-traqueurs.html
# uWSGI
Posté par Glandos . Évalué à 4.
Très intéressant, merci.
Je m'étais attelé à la même tâche avec uWSGI, que je trouvais justement plus facile d'intégrer à l'activation par socket de systemd. Malheureusement… le plugin PHP (voir uWSGI en entier) est plutôt à l'abandon et le support de PHP 8.1 (qui vient d'arriver dans Debian/testing) est encore bien cassé. Le correctif a été intégré, mais il n'y a toujours pas de version publiée.
Donc je vais me pencher pour l'utilisation de PHP-FPM, en enlevant les pools que j'utilisais jusque là.
J'irais même jusqu'à créer des utilisateurs différents par applications (www-application1, etc) et à le mettre comme paramètre de l'instance
php-fpm@.socket
:Mais y a un truc que je ne comprends pas… Dans
php-fpm@.service
il y aSupplementaryGroups=%i
ce qui veut dire que le groupemon-application
doit exister. Par contre,php-fpm@.socket
crée un socket 0660 appartenant àphp:www-data
. Il manque pas un truc ?Ah, et pour
ReadWritePaths
avecDynamicUser
, ça fait quoi ? Les fichiers sont écrits avec un UID/GID qui peut être réutilisé :J'ai l'impression qu'un utilisateur statiquement défini est préférable si l'application a besoin d'écrire son fichier de configuration (au hasard, Nextcloud).
Pour rebondir, d'après la documentation, si
DynamicUsers=true
, plein d'options sont mises, donc le fichier est plein de redondances. Mais, c'est vrai, des fois, il vaut mieux être explicite…[^] # Re: uWSGI
Posté par Glandos . Évalué à 3.
Je me réponds à :
En effet, pour la documentation de
StateDirectory
et consort, ça dit :C'est compliqué. Ça veut dire aussi que l'application doit se trouver dans
/var/lib/mon-application
. J'avoue que j'aime bien/var/www/application.example.com/mon-application
…[^] # Re: uWSGI
Posté par Glandos . Évalué à 5.
Allez, je partage mon retour, parce que je pense avoir amélioré le truc. Attention, seulement la partie FPM, je suis resté classique pour nginx, il tourne toujours en root.
Ce que je n'aime pas, c'est devoir écrire partout
mon-application
. Pour installer une nouvelle application, il faut copier-coller un fichier, et faire du rechercher-remplacer partout. Moins j'en fais, mieux je me porte. Et surtout, la maintenance est plus facile en cas d'édition d'un fichier.Il se trouve que PHP (et donc FPM) supporte les variables dans le fichier de configuration. Donc on peut faire son fichier qui ressemble à :
/etc/php/7.4/fpm/base.conf
Oui, je préfère
ondemand
, mais ce n'est pas le sujet. Le sujet, c'est${APP_NAME}
. Comment on lui passe ? Hé bien, on demande à systemd :/etc/systemd/system/php-fpm@.service
Et avec ça, il n'y a parfois aucune configuration supplémentaire. Par exemple, pour Roundcube packagé par Debian, j'ai un simple lien symbolique de
roundcube.conf
versbase.conf
. Le service systemd est édité, pour autoriser les connexions à mon serveur de courriel.D'ailleurs, je me suis permis de renforcer la sécurité en utilisant
security.limit_extensions = /index.php
ce qui empêche PHP d'accepter autre chose que/index.php
. La valeur par défaut est.php
, acceptant tous les fichiers PHP. Comme la plupart des applications n'ont plus qu'un seul point d'entrée, c'est toujours bon à prendre. Même si ça ne vous empêche pas de faire une configuration correcte de votre serveur Web en amont.Enfin, clairement,
DynamicUser=true
, c'est compliqué. Par exemple, Roundcube de Debian peuple/var/lib/roundcube
avec des liens symboliques un peu partout, et il faut que tout ça soit accessible à cet utilisateur dynamique. C'est quand même compliqué.Donc j'ai mis en commentaire ça, et pour que les sessions marchent, j'ai remis
ReadWritePaths=/var/lib/php/sessions
. Oui, a priori ça va mettre les sessions dans le même répertoire, mais les droits semblent corrects…# Non documenté
Posté par barmic 🦦 . Évalué à 8.
Il sait faire attention avec les fonctionnalités non documentées. Ça peut venir du fait que ça n'est pas sensé être utilisé (c'est encore expérimental ou ça sert pour l'intégration continue des développeurs). Ça risque d'être bugué ou d'évoluer d'une version à l'autre (même sur une version micro).
Ça vaut le coup de proposer de le documenter pour lancer la discussion chez les dev et voter si c'est effectivement quelque chose d'exposé ou non.
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
# Super intéressant !
Posté par Yves (site web personnel) . Évalué à 1.
Merci pour ces détails techniques dont je vais pouvoir m’inspirer :-)
J’avais déjà commencé à m’attaquer à la sécurité des processus serveurs (web, mais aussi mail, etc.). Pour le web par exemple, j’ai un HAProxy qui fait toute l’écoute réseau, et ensuite des sockets Unix permettent les communications HAProxy–Nginx et Nginx–php-fpm. Ça, plus une conf nftables très restrictive, font que Nginx n’a pas accès au réseau.
Mais, ayant appris systemd sur le tas, je sais que je peux améliorer ce premier jet, et ton article va m’y aider :-)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.