wiki/HowtoApache.md
2018-11-28 14:50:43 +01:00

1166 lines
34 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
categories: web
title: Howto Apache
...
* Documentation : <https://httpd.apache.org/docs/2.4/>
* Rôle Ansible : <https://forge.evolix.org/projects/ansible-roles/repository/show/apache>
[Apache](https://httpd.apache.org/) est le serveur [HTTP](HowtoHTTP)
le plus utilisé sur le web depuis 1996.
## Installation
Nous utilisons la version [Apache-ITK](http://mpm-itk.sesse.net/)
depuis des années en production sur de nombreux serveurs critiques.
Apache-ITK permet de préciser pour chaque VirtualHost un utilisateur,
un groupe et une option *MaxClients* spécifiques, 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.25 (Debian)
Server built: 2017-07-18T18:37:33
Server's Module Magic Number: 20120211:68
Server loaded: APR 1.5.2, APR-UTIL 1.5.4
Compiled using: APR 1.5.2, APR-UTIL 1.5.4
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_SYSVSEM_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; vendor preset:
Process: 32127 ExecReload=/usr/sbin/apachectl graceful (code=exited, status=0/
Process: 1857 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCE
Main PID: 2031 (apache2)
Tasks: 6 (limit: 4915)
CGroup: /system.slice/apache2.service
├─ 2031 /usr/sbin/apache2 -k start
├─32134 /usr/sbin/apache2 -k start
├─32135 /usr/sbin/apache2 -k start
├─32136 /usr/sbin/apache2 -k start
├─32137 /usr/sbin/apache2 -k start
└─32138 /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 toujours 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
Require all granted
# "Require not env XXX" is not supported :(
Deny from env=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}
Allow from 192.0.2.42
~~~
Pour la [#gestion-des-droits]() on ajoute dans le fichier `/etc/apache2/envvars` :
~~~{.apache}
umask 007
~~~
### Valider la configuration
Avant de redémarrer le serveur, vérifier que vous n'ayez pas introduit
des erreurs de syntaxes 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
Deny from all
Include ipaddr_whitelist.conf
Allow from 192.0.2.43
Satisfy any
</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://forge.evolix.org/projects/evoadmin-web>
## Gestion des droits
### Séparation complète des VirtualHost
L'utilisation d'Apache-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 [OR]
RewriteCond %{HTTP_HOST} !^secure.example.com$
RewriteRule ^/(.*) https://%{SERVER_NAME}/$1 [L,R=permanent]
</VirtualHost>
~~~
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.
## 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 dexécution
Si l'on veut loguer, dans un log séparé, la taille de la requête
et le temps dexé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
~~~
## Configuration avancée
### mod_deflate
La compression des fichiers texte/Javascript/CSS/RSS en GZIP se
fait désormais par défaut car le module **mod_deflate** est activé
dès l'installation.
### 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 *>
Allow from All
</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 *>
Allow from All
</Proxy>
~~~
### mod_rpaf / mod_remoteip
Le module
[mod_remoteip](https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html)(**mod_rpaf** en Wheezy)
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 **mod_access** (Allow/Deny From).
Voici un exemple d'utilisation en Wheezy pour un reverse-proxy avec
l'adresse IP 192.0.2.10 :
~~~
# 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
</IfModule>
~~~
*Note :* bien mettre l'IP du reverse-proxy dans `RPAFproxy_ips`
Voici un exemple d'utilisation en Stretch pour un reverse-proxy
avec le réseau 172.131.0.0/16 :
~~~
# a2enmod remoteip
# cat /etc/apache2/conf-available/zzz-evolinux-custom.conf
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 172.31.0.0/16
# systemctl reload apache2
~~~
Attention au niveau des logs cela ne suffit pas. L'astuce est 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
Order allow,deny
Allow from all
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-contrib 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>
~~~
## 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 -c /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
~~~
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>
Voici quelques motifs classiques de redirection vers un nouveau
domaine (HTTP 302) … du plus simple au plus compliqué :
~~~
# 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 %{REQUEST_URI} !^/.well-known/pki-validation/
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"
~~~
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 %{HTTP:X-Forwarded-Proto} =http
RewriteRule .* https://%{HTTP:Host}%{REQUEST_URI} [L,R=permanent]
~~~
## 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.
~~~{.apache}
<IfModule mod_evasive20.c>
DOSHashTableSize 3097
DOSPageCount 5
DOSSiteCount 30
DOSPageInterval 3
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 :
~~~
DOSWhitelist 192.168.0.*
~~~
On peut aussi utiliser un wildcard * peut être autorisé sur les 3
derniers octets, si nécessaire.
### 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 de [ModSecurity](http://www.modsecurity.org) permet
de bloquer certaines requêtes HTTP (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 ModSecurity couvrant un large spectre d'attaques possibles.
~~~
# apt install libapache2-mod-security2 modsecurity-crs
~~~
Nous faisons une configuration minimale via
`/etc/apache2/conf-available/modsecurity.conf` :
~~~{.apache}
<IfModule security2_module>
SecRuleEngine On
SecRequestBodyAccess On
#SecRequestBodyLimit 134217728
#SecRequestBodyInMemoryLimit 131072
SecResponseBodyAccess Off
#SecResponseBodyLimit 524288
SecResponseBodyMimeType (null) text/html text/plain text/xml
SecUploadDir /tmp
SecUploadKeepFiles Off
SecDefaultAction "log,auditlog,deny,status:406,phase:2,t:none"
SecAuditEngine Off
#SecAuditLogRelevantStatus "^[45]"
SecAuditLogType Serial
SecAuditLog /var/log/apache2/modsecurity_audit.log
SecAuditLogParts "ABIFHZ"
#SecArgumentSeparator "&"
SecCookieFormat 0
SecDebugLog /var/log/apache2/modsec_debug.log
SecDebugLogLevel 0
SecTmpDir /tmp
SecRule REQUEST_FILENAME "modsecuritytest1"
SecRule REQUEST_URI "modsecuritytest2"
SecRule REQUEST_FILENAME "(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe"
# 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"
ErrorDocument 406 http://SERVERNAME/406.html
</IfModule>
~~~
Nous désactivons le log d'audit par défaut, puisque linformation
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 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 lobtention de statistiques du audit log.
On peut aussi ajuster certains paramètres de ModSecurity Core Rule
Set en modifiant le fichier `/etc/modsecurity/crs/crs-setup.conf`.
C'est notamment ici qu'il faudra ajuster si on utilise d'autres
méthodes HTTP (comme PATCH, PUT, DELETE...) qui sont bloquées par défaut.
On pourra désactiver modsecurity dans des vhosts ou sur des dossiers
en particulier en jouant avec la directive `SecRuleEngine Off`.
Ceci peut être utile en cas de problèmes, mais il est toujours mieux
de faire un léger audit afin didentifier les règles problématiques
et les désactiver avec `SecRuleRemoveById XXXX`, comme nous le
faisont avec les règles 910*.
~~~
<Directory "/home/monsite/www/wp-admin">
<IfModule security2_module>
SecRuleEngine Off
</IfModule>
</Directory>
~~~
Le Wiki À propos de [ces directives](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual)
## Awstats
[AWStats](http://www.awstats.org/) est un outil pour générer des
statistiques en fonction d'un fichier de logs.
Voir <https://wiki.evolix.org/HowtoLAMP/AwStats>
*Note :* une configuration AWStats peut être forcée au sein d'un
VirtualHost grâce à la directive `SetEnv AWSTATS_FORCE_CONFIG
example`
## 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
Deny from all
Include ipaddr_whitelist.conf
Allow from 192.0.2.43
Allow from 127.0.0.1
</Location>
</IfModule>
~~~
### Munin
Pour activer les plugins Munin pour Apache :
~~~
# 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 Apache pour Munin se base *server-status*, on configure
ainsi via le fichier `/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 le bon fonctionnement des plugins Apache pour Munin :
~~~
# sudo -u munin munin-run apache_accesses
~~~
Ceci doit vous renvoyer une valeur du type accesses80.value 19372070.
Si la commande vous renvoie U, vous avez un soucis d'accès à la
page *server-status*, à vérifier via
`GET http://127.0.0.1:%d/server-status-XXXX?auto`.
## 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/>
Order allow,deny
Allow from all
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.
### Interdire en fonction du User-Agent
~~~{.apache}
SetEnvIf User-Agent "^BadBot$" GoAway=1
SetEnvIf User-Agent "Nutch" GoAway=1
<Directory />
Deny from env=GoAway
</Directory>
~~~
### 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. »
Exemple :
`E_ALL` et `~E_DEPRECATED` et `~E_STRICT`
`php_value error_reporting 22527`
Vous pouvez le calculer à la main ou avec un outil comme celui-ci :
<http://www.bx.com.au/tools/ultimate-php-error-reporting-wizard>
### 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
~~~