Installation de configuration du serveur web Apache 2.4 sous Debian Bullseye

De Linux Server Wiki
Aller à la navigation Aller à la recherche


1 Les différents mpm

Les différents mpm de apache sont fournis sous forme de modules et ce dès l'installation du paquet apache2. La seule exception reste le mpm ITK qui nécessite l'installation du paquet libapache2-mpm-itk.

2 Installation

apt install apache2

3 Considérations de sécurité

  • Même si les directives sont lues dans l'ordre dans lequel vous les inscrivez, certaines restent appliqués avant les autres (cf https://httpd.apache.org/docs/2.4/sections.html#merging). L'ordre d'application de apache est <Directory> -> htaccess -> <DirectoryMatch> -> <files> et <filesmatch> -> <Location> et <LocationMatch>. La dernière directive appliquée prendra le dessus sur les autres.
  • Encore une fois, prenez garder à la manière dont les directives se chaînent. Cela peut conduire à de graves défaut de sécurité étant donné que la directive authmerging est par défaut à off dans apache. En gros, si vous interdisez l'accès au répertoire "config" avec une directive <Directory> puis que vous autorisez l'accès au répertoire parent à l'aide d'une directive <Location>, le répertoire config deviendra accessible en lecture et ce pour deux raisons : l'ordre d'application des directives (Location appliqué après Directory) et le fait que les autorisations d'accès s'annulent les unes les autres (authmerging à off).
  • De manière globale, préférez la directive <Directory> (et ses dérivés comme <files> <DirectoryMatch> ou <FilesMatch>) a la directive <Location> (et ses dérivés comme <LocationMatch>) quand vous sécurisez des chemins du système de fichier de votre machine. N'utilisez <Location> que si vous définissez les règles d'accès à une ressource qui ne se situe pas sur le système de fichier (page générée par une base de donnée par exemple)
  • La configuration de apache sous debian est modulaire, pensez cependant à lire l'ensemble de cette configuration, vous pourriez y trouver des surprises (y compris /etc/apache2/apache2.conf !). N'oubliez pas non plus l'ordre de lecture qui peut changer la manière dont les règles s'appliquent : /etc/apache2/apache2.conf, puis conf-enabled/*.conf (ordre alphanumérique), puis sites-enabled/*.conf (ordre alphanumérique).
  • Faites attention aux directives Include fournies par certains paquets. Après installation, ils sont parfois automatiquement chargés, parfois il faudra passer par a2enconf. Lisez toujours tout fichier que vous incluez à la configuration de apache ou d'une vhost !
  • De même, faites attentions à certains paquets qui ont tendance à ajouter par défaut un fichier de configuration et à l'activer dans conf-enabled/. C'est par exemple le cas de phpmyadmin qui se retrouve activé par défaut via conf-enabled/ et qui est donc accessible globalement y compris si vous avez défini des virtualhosts avec des règles de sécurité pour phpmyadmin (qui seront donc caduques).
  • Par défaut, debian autorise l'accès à /usr/share. Il peut être bon de désactiver globalement ce comportement en créant un fichier de configuration dans conf-enabled, quitte à réactiver l'accès au cas par cas.

3.1 Exemple d'erreur a ne pas faire

  • Dans cet exemple, on autorise l'accès a /srv/http/, puis on interdit l'accès a /srv/http/admin, puis on réautorise l'accès a l'ensemble de l'arborescence avec la dernière directive Location. Résultat : votre interface d'admin est accessible de l'extérieur.
DocumentRoot /srv/http/
<Directory /srv/http/>
    require all granted
</Directory>
<Directory /srv/http/admin>
    require all denied
</Directory>
<Location />
  require all granted
</Location>
  • Dans cet exemple, l'utilisation de la directive location rendra accessible le fichier /srv/http/.htaccess malgré l'utilisation d'un FilesMatch antérieur.
<FilesMatch ".htaccess">
        Require all denied
</FilesMatch>
DocumentRoot /srv/http/
<Location />
  require all granted
</Location>

4 Configuration

4.1 Configuration par défaut de apache

  • /etc/apache2/apache2.conf

Le fichier de configuration par défaut de apache. Il défini quelques variables dont vous ne devriez, dans la majorité des cas, pas avoir à vous soucier. Plus important, c'est grâce a lui que les autres fichiers de configuration sont chargés.
A noter cependant, ce fichier défini par défaut la variable AllowOverride None pour la totalité de votre arborescence linux (ce qui empêche un éventuel fichier htaccess quelque part dans votre arborescence de modifier la configuration de sécurité d'un dossier) et interdit l'accès à toute votre arborescence à l'exception de /usr/share
Evitez de faire des modifications dans ce fichier, nous le feront plus loin dans les fichiers de configuration indépendants.

  • /etc/apache2/ports.conf

Ce fichier se charge de définir les ports sur lesquels apache écoutera. Si vous souhaitez utiliser apache sur d'autres ports sur 80 et 443, il faudra jouter des lignes Listen pour chaque port.

  • /etc/apache2/mods-enabled/*.conf : fichiers chargés dans l'ordre numérique puis alphabétique

Charge les différents modules installés ainsi que leur configuration. Dans la majorité des cas, la configuration par défaut des modules est tout à fait convenable d'un point de vue sécurité.
Gardez cependant en mémoire les fichiers mpm_event.conf et mpm_prefork.conf qui peuvent vous permettre de d'optimiser les performances de apache. A noter le fichier status.conf qui restreint l'utilisation de module status a l'ip locale du serveur.

  • /etc/apache2/conf-enabled/*.conf : fichiers chargés dans l'ordre numérique puis alphabétique
    • security.conf : passez principalement la variable ServerTokens prod
    • javascript-common.conf : fichier à surveiller étant donné qu'il créé un alias global /javascript, cependant, l'accès ne sera pas autorisé par défaut (voir plus loin)
    • serve-cgi-bin.conf : un trou de sécurité à part entière qui active les scripts cgi installés dans toutes les vhosts ... je vous conseille rapidement de désactiver ce script tout simplement et d'activer les scripts cgi a la demande dans les vhosts le necessitant. Attention cependant, désactiver ce script rendra potentiellement inutilisable les configurations par défaut fournis avec certains paquets (smokeping par exemple). Pour le désactiver, rien de plus simple :
      a2disconf serve-cgi-bin.conf

4.2 Configuration de sécurité en sus

Nous allons créer un fichier afin d'interdire l'accès à certains dossiers qu'apache autorise en général par défaut. Notez qu'activer ces options empêchera la configuration par défaut de nombreux paquets debian de fonctionner.
Créez le fichier /etc/apache2/conf-available/zzz-local-security.conf (le faire commencer par zzz devrait vous assurer qu'il soit lu en dernier) contenant :

# Disable access to / by default
<Directory />
        Options -Includes -IncludesNOEXEC -ExecCGI -Indexes -FollowSymLinks -SymLinksIfOwnerMatch -MultiViews
        AllowOverride None
        Require all denied
</Directory>

# Disable access to /usr/share by default
<Directory /usr/share/>
        Options -Includes -IncludesNOEXEC -ExecCGI -Indexes -FollowSymLinks -SymLinksIfOwnerMatch -MultiViews
        AllowOverride None
        Require all denied
</Directory>

# Disable access to /usr/lib/cgi-bin/ by default (will override conf-enabled/serve-cgi-bin.conf if enabled)
<Directory /usr/lib/cgi-bin/>
        Options -Includes -IncludesNOEXEC -ExecCGI -Indexes -FollowSymLinks -SymLinksIfOwnerMatch -MultiViews
        AllowOverride None
        Require all denied
</Directory>

# Disable access to svn tree
<DirectoryMatch "/\.svn">
        Require all denied
</DirectoryMatch>

# Disable access to git tree
<DirectoryMatch "/\.git">
        Require all denied
</DirectoryMatch>

# Disable access to .htaccess and .htpasswd files
<FilesMatch "^\.ht">
        Require all denied
</FilesMatch>

# Disable access to htpasswd and htdigest files as some users don't make them hidden
<Files htpasswd>
        Require all denied
</Files>
<Files htdigest>
        Require all denied
</Files>

# Disable access to .user.ini (used by php for local config)
<Files .user.ini>
        Require all denied
</Files>

# Deny access to most php project configuration files (localsettings.php, config.php but also things like config.anything.php
<FilesMatch "(?i)^(config-db|wp-config|commonsettings|localsettings|config)(\.([a-z]|[0-9])+)*\.ph(ar|p|tml)$">
        Require all denied
</FilesMatch>

# Enforce a restricted open_basedir if php7 module is loaded
<IfModule php7_module>
        php_admin_value open_basedir /var/www/
</IfModule>

# Enforce mpm-itk security if mpm itk is loaded
<IfModule mpm_itk_module>
        # first uid need to be 33 for www-data (default uid/gid, can be tuned)
        LimitUIDRange 33 2000
        LimitGIDRange 33 2000
        # Drop most root capabilities in the parent process.
        #  Instead run as the user given by the User/Group directives with some extra capabilities 
        #  Somewhat more secure.
        EnableCapabilities on
</IfModule>

Vous pouvez noter que les sections concernant /usr/lib/cgi-bin/ et /usr/share/ sont superflues étant données que la section concernant / couvre déjà ces répertoires. Il est néanmoins pratique d'avoir prédéfini des règles pour ces deux dossiers car ce sont ceux pour lesquels vous risquez le plus d'avoir à assouplir les règles de sécurité à l'échelle du serveur entier.

Activez le fichier de configuration avec a2enconf :

a2enconf zzz-local-security.conf
/etc/init.d/apache2 reload

4.3 Imbrications des directives

Pour la directive Location, le mappage se fait du moins spécifique au plus spécifique :

<Location "/foo">
</Location>
<Location "/foo/bar">
</Location>

Pour les Alias et les proxy (ProxyPass), le mappage se fait dans le sens inverse, du plus spécifique au moins spécifique :

Alias "/foo/bar" "/srv/www/uncommon/bar"
Alias "/foo" "/srv/www/common/foo"
ProxyPass "/special-area" "http://special.example.com"
ProxyPass "/" "http://www.example.com"

4.4 Vérifier la configuration de apache

apache2ctl configtest

5 php via mod-php

apt install libapache2-mod-php
a2enmod php7*

Seul les mpm prefork et itk sont compatibles avec ce module php. C'est le plus simple à configurer, mais les performances sont limitées.

5.1 Sécurité recommandée

  • open_basedir avec une règle par défaut définie dans /etc/apache2/mods-enabled/zzz_localsecurity.conf
    • Vous pouvez ensuite appliquer pour chaque vhost une règle spécifique avec php_admin_value open_basedir /var/www/path1/:/var/www/path2/
  • le mpm itk afin de pouvoir lancer les scripts php avec les permissions utilisateurs
    • Notez que me mpm-itk impose d'activer le module mpm-prefork (et ne permet pas d'utiliser mpm-worker ou mpm-event)

6 php via php-fpm

Ce mode de fonctionnement est plus complexe, mais augmente les performances et permet d'utiliser les mpm worker et event (eux même plus performants que prefork et itk).
Le principe est d'avoir un daemon php-fpm exécutant les scripts php. Apache s'y connecte en fcgi au moyen d'un socket.
Chaque daemon php-fpm existe au sein d'un pool qui a son propre socket. Pour des raisons de sécurité, il est conseillé d'utiliser un pool (donc un socket) différent pour chaque site. En effet, si un script malveillant est exécuté au sein d'un pool, il a le potentiel de contaminer l'ensemble du pool et donc l'ensemble des sites utilisant le pool. Il est également possible d'attribuer un utilisateur/groupe spécifique à chaque pool ce qui compense l'impossibilité d'utiliser mpm-itk.

Pour installer et activer php-fpm :

apt install php-fpm
a2enmod proxy_fcgi setenvif
a2enconf php7.4-fpm
/etc/init.d/apache2 restart

NB : sous buster il faut également installer libapache2-mod-fastcgi

Si vous aviez installé le module php classique, désactivez le (il est utilisé par défaut) et passez également sur le mpm worker :

a2dismod php7.4
a2dismod mpm_prefork mpm_itk
a2enmod mpm_worker
apt remove libapache2-mod-php7.4  libapache2-mod-php
/etc/init.d/apache2 restart

Quand vous faites des modifications, il faudra selon le cas relancer apache2, fpm, ou les deux :

/etc/init.d/apache2 restart
/etc/init.d/php7.4-fpm restart

6.1 Passer des options de configuration à php-fpm

Il existe 5 manières de passer des options/arguments à php avec FPM.

6.1.1 php.ini

La première méthode consiste à modifier directement le fichier de configuration principal de php/fpm : /etc/php/7.4/fpm/php.ini.
Les options appliquées le seront à l'ensemble des scripts lancé par php/fpm et ce quelque soit le pool utilisé. Cela n'est donc viable que pour une configuration "par défaut" valable pour l'ensemble de votre serveur et quelque soit le site (ou que vous modifierez par d'autre méthode décrites plus bas).

6.1.2 php-fpm.conf

Cette méthode n'est pas recommandée
Elle consiste à éditer directement le fichier /etc/php/7.4/fpm/php-fpm.conf afin d'y ajouter les php_flag, php_value ou php_admin_value souhaitées. Ces valeurs s'appliqueront à l'ensemble des pools.

Exemples :

php_flag[display_errors] = off
php_admin_value[open_basedir] = /srv/admin/
php_value[date.timezone] = Europe/Paris

6.1.3 Via la configuration de apache ou .htaccess

Cette méthode n'est pas recommandée
Le principe est d'utiliser des variables d'environnement avec SetEnv pour définir des valeurs de configuration.

Par exemple :

# attention à bien respecter l'absence d'absence avant et après le signe =
SetEnv PHP_ADMIN_VALUE "open_basedir=/srv/admin/"
SetEnv PHP_VALUE "date.timezone=Europe/Paris"

Ce mode de fonctionnement est risqué et imprévisible, car les variables d'environnement restent actives longtemps au sein d'un même pool. Or, vous pourriez être amené, au sein d'un même site, de modifier différemment la configuration php selon le contexte.

Par exemple : host1 et host2 ; host1 a pas de valeur date.timezone, host2 a une valeur date.timezone définie par SetEnv

  • On visite host1 : date.timezone = default from php.ini (logique)
  • On visite host2 : date.timezone = appliqué via SetEnv (logique)
  • On re-visite host1 : date.timezone = appliqué via SetEnv ! (comportement non voulu)

Dans le cas présent, une "ancienne" valeur de date.timezone est utilisée alors qu'elle ne devrait pas l'être, parce que vous avez chargé le site host1 avec le même thread du même pool que host2. Si par chance vous changez de thread (sur un thread qui n'a pas encore chargé host1) vous aurez la valeur par défaut a nouveau.

6.1.4 pool.d/www.conf

C'est la méthode préférée pour définir une valeur appliquée à un site spécifique sans modifier le fichier php.ini.
Le principe est de créer, pour chaque site, un fichier de configuration dans /etc/php/7.4/fpm/pool.d/ définissant un socket séparé.
Cette méthode permet également de faire tourner les scripts php sous un autre utilisateur/groupe.

Par défaut, après avoir activé php-fpm, apache est configuré par /etc/apache2/conf-enabled/php7.4-fpm.conf pour utiliser le socket /run/php/php7.4-fpm.sock lui même défini par /etc/php/7.4/fpm/pool.d/www.conf

Par exemple, copiez /etc/php/7.4/fpm/pool.d/www.conf en tant que /etc/php/7.4/fpm/pool.d/test.conf en modifiant :

; on remplace [www] par [test] (c'est le nom du pool)
[test]
user = foo
group = bar
listen = /run/php/php7.4-fpm-test.sock
php_value[date.timezone] = Europe/Paris

Après avoir redémarré php-fpm (/etc/init.d/php7.4-fpm restart), vous aurez un socket spécifique dans /run/php/php7.4-fpm-test.sock permettant d'accéder à ce pool, qui lui même executera les scripts sous l'utilisateur foo et le groupe bar et en définissant la valeur date.timezone = Europe/Paris

Ensuite, modifiez la configuration de la VirtualHost dont vous souhaitez adapter les valeurs php ou l'utilisateur en y ajoutant :

<FilesMatch ".+\.ph(ar|p|tml)$">
    SetHandler "proxy:unix:/run/php/php7.4-fpm-test.sock|fcgi://localhost"
</FilesMatch>

Notez que cette méthode définie à la fois les valeurs Local et Master de php, et s'harmonise bien avec la méthode suivante (qui ne modifie que les valeurs Local).

6.1.5 fichier .user.ini

Cette dernière méthode permet de définir uniquement les valeurs Local de php, a l'inverse de la précédente qui définie les valeurs Local et Master.
Elle consiste à créer, dans le path de votre documentroot, un fichier .user.ini dans lequel vous pouvez adapter les valeurs de php. Vous pouvez utiliser plusieurs de ces fichiers dans plusieurs répertoires de votre documentroot, en gardant à l'esprit qu'une valeur définie dans le répertoire parent est appliqué dans le répertoire enfant (mais peut être redéfinie autrement). N'oubliez pas que ce fichier doit pouvoir être lu par l'utilisateur de php-fpm (par défaut www-data).

Par exemple :

  • dans /srv/admin/.user.ini : date.timezone = Europe/London
  • dans /srv/admin/main/.user.ini : date.timezone = Europe/Paris

Les fichiers php exécuté dans /srv/admin/ auront la timezone définie à Europe/London et ceux dans /srv/admin/main/ auront la timezone Europe/Paris.

Si la même valeur est définie dans le pool.d et dans un .user.ini, c'est celle de .user.ini qui sera appliquée. Attention quand même à quelques spécificités, par exemple, la valeur de open_basedir locale ne peut-être définie que dans des sous-dossiers de la valeur globale de pool.d.

Vous pouvez désactiver cette fonctionnalité (par sécurité et si vous n'en avez pas besoin) en vidant la variable suivante dans /etc/php/7.4/php.ini :

user_ini.filename =

6.2 Attribuer un pool a une vhost

# PHP-FPM admin sock
<FilesMatch ".+\.ph(ar|p|tml)$">
        SetHandler "proxy:unix:/run/php/php7.4-fpm-poolname.sock|fcgi://localhost"
</FilesMatch>

7 Note on PHP

  • php_value : peut-être utilisée dans la configuration apache, dans un virtualhost, dans un .htaccess. Ne peut que modifier les paramètres PHP_INI_ALL et PHP_INI_PERDIR défini sur https://www.php.net/manual/en/ini.list.php
  • php_admin_value : peut-être utilisée uniquement dans la configuration apache et virtualhost, pas en .htaccess. Peut modifier tous les paramètres php.

8 http2

Ne fonctionnera qu'avec mpm_event ou mpm_worker ce qui implique soit de ne pas utiliser php (ni mpm_itk), soit de passer par fcgi / php-fpm :

a2enmod http2

9 la gestion des virtualhosts

L'architecture de configuration modulaire de apache sous debian propose de créer les fichiers de configuration des vhosts dans /etc/apache2/sites-available/ puis de les activer avec a2ensite afin qu'ils soient lus via le dossier /etc/apache2/sites-enabled
Je vous conseille cependant de créer, en plus, un dossier /etc/apache2/sites-config afin de créer des fichiers de configuration pouvant être utilisé dans plusieurs vhosts (par exemple, le même site, l'un en https, l'autre non) :

mkdir /etc/apache2/sites-config

9.1 Configuration recommandée dans toute virtualhost

9.1.1 open_basedir

open_basedir permet de limiter le champ d'action des fichiers php en terme de dossiers. Etant donné que nous avons défini un open_basedir restrictif un peu plus haut, si vous utilisez php, il est obligatoire de rédéfinir open_basedir pour correspondre aux dossiers auxquelles vos fichiers php doivent pouvoir accéder. En général, c'est tout simplement le même dossier que votre DocumentRoot

php_admin_value open_basedir /votre/documentroot/

Vous pouvez spécificer plusieurs path séparés par des :
Notez qu'il est important de faire terminer le path par un / (le path /votre/documentroot est considéré comme /votre/documentroot*

9.1.2 AllowOverride et .htaccess

AllowOverride permet de définir les règles autorisées à être modifiées par un fichier .htaccess
Notez que cette directive doit obligatoirement être définie dans un champ <Directory>
La configuration apache que nous avons vu plus tôt désactive complètement les fichiers .htaccess dans la totalité du système de fichier. Le problème de ce comportement est que les fichiers htaccess seront tout simplement ignoré, or, de nombreux projets utilisent les fichiers htaccess pour protéger de la lecture certains répertoires sensibles (fichiers de configurations, etc).
Plusieurs options s'offrent à vous :

  • Si vous êtes certain que votre projet n'utilise pas de fichiers .htaccess, la question ne se pose même pas, gardez les fichiers htaccess désactivés ! Même s'ils sont pratiques, ils restent des fichiers relativement faciles à modifier (un peu de code foireux dans un module externe d'un CMS par exemple) et offrent donc une sécurité bien moindre que les directives définies directement dans la configuration de votre virtualhost apache ! Ils ont également un impact sur les performances de apache (nécessité pour apache de remonter l'arborescence à la recherche d'un .htaccess à chaque requête).
  • Si votre projet utilise des fichiers .htaccess, il est possible d'intégrer leur contenu à la configuration de votre virtualhost. Vous y gagniez ainsi en sécurité et en performances tout en limitant un vecteur d'attaque.
  • Si votre projet utilise des fichiers .htaccess mais qu'ils ne peuvent pas être intégrés à la configuration de votre virtualhost (fichiers générés par un client dans le cadre d'une activité de hosting, fichier dynamique généré par le projet lui même, etc), l'idéal est de restreindre les capacités des fichiers .htaccess, ce que vous allons voir plus loin.


Dans tous les cas :

  • Évitez absolument d'allowoverride fileinfo (permet de modifier l'handler par défaut d'un fichier)
  • Évitez si possible d'allowoverride options (car permet principalement d'activer l'execution CGI)
  • Si vous êtes certains que votre projet n'utilise pas de fichiers .htaccess ou que vous pouvez les intégrer à la configuration de la virtualhost :
AllowOverride none
  • Le cas échéant, la configuration minimale suivante est recommandée, étant donné que de nombreux projets utilisent des fichiers .htaccess afin de limiter l'accès a des répertoires :
AllowOverride Limit
AllowOverrideList Require
  • Si jamais le projet en question nécessite les directives d'authentifications, on pourra définir comme cela :
AllowOverride Limit AuthConfig
  • Si vous faites de l'hosting, de nombreux utilisateurs auront besoin des directives AuthConfig, Indexes, mais aussi des règles de redirections et de rewrite :
AllowOverride AuthConfig Indexes Limit Options=Indexes
AllowOverrideList Redirect RedirectMatch RedirectTemp RedirectPermanent RewriteEngine RewriteOptions RewriteBase RewriteCond RewriteRule ErrorDocument

9.1.3 Gestion des index et autoindex

9.1.3.1 DirectoryIndex

DirectoryIndex, c'est ce petit machin qui permet (à condition d'avoir le module dir activé) à apache de recherche automatiquement un fichier d'index (index.html, index.php, ...) pour afficher la page si le client n'en précise pas (quand vous chargez https://www.online.net/ c'est https://www.online.net/index.php qui est affiché). Si vous ne souhaitez pas bénéficier de cette fonctionnalité (par exemple si le client doit de toute façon charge une page précise sans passer par un quelconque index, désactivez le :

DirectoryIndex disabled

9.1.3.2 autoindex

mod_autoindex est un petit module qui permet à apache d'afficher l'arborescence fichier. Cela peut être très pratique, mais aussi délétère et peut rendre votre site plus facile à scanner. Si vous n'en avez pas besoin, pensez à le désactiver ou a ne l'activer que dans les dossiers que vous voulez rendre visible totalement au publiques. Notez que la configuration par défaut de apache laisse le module désactivé.

Options -Indexes

9.1.4 AssignUserId

Si vous avez chargé le module mpm_itk, la directive AssignUserId permet de changer l'utilisateur du processus apache. Par exemple si dans une vhost vous définissez :

AssignUserId foo bar

Les processus apache de cette vhost seront lancés avec l'utilisateur foo et le groupe bar. Si cette vhost créé des fichiers (avec php par exemple), ils seront donc attribué à l'utilisateur foo et le groupe bar. Même principe pour la gestion des droits de lecture et d’exécution.

9.2 Ip-based vs Named-based virtualhosts

9.2.1 Ip-based

Les virtualhosts par IP utilisent l'adresse IP afin de déterminer quelle virtualhost doit répondre à une requête. Par conséquent, il faut disposer d'adresses IP différentes pour chaque virtualhost. Cela est tout à fait réalisable en IPv6 si vous avez accès à un bloc, beaucoup moins en IPv4 depuis la pénurie d’adresses.
Cela garde néanmoins un intérêt si vous souhaitez par exemple ne rendre accessible une vhost que sur une adresse IP spécifique dans le cadre d'un serveur disposant de plusieurs adresses IPs (cas par exemple d'une virtualhost d'administration qui n'écouterait que sur un port local/vpn).

Notez que par défaut, apache bind sur les ports 80 et 443 sans définir IP (cf /etc/apache2/ports.conf), ce qui signifie que apache servira des requêtes sur toutes les adresses IPs attribuées au serveur, et que chacune de ces adresses IPs pourra servir à définir une virtualhost ip-based et/ou named-based.

Par exemple, si votre serveur possède les adresses IPs 192.168.10.1 et 192.168.20.2 et que vous définissez la virtualhost suivante :

<VirtualHost 192.168.20.2:80>
    ServerName admin.server.localdomain
    DocumentRoot /srv/admin/
</VirtualHost>

Cette virtualhost ne sera accessible que si le client se connecte sur le couple IP:Port 192.168.20.2:80. S'il se connecte sur 192.168.10.1:80, il ne pourra jamais atteindre cette virtualhost et ce même s'il spécifie via une en-tête HTTP qu'il souhaite atteindre le serveur admin.server.localdomain.
Notez qu'on défini la directive ServerName qui est capitale pour les named-based virtualhost, mais qui est également importante ici pour donner un nom au serveur (dans les faits, cette virtualhost est à la fois Ip-based et named-based). Si on ne défini aucune directive ServerName, le serveur utilisera par défaut le FQDN déduit du nom d'hôte du système. Cette configuration sans ServerName explicite peut conduire à des erreurs de choix virtualhost à utiliser et est déconseillée.

9.2.2 name-based

Les virtualhosts par nom s'appuient sur les informations transmises par les en-têtes HTTP des requêtes du client (spécifiquement, le Header: Host). Cela permet de définir plusieurs virtualhosts sur la même adresse IP.
La sélection de la bonne virtualhost en fonction du nom ne se fait que parmi les virtualhosts pour lesquelles la correspondance avec la paire IP/Port est la plus exacte (best-match). La résolution basée sur l'IP n'est pas effectuée si on utilise un caractère générique (*, *:80 ou *:443) pour l'adresse IP dans toutes les directives VirtualHost du serveur.

Quand une requête arrive, après avoir choisi la VirtualHost présentant la meilleure correspondance IP:Port, si plusieurs VirtualHosts possèdent cette même paire IP:port, apache va comparer les directives ServerName et ServerAlias avec le nom de serveur présent dans la requête.

Notez que (encore une fois), l'ordre dans lequel apache charge les VirtualHost a son importance : si aucune directive ServerName ou ServerAlias ne correspond dans la liste des VirtualHost présentant la meilleure correspondance du couple IP:port, c'est le premier VirtualHost de cette liste qui sera utilisé (serveur par défaut).

9.2.3 Exemples

Considérant un serveur ayant attribué les 2 couples ip:ports 192.168.10.1:80 et 192.168.20.2:80 avec la configuration suivante :

  • sites-enabled/000-default.conf : VirtualHost *:80 + ServerName localhost
  • sites-enabled/admin.domain.tld.conf : VirtualHost 192.168.20.2:80 + ServerName admin.domain.tld
  • sites-enabled/other.domain.tld.conf : VirtualHost *:80 + ServerName other.domain.tld
  • sites-enabled/www.domain.tld.conf : VirtualHost *:80 + ServerName www.domain.tld

Cette configuration sera chargée par apache dans cet ordre (alphanumérique).

Si vous vous connectez sur 192.168.10.1:80 :

  • Sans spécifier un header Host : le serveur sert le contenu de 000-default.conf
  • En spécifiant un header Host 192.168.10.10 : le serveur sert le contenu de 000-default.conf
  • En spécifiant un header Host undefined.domain.tld : le serveur sert le contenu de 000-default.conf
  • En spécifiant un header Host admin.domain.tld : le serveur sert le contenu de 000-default.conf
  • En spécifiant un header Host other.domain.tld : le serveur sert le contenu de other.domain.tld.conf
  • En spécifiant un header Host www.domain.tld : le serveur sert le contenu de www.domain.tld.conf

Si vous vous connectez sur 192.168.20.2:80 :

  • Sans spécifier un header Host : le serveur sert le contenu de admin.domain.tld.conf
  • En spécifiant un header Host (n'importe lequel) : le serveur sert le contenu de admin.domain.tld.conf

9.3 Activer la vhost par défaut

Cette vhost sera utilisé pour toute requête n'en matchant aucune autre.

a2ensite 000-default

Pour la vhost SSL, c'est un peu plus complexe.
Commencez par ré-générez votre certificat machine :

make-ssl-cert generate-default-snakeoil --force-overwrite

Puis renommez le fichier de configuration de la vhost par défaut SSL, et ce afin qu'il soit toujours lu en premier :

cp /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-available/000-default-ssl.conf

Puis chargez le :

a2ensite 000-default-ssl

Après tout ça, relancez apache :

/etc/init.d/apache2 restart

Notez que ces vhosts ne spécifient pas de ServerName, il sera donc déduit du nom d'hôte FQDN de la machine (hostname -f).
Attention, cela implique que vous ne créez aucune autre virtualhost ayant "simplement" le nom d'hôte de la machine comme ServerName, sinon vous aurez 2 virtualhost avec le même nom d'hôte, et la première chargée en configuration (ordre alphamumérique) sera celle présentée au client (mais pas forcément celle que vous souhaitez).

9.4 Activer server-status / vhost d'admin

9.4.1 Création d'une vhost d'administration

<VirtualHost 192.168.10.1:80>
        ServerName admin.server.localdomain
        ServerAlias admin_alt.server.localdomain
        DocumentRoot /path/to/admin/interface/
        #AssignUserId www-data www-data # Décommentez uniquement si vous utilisez mpm-itk
        ErrorLog ${APACHE_LOG_DIR}/admin.server.localdomain.error.log
        CustomLog /var/log/apache2/admin.server.localdomain.access.log vhost_combined
</VirtualHost>

Pour sécuriser l'accès, et n'autorisant par exemple que les IPs 192.168.50.0/24 :

<Directory /path/to/admin/interface/>
        Options +Indexes
        IndexOptions FancyIndexing FoldersFirst IgnoreCase NameWidth=* Charset=UTF-8 XHTML
        # Allow only VPN and nancy-lan
        Require all denied
        Require ip 192.168.50.0/24
</Directory>

9.4.1.1 PhpMyAdmin

# phpmyadmin installed from package
include /etc/phpmyadmin/apache.conf
<Directory /usr/share/phpmyadmin/>
        Require all denied
        Require ip 192.168.50.0/24
</Directory>
<Directory /usr/share/phpmyadmin/setup>
        Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/templates>
        Require all denied
</Directory>
<Directory /usr/share/phpmyadmin/libraries>
        Require all denied
</Directory>

Notez que /etc/phpmyadmin/apache.conf défini les valeurs php open_basedir et upload_tmp_dir pour PhpMyAdmin. Cela ne fonctionnera néanmoins qu'avec mod-php, pas avec php-fpm (pour cela il faudra créer un fichier .user.ini à la racine de phpmyadmin dans /usr/share/phpmyadmin/ contenant open_basedir = suivi des mêmes arguments que dans /etc/phpmyadmin/apache.conf

9.4.1.2 phpsysinfo

# phpsysinfo from packages
alias /phpsysinfo /usr/share/phpsysinfo/
<Directory /usr/share/phpsysinfo/>
        <IfModule php7_module>
            php_admin_value open_basedir none
        </IfModule>
        Require all denied
        Require ip 192.168.50.0/24
</Directory>

9.4.1.3 apache Status

Activez le module status

a2enmode status

Observez le fichier mods-enabled/status.conf. Par défaut il active les options de status sur tous les sites (en dehors d'un virtualhost), mais avec une sécurité suffisante (requière une IP locale pour y accéder). Cette configuration est également nécessaire pour permettre à certains binaires d'accéder au status d'apache (par exemple apache2ctl status.

Afin d'éviter de modifier directement ce fichier de configuration, nous allons réactiver le module status dans votre vhost d'admin :

# apache status page at /server-status
# ensure mods-enabled/status.conf does not allow an insecured /server-status
# mods-enabled/status.conf needs to be kept activated for apache local process like apache2ctl status
<Location /main/server-status>
        SetHandler server-status
        Require all denied
        Require local
        Require ip 192.168.50.0/24
</Location>

Notez que les valeurs ExtendedStatus On ou ProxyStatus On issue de mods-enabled/status.conf sont appliqués automatiquement dans cette vhost également (vous ne pouvez pas les re-préciser dans une vhost).

9.4.1.4 server info

Je déconseille d'activer le module info en prod, car il donne beaucoup d'informations sur la configuration du serveur, et en cas de faille (si quelqu'un peut y accéder) cela peut être délétère.

Pour information, pour activer le module info :

a2enmode info

Le fichier mods-enabled/info.conf/ active les options d'information sur tous les sites (en dehors d'un virtualhost), mais avec une sécurité renforcée (requière une IP locale pour y accéder).

9.4.1.5 FPM Status

Si vous utilisez php-fpm :

# fpm status page
<Location "/fpm-status">
        Require all denied
        Require ip 192.168.50.0/24
        ProxyPass "unix:/run/php/php7.4-fpm.sock|fcgi://localhost/fpm-status"
</Location>

Et dans /etc/php/7.4/fpm/pool.d/<votre pool>.conf (par défaut dans le cas présent ça devrait être le pool www !

pm.status_path = /fpm-status

Notez que vous pouvez créer autant de page fpm-status que de pool. Il faudra juste veiller à ce que le chemin (ici /fpm-status soit différent pour chaque pool (il faut donc éditer la configuration de apache et celle du pool).

Pour plus d'information : https://www.php.net/manual/fr/fpm.status.php

10 Authentification

10.1 Authentification Basic

  • C'est le mode d'authentification HTTP le plus fréquement utilisé
  • le mot-de-passe est stocké de manière cryptée sur le serveur
  • le client envoi le mot-de-passe en clair au serveur (en base64 en fait, mais c'est du pareil au même), ce qui rend ce mode non sécurisé si la connexion n'est pas effectuée en HTTPS
  • Préférez la création d'un fichier nommé .htpasswd étant donné que ces fichiers sont interdits de lecture par la configuration par défaut de apache (/etc/apache2/apache2.conf). Dans la mesure du possible, ne créez des fichiers .htpasswd que dans un dossier de toute façon inaccessible au serveur web (donc en dehors du documentroot et de tout alias). En effet, les directives d'autorisations de apache d'une section supérieure annulent les directives d'autorisations précédentes de apache (AuthMerging à off par défaut), donc si vous autorisez l'accès complet à un repertoire (require all granted), vous risquez de supplanter la directive interdisant l'accès aux fichiers protégés


Création du fichier contenant les mots-de-passe :

htpasswd -c .htpasswd pfoo
New password: 
Re-type new password: 
Adding password for user pfoo

Pour ajouter un nouvel utilisateur, ou modifier le mot-de-passe d'un utilisateur existant, la commande est la même sans l'argument -c

htpasswd .htpasswd pfoo

Le fichier .htgroup permet de rassembler plusieurs utilisateurs dans un même groupe. Il suffit de créer un fichier (.htgroup) texte contenant par exemple (pour créer un groupe admin et un groupe user) :

admin: pfoo plop
user: blah

Pour que tous les utilisateurs valides (qui sont présent dans le .htpasswd) aient accès au dossier :

AuthUserFile /path/to/.htpasswd
AuthName "Accès Restreint"
AuthType Basic
require valid-user

Accès pour l'utilisateur pfoo et l'ensemble du groupe user uniquement :

AuthUserFile /path/to/.htpasswd
AuthGroupFile /path/to/.htgroup
AuthName "Accès Restreint"
AuthType Basic
require user pfoo
require group user

10.2 Authentification Digest

  • le mot-de-passe est stocké en clair sur le serveur ce qui le rend plus vulnérable à une fuite de données côté serveur (en clair ou sous forme condensée, sans salage possible, donc un pirate obtenant l'accès au fichier contenant la base de donnée digest aura un accès complet aux ressources du serveur et sans étape de décryptage)
  • le client envoi le mot-de-passe hashé avec un nonce et un realm, ce qui assure que le mot-de-passe ne circule jamais en clair sur le réseau et ce même sans HTTPS
  • Préférez la création d'un fichier nommé .htpasswd étant donné que ces fichiers sont interdits de lecture par la configuration par défaut de apache (/etc/apache2/apache2.conf)


L'outil htdigest permet de créer un fichier .htpasswd utilisant cette méthode d'authentification. Vous pouvez entrer ce que vous voulez en realm.

htdigest -c .htpasswd "votre realm" username

Pour ajouter un nouvel utilisateur, ou modifier le mot-de-passe d'un utilisateur existant, la commande est la même sans l'argument -c :

htdigest .htpasswd "votre realm" pfoo

Le fichier .htgroup permet de rassembler plusieurs utilisateurs dans un même groupe. Il suffit de créer un fichier (.htgroup) texte contenant par exemple (pour créer un groupe admin et un groupe user) :

admin: pfoo plop
user: blah

Pour que tous les utilisateurs valides (qui sont présent dans le .htpasswd) aient accès au dossier :

AuthType Digest
AuthName "votre realm"
AuthDigestDomain /
AuthDigestProvider file
AuthUserFile /path/to/.htpasswd
AuthGroupFile /path/to/.htgroup
Require valid-user

Accès pour l'utilisateur pfoo et l'ensemble du groupe user uniquement :

AuthType Digest
AuthName "votre realm"
AuthDigestDomain /
AuthDigestProvider file
AuthUserFile /path/to/.htpasswd
AuthGroupFile /path/to/.htgroup
require user pfoo
require group user

11 HTTPS

11.1 Redirection HTTP vers HTTPs

  • Solution avec le module Rewrite, applicable à la fois dans une vhost écoutant sur les port 80 ou 443 :
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R=301,L]

Il peut être intéressant de ne pas rewrite certaine URLs. Dans l'exemple ci dessous, les urls en lien avec les challenges letsencrypt :

RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/.*
  • Solution avec le module Alias, à ne surtout pas appliquer dans une vhost écoutant sur le port 443 (redirection infinie) :
Redirect permanent / https://domain.tld/

12 Tests de sécurité divers

https://observatory.mozilla.org/
https://tls.imirhil.fr/
https://www.ssllabs.com/ssltest/