--- categories: web title: Howto Apache ... * Documentation : * Rôle Ansible : [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 AllowOverride None Require all granted # "Require not env XXX" is not supported :( Deny from env=GoAway SSLProtocol all -SSLv2 -SSLv3 SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4 ~~~ 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} ServerName www.example.com ServerAlias example.com DocumentRoot /home/example/www/ Options SymLinksIfOwnerMatch AllowOverride AuthConfig Limit FileInfo Indexes ScriptAlias /cgi-foo /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 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] # 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 ## 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} 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] ~~~ Pour une configuration avancée des paramètres SSL, voir [HowtoSSL#configuration-apache]() > *Note* : la syntaxe `` n'est possible qu'à partir de Debian 8. ## Logs 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 ~~~ ## 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 Allow from All ~~~ 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/ Allow from All ~~~ ### 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 RPAFenable On RPAFsethostname On #RPAFheader X-Forwarded-For RPAFproxy_ips 127.0.0.1 192.0.2.10 ~~~ *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} Alias /error/ "/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 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 ~~~ ### 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 ~~~ GeoIPEnable On GeoIPDBFile /usr/share/GeoIP/GeoIP.dat ~~~ 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 : ~~~ Require expr %{GEOIP_COUNTRY_CODE } == 'FR' ~~~ 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 : ~~~ ServerName dav.example.com DocumentRoot /home/webdav/ Dav On AuthType Basic AuthName DAV AuthUserFile "/etc/apache2/webdav.htpasswd" Require valid-user 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 ~~~ ## 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 : * drapeaux utilisables : 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 : ### 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 n'est disponible que pour les versions >= 2.4.26). ~~~{.bash} SetEnv APP_ENV "test" ... ~~~ 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} DOSHashTableSize 3097 DOSPageCount 5 DOSSiteCount 30 DOSPageInterval 3 DOSSiteInterval 1 DOSBlockingPeriod 60 DOSEmailNotify security@example.com ~~~ 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 ### 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} 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 ~~~ 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 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. 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 d’identifier les règles problématiques et les désactiver avec `SecRuleRemoveById XXXX`, comme nous le faisont avec les règles 910*. ~~~ SecRuleEngine Off ~~~ 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 *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} ExtendedStatus On SetHandler server-status Deny from all Include ipaddr_whitelist.conf Allow from 192.0.2.43 Allow from 127.0.0.1 ~~~ ### 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/ Order allow,deny Allow from all DirectoryIndex page.html ~~~ *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 Deny from env=GoAway ~~~ ### 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 : « 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 : ### 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 ~~~