httpd

Image Docker HTTPD

Image Apache HTTPD 2.4 de départ pour les conteneurs HTTP (et Reverse-proxy FCGI).

Cette image répond aux contraintes de production et intègre des paramétrages préconisés suite à audit de sécurité, et permet notamment de lancer le conteneur sous une identité arbitraire. Tout ou presque est paramétrable par passage de variable d'environnement.

Elle est paramétrable, notamment concernant le choix et les réglages du MPM, le tuning, les modules à charger... et permet d'ajouter des directives arbitraires.

Les variables servant au paramétrage (https://12factor.net/fr/config) peuvent être définies dans vos Dockerfile, ou au lancement du service/conteneur (compose.yml par exemple).

Utilisation comme base

Exemple basique :

FROM ..../docker/httpd:${TAG}
COPY . /var/www/html
ENV  HTTPD_LOAD_MODULES buffer cache cache_disk
...

TAGS

Les tags disponibles sont :

  • 2.4-debian : basée sur l'image Docker Officielle "httpd:2.4-bullseye". Cette version est celle à privilégier.
  • 2.4-alpine : basée sur alpine:3.16, et construite à partir des packages. Attention, cette version est là pour sa pettie taille, et est amenée à être remplacée par une base "httpd:2.4-alpine". Les chemins changeront donc pour se rapprocher de ceux utilisés dans la base Debian.

Restrictions

Entrtypoint et Command

Ne pas remplacer ni surcharger ces deux éléments :

  • ENTRYPOINT : s'il faut lui ajouter des traitements, placer des scripts dans le répertoire /ep.d/ du conteneur. L'entrypoint les prend en charge.

  • CMD : généré et complété par l'entrypoint par analyse des variables d'environnements fournies, il compose la liste des "DEFINE" (au sens -D de httpd) et active ou pas les différents paramétrages prévus (destination des logs, /httpd-status par exemple).

Configuration d'Apache HTTPD

Ne pas traffiquer, chipoter, modifier le fichier httpd.conf (ne pas y toucher, en résumé). Même pour ajouter des modules. La variables d'environnement HTTPD_LOAD_MODULES permet simplement de citer les modules à activer, le but étant de n'activer que ceux nécessaires, tout comme HTTPD_MPM qui offre le choix du MPM.

Configuration du VirtualHost

Ne pas traffiquer, chipoter, modifier la déclaration du VirtualHost. S'il y manque des choses, des variables d'environnement préfixées par HTTPD_GLOBAL__ permettent de passer de la configuration arbitraire.

Pour l'ajout d'éléments de configuration non pris en charge par les variables HTTPD_GLOBAL__, il suffit de préparer un drop-in config-file...

$ cat zz-maconfig.conf
<Directory "/data">
    Require all granted
</Directory>
Alias "/data" "/data"

Et de l'intégrer dans le répertoire ${CONF_PATH}/conf.d/

$ cat Dockerfile
FROM registry.actilis.net/docker-images/httpd:2.4-alpine

ENV HTTPD_LOAD_MODULES alias

COPY zz-maconfig.conf ${CONF_PATH}/conf.d/zz-maconfig.conf

RUN set -eux && mkdir -p /data && echo "Done !" > /data/index.html

Ce fichier sera pris en compte dans le contexte "server-config" d'Apache.

En aucun cas il ne faut modifier le VirtualHost de base, car celui-ci dérive d'un template.

Variables d'Environement

LOGS & Debug

Par défaut, le conteneur n'affiche pas les infos de configuration au démarrage.

DEBUG (false) : affichage des informations de démarrage si true.

Le conteneur produit les logs de HTTPD dans stdout (access_log) et stderr (error_log). C'est ce qui est souhaitable sur tous les environnements pour une remontée des logs sur un sytème comme Graylog.

Ce comportement peut cependant être modifié :

  • HTTPD_DISABLE_STDOUT_ACCESSLOG (false) : désactive l'envoi de l'AccesLog vers la sortie standard si true

D'anciennes version ont permis à une époque de stocker les logs dans un répertoire /httpdlogs. C'est contraire aux bonnes pratiques et le code concernant cette approche a été supprimé.

Identité / Permisisons

Le conteneur peut être lancé avec une identité arbitraire.

Par défaut, si le conteneur est lancé en tant que root, les processus Apache auront l'identité www-data:www-data (33:33).

La directive user: uuu:ggg de docker compose ou la directive --user de docker container run permettent de choisir une identité arbitraire (qui n'est pas nécessairement existante dans le conteneur, ni sur l'hôte) telle qu'on l'entend dans la clause user: "1971:1976" d'un fichier compose.yaml. Tous les processus du conteneur auront alors cette idéndité, qui doit donc poséder les droits de lecture/écriture nécessaires au bon fonctionnement de l'application.

Si la variable FIX_WEBCONTENT_OWNER est positionnée à true, le propriétaire et le groupe des fichiers présents dans /var/www/html sera positionné à www-data:www-data (ou à l'identité choise au lancement du conteneur) pour assurer le droit de lecture/écriture par le serveur HTTP. Cette variable n'est pas définie par défaut.

Système

  • LDAP_TLS_REQCERT : valeur affectée à TLS_REQCERT dans /etc/opendldap/ldap.conf (par défaut : never)

  • LANG : valeur par défaut = fr_FR.UTF-8

  • TIMZEONE : valeur par défaut = Europe/Paris

HTTPD

Port d'écoute

Le port d'écoute est paramétrable par la variable HTTPD_LISTEN_PORT : 80 par défaut. Choisir un port non privilégié est possible mais n'est pas une obligation, même pour démarrer en mode root-less (Docker >= 20.10).

MPM

  • HTTPD_MPM : choix du MPM d'Apache (prefork, worker, event). Il n'y a pas de bonne raison de changer de MPM.

Modules chargés

Par défaut, dans le but de réduire la surface d'attaque, seuls les modules suivants sont chargés :

  • le MPM choisi (mpm_event par défaut)

  • les modules statiques suivants :

    • core_module (static)
    • http_module (static)
    • so_module (static)
  • les modules DSO suivants :

    • allowmethods_module (shared)
    • authz_core_module (shared)
    • authz_host_module (shared)
    • deflate_module (shared)
    • dir_module (shared)
    • env_module (shared)
    • filter_module (shared)
    • headers_module (shared)
    • log_config_module (shared)
    • mime_module (shared)
    • negotiation_module (shared)
    • rewrite_module (shared)
    • setenvif_module (shared)
    • unixd_module (shared)
    • version_module (shared)

La variable HTTPD_LOAD_MODULES permet d'énumérer les modules supplémentaires à charger. Exemple : HTTPD_LOAD_MODULES=un deux trois_et_demi pour charger mod_un, mod_deux, et mod_trois_et_demi.

Si une fonctionnalité dépendante d'un module est demandée par une des variables prévues (Status, Proxy FastCGI, CGI, Rewrite, ...), alors le module nécessaire est automatiquement chargé. Il n'est donc pas nécessaire de demander à charger les modules suivants :

  • proxy_fcgi : chargés si HTTPD_ENABLE_PHPFPM_INET est à true,

  • cgi ou mod_cgid : chargé (en fonction du MPM) si HTTPD_ENABLE_CGI est à true,

  • status : chargé si HTTPD_ENABLE_STATUS est à true (ce qui est le cas par défaut).

  • proxy: chargé si HTTPD_ENABLE_PHPFPM_INET ou HTTPD_SIMPLE_HTTP_PROXYPASS est à true ou si HTTPD_LOAD_MODULES demande un des modules proxy_chose qui en dépend.

Les modules headers et rewrite font partie de ceux chargés systématiquement.

Configuration Globale

Un fichier "httpd-global.conf" est instancié et pris en compte si nécessaire avec les directives fournies comme suit :

  • HTTPD_GLOBAL__DirectIve="v1 v2" : déclare Directive v1 v2 dans la configuration globale.

Cela autorise toute variable mentionnée avec un préfixe "HTTPD_GLOBAL__", et peut donc être utilisé pour tout ce qui doit être déclaré dans le contexte "ServerConfig", donc hors "VirtualHost".

Exemple :

  environment:
    - HTTPD_GLOBAL__ServerTokens=prod
    - HTTPD_MPM=event
    - HTTPD_GLOBAL__MaxRequestWorkers=4000
    - HTTPD_GLOBAL__MinSpareThreads=100
    - HTTPD_GLOBAL__MaxSpareThreads=400
    - HTTPD_GLOBAL__ServerLimit=20
    - HTTPD_GLOBAL__ThreadsPerChild=50
    ...
    - HTTPD_GLOBAL__FallbackResource=/index.php
    ...

L'ordre final dans la config suivra le nom alphanum des variables

Comme le serveur n'embarque qu'un seul virtualHost, toutes les directives qu'on souhaiterait dans le contexte "VirtualHost" mais qui sont aussi acceptables dans le contexte "Server-Config" peuvent, et doivent donc (si possible, voir exception rewrite ci-dessous) être passées de cette manière.

Paramétrage du VirtualHost

Il est raisonnable de ne pas casser, modifier, bidouiller, chipoter le VirtualHost proposé, celui-ci étant sensible aux variables de configuration.

Solutions pour son paramétrage :

  • Bon : Utiliser des variables HTTPD_GLOBAL__truc

  • Bon : L'utilisation d'un drop-in file placé dans le dossier ${CONF_PATH}/conf.d/ et nommé quelque-chose.conf est permise s'il est nécessaire d'agir sur la configuration implémentée dans le VirtualHost.

  • Bon : L'utilisation du .htaccess est permise s'il est nécessaire d'agir sur la config implémentée dans le VirtualHost.

  • Négociable : S'il manque des choses, votre projet n'est surement pas le seul, mais juste le premier qui peut avoir besoin de ces choses là. Elles peuvent être ajoutées dans l'image de départ, il suffit de demander.

  • Mauvais : Remplacer le virtualhost déclaré (dans /etc/httpd/conf/d/vhost.conf sur la base CentOS et dans /etc/apache2/conf.d/vhost.conf sur la base Alpine) par votre propre surcharge, en réutilisant les variables que celui par défaut utilise. Cette approche n'est pas conseillée car une évolution de la structure de ce fichier dans l'image pourrait "casser vos choses".

Contexte des directives : Dans l'exemple précédent, on pourrait choisir de retirer la variable HTTPD_GLOBAL__FallbackResource, et placer la directive dans un fichier ".htaccess" dans le seul répertoire que cette directive concerne. Mais en supposant qu'elle concerne tout le site, c'est bien au niveau global que sa place est justifiée.

Cas de rewrite : Même si on peut activer le moteur par HTTPD_GLOBAL__RewriteEngine=on, il est évidemment impossible de placer plusieurs directives du type RewriteRule ou RewriteCond par des variables HTTPD_GLOBAL__truc. Même pour une seule règle simple, cela fonctionne, on devrait plutôt prévilégier un .htaccess , de toute façon nécessaire dès qu'il y a plusieurs règles, .

Status : mod_status & observabilité

  • PROMETHEUS_EXPORTER_ENABLED : Ajout de l'exporter prometheus (true)

  • PROMETHEUS_EXPORTER_LISTEN_PORT : port d'écoute de l'exporter (9117)

  • PROMETHEUS_EXPORTER_URI : URI des données exportées (/metrics)

  • HTTPD_SHOW_SERVER_LOAD : (indépendant du mod_status) Active la publication de la charge du serveur dans les en-têtes de la réponse et dans les logs.

Les informations sont : l=, b=, i= (voir mod_headers)

  • l : Indication de la charge moyenne
  • b : pourcentage de workers busy
  • i : pourcentage de workers idle

Ces informations se trouvent dans les en-têtes de la réponse retournée au client, dans le Header "Load", que le reverse-proxy pourrait/devrait retirer : Exemple : Load: l=2.97/1.95/1.45, b=1, i=99

  • HTTPD_ENABLE_STATUS : activation (si true) du module mod_status (alors chargé automatiquement)

  • HTTPD_STATUS_URI : l'URI (par défaut : /httpd-status)

  • HTTPD_STATUS_ALLOWED_IP : liste des adresses IP depuis lesquelles on peut accéder au status

    Par défaut : seule 127.0.0.1 est autorisée (status inaccessible par ailleurs)

    Pour autoriser toute adresse : HTTPD_STATUS_ALLOWED_IP=any

    Pour autoriser plusieurs réseaux : HTTPD_STATUS_ALLOWED_IP="192.168.122 172.16. 10.0.0.0/8"

  • HTTPD_STATUS_ALLOWED_X_FW_F_IP : l'adresse IP distante autorisée à accéder au status depuis l'extérieur de la stack.

Méthodes HTTP

La méthode TRACE est par défaut désactivée (TraceEnable Off) grâce à la variable HTTPD_GLOBAL__TraceEnable.

HTTPD_ALLOWED_METHODS : liste des méthodes séparées par un espace. Valeur par défaut : HEAD GET POST

Utiliser la valeur ALL pour désactiver le contrôle. Dans ce cas le module allowmethods n'est pas chargé.

Pour une API REST, il est probable que cette variable doive être positionnée à HEAD GET POST PUT DELETE (quoting: attention à ne pas former qu'un seul argument, mais bien un argument par méthode).

WebDAV

  • HTTPD_ENABLE_WEBDAV : Activer WebDAV ou non (false)

Si cette option est à true, alors les modules dav, dav_fs et dav_lock sont chargés.

CGI

  • HTTPD_ENABLE_CGI : Activer CGI ou non (false)
  • HTTPD_CGI_SUFFIXES : Suffixes des fichiers pour lesquels le Handler CGI est actif (cgi sh)

Mode SIMPLE_HTTP_PROXYPASS

Ce mode est prévu pour les usages où le container/service httpd ne sert que de passe-plat à un serveur backend.

Dans ce mode les variables PROXYPASS_URI et PROXYPASS_TARGET sont utilisées par une directive ProxyPassMatch :

  • HTTPD_SIMPLE_HTTP_PROXYPASS : Activer ou pas ce mode ( false ).

  • PROXYPASS_URI : correspond à l'URI entrante ("chemin" / "regex" dans la doc de ProxyPass & ProxyPassMatch). Valeur par défaut si le mode SIMPLE_HTTP_PROXYPASS est activé (/).

  • PROXYPASS_TARGET : Valeur par défaut si le mode SIMPLE_HTTP_PROXYPASS est activé (http://www.ademe.fr:80/).

  • PROXY_PRESERVE_HOST : Valeur par défaut à Off. Cette variable aurait pu être évitée et remplacée par HTTPD_GLOBAL__ProxyPreserveHost=On

Interface avec PHP-FPM

En mode tcp :

  • HTTPD_ENABLE_PHPFPM_INET : Activer ou pas le proxy FastCGI ( false )

  • PHPFPM_HOST : nom d'hote ou de conteneur à contacter (phpfpm)

  • PHPFPM_PORT : port d'écoute php-fpm ( 9000 )

Pour conteneuriser une application PHP, on a donc un composant "httpd" basé sur cette image et N composants "phpfpm". Le service à déclarer dans la stack (ou dans docker compose) est pointé par les variables PHPFPM_HOST:PHPFPM_PORT, et on peut ainsi scaler de manière indépendante httpd et les réplicas de phpfpm (Load-Balancer implicite de Docker). Attention à vos sessions dans ce cas.

Paramétrage applicatif

Le DocumentRoot est /var/www/html, mais il peut être nécessaire de pointer vers un de ses sous dossiers...

  • WEB_APP_DIR : sous-répertoire du DocumentRoot à servir par le serveur.

    valeurs usuelles :

    • public (Symfony 4),

    • web (Symfony 3.4),

    • . si le contenu dans le DocumentRoot

    valeur par défaut = vide --> DocumentRoot /var/www/html

  • WEB_APP_INDEX : nom de l'index (index.html, index.php, app.php, app.cgi...)

  • DOCROOT_EXTRA_OPTIONS : Permet d'ajouter des Options d'exploration ("Directive Options d'Apache") au niveau du DocumentRoot. Par défaut, uniquement FollowSymLinks est activée. Tout audit de sécurité préconise de ne pas activer l'option Indexes.

Sécurité

Le virtualHost est paramétré avec (notamment), ces directives :

# --> Security & headers
<IfDefine !DISABLE_HEADER_XFRAMEOPTIONS>
Header always append X-Frame-Options SAMEORIGIN
</IfDefine>
Header always append X-XSS-Protection "1; mode=block"
Header always append Referrer-Policy "no-referrer-when-downgrade"
Header always append X-Content-Type-Options "nosniff"
Header always append Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'"
Header always append Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure;SameSite=Strict

# Globally disable some headers
Header always unset ETag
Header always unset Vary
Header always unset X-Powered-By

Certains Headers pouvant poser problèmes (intégration dans des Iframes "venues d'ailleurs"), ils peuvent être désactivés, comme X-Frame-Options par exemple.

Variables :

  • HTTPD_DISABLE_HEADER_XFRAMEOPTIONS (false) : on omet le header X-Frame-Options si positionnée à true.

Changements récents

2025-11-19 (2.4.65) : Observabilité -> Prometheus Exporter Ajout d'un script de HEALTHCHECK (/usr/local/bin/healthcheck.sh) Ajout de l'exporter prometheus, écoute sur EXPORTER_LISTEN_PORT (9117)

2024-02-17 (2.4.63) : Ajout du mode PROXY_PRESERVE_HOST, valeur par défaut à Off. (Demande de J. Labbe)

2023-03-13 (2.4.56) : Ajout du mode HTTPD_SIMPLE_HTTP_PROXYPASS

  • (Demande de J. Labbe) Permet de faire passe-plat pour une URI spécifique

2023-02-28 (2.4.55) : Gestion de l'identité des child-processes

  • Debian/Alpine : possibilité root-less conservée (pas dispo sur CentOS)
  • Debian/Alpine/CentOS : User WEB_USERNAME & Group WEB_GROUPNAME dans server-config

2023-02-23 (2.4.55) : Passage à docker compose v2 sur toutes les images.

  • docker-compose.yml -> compose.yaml
  • docker-compose -> docker compose

2022-09-05 (2.4.54) : Nettoyage + Maj README

  • Suppression de tout ce qui concernait Fedora.
  • Compléments README.md (drop-in dir)
  • Ajouts ENV vars MODULES_PATh, RUN_DIR, retrait ENV var HTTPD_SERVER_ROOT

2022-08-18 (2.4.54) : mode root-less totalement fonctionnel

  • identité d'avant-plan.
  • Suppression du code concernant les logs dans /httpdlogs.
  • Nettoyages divers (mais d'été).
  • Mise à jour de ce README.

2022-08-18 (2.4.54) : compléments dans la doc + ajout Var DOCROOT_EXTRA_OPTIONS

  • Paramétrage des options d'exploration par la variable DOCROOT_EXTRA_OPTIONS.
  • Suppression de la possibilité de logguer dans /httpdlogs, et des deux variables associées (HTTPD_ENABLE_PERSISTENT_ERRORLOG et HTTPD_ENABLE_PERSISTENT_ACCESSLOG).

2022-08-08 (2.4.54) : Root-Less ability (Sur bases Debian & Alpine uniquement)

  • port d'écoute est désormais paramétrable, 8000 par défaut.
  • ajout de la possibilté de lancer le conteneur avec l'identité "www-data" (tel que das la clause user: www-data) du docker-compose.
  • retrait de la sortie de logs sur /httpdlogs (sortie uniquement sur stdout / stderr)
  • arrêt des builds basés sur CentOS et Fedora
  • builds Alpine basés sur alpine:3.16
  • builds Debian basés sur httpd:2.4-bullseye

2022-03-02 (2.4.52) : Security Headers

  • ajout de la possibilté d'omettre le Header X-Frame-Options.

2022-01-18 (2.4.52) : Logs

  • ajout des temps de traitement (en ms) de la requête dans les logs (voir %T dans LogFormat)
  • ajout d'une publication de la charge du service dans les en-têtes (si demandé par booléen HTTPD_SHOW_SERVER_LOAD), l'information apparaît alors aussi dans l'access_log.