Login Logout

Howto Apache

Apache est le serveur HTTP le plus utilisé sur le web depuis 1996.

Installation

Nous utilisons la version Apache-ITK 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-mpm-itk libapache2-mod-evasive apachetop libwww-perl

# apache2ctl -V
Server version: Apache/2.4.10 (Debian)
Server built:   Sep 15 2016 20:44:43
Server's Module Magic Number: 20120211:37
Server loaded:  APR 1.5.1, APR-UTIL 1.5.4
Compiled using: APR 1.5.1, 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"

Configuration de base

Fichiers de configuration :

/etc/apache2/
├── apache2.conf
├── conf-available
│   └── *.conf
├── conf-enabled
│   └── *.conf -> ../conf-available/*.conf
├── envvars
├── magic
├── mods-available
│   ├── *.conf
│   └── *.load
├── mods-enabled
│   ├── *.conf -> ../mods-available/*.conf
│   └── *.load -> ../mods-available/*.load
├── ports.conf
├── sites-available
│   └── *.conf
└── sites-enabled
    └── *.conf -> ../sites-available/*.conf

Nous activons toujours les modules suivants :

# a2enmod rewrite expires headers rewrite cgi

Le fichier /etc/apache2/conf-available/z-evolinux-defaults.conf contient nos optimisations basiques :

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
</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 :

Allow from 192.0.2.42

Pour la #gestion-des-droits on ajoute dans le fichier /etc/apache2/envvars :

umask 007

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 :

<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 à :

<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
    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.

CustomLog log/global_access.log vhost_combined
CustomLog log/access.log combined

Si besoin, on peut ignorer certaines requêtes HTTP ainsi :

SetEnvIf User-Agent "Foo" dontlog
CustomLog log/access.log combined env=!dontlog

La directive ErrorLog permet de définir le journal d’erreurs Apache.

ErrorLog  log/error.log

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 permet d’utiliser Apache en tant que proxy, notamment reverse-proxy HTTP :

# a2enmod proxy_http

Exemple avec un service HTTP local :

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) :

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 (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

mod_xsendfile

Le module 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.

Authentification HTTP

HTTP Basic Authentication (mod_auth_basic)

Basic est la méthode d’authenfication HTTP la plus simple et la plus répandue. La configuration se fait via VirtualHost ou .htaccess :

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.

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 :

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 :

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

Voici quelques motifs classiques de redirection vers un nouveau domaine (HTTP 302) … du plus simple au plus compliqué :

RedirectPermanent / http://new.example.com

RedirectMatch ^/(.*)$ http://new.example.com/$1

# GET / --> /sub/
RedirectMatch ^/$ /sub/

RewriteRule ^/(.*) http://new.example.com/$1 [L,R=permanent]

RewriteCond %{REQUEST_URI} !^/foo.txt
RewriteRule ^/(.*) https://%{SERVER_NAME}/$1 [L,R]

# le drapeau NC pour 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]

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/

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.

<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.

Fail2Ban

Fail2Ban 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.

http://trac.evolix.net/infogerance/wiki/HowtoFail2Ban#ApacheNginxDDOS

ModSecurity

L’utilisation de ModSecurity permet d’interdire certaines requêtes (attaques XSS connues, etc.) au niveau d’Apache.

# apt install libapache2-mod-security2 modsecurity-crs

Nous faisons une configuration minimale via /etc/apache2/conf-available/modsecurity.conf :

<IfModule security2_module>

    SecRuleEngine On
    SecRequestBodyAccess On
    #SecRequestBodyLimit 134217728
    #SecRequestBodyInMemoryLimit 131072
    SecResponseBodyAccess Off
    #SecResponseBodyLimit 524288
    SecResponseBodyMimeType (null) text/html text/plain text/xml
    #SecServerSignature "Apache/2.2.0 (Fedora)"
    SecUploadDir /tmp
    SecUploadKeepFiles Off
    SecDefaultAction "log,auditlog,deny,status:406,phase:2,t:none"
    SecAuditEngine RelevantOnly
    #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"

    #Include /usr/share/modsecurity-crs/*.conf

    ErrorDocument 406 http://SERVERNAME/406.html
</IfModule>

Pour une configuration avancée, on ajuste l’utilisation du jeu de règles modsecurity-crs et l’on gère nos propres règles personnalisées.

Awstats

AWStats est un outil pour générer des statistiques en fonction d’un fichier de logs.

Voir http://trac.evolix.net/infogerance/wiki/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 :

file = /var/log/apache2/error.log
  pattern = "Segmentation fault"
  mailto = security@example.com
  template = /etc/log2mail/template.apache
# 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.

<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 Apache pour Munin :

# 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 :

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 :

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

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

SetEnvIf User-Agent "^BadBot$" GoAway=1
SetEnvIf User-Agent "Nutch" GoAway=1
<Directory />
    Deny from env=GoAway
</Directory>