1729 lines
55 KiB
Markdown
1729 lines
55 KiB
Markdown
---
|
||
categories: web
|
||
title: Howto Apache
|
||
...
|
||
|
||
* Documentation : <https://httpd.apache.org/docs/2.4/>
|
||
* Rôle Ansible : <https://gitea.evolix.org/evolix/ansible-roles/src/branch/stable/apache>
|
||
* Statut de cette page : stable / bookworm
|
||
|
||
[Apache](https://httpd.apache.org/) est le serveur [HTTP](HowtoHTTP)
|
||
le plus utilisé sur le web depuis 1996.
|
||
|
||
## Installation
|
||
|
||
Nous utilisons la version Apache en mode [MPM-ITK](http://mpm-itk.sesse.net/)
|
||
depuis des années en production sur de nombreux serveurs critiques.
|
||
MPM-ITK permet de préciser pour chaque VirtualHost un utilisateur,
|
||
un groupe et une option *MaxClients* spécifique, ce qui est utile
|
||
pour la sécurité d'un serveur multi-sites.
|
||
|
||
|
||
~~~
|
||
# apt install apache2 libapache2-mpm-itk libapache2-mod-evasive apachetop libwww-perl
|
||
|
||
# apache2ctl -V
|
||
Server version: Apache/2.4.57 (Debian)
|
||
Server built: 2023-04-13T03:26:51
|
||
Server's Module Magic Number: 20120211:127
|
||
Server loaded: APR 1.7.2, APR-UTIL 1.6.3, PCRE 10.42 2022-12-11
|
||
Compiled using: APR 1.7.2, APR-UTIL 1.6.3, PCRE 10.42 2022-12-11
|
||
Architecture: 64-bit
|
||
Server MPM: prefork
|
||
threaded: no
|
||
forked: yes (variable process count)
|
||
Server compiled with....
|
||
-D APR_HAS_SENDFILE
|
||
-D APR_HAS_MMAP
|
||
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
|
||
-D APR_USE_PROC_PTHREAD_SERIALIZE
|
||
-D APR_USE_PTHREAD_SERIALIZE
|
||
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
|
||
-D APR_HAS_OTHER_CHILD
|
||
-D AP_HAVE_RELIABLE_PIPED_LOGS
|
||
-D DYNAMIC_MODULE_LIMIT=256
|
||
-D HTTPD_ROOT="/etc/apache2"
|
||
-D SUEXEC_BIN="/usr/lib/apache2/suexec"
|
||
-D DEFAULT_PIDLOG="/var/run/apache2.pid"
|
||
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
|
||
-D DEFAULT_ERRORLOG="logs/error_log"
|
||
-D AP_TYPES_CONFIG_FILE="mime.types"
|
||
-D SERVER_CONFIG_FILE="apache2.conf"
|
||
|
||
# systemctl status apache2
|
||
● apache2.service - The Apache HTTP Server
|
||
Loaded: loaded (/lib/systemd/system/apache2.service; enabled; preset: enabled)
|
||
Active: active (running) since Fri 2023-07-28 15:59:33 CEST; 1min 33s ago
|
||
Docs: https://httpd.apache.org/docs/2.4/
|
||
Main PID: 3145 (apache2)
|
||
Tasks: 6 (limit: 2356)
|
||
Memory: 4.2M
|
||
CPU: 27ms
|
||
CGroup: /system.slice/apache2.service
|
||
├─3145 /usr/sbin/apache2 -k start
|
||
├─3147 /usr/sbin/apache2 -k start
|
||
├─3148 /usr/sbin/apache2 -k start
|
||
├─3149 /usr/sbin/apache2 -k start
|
||
├─3150 /usr/sbin/apache2 -k start
|
||
└─3151 /usr/sbin/apache2 -k start
|
||
~~~
|
||
|
||
> *Note* : Pour Debian 8, il faut installer ainsi :
|
||
>
|
||
> ~~~
|
||
> # apt install apache2-mpm-itk libapache2-mod-evasive apachetop libwww-perl
|
||
> ~~~
|
||
|
||
|
||
## Configuration de base
|
||
|
||
Fichiers de configuration :
|
||
|
||
~~~
|
||
/etc/apache2
|
||
├── apache2.conf
|
||
├── conf-available/
|
||
│ └── X.conf
|
||
├── conf-enabled/
|
||
│ └── X.conf -> ../conf-available/X.conf
|
||
├── envvars
|
||
├── magic
|
||
├── mods-available/
|
||
│ ├── X.load
|
||
│ └── X.conf
|
||
├── mods-enabled/
|
||
│ ├── X.load -> ../mods-available/X.load
|
||
│ └── X.conf -> ../mods-available/X.conf
|
||
├── ports.conf
|
||
├── sites-available/
|
||
│ ├── 000-default.conf
|
||
│ ├── default-ssl.conf
|
||
│ └── X.conf
|
||
└── sites-enabled/
|
||
│ ├── 000-default.conf -> ../sites-available/000-default.conf
|
||
└── X.conf -> ../sites-available/X.conf
|
||
~~~
|
||
|
||
La configuration principale se trouve dans le fichier
|
||
`/etc/apache2/apache2.conf` qui inclut de nombreux fichiers séparés.
|
||
|
||
Nous activons au minimum les modules suivants :
|
||
|
||
~~~
|
||
# a2enmod rewrite expires headers rewrite cgi
|
||
~~~
|
||
|
||
Le fichier `/etc/apache2/conf-available/z-evolinux-defaults.conf`
|
||
contient nos optimisations basiques :
|
||
|
||
~~~{.apache}
|
||
ServerTokens Prod
|
||
Timeout 10
|
||
KeepAliveTimeout 2
|
||
MaxKeepAliveRequests 10
|
||
ServerLimit 250
|
||
#MaxClients 250
|
||
MaxRequestWorkers 250
|
||
StartServers 50
|
||
MinSpareServers 20
|
||
MaxSpareServers 30
|
||
MaxRequestsPerChild 100
|
||
<Directory /home/>
|
||
AllowOverride None
|
||
# Equivalent à "Require not env GoAway"
|
||
Require expr "-z %{reqenv:GoAway}"
|
||
</Directory>
|
||
<IfModule mod_ssl.c>
|
||
SSLProtocol all -SSLv2 -SSLv3
|
||
SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4
|
||
</IfModule>
|
||
~~~
|
||
|
||
que l'on active à l'installation via la commande :
|
||
|
||
~~~
|
||
# a2enconf z-evolinux-defaults.conf
|
||
~~~
|
||
|
||
Le fichier `/etc/apache2/ipaddr_whitelist.conf` centralise les
|
||
adresses IP privilégiées, on peut ainsi utiliser `Include ipaddr_whitelist.conf`
|
||
à différents endroits dans la configuration d'Apache sans dupliquer ces adresses :
|
||
|
||
~~~{.apache}
|
||
Require ip 192.0.2.42
|
||
~~~
|
||
|
||
Pour la [gestion des droits](#gestion-des-droits) on ajoute dans le fichier `/etc/apache2/envvars` :
|
||
|
||
~~~{.apache}
|
||
umask 007
|
||
~~~
|
||
|
||
Et pour éviter les erreurs liés à [la limite de fichiers ouverts trop basse](#too-many-open-files) on ajoute dans le fichier `/etc/apache2/envvars` :
|
||
|
||
~~~{.apache}
|
||
APACHE_ULIMIT_MAX_FILES='ulimit -n 65536'
|
||
~~~
|
||
|
||
### Valider la configuration
|
||
|
||
Avant de redémarrer le serveur, vérifier que vous n'ayez pas introduit
|
||
des erreurs de syntaxe dans la configuration :
|
||
|
||
~~~
|
||
# apache2ctl configtest
|
||
Syntax OK
|
||
~~~
|
||
|
||
## VirtualHost
|
||
|
||
Le terme **VirtualHost** correspond à la séparation de plusieurs
|
||
sites sur un même serveur HTTP. Apache permet d'avoir des VirtualHost
|
||
basés sur des noms de domaine différents (ou des adresses IP
|
||
différentes).
|
||
|
||
Exemple d'un VirtualHost basé sur un nom de domaine via
|
||
`/etc/apache2/sites-available/example.conf` :
|
||
|
||
~~~{.apache}
|
||
<VirtualHost *:80>
|
||
ServerName www.example.com
|
||
ServerAlias example.com
|
||
|
||
DocumentRoot /home/example/www/
|
||
<Directory /home/example/www/>
|
||
Options SymLinksIfOwnerMatch
|
||
AllowOverride AuthConfig Limit FileInfo Indexes
|
||
</Directory>
|
||
|
||
ScriptAlias /cgi-foo /usr/lib/cgi-bin/
|
||
<Directory /usr/lib/cgi-bin/>
|
||
Options +ExecCGI -MultiViews
|
||
AllowOverride None
|
||
|
||
AuthType Basic
|
||
AuthName "Restricted"
|
||
AuthUserFile /home/example/.htpasswd
|
||
require valid-user
|
||
|
||
Include ipaddr_whitelist.conf
|
||
Require ip 192.0.2.43
|
||
</Directory>
|
||
|
||
AssignUserID www-example example
|
||
MaxClientsVHost 150
|
||
|
||
CustomLog /var/log/apache2/access.log vhost_combined
|
||
CustomLog /home/example/log/access.log combined
|
||
ErrorLog /home/example/log/error.log
|
||
|
||
RewriteEngine On
|
||
UseCanonicalName On
|
||
RewriteCond %{HTTP_HOST} !^www.example.com$
|
||
RewriteRule ^/(.*) http://%{SERVER_NAME}/$1 [L,R]
|
||
|
||
</VirtualHost>
|
||
# vim: set filetype=apache expandtab shiftwidth=4 softtabstop=4 tabstop=4 :
|
||
~~~
|
||
|
||
~~~
|
||
# adduser example
|
||
# adduser --ingroup example www-example
|
||
# mkdir /home/example/{www,log,awstats} && chown example: /home/example/{www,log,awstats}
|
||
# a2ensite example
|
||
~~~
|
||
|
||
En cas de gestion de multiples VirtualHost nous utilisons l'interface
|
||
web <https://gitea.evolix.org/evolix/evoadmin-web.git>
|
||
|
||
## Gestion des droits
|
||
|
||
### Séparation complète des VirtualHost
|
||
|
||
L'utilisation d'Apache avec MPM-ITK nous permet d'utiliser des utilisateurs/groupes
|
||
Unix distincts pour chaque VirtualHost. Il est donc impossible
|
||
pour un utilisateur d'accéder en lecture à des fichiers d'un autre
|
||
site, même si un langage dynamique comme PHP est utilisé.
|
||
|
||
|
||
~~~
|
||
# chmod 750 /home/example
|
||
~~~
|
||
|
||
### Restriction en écriture pour Apache
|
||
|
||
Par défaut on souhaite donner uniquement un droit de lecture à
|
||
Apache, sauf sur certains répertoires du type *uploads/*, *cache/*,
|
||
etc. On utilise pour cela un utilisateur distinct pour Apache
|
||
(`www-example`) et la gestion du code via FTP/SSH/SFTP/SCP/RSYNC
|
||
(example), le groupe étant identique. Ainsi on donnera la permission
|
||
**`g+w`** pour les répertoires nécessitant un droit en écriture
|
||
pour Apache. Les fichiers uploadés via Apache auront des permissions
|
||
adéquats (`g+rwX`) pour être lus/modifiés/effacés via
|
||
FTP/SSH/SFTP/SCP/RSYNC (l'opération `chmod` est impossible mais
|
||
n'est pas nécessaire car le umask d'Apache est forcé à 007).
|
||
|
||
|
||
~~~
|
||
# echo "umask 027" >> /etc/profile
|
||
# find /home/example -type f -user www-example -exec chmod 660 {} \;
|
||
# find /home/example -type d -user www-example -exec chmod 770 {} \;
|
||
~~~
|
||
|
||
Attention, il ne faut jamais forcer les droits récursivement sur
|
||
toute l'arborescence. Si la restriction en écriture pour Apache est
|
||
impossible à gérer, on n'utilisera pas d'utilisateur distinct en
|
||
indiquant `AssignUserID example example` ce qui éliminera 100% des
|
||
problèmes de droits (mais pose des problèmes de sécurité).
|
||
|
||
|
||
## SSL
|
||
|
||
Apache peut gérer des connexions sécurisées via HTTPS.
|
||
|
||
~~~
|
||
# a2enmod ssl
|
||
~~~
|
||
|
||
Il faut alors générer une clé privée et un certificat auto-signé
|
||
ou délivré par une autorité de certification.
|
||
|
||
Voir [HowtoSSL]()
|
||
|
||
Exemple pour générer un certificat auto-signé :
|
||
|
||
~~~
|
||
# openssl req -newkey rsa:2048 -sha256 -nodes -keyout private.key -out demande.csr
|
||
# openssl x509 -req -days 3650 -sha256 -in demande.csr -signkey private.key -out certificate.crt
|
||
# mv private.key /etc/ssl/private/ && chown root:ssl-cert /etc/ssl/private/private.key && chmod 640 /etc/ssl/private/private.key
|
||
# mv certificate.crt /etc/ssl/certs/ && chown root:root /etc/ssl/certs/certificate.crt && chmod 644 /etc/ssl/certs/certificate.crt
|
||
~~~
|
||
|
||
La configuration d'un VirtualHost pour HTTPS pourra ainsi ressembler à :
|
||
|
||
~~~{.apache}
|
||
<VirtualHost *:80 *:443>
|
||
ServerName secure.example.com
|
||
ServerAlias www.example.com example.com
|
||
|
||
SSLEngine on
|
||
SSLProtocol all -SSLv2 -SSLv3
|
||
SSLCertificateKeyFile /etc/ssl/private/private.key
|
||
SSLCertificateFile /etc/ssl/certs/certificate.crt
|
||
#SSLCertificateChainFile /etc/ssl/certs/certificates_chain.pem
|
||
|
||
RewriteEngine On
|
||
RewriteCond %{HTTPS} !=on
|
||
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
|
||
RewriteRule ^/(.*) https://%{SERVER_NAME}/$1 [L,R=permanent]
|
||
</VirtualHost>
|
||
~~~
|
||
|
||
La chaine de certification ou le certificat de l'autorité peut aussi être ajouté à la fin du fichier contenant le certificat `/etc/ssl/certs/certificate.crt`.
|
||
|
||
Pour une configuration avancée des paramètres SSL, voir
|
||
[HowtoSSL#configuration-apache]()
|
||
|
||
> *Note* : la syntaxe `<VirtualHost *:80 *:443>` n'est possible
|
||
qu'à partir de Debian 8.
|
||
|
||
|
||
### Codes d'erreurs :
|
||
|
||
`SSL_ERROR_RX_RECORD_TOO_LONG` :
|
||
|
||
* Cela peut être causé par un problème dans les directives <VirtualHost>. Par exemple, si certains vhost écouent sur `*:$port` et d'autre sur `$IP:$port`.
|
||
* Il est aussi possible que la résolution IPv6 ne se fasse pas correctement.
|
||
|
||
|
||
|
||
### SSL Client
|
||
|
||
Il est aussi possible de faire du SSL client pour authentifier celui-ci. Pour cela vous devez avoir une CA en mesure d'émetre des certificats SSL pour vos clients. Vous pouvez utiliser notre outil [Shellpki](https://gitea.evolix.org/evolix/shellpki)
|
||
|
||
Pré-requis : Le vhost doit être en SSL.
|
||
|
||
On va alors ajouter les directives suivante dans le(s) vhosts qui doivent faire de l'authentification client :
|
||
|
||
~~~
|
||
|
||
# SSL Client, CA example.net
|
||
SSLCACertificateFile /etc/ssl/certs/example.net_CA_R01.pem
|
||
#SSLCARevocationFile /etc/ssl/crl/example.net_CA_R01.pem
|
||
SSLVerifyClient require
|
||
SSLVerifyDepth 1
|
||
~~~
|
||
|
||
Explication rapide des directives :
|
||
|
||
* `SSLCACertificateFile` : Chemin vers le certificat faisant autorité
|
||
* `SSLCARevocationFile` : Chemin vers la liste de révocation
|
||
* `SSLVerifyClient` :
|
||
* *require* : Le client **doit** présenter un certificat valide
|
||
* *optional* : Le client **peut** présenter un certificat valide
|
||
* *none* : Le client n'as pas à présenter un certificat valide
|
||
* `SSLVerifyDepth` : La profondeur de vérification
|
||
|
||
|
||
Après, au travers de la configuration, on a accès aux variables donnant des informations sur le certificat client. On peut ainsi faire facilement des autorisations avec des expressions régulières pour autoriser un groupe de client selon le CN du certificat :
|
||
|
||
~~~
|
||
Require expr "%{SSL_CLIENT_S_DN_CN} =~ /.*\.france\.example\.net/"
|
||
~~~
|
||
|
||
Voir la documentation du [module SSL d'Apache](https://httpd.apache.org/docs/2.4/fr/mod/mod_ssl.html)
|
||
|
||
## Logs
|
||
|
||
<https://httpd.apache.org/docs/2.4/fr/logs.html>
|
||
|
||
La directive **CustomLog** permet de définir le journal des accès
|
||
; plusieurs formats existent : combined, common, vhost_combined,
|
||
etc. Cette directive peut être utilisée plusieurs fois, il y aura
|
||
plusieurs fichiers de logs différents.
|
||
|
||
~~~{.apache}
|
||
CustomLog log/global_access.log vhost_combined
|
||
CustomLog log/access.log combined
|
||
~~~
|
||
|
||
Si besoin, on peut ignorer certaines requêtes HTTP ainsi :
|
||
|
||
~~~{.apache}
|
||
SetEnvIf User-Agent "Foo" dontlog
|
||
CustomLog log/access.log combined env=!dontlog
|
||
~~~
|
||
|
||
La directive **ErrorLog** permet de définir le journal d'erreurs Apache.
|
||
|
||
~~~{.apache}
|
||
ErrorLog log/error.log
|
||
~~~
|
||
|
||
### Logué la taille de la requête et le temps d’exécution
|
||
|
||
Si l'on veut loguer, dans un log séparé, la taille de la requête
|
||
et le temps d’exécution par Apache de cette requête, on peut créer
|
||
une configuration apache avec le LogFormat suivant, dans
|
||
*/etc/apache2/**conf-available**/access-log-time.conf* :
|
||
|
||
~~~
|
||
LogFormat "%h %t \"%r\" %B %D" measure-time
|
||
~~~
|
||
|
||
Et mettre dans le vhost concerné le CustomLog suivant :
|
||
|
||
~~~
|
||
CustomLog /home/example/log/access_log_time.log measure-time
|
||
~~~
|
||
|
||
Ainsi nous aurons ces infos :
|
||
IP client, date, la requête, taille de la réponse en octet, temps de réponse en microsecondes.
|
||
|
||
## Configuration avancée
|
||
|
||
### mod_deflate
|
||
|
||
La compression des fichiers HTML/TXT/XML/CSS/JS/RSS en GZIP se fait désormais par défaut car le module **mod_deflate** est activé dès l'installation.
|
||
|
||
Voici ses paramètres par défaut :
|
||
|
||
~~~
|
||
<IfModule mod_deflate.c>
|
||
<IfModule mod_filter.c>
|
||
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
|
||
AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
|
||
AddOutputFilterByType DEFLATE application/rss+xml
|
||
AddOutputFilterByType DEFLATE application/xml
|
||
</IfModule>
|
||
</IfModule>
|
||
~~~
|
||
|
||
On conseille d'ajouter :
|
||
|
||
~~~
|
||
AddOutputFilterByType DEFLATE image/svg+xml
|
||
AddOutputFilterByType DEFLATE text/javascript
|
||
AddOutputFilterByType DEFLATE application/json
|
||
~~~
|
||
|
||
### mod_log_forensic
|
||
|
||
Le module [log_forensic](http://httpd.apache.org/docs/2.4/mod/mod_log_forensic.html) trace les entêtes de chaque requête. C'est particulièrement pratique pour comprendre si un proxy envoie bien les bons en-têtes… et dans quel ordre les réponses sont renvoyées par rapport aux requêtes (grâce à des identificants uniques).
|
||
|
||
~~~
|
||
# a2enmod log_forensic
|
||
# systemctl restart apache2
|
||
~~~
|
||
|
||
Il faut ensuite activer par VHost :
|
||
|
||
~~~
|
||
<VirtualHost>
|
||
[…]
|
||
ForensicLog /pat/to/log/forensic.log
|
||
[…]
|
||
</VirtualHost>
|
||
~~~
|
||
|
||
|
||
### mod_proxy_http
|
||
|
||
Le module [proxy](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html)
|
||
permet d'utiliser Apache en tant que proxy, notamment reverse-proxy HTTP :
|
||
|
||
~~~
|
||
# a2enmod proxy_http
|
||
~~~
|
||
|
||
Exemple avec un service HTTP local :
|
||
|
||
~~~{.apache}
|
||
ProxyPreserveHost On
|
||
ProxyPass /foo/ http://127.0.0.1:8080/bar
|
||
ProxyPassReverse /foo/ http://127.0.0.1:8080/bar
|
||
<Proxy *>
|
||
Require all granted
|
||
</Proxy>
|
||
~~~
|
||
|
||
Exemple avec un service HTTP distant (pratique pour une migration
|
||
immédiate d'un serveur vers un autre) :
|
||
|
||
~~~{.apache}
|
||
ProxyPreserveHost On
|
||
ProxyPass / http://192.0.2.17/
|
||
ProxyPassReverse / http://192.0.2.17/
|
||
<Proxy *>
|
||
Require all granted
|
||
</Proxy>
|
||
~~~
|
||
|
||
et pour un HTTPS distant, on ajoute `SSLProxyEngine On` :
|
||
|
||
~~~{.apache}
|
||
SSLProxyEngine On
|
||
ProxyPreserveHost On
|
||
ProxyPass / https://192.0.2.17/
|
||
ProxyPassReverse / https://192.0.2.17/
|
||
<Proxy *>
|
||
Require all granted
|
||
</Proxy>
|
||
~~~
|
||
|
||
S'il y a besoin d'appliquer des règles spécifiques tel que des Alias avant que les requêtes soient proxifiés:
|
||
|
||
~~~
|
||
Alias /dossier/ /home/$USER/dossier/
|
||
<Directory /home/$USER/dossier/>
|
||
Options +SymLinksIfOwnerMatch +Indexes
|
||
Require all granted
|
||
</Directory>
|
||
|
||
ProxyPreserveHost On
|
||
ProxyPassMatch ^/dossier !
|
||
ProxyPass / http://127.0.0.1:3504/
|
||
ProxyPassReverse / http://127.0.0.1:3504/
|
||
~~~
|
||
|
||
Il est possible d'ajouter à la fin de directive `ProxyPass` l'argument `timeout=<DELAY>` (en secondes). La valeur par défaut est celle de `ProxyTimeout`, qui elle-même a pour valeur par défaut la valeur de `Timeout`.
|
||
|
||
|
||
### mod_remoteip / mod_rpaf
|
||
|
||
Le module [mod_remoteip](https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html) (anciennement **mod_rpaf**) permet d'utiliser la 1ère adresse IP située dans un entête HTTP type *X-Forwarded-For* pour les logs Apache et directives "Require" de**mod_authz_host**.
|
||
|
||
Voici un exemple d'utilisation en Wheezy pour un reverse-proxy avec les adresses IP 192.0.2.10 et 192.0.2.11 :
|
||
|
||
~~~
|
||
# apt install libapache2-mod-rpaf
|
||
# cat /etc/apache2/mods-enabled/rpaf.conf
|
||
<IfModule rpaf_module>
|
||
RPAFenable On
|
||
RPAFsethostname On
|
||
#RPAFheader X-Forwarded-For
|
||
RPAFproxy_ips 127.0.0.1 192.0.2.10 192.0.2.11
|
||
</IfModule>
|
||
~~~
|
||
|
||
*Note :* bien mettre l'IP du reverse-proxy dans `RPAFproxy_ips`
|
||
|
||
Voici un exemple d'utilisation en Stretch pour un reverse-proxy avec l'adresse IP publique 192.0.2.10 et le réseau public 192.0.2.128/25 :
|
||
|
||
~~~
|
||
# a2enmod remoteip
|
||
# cat /etc/apache2/conf-available/zzz-evolinux-custom.conf
|
||
|
||
<IfModule remoteip_module>
|
||
RemoteIPHeader X-Forwarded-For
|
||
|
||
RemoteIPTrustedProxy 192.0.2.10
|
||
RemoteIPTrustedProxy 192.0.2.128/25
|
||
</IfModule>
|
||
|
||
# systemctl reload apache2
|
||
~~~
|
||
|
||
> *Note* : sur Debian 9 (Stretch, Apache < 2.4.38), si l’IP 127.0.0.1 est logguée par Apache à la place de celle du client, ajoutez l’IP 127.0.0.1 à la directive RemoteIPInternalProxy.
|
||
|
||
> *Note* : Le traitement effectué par remoteip "vide" le header *X-Forwarded-For*. Les IP intermédiaires des proxy listés avec la directive `RemoteIPInternalProxy` seront retirées (comme si on "dépile").
|
||
Pour une application en PHP. Il est donc possible que le header ne soit plus présent. L'IP du visiteur sera directement récupérable via `$_SERVER['REMOTE_ADDR']`
|
||
|
||
Attention au niveau des logs il peut aussi être nécessaire de modifier le `LogFormat`, en remplaçant `%h` par `%a` :
|
||
|
||
~~~
|
||
# cat /etc/apache2/conf-available/zzz-evolinux-custom.conf
|
||
|
||
LogFormat "%v:%p %a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
|
||
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||
LogFormat "%a %l %u %t \"%r\" %>s %O" common
|
||
~~~
|
||
|
||
### mod_xsendfile
|
||
|
||
Le module [xsendfile](https://tn123.org/mod_xsendfile/) permet de
|
||
rediriger l'envoi d'un fichier vers Apache via un Header HTTP,
|
||
notamment utilisé pour servir des fichiers via Apache tout en
|
||
permettant de gérer le contrôle d'accès via une application web.
|
||
|
||
~~~
|
||
# apt install libapache2-mod-xsendfile
|
||
~~~
|
||
|
||
Pour autoriser son utilisation via *.htaccess*, on ajoute `Options`
|
||
à `AllowOverride`.
|
||
|
||
### mod_negotiation
|
||
|
||
Le module
|
||
[negotiation](https://httpd.apache.org/docs/2.4/mod/mod_negotiation.html)
|
||
permet de servir des documents en fonction de critère, notamment
|
||
la langue acceptée par le client HTTP.
|
||
|
||
~~~
|
||
# a2enmod negotiation include alias
|
||
~~~
|
||
|
||
Vous pouvez éditer le fichier
|
||
`/etc/apache2/conf-enabled/localized-error-pages.conf` qui permet
|
||
d'afficher des pages d'erreur en différentes langues, et décommenter
|
||
cette partie :
|
||
|
||
Exemple avec un service HTTP local :
|
||
|
||
~~~{.apache}
|
||
<IfModule mod_negotiation.c>
|
||
<IfModule mod_include.c>
|
||
<IfModule mod_alias.c>
|
||
|
||
Alias /error/ "/usr/share/apache2/error/"
|
||
|
||
<Directory "/usr/share/apache2/error">
|
||
Options IncludesNoExec
|
||
AddOutputFilter Includes html
|
||
AddHandler type-map var
|
||
Require all granted
|
||
LanguagePriority en cs de es fr it nl sv pt-br ro
|
||
ForceLanguagePriority Prefer Fallback
|
||
</Directory>
|
||
|
||
ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var
|
||
ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var
|
||
ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var
|
||
ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var
|
||
ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var
|
||
ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var
|
||
ErrorDocument 410 /error/HTTP_GONE.html.var
|
||
ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var
|
||
ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var
|
||
ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var
|
||
ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var
|
||
ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var
|
||
ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var
|
||
ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var
|
||
ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var
|
||
ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var
|
||
ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var
|
||
</IfModule>
|
||
</IfModule>
|
||
</IfModule>
|
||
~~~
|
||
|
||
### mod_geoip
|
||
|
||
Ce module permet de pouvoir traiter différemment des visiteurs par
|
||
rapport à leur pays d'origine directement depuis la configuration
|
||
d'Apache. Il utilise pour cela la base GeoIP de
|
||
[Maxmind](https://www.maxmind.com).
|
||
|
||
~~~
|
||
# apt install geoip-database-extra libapache2-mod-geoip
|
||
~~~
|
||
|
||
Note: geoip-database-contrib (dans les dépots contrib) va installer
|
||
un cron qui va mettre à jour les fichiers de la base GeoIP. Ces
|
||
fichiers de base se trouvent dans `/usr/share/GeoIP/`
|
||
|
||
Quand on va avoir besoin de GeoIP, il faut penser à l'activer dans
|
||
le(s) fichier(s) de confs
|
||
|
||
~~~
|
||
<IfModule mod_geoip.c>
|
||
GeoIPEnable On
|
||
GeoIPDBFile /usr/share/GeoIP/GeoIP.dat
|
||
</IfModule>
|
||
~~~
|
||
|
||
De là, le pays d'origine du visiteur, ainsi que d'autres informations
|
||
sont placées dans des variables d'environnement utilisables dans
|
||
le VHOST.
|
||
|
||
Exemple : Autoriser que les visiteurs venant de France pour un
|
||
accéder à un dossier précis :
|
||
|
||
~~~
|
||
<Directory /var/www/foo/bar>
|
||
Require expr %{GEOIP_COUNTRY_CODE } == 'FR'
|
||
</Directory>
|
||
~~~
|
||
|
||
Faire une redirection suivant le pays :
|
||
|
||
~~~
|
||
RewriteEngine on
|
||
RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^FR$
|
||
RewriteRule ^(.*)$ https://www.example.fr$1 [R,L]
|
||
~~~
|
||
|
||
La documentation complète du module est là Pour plus d'info, il y
|
||
a la [documentation complète du
|
||
module](https://dev.maxmind.com/geoip/legacy/mod_geoip2/)
|
||
|
||
### mod_davfs
|
||
|
||
Ce module permet de prendre en charge WebDAV.
|
||
|
||
Pour l'activer :
|
||
|
||
~~~
|
||
# a2enmod davfs
|
||
~~~
|
||
|
||
Ensuite pour activer le module, il suffit de rajouter `Dav On` dans
|
||
le virtualhost voulu.
|
||
|
||
Exemple de vhost :
|
||
|
||
~~~
|
||
<VirtualHost *:443>
|
||
|
||
ServerName dav.example.com
|
||
|
||
DocumentRoot /home/webdav/
|
||
<Directory "/home/webdav/">
|
||
Dav On
|
||
|
||
AuthType Basic
|
||
AuthName DAV
|
||
AuthUserFile "/etc/apache2/webdav.htpasswd"
|
||
Require valid-user
|
||
</Directory>
|
||
|
||
ErrorLog ${APACHE_LOG_DIR}/dav_error.log
|
||
CustomLog ${APACHE_LOG_DIR}/dav_access.log combined
|
||
|
||
SSLEngine on
|
||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||
</VirtualHost>
|
||
~~~
|
||
|
||
### mod_qos
|
||
|
||
<http://mod-qos.sourceforge.net/>
|
||
|
||
Le module qos (Quality of Service) est un module de qualité de service pour le serveur Web Apache mettant en œuvre des mécanismes de contrôle qui peuvent fournir différents niveaux de priorité à différentes requêtes HTTP.
|
||
|
||
mod_qos peut être utilisé pour déterminer quelles requêtes doivent être traitées et lesquelles ne doivent pas l'être afin de ne pas avoir une surcharge sur les ressources du serveur Apache.
|
||
|
||
Le module collecte différents attributs tels que l'URL de demande, les en-têtes de demande et de réponse HTTP, l'adresse IP source, les codes de pays, le code de réponse HTTP, les données d'historique (basées sur la session utilisateur et l'adresse IP source), le nombre de demandes simultanées au serveur (total ou requêtes ayant des attributs similaires), le nombre de connexions TCP simultanées (totales ou à partir d'une seule IP source), etc...
|
||
|
||
Dans une configuration simple on peux utilisé les variables suivantes :
|
||
|
||
~~~
|
||
<IfModule qos_module>
|
||
QS_SrvRequestRate 120
|
||
QS_SrvMaxConn 150
|
||
QS_SrvMaxConnClose 200
|
||
QS_SrvMaxConnPerIP 20
|
||
</IfModule>
|
||
~~~
|
||
|
||
`QS_SrvRequestRate` permet d'appliquer un débit de téléchargement minimal en lecture uniquement (en octet/sec)
|
||
`QS_SrvMaxConn` limites de connexion par virtual host
|
||
`QS_SrvMaxConnClose` permet un support keep-alive jusqu'à ce que le serveur atteigne n connexions
|
||
`QS_SrvMaxConnPerIP` autorise n connexion par adresse ip
|
||
|
||
Si l'on veux mettre en liste blanche une ip bien précise on peux le faire avec la configuration suivante (remplacer $IP par la bonne valeur) :
|
||
|
||
~~~
|
||
SetEnvIf Remote_Addr $IP QS_VipRequest=yes
|
||
~~~
|
||
|
||
### mod_substitute
|
||
|
||
1. Activer le module avec la commande `a2enmod substitute`, puis redémarrer Apache.
|
||
2. Dans le _vhost_ où on souhaite faire la substitution :
|
||
|
||
~~~
|
||
AddOutputFilterByType SUBSTITUTE text/html
|
||
Substitute "s/REGEXP/REMPLACEMENT/i"
|
||
~~~
|
||
|
||
> Le « i » à la fin de la commande de substitution permet d’ignorer la casse.
|
||
|
||
#### Cas d’un proxy
|
||
|
||
Si le _vhost_ qui fait la substitution fait aussi proxy entre le client et le serveur web, la substitution ne fonctionnera pas, car le corps de la réponse est compressé. Pour faire la substitution, il faut modifier la manière dont Apache traite la réponse avant de l’envoyer au client :
|
||
|
||
~~~
|
||
SetOutputFilter INFLATE;proxy-html;DEFLATE
|
||
AddOutputFilterByType SUBSTITUTE text/html
|
||
Substitute "s/REGEXP/REMPLACEMENT/i"
|
||
~~~
|
||
|
||
## Autorisations
|
||
|
||
Historiquement Apache, utilisait les directives `Order`, `Allow`, `Deny`, et `Satisfy` pour définir les autorisations des ressources qu'il fournissait. Depuis la version 2.4 ces directives sont dépréciées, il ne faut donc plus les utiliser ; bien qu'elles soient toujours utilisables via le module [`mod_access_compat`](https://httpd.apache.org/docs/2.4/fr/mod/mod_access_compat.html). Ces quatres directives sont remplacées par `Require` fournies par le module [`mod_authz_host`](https://httpd.apache.org/docs/2.4/fr/mod/mod_authz_host.html) ; la migration sommaire d'un module à l'autre est décrit dans la [release note 2.4](https://httpd.apache.org/docs/2.4/fr/upgrading.html#run-time).
|
||
|
||
**/!\\ Il ne faut jamais mélanger d'anciennes directives comme Order, Allow ou Deny avec des nouvelles comme Require, car le comportement n'est pas défini ce qui vous emenera dans du deboguage interminable. /!\\**
|
||
|
||
Voici quelques exemples d'utilisation de la directive `Require` :
|
||
|
||
~~~{.apache}
|
||
# Autoriser l'accès à tout le monde
|
||
Require all granted
|
||
# Interdire l'accès à tout le monde
|
||
Require all denied
|
||
# Limiter l'accès à une IP
|
||
Require ip 192.0.2.21
|
||
# Autoriser l'accès "local"
|
||
Require local
|
||
# Autoriser l'accès à tout le monde mais bloquer une IP spécifique
|
||
<RequireAll>
|
||
Require all granted
|
||
Require not ip 192.0.2.21
|
||
</RequireAll>
|
||
# Limiter l'accès à des utilisateurs valides (authentification HTTP)
|
||
AuthType Basic
|
||
AuthBasicProvider file
|
||
AuthUserFile /foo/.htpasswd
|
||
Require valid-user
|
||
# Autoriser une IP OU un utilisateur valide (équivalent de l'ancien "Satisfy Any")
|
||
AuthType Basic
|
||
AuthBasicProvider file
|
||
AuthUserFile /foo/.htpasswd
|
||
Require valid-user
|
||
Require ip 192.0.2.21
|
||
# Autoriser des IP ET un utilisateur valide
|
||
AuthType Basic
|
||
AuthBasicProvider file
|
||
AuthUserFile /foo/.htpasswd
|
||
<RequireAll>
|
||
<RequireAny>
|
||
Require ip 192.0.2.21
|
||
Require ip 192.0.2.22
|
||
</RequireAny>
|
||
require valid-user
|
||
</RequireAll>
|
||
~~~
|
||
|
||
### Authentification HTTP
|
||
|
||
#### HTTP Basic Authentication (mod_auth_basic)
|
||
|
||
[Basic](http://httpd.apache.org/docs/2.4/mod/mod_auth_basic.html)
|
||
est la méthode d'authenfication HTTP la plus simple et la plus
|
||
répandue. La configuration se fait via *VirtualHost* ou *.htaccess*
|
||
:
|
||
|
||
~~~{.apache}
|
||
AuthType Basic
|
||
AuthName "Restricted"
|
||
AuthUserFile /foo/.htpasswd
|
||
AuthGroupFile /dev/null
|
||
require valid-user
|
||
~~~
|
||
|
||
La gestion du fichier *.htpasswd* se gère via la commande `htpasswd`.
|
||
|
||
Pour créer l'utilisateur *foo* :
|
||
|
||
~~~{.bash}
|
||
$ htpasswd /foo/.htpasswd foo
|
||
New password:
|
||
Re-type new password:
|
||
Adding password for user foo
|
||
~~~
|
||
|
||
|
||
#### HTTP Digest Authentication (mod_auth_digest)
|
||
|
||
Il est possible d'utiliser la méthode d'authentification Digest
|
||
plutôt que Basic en activant le module :
|
||
|
||
~~~
|
||
# a2enmod auth_digest
|
||
~~~
|
||
|
||
La configuration est quasi-identique au type Basic :
|
||
|
||
~~~{.apache}
|
||
AuthType Digest
|
||
AuthName "Restricted"
|
||
AuthUserFile /foo/.htpasswd
|
||
Require valid-user
|
||
~~~
|
||
|
||
La gestion du fichier *.htpasswd* se gère alors via la commande `htdigest`.
|
||
|
||
### Authentification via LDAP (mod_authnz_ldap)
|
||
|
||
~~~
|
||
# a2enmod authnz_ldap
|
||
~~~~
|
||
|
||
Voici un exemple de configuration :
|
||
|
||
~~~{.apache}
|
||
AuthBasicProvider ldap
|
||
AuthName "Restricted"
|
||
AuthType Basic
|
||
AuthLDAPURL ldaps://ldap.example.com/ou=people,dc=example,dc=com?uid?one?(filter)
|
||
Require valid-user
|
||
~~~
|
||
|
||
> Pour le cas d’une connexion à un Active Directory, il faut probablement remplacer `uid` par `sAMAccountName`.
|
||
>
|
||
> ~~~ { .apache }
|
||
> AuthLDAPURL ldaps://ldap.example.com/ou=people,dc=example,dc=com?sAMAccountName?one?(filter)
|
||
> ~~~
|
||
|
||
|
||
Note : pour utiliser *ldaps* avec un certificat non reconnu par le
|
||
système, il faut ajouter la directive suivante dans la configuration
|
||
globale d'Apache `LDAPVerifyServerCert off`.
|
||
|
||
## Rewrite Rules
|
||
|
||
* mod_rewrite : <http://httpd.apache.org/docs/2.4/mod/mod_rewrite.html>
|
||
* drapeaux utilisables : <https://httpd.apache.org/docs/2.4/rewrite/flags.html>
|
||
|
||
On doit mettre la valeur de la directive suivante pour activer les règles de réécriture :
|
||
|
||
~~~
|
||
RewriteEngine On
|
||
~~~
|
||
|
||
Voici quelques exemple de règles de redirections:
|
||
|
||
~~~
|
||
# rediriger la page d'accueil avec un code 301
|
||
RedirectPermanent / http://new.example.com
|
||
|
||
# rediriger n'importe quelle requête en conservant le chemin
|
||
RedirectMatch ^/(.*)$ http://new.example.com/$1
|
||
|
||
# rediriger la page d'accueil vers un autre chemin
|
||
RedirectMatch ^/$ /sub/
|
||
|
||
# rediriger n'importe quelle requête en conservant le chemin avec des exceptions
|
||
RewriteCond %{REMOTE_ADDR} !^192\.0\.2\.129
|
||
RewriteRule ^/(.*) http://new.example.com/$1 [L,R=permanent]
|
||
|
||
# rediriger vers HTTPS sauf pour certaines requetes
|
||
RewriteCond %{HTTPS} !=on
|
||
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
|
||
RewriteCond %{REQUEST_URI} !^/.well-known/pki-validation/
|
||
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge/
|
||
RewriteRule ^/(.*) https://%{SERVER_NAME}/$1 [L,R]
|
||
|
||
# le drapeau NC permet de ne pas tenir compte de la casse
|
||
RewriteRule ^/FoO.tXt /sub/ [L,R,NC]
|
||
|
||
# empêcher des requêtes POST sur une URL particulière
|
||
RewriteCond %{REQUEST_METHOD} POST
|
||
RewriteRule ^/foo.txt [L,F]
|
||
|
||
# rediriger vers HTTPS dans un VirtualHost mixte HTTP/HTTPS
|
||
RewriteCond %{HTTPS} !=on
|
||
RewriteRule ^/(.*) https://%{SERVER_NAME}/$1 [L,R=permanent]
|
||
|
||
# mettre un site en maintenance (code 503) avec des exceptions
|
||
RewriteCond %{REMOTE_ADDR} !^192\.0\.2\.129
|
||
RewriteRule ^.*$ / [R=503,L]
|
||
ErrorDocument 503 "Maintenance temporaire, veuillez patienter. Merci."
|
||
#ErrorDocument 503 http://maintenance.evolix.org/
|
||
#Header Set Cache-Control "no-cache, no-store"
|
||
#Header Set Pragma "no-cache"
|
||
|
||
# Rediriger un sous-domaine *-app.domaine.tld vers *.domaine.tld :
|
||
RewriteCond %{HTTP_HOST} ^(.*)-app.domaine.tld$
|
||
RewriteRule ^/(.*)$ https://%1.domaine.tld/$1 [R]
|
||
|
||
# Afficher une page de maintenance si elle existe et le contenu du site s'affiche pour ceux ayant l'IP spécifié.
|
||
RewriteCond %{REMOTE_ADDR} !^123\.456\.X\.X
|
||
RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
|
||
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
|
||
RewriteRule ^.*$ /maintenance.html [R=503,L]
|
||
ErrorDocument 503 /maintenance.html
|
||
Header Set Cache-Control "max-age=0, no-store"
|
||
|
||
# Rediriger sur une page sans ses paramètres et valeurs.
|
||
RewriteCond %{REQUEST_URI} ^/index\.php$
|
||
RewriteCond %{QUERY_STRING} ^parametre=valeur$
|
||
RewriteRule (.*) /page-destination [QSD,R=301,L]
|
||
|
||
# Substituer un caractère (tel que page.php/ en page.php? ) :
|
||
RewriteCond %{REQUEST_URI} (.*page.php)\/(key=.*)
|
||
RewriteRule (.*) /%1?%2 [QSD,R=301,L]
|
||
|
||
~~~
|
||
|
||
Pour supprimer un Query String avec une Rewrite Rule :
|
||
<https://www.philipphoffmann.de/blog/2012/08/16/how-to-discard-the-query-string-in-a-rewriterule-apache-mod_rewrite/>
|
||
|
||
### Redirection https
|
||
|
||
Dans le cas où le serveur n'écoute que sur le port 80, derrière un
|
||
proxy qui fait la terminaison SSL mais ne gère pas les redirections
|
||
(exemple Amazon ELB), on peut forcer la redirection directement
|
||
dans Apache en utilisant la valeur de l'en-tête `X-Forwarded-Proto`
|
||
:
|
||
|
||
~~~
|
||
RewriteEngine On
|
||
RewriteCond %{HTTPS} !=on
|
||
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
|
||
RewriteCond %{REQUEST_URI} !^/.well-known/
|
||
RewriteRule ^ https://%{HTTP:Host}%{REQUEST_URI} [L,R=permanent]
|
||
~~~
|
||
|
||
> Il est recommandé que la redirection HTTP vers HTTPS soit la première redirection rencontré pour un site (avant même la redirection vers le nom de domaine principale) et que toute redirection après celle-ci reste en HTTPS.
|
||
|
||
## Conditions
|
||
|
||
À partir de la version Apache 2.4, on peut utiliser des conditions
|
||
pour l'application des directives (l'imbrication de multiples <If>
|
||
n'est disponible que pour les versions >= 2.4.26).
|
||
|
||
~~~{.bash}
|
||
<If "%{HTTP_HOST} == 'test.example.com'">
|
||
SetEnv APP_ENV "test"
|
||
</If>
|
||
<If "%{HTTP_QUERY} =~ /wp-admin*/">
|
||
...
|
||
</If>
|
||
~~~
|
||
|
||
Expressions possibles : [https://httpd.apache.org/docs/2.4/expr.html](https://httpd.apache.org/docs/2.4/expr.html)
|
||
|
||
|
||
## Attaques DOS (Denial Of Service) ou XSS
|
||
|
||
### mod_evasive
|
||
|
||
Le module **mod_evasive** permet de limiter certaines attaques DoS
|
||
en limitant l'accès à une ou plusieurs pages par un temps de
|
||
banissement d'une IP source. Par défaut, nous ajustons la configuration
|
||
de façon à ce que si une adresse IP accède plus de 5 fois à la même
|
||
page en 30s, ou à plus de 30 requêtes sur tout le site en 1s, il
|
||
sera banni (erreur HTTP 403) pendant 60s. Une notification sera
|
||
alors envoyée à syslog et par email.
|
||
|
||
Dans la configuration de mod_evasive (dans `/etc/apache2/conf-enabled/`) :
|
||
|
||
~~~{.apache}
|
||
<IfModule mod_evasive20.c>
|
||
DOSHashTableSize 3097
|
||
# Si une adresse IP accède plus de 5 fois à la même page en 30s
|
||
DOSPageCount 5
|
||
DOSPageInterval 30
|
||
# Si une adresse IP accède plus de 30 fois le site en 1s
|
||
DOSSiteCount 30
|
||
DOSSiteInterval 1
|
||
DOSBlockingPeriod 60
|
||
DOSEmailNotify security@example.com
|
||
</IfModule>
|
||
~~~
|
||
|
||
Attention, pour certains sites avec de nombreuses ressources statiques
|
||
sur le même serveur HTTP, il faut souvent désactiver **mod_evasive**
|
||
pour éviter des faux-positifs et ne l'utiliser qu'en cas d'attaques
|
||
récurrentes.
|
||
|
||
On peut mettre en liste blanche une ip ou une plage d'ip, pour que
|
||
le mod_evasive ignore les requêtes venant de ceux-ci :
|
||
|
||
~~~{.apache}
|
||
DOSWhitelist 192.168.2.*
|
||
DOSWhitelist 192.168.7.*
|
||
DOSWhitelist 192.168.1.*
|
||
DOSWhitelist 192.168.8.*
|
||
~~~
|
||
|
||
N.B. : si on veut ignorer plusieurs IP, il faut ajouter des lignes.
|
||
|
||
On peut aussi utiliser un wildcard * peut être autorisé sur les 3
|
||
derniers octets, si nécessaire.
|
||
|
||
Pour "désactiver" mod_evasive spécifiquement pour un vhost :
|
||
|
||
~~~{.apache}
|
||
<IfModule mod_evasive20.c>
|
||
DOSBlockingPeriod 0
|
||
</IfModule>
|
||
~~~
|
||
|
||
|
||
### Fail2Ban
|
||
|
||
[Fail2Ban](HowtoFail2Ban) est indépendant d'Apache, mais peut être
|
||
utilisé pour de la détection plus précise que *mod_evasive* : il
|
||
va lire en permanence les journaux générés par Apache (ce qui peut
|
||
être coûteux en ressources) et il pourra bannir des adresses IP si
|
||
cela répond à certaines règles comme trop de connexions à une page
|
||
d'authentification par exemple.
|
||
|
||
Voir <https://wiki.evolix.org/HowtoFail2Ban#apache-nginx>
|
||
|
||
### ModSecurity
|
||
|
||
L'utilisation du moteur [ModSecurity](http://www.modsecurity.org) permet de bloquer certaines requêtes HTTP (injections SQL, attaques XSS connues, etc.) au niveau d'Apache. On l'installe avec le [OWASP ModSecurity Core Rule Set](https://coreruleset.org/) (CRS) qui est un ensemble de règles couvrant un large spectre d'attaques possibles. Elles sont définit dans le dossier `/usr/share/modsecurity-crs/rules/`
|
||
|
||
Faisant office de pare-feu applicatif, le contenu des demandes clients ET les réponses serveurs sont analysés à la recherche de signes d'attaques pouvant générer de faux-positifs. Il faudra par la suite ajuster les réglages en fonction du niveau de sécurité que l'on veut appliquer à l'application.
|
||
|
||
#### Intégration de ModSecurity dans Apache
|
||
|
||
Installer les deux paquets suivants :
|
||
|
||
~~~
|
||
# apt install libapache2-mod-security2 modsecurity-crs
|
||
~~~
|
||
|
||
Nous faisons une configuration minimale via `/etc/apache2/conf-available/modsecurity.conf` :
|
||
|
||
~~~{.apache}
|
||
<IfModule mod_security2.c>
|
||
|
||
# Activation du moteur gérant les règles CRS
|
||
SecRuleEngine On
|
||
|
||
## Les demandes produit par le client
|
||
# Activation de l'analyse du contenu
|
||
SecRequestBodyAccess On
|
||
# Taille limite du contenu en octets
|
||
#SecRequestBodyLimit 134217728
|
||
# Taille maximale du contenu en mémoire et déplacé sur le disque si besoin
|
||
#SecRequestBodyInMemoryLimit 131072
|
||
# Emplacement sur le disque de données temporaires
|
||
SecTmpDir /tmp
|
||
|
||
## Les réponses produite par le serveur
|
||
# Activation de l'analyse du contenu
|
||
SecResponseBodyAccess Off
|
||
# Taille maximale du contenu en mémoire et déplacé sur le disque si besoin
|
||
#SecResponseBodyLimit 524288
|
||
# Autoriser les réponses de ces types
|
||
SecResponseBodyMimeType (null) text/html text/plain text/xml
|
||
|
||
# Changer l'entête HTTP "Server: Apache"
|
||
#SecServerSignature "Apache/2.2.0 (Fedora)"
|
||
|
||
# Emplacement des fichiers suspicieux envoyé au serveur
|
||
SecUploadDir /tmp
|
||
# Conserver les fichiers interceptés
|
||
SecUploadKeepFiles Off
|
||
# Emplacement des données persistantes (données d'adresse IP, données de sessions, etc)
|
||
SecDataDir /tmp
|
||
|
||
# Action par défaut
|
||
SecDefaultAction "log,auditlog,deny,status:406,phase:2"
|
||
|
||
# Activer le réglage fin du moteur
|
||
SecAuditEngine Off
|
||
# Définis quels codes HTTP doivent être enregistrés dans les logs
|
||
#SecAuditLogRelevantStatus "^[45]"
|
||
# Utiliser un seul fichier de log
|
||
SecAuditLogType Serial
|
||
# Emplacement du fichier de log
|
||
SecAuditLog /var/log/apache2/modsec_audit.log
|
||
# Définis les types de métadonnées qui seront conservés
|
||
SecAuditLogParts "ABIFHZ"
|
||
|
||
# Définis l'argument utilisé pour séparer les URL encodés
|
||
#SecArgumentSeparator "&"
|
||
# Définis le format du cookie
|
||
SecCookieFormat 0
|
||
# Emplacement du fichier de debug
|
||
SecDebugLog /var/log/apache2/modsec_debug.log
|
||
# On définit pas de log
|
||
SecDebugLogLevel 0
|
||
|
||
#########
|
||
# RULES
|
||
#########
|
||
|
||
# Removed because it does not play well with apache-itk
|
||
# Can be removed when modsecurity 2.9.3 hits debian
|
||
# See https://github.com/SpiderLabs/ModSecurity/issues/712
|
||
SecRuleRemoveById "910000-910999"
|
||
SecRuleRemoveById "920000"
|
||
SecRuleRemoveById "930000"
|
||
|
||
</IfModule>
|
||
~~~
|
||
|
||
[Documentation de référence sur les directives Apache de ModSecurity](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x))
|
||
|
||
Nous désactivons le log d'audit par défaut, puisque l’information enregistrée dans le log d'erreur Apache est suffisante pour connaître la raison d'un bloquage. Par contre, il est utile de le réactiver (avec `SecAuditEngine RelevantOnly`) lorsque vous faites un audit des règles applicables a un serveur ou lors de la rédaction de nouvelles règles. Dans ce cas, il existe des [outils](https://github.com/Apache-Labor/labor/blob/master/bin/.apache-modsec.alias)
|
||
pour faciliter l’obtention de statistiques du audit log.
|
||
|
||
|
||
#### Ajustement des règles de ModSecurity
|
||
|
||
Les paramètres du Core Rule Set de ModSecurity peuvent être personalisées dans `/etc/modsecurity/crs/crs-setup.conf`.
|
||
|
||
C'est notamment là qu'on peut autoriser les méthodes HTTP bloquées par défaut, comme PATCH, PUT, DELETE...
|
||
|
||
|
||
#### Affinage des règles pour les applications web type Wordpress
|
||
|
||
Voir <https://coreruleset.org/docs/concepts/false_positives_tuning/>
|
||
|
||
Le ModSecurity Core Rule Set contient des règles spécifiques pour traiter les faux positifs liés à l'utilisation de certaines applications web tel que WordPress.
|
||
|
||
Il faut alors activer une règle de ce type en sélectionnant la ligne correspondant à son applications, par exemple `tx.crs_exclusions_wordpress=1` :
|
||
|
||
~~~
|
||
#SecAction \
|
||
# "id:900130,\
|
||
# phase:1,\
|
||
# nolog,\
|
||
# pass,\
|
||
# t:none,\
|
||
# setvar:tx.crs_exclusions_cpanel=1,\
|
||
# setvar:tx.crs_exclusions_dokuwiki=1,\
|
||
# setvar:tx.crs_exclusions_drupal=1,\
|
||
# setvar:tx.crs_exclusions_nextcloud=1,\
|
||
# setvar:tx.crs_exclusions_phpbb=1,\
|
||
# setvar:tx.crs_exclusions_phpmyadmin=1,\
|
||
# setvar:tx.crs_exclusions_wordpress=1,\
|
||
# setvar:tx.crs_exclusions_xenforo=1"
|
||
|
||
SecAction \
|
||
"id:900130,\
|
||
phase:1,\
|
||
nolog,\
|
||
pass,\
|
||
t:none,\
|
||
setvar:tx.crs_exclusions_wordpress=1"
|
||
~~~
|
||
|
||
|
||
#### Désactiver des règles au cas par cas
|
||
|
||
En général, plutôt que de les désactiver, on préfère régler plus finement le règles définies dans le Core Rules Set (CRS) dans `/etc/modsecurity/crs/crs-setup.conf`.
|
||
|
||
Si après cela, certaines règles posent toujours des problèmes, on peut les désactiver au cas par cas.
|
||
|
||
Pour trouver dans un log toutes les règles qui se sont déclenchées ainsi que leur fréquence :
|
||
|
||
~~~
|
||
# grep ModSecurity <LOG_PATH> | grep -E 'id "[0-9]{6}"' | sed -E 's/.*([0-9]{6}).*/\1/' | sort | uniq -c
|
||
5 911100
|
||
1 920180
|
||
287 920420
|
||
8 930130
|
||
300 949110
|
||
300 980130
|
||
~~~
|
||
|
||
On peut ensuite les désactiver au cas par cas dans un vhost ou dans un `conf.d` d'Apache :
|
||
|
||
~~~
|
||
<IfModule mod_security2.c>
|
||
SecRuleRemoveById "980130"
|
||
...
|
||
</IfModule>
|
||
~~~
|
||
|
||
#### Ajouter des règles
|
||
|
||
On peut écrire ses propres règles que l'on mettra par exemple dans un fichier `/etc/modsecurity/*.conf`.
|
||
|
||
~~~
|
||
$ cat /etc/modsecurity/cve-2018-6389.conf
|
||
|
||
SecRule REQUEST_URI "@rx (?i:/wp-admin/load-scripts.php?.*?(load%5B%5D|load\[\]|load%5B\]|load\[%5D)=([^&,]*,){13,})" "id:20186389,msg:'Potential use of CVE-2018-6389',deny"
|
||
~~~
|
||
|
||
#### Désactiver ModSecurity pour un vhost ou un répertoire
|
||
|
||
On évite de désactiver complètement ModSecurity.
|
||
|
||
Si l'affinage des règles et leur désactivation au cas par cas ne suffit pas, on peut désactiver ModSecurity spécifiquement pour un vhost ou un répertoire avec la directive `SecRuleEngine Off`.
|
||
|
||
Ceci peut être utile en cas de problèmes, mais on préfère identifier les règles problématiques dans les logs et les désactiver au cas par cas avec `SecRuleRemoveById XXXXXX`, comme nous le faisons avec les règles `910XXX`.
|
||
|
||
~~~
|
||
<Directory "/home/monsite/www/wp-admin">
|
||
<IfModule security2_module>
|
||
SecRuleEngine Off
|
||
</IfModule>
|
||
</Directory>
|
||
~~~
|
||
|
||
|
||
## Statistiques
|
||
|
||
### AWStats
|
||
|
||
Voir [HowtoAwstats]().
|
||
|
||
*Note* : une configuration AWStats peut être forcée au sein d'un
|
||
VirtualHost grâce à la directive `SetEnv AWSTATS_FORCE_CONFIG example`
|
||
Les stats AWStats du VirtualHost sont alors accessibles via l'url : <https://mondomaine.com/cgi-foo/awstats.pl>
|
||
|
||
### GoAccess
|
||
|
||
Voir [HowtoGoaccess]().
|
||
|
||
### Matomo
|
||
|
||
Voir [HowtoMatomo]().
|
||
|
||
|
||
## Monitoring
|
||
|
||
### log2mail
|
||
|
||
Pour être alerté en cas de *Segmentation fault*, on ajoute la
|
||
configuration suivante au logiciel
|
||
[log2mail](https://wiki.evolix.org/HowtoLog2mail) :
|
||
|
||
~~~
|
||
file = /var/log/apache2/error.log
|
||
pattern = "Segmentation fault"
|
||
mailto = alert@example.com
|
||
~~~
|
||
|
||
~~~
|
||
# adduser log2mail adm
|
||
# servicectl restart log2mail
|
||
~~~
|
||
|
||
### apachetop
|
||
|
||
L'indispensable **apachetop** permet de surveiller en direct
|
||
l'activité d'Apache via un fichier d'access_log (format *combined*)
|
||
:
|
||
|
||
~~~
|
||
$ apachetop -f access.log -T 3600 -q
|
||
~~~
|
||
|
||
On peut alors visualiser les URLs les plus sollicitées (querystrings
|
||
inclus grâce à l'option *-q*), et switcher à l'aide de la touche
|
||
**d** pour voir les adresses IP source ; à l'aide de la touche
|
||
**n** pour voir la répartition en codes HTTP 2xx/3xx/4xx/5xx.
|
||
|
||
Voici l'ensemble des commandes disponibles :
|
||
|
||
~~~
|
||
d : switch l'affichage entre URLs/IPs source/Referrers
|
||
n : switch l'affichage entre le nombre de requêtes et la répartition en codes HTTP 2xx/3xx/4xx/5xx
|
||
h or ? : afficher l'aide
|
||
p : (un)freeze l'affichage
|
||
q : quitter
|
||
up/down : sélectionner une ligne (affichage d'une '*' sur la ligne)
|
||
right/left : entrer/sortir dans les détails pour la ligne sélectionnées
|
||
|
||
DÉTAILS:
|
||
s: TRI PAR:
|
||
r) requêtes R) reqs/sec b) bytes B) bytes/sec
|
||
2) 2xx 3) 3xx 4) 4xx 5) 5xx
|
||
|
||
t: AFFICHE DANS LE SOUS-MENU:
|
||
u) urls r) referrers h) adresses IP source
|
||
|
||
f: FILTRES:
|
||
a) ajout d'un filtre c) suppression des filtres s) montrer les filtres actifs
|
||
a: AJOUT D'UN FILTRE TYPE REGEX
|
||
u) appliqué à l'URL r) appliqué au Refferer h) appliqué à l'adresse IP source
|
||
~~~
|
||
|
||
|
||
### server-status
|
||
|
||
L'activation du *server-status* est utile pour une observation
|
||
manuelle ou automatique.
|
||
|
||
~~~{.apache}
|
||
<IfModule mod_status.c>
|
||
ExtendedStatus On
|
||
<Location /server-status-XXXX>
|
||
SetHandler server-status
|
||
Include ipaddr_whitelist.conf
|
||
Require ip 192.0.2.43
|
||
Require ip 127.0.0.1
|
||
</Location>
|
||
</IfModule>
|
||
~~~
|
||
|
||
#### Sauvegarde automatique du server-status
|
||
|
||
Placer le code suivant dans un fichier tel que /usr/share/scripts/save_apache_status.sh
|
||
|
||
~~~
|
||
#!/bin/bash
|
||
|
||
set -e
|
||
|
||
DIR=/var/log/apache-status
|
||
URL=http://127.0.0.1/server-status-XXXX
|
||
TS=`date +%Y%m%d%H%M%S`
|
||
FILE="$DIR/$TS.html"
|
||
|
||
mkdir -p $DIR
|
||
curl -s $URL > $FILE
|
||
chmod 640 $FILE
|
||
find $DIR -type f -mtime +1 -delete
|
||
|
||
exit 0
|
||
~~~
|
||
|
||
Si on veux le sauvegarder dans /var/www/ pour y acceder par le serveur web, on peux utilisé un script de ce type :
|
||
|
||
~~~
|
||
#!/bin/sh
|
||
|
||
DATE=$(date +%Y-%m-%d-%H-%M)
|
||
URL=$(grep -m1 "<Location.*server-status" /etc/apache2/sites-enabled/000-default.conf|awk '{ print $2 }' |cut -d'>' -f1)
|
||
|
||
mkdir -p /var/www/apache-status
|
||
curl -Ls http://127.0.0.1${URL} > "/var/www/apache-status/${DATE}.html" && chmod 644 "/var/www/apache-status/${DATE}.html"
|
||
find "/var/www/apache-status/" -type f -mtime +4 -delete
|
||
~~~
|
||
|
||
Il sera ensuite appelé dans une tâche cron de root :
|
||
|
||
~~~
|
||
* * * * * /usr/share/scripts/save_apache_status.sh
|
||
~~~
|
||
|
||
### Munin
|
||
|
||
Activer les plugins Munin pour Apache ainsi :
|
||
|
||
~~~
|
||
# cd /etc/munin/plugins
|
||
# ln -s /usr/share/munin/plugins/apache_accesses
|
||
# ln -s /usr/share/munin/plugins/apache_processes
|
||
# ln -s /usr/share/munin/plugins/apache_volume
|
||
~~~
|
||
|
||
Les plugins se basent sur *server-status*, configurable via `/etc/munin/plugin-conf.d/munin-node` :
|
||
|
||
~~~
|
||
[apache_*]
|
||
env.url http://127.0.0.1:%d/server-status-XXXX?auto
|
||
env.port 80
|
||
~~~
|
||
|
||
Pour tester :
|
||
|
||
~~~
|
||
# sudo -u munin munin-run apache_accesses
|
||
~~~
|
||
|
||
Une valeur du type `accesses80.value 19372070` doit être renvoyé.
|
||
Si la commande indique U, C'est qu'il y a un soucis d'accès à la page *server-status*.
|
||
Vérifier avec `GET http://127.0.0.1:%d/server-status-XXXX?auto`.
|
||
|
||
#### Support du ssl
|
||
|
||
Adapter la configuration via `/etc/munin/plugin-conf.d/munin-node` :
|
||
|
||
~~~
|
||
[apache_*]
|
||
env.url https://127.0.0.1:%d/server-status-XXXX?auto
|
||
env.port 443
|
||
env.PERL_LWP_SSL_VERIFY_HOSTNAME 0
|
||
~~~
|
||
|
||
## FAQ
|
||
|
||
### Autorisation DirectoryIndex via .htaccess ?
|
||
|
||
Ajouter `Indexes` dans `AllowOverride`.
|
||
|
||
### Autorisation directives mod_expires ou mod_caches via .htaccess ?
|
||
|
||
Ajouter `Indexes` dans `AllowOverride`.
|
||
|
||
### Autorisation de rewrite rules (mod_rewrite) via .htaccess ?
|
||
|
||
Ajouter `FileInfo` dans `AllowOverride` et `+SymLinksIfOwnerMatch`
|
||
ou `+FollowSymLinks` dans `Options`.
|
||
|
||
### Autorisation Options via .htaccess ?
|
||
|
||
Si l'on modifie l'option `php_admin_value memory_limit` : un
|
||
_reload_/_graceful_ suffit pour prendre en compte la modification.
|
||
|
||
Exemple pour le AllowOverride :
|
||
|
||
~~~
|
||
AllowOverride Options=All,MultiViews Indexes AuthConfig Limit FileInfo
|
||
~~~
|
||
|
||
### Authentification LDAP avec serveur LDAP en local
|
||
|
||
Quand on utilise l'authentification LDAP avec un serveur en local,
|
||
il faut démarrer **slapd** avant de démarrer Apache, sinon celui-ci
|
||
se plaint qu'il ne peut pas contacter le serveur LDAP, et bloque
|
||
l'accès aux pages … (erreur 500).
|
||
|
||
Pour cela, dans le script d'init d'apache2 ajouter slapd dans la
|
||
directive `Required-Start:`
|
||
|
||
~~~
|
||
# Required-Start: $local_fs $remote_fs $network $syslog $named slapd
|
||
~~~
|
||
|
||
Supprimer les liens logiques et ré-générer les.
|
||
|
||
~~~
|
||
# insserv -r apache2
|
||
# insserv apache2
|
||
~~~
|
||
|
||
### Page personnalisée lors code erreur HTTP
|
||
|
||
On peut ajouter une configuration générale à tous les vhosts afin
|
||
de faire apparaître le contenu d'une page personnalisée selon le
|
||
code de retour HTTP.
|
||
|
||
Pour cela on ajoute un nouveau fichier de configuration à l'intérieur
|
||
de la racine apache, car cela concernera tout les vhosts.
|
||
|
||
`/etc/apache2/error.conf` :
|
||
|
||
~~~{.apache}
|
||
ErrorDocument XXX /YYYYYY/page.html
|
||
Alias /YYYYYY /var/www/
|
||
<Directory /var/www/>
|
||
Require all granted
|
||
DirectoryIndex page.html
|
||
</Directory>
|
||
~~~
|
||
|
||
*Remplacer XXX par le code erreur HTTP souhaité et YYYYY par le nom
|
||
de Location souhaité (URL) - vu que global à tous les vhosts, prendre
|
||
une chaîne aléatoire.*
|
||
|
||
Ensuite, il suffit simplement d'ajouter le fichier dans la configuration
|
||
générale d'Apache :
|
||
|
||
`/etc/apache2/apache2.conf` :
|
||
|
||
~~~{.apache}
|
||
Include error.conf
|
||
~~~
|
||
|
||
### Site web cassé en https avec un reverse proxy
|
||
|
||
Il est possible que l'application (joomla par exemple) pense qu'elle
|
||
est accédée en HTTP à tort parce qu'en réalité c'est un reverse
|
||
proxy qui fait la terminaison ssl. On peut indiquer dans le vhost
|
||
|
||
~~~{.apache}
|
||
SetEnvIfNoCase X-Forwarded-Proto https HTTPS=on
|
||
~~~
|
||
|
||
Ainsi l'application saura qu'elle est accédée en HTTPS à condition
|
||
que le reverse proxy soit bien configuré et envoie X-Forwarded-Proto.
|
||
|
||
### Bloquer un User-Agent
|
||
|
||
On peut bloquer des bots faisant des requêtes non légitimes via :
|
||
|
||
~~~{.apache}
|
||
RewriteEngine on
|
||
RewriteCond %{HTTP_USER_AGENT} python-requests.* [NC]
|
||
RewriteRule . - [R=403,L]
|
||
~~~
|
||
|
||
### Bloquer certaines URI
|
||
|
||
Par exemple, pour retourner une 404 si on interroge `/.git` :
|
||
|
||
~~~ { .apache }
|
||
RedirectMatch 404 /\.git
|
||
~~~
|
||
|
||
### Site avec accents cassés
|
||
|
||
Si vous avez des vieux fichiers sources (TXT, HTML), il est probable
|
||
qu'ils utilisent l'encodage ISO-8859 au lieu d'Unicode. On peut
|
||
alors forcer la reconnaissance de cet encodage (ajout de charset=
|
||
dans l'entête HTTP Content-Type) via l'option `AddDefaultCharset`
|
||
utilisable globalement, dans un VirtualHost, dans un Directory ou
|
||
même un .htaccess si autorisé :
|
||
|
||
|
||
~~~
|
||
AddDefaultCharset ISO-8859-15
|
||
~~~
|
||
|
||
> *Note* : si vous avez des fichiers PHP en ISO-8859, vous devrez
|
||
> forcer l'option `default_charset` de PHP :
|
||
>
|
||
>
|
||
> ~~~
|
||
> php_value default_charset ISO-8859-15
|
||
> ~~~
|
||
|
||
### php_value error_reporting
|
||
|
||
Pour régler la valeur PHP `error_reporting` par vhost, il ne faut
|
||
pas utiliser les constantes PHP mais le masque binaire.
|
||
|
||
Extrait de <http://php.net/manual/fr/errorfunc.constants.php> :
|
||
« Note: Vous pouvez utiliser ces constantes dans le fichier `php.ini`
|
||
mais pas hors de PHP, comme dans le fichier `httpd.conf`, où vous
|
||
devez utiliser les valeurs des champs de bits. »
|
||
|
||
Cela se calcule en partant de 32767 et en retirant les valeurs que l'on ne veut pas.
|
||
|
||
Exemples :
|
||
|
||
* `E_ALL` et `~E_DEPRECATED` et `~E_STRICT` : `php_value error_reporting 22527`
|
||
* `E_ALL` et `~E_DEPRECATED` et `~E_STRICT` et `~E_NOTICE` : `php_value error_reporting 22519`
|
||
|
||
### Ne pas logguer le query-string dans le access.log
|
||
|
||
On peut ajouter un nouveau format de log dans `/etc/apache2/apache.conf`
|
||
et l'utiliser après dans les définitions de fichier de logs :
|
||
|
||
~~~
|
||
LogFormat "%h %l %u %t \"%m %U %H\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combinednoqtring
|
||
~~~
|
||
|
||
### Proxy WebSocket socket.io
|
||
|
||
~~~
|
||
# a2enmod proxy_wstunnel
|
||
~~~
|
||
|
||
~~~
|
||
# Socket.io
|
||
RewriteRule /socket.io/websocket/ - [R=200,L]
|
||
ProxyPass /socket.io/socket.io.js http://127.0.0.1:8080/socket.io/socket.io.js
|
||
ProxyPassReverse /socket.io/socket.io.js http://127.0.0.1:8080/socket.io/socket.io.js
|
||
ProxyPass /socket.io/websocket ws://127.0.0.1:8080/socket.io/websocket
|
||
ProxyPassReverse /socket.io/websocket ws://127.0.0.1:8080/socket.io/websocket
|
||
ProxyPass /socket.io/ http://127.0.0.1:8080/socket.io/
|
||
ProxyPassReverse /socket.io/ http://127.0.0.1:8080/socket.io/
|
||
RewriteCond %{REQUEST_URI} ^/socket.io [NC]
|
||
RewriteCond %{QUERY_STRING} transport=websocket [NC]
|
||
RewriteRule /(.*) ws://127.0.0.1:8080/$1 [P,L]
|
||
ProxyPass / http://127.0.0.1:8080/
|
||
ProxyPassReverse / http://127.0.0.1:8080/
|
||
~~~
|
||
|
||
### IncludeOptional génère une erreur quand le fichier n'existe pas !
|
||
|
||
C'est un comportement de Apache < 2.4.30 (Jessie, Stretch). La directive ne va pas générer d'erreur de syntaxe (contrairement à Include) si et seulement si, des jokers (comme *) sont utilisés.
|
||
|
||
A partir d'Apache 2.4.30 (Buster), IncludeOptional ne va pas émettre d'erreur de syntaxe dans ce cas particulier.
|
||
|
||
C'est expliqué dans [la documentation d'apache](https://httpd.apache.org/docs/2.4/mod/core.html#includeoptional)
|
||
|
||
Un contournement si on souhaite vraiment inclure qu'un seul fichier qui peut ne pas exister :
|
||
|
||
~~~
|
||
IncludeOptional /etc/apache2/ssl/XXX.con[f]
|
||
~~~
|
||
|
||
### J'ai activé HTTP/2, mais ça ne fonctionne pas :(
|
||
|
||
Malheureusement, Apache en mode prefork (ou itk qui est un dérivé du prefork) n'est pas adapté au fonctionnement du protocole HTTP/2. On peut notamment voir dans les journaux le message suivant :
|
||
|
||
~~~
|
||
# tail /var/log/apache2/error.log
|
||
Thu Oct 02 10:42:00.052627 2019] [http2:warn] [pid 9682] AH10034: The mpm module (prefork.c) is not supported by mod_http2. The mpm determines how things are processed in your server. HTTP/2 has more demands in this regard and the currently selected mpm will just not do. This is an advisory warning. Your server will continue to work, but the HTTP/2 protocol will be inactive.
|
||
~~~
|
||
|
||
Dans tous les cas, PHP n'étant pas compatible avec Apache fonctionnant en mode thread, il est nécessaire d'être en mpm prefork ou itk. Donc pas de HTTP/2 possible.
|
||
|
||
### Too many open files
|
||
|
||
Si vous avez beaucoup de VirtualHosts, vous pouvez avoir des erreurs `Too many open files` ou même une erreur bizarre si vous utilisez du PHP : `Fatal error: date(): Timezone database is corrupt - this should *never* happen!`
|
||
|
||
Il faut alors augmenter la limite max open files d'Apache qui est par défaut à 8192.
|
||
|
||
Cela se fait via le fichier `/etc/apache2/envvars` :
|
||
|
||
~~~
|
||
APACHE_ULIMIT_MAX_FILES='ulimit -n 65536'
|
||
~~~
|
||
|
||
### CORS Allow-Origin
|
||
|
||
~~~
|
||
Header set Access-Control-Allow-Origin "*"
|
||
~~~
|
||
|
||
### NFS et Apache-ITK
|
||
|
||
Pout utiliser des montages NFS avec Apache-ITK (que nous utilisons par défaut),
|
||
il faut désactiver l'option suivante :
|
||
|
||
~~~
|
||
EnableCapabilities off
|
||
~~~
|
||
|
||
### Apache ajoute l'entête HTTP "Vary" inopinément
|
||
|
||
Lorsque vous évaluez une variable HTTP_USER_AGENT, certaines versions d'Apache (notamment celle de Debian 9) peuvent avoir la mauvaise idée d'ajouter un entête HTTP `Vary: User-Agent`.
|
||
|
||
Cela peut être une évaluation dans votre configuration Apache, votre VirtualHost ou même dans vos .htaccess
|
||
|
||
Exemple de configuration qui génère le comportement problématique :
|
||
|
||
~~~
|
||
Require expr %{HTTP_USER_AGENT} !~ /Nutch/
|
||
~~~
|
||
|
||
Cela pourrait s'appliquer aussi aux "Rewrite Rule" et à d'autres variables comme HTTP_HOST, etc.
|
||
|
||
Le bug semble corrigé dans les versions récentes d'Apache : <https://bz.apache.org/bugzilla/show_bug.cgi?id=58231>
|
||
|
||
La documentation Apache mentionne une fonction `req_navary()` qui permettrait d'empêcher ce comportement : <https://httpd.apache.org/docs/2.4/expr.html#functions>
|
||
|
||
Lien qui nous a mis sur la piste : <https://www.nivas.hr/blog/2017/02/13/apache-sending-vary-host-making-things-uncacheable-varnish/>
|
||
|
||
### Gestion des droits avec Apache-ITK et PHP-FPM
|
||
|
||
Si l'on utilise [PHP-FPM](HowtoPHP#configuration-fpm) avec Apache-ITK,
|
||
on veillera à avoir un pool FPM qui utilise les mêmes utilisateur/groupe qu'Apache-ITK.
|
||
En particulier si on définit `AssignUserID foo bar` il faut s'assurer que `user = foo` et `group = bar` dans le configuration du pool PHP-FPM.
|
||
|
||
### Apache plante au 1024e restart
|
||
|
||
En Debian 9, avec un Apache utilisant mod_php et imagemagick, quand Apache est rechargé la 1024e fois apache plante en idiquant dans les logs `libgomp: could not create thread pool destructor.`. Pour plus d'info lire cet [article](https://levelup.gitconnected.com/this-is-why-our-3000-apache-servers-went-down-on-the-first-day-of-2022-3cc5e9639587).
|
||
|
||
|