Table des matières
Serveur web Apache
Installation LAMP classique:
apt update && apt install apache2 php mysql-server libapache2-mod-php php-mysql a2enmod rewrite a2enmod vhost_alias
vérifier les priorités des éntrées dans le
DirectoryIndex
de /etc/apache2/mods-enabled/dir.conf
: si vous faites en sorte qu'un index.php
passe après le test de la présence d'un index.html
, cela vous permettra de placer un simple fichier HTML prioritaire lors des mises à jours du serveur par exemple).
Un "virtualhost" par domaine hébergé
On va créer un dossier par site sur le serveur (selon le principe des VirtualHosts). Par exemple ici avec sub.mondomaine.com
. Soit on répète ce bloc pour chacun des domaines hébergés par ce serveur ( correspondants aux champs A de son DNS), soit on utilise un "wildcard" (ci-après).
# Désactiver le site par défaut: a2dissite 000-default.conf # Nom du domaine à servir: dom="sub.mondomaine.com" # On crée le dossier associé au domaine, avec un index.php "bidon" mais pratique pur vérifier: mkdir -m 0755 "/var/www/$dom" chown www-data:www-data "/var/www/$dom" echo -e "Ce serveur est: $dom\n<?php phpinfo(); ?>" > "/var/www/$dom/index.php" # On ajoute un "virtualhost" pour ce domaine dans apache: cat > /etc/apache2/sites-available/$dom.conf << EOF <VirtualHost *:80> ServerAdmin webmaster@localhost ServerName $dom DocumentRoot /var/www/$dom ErrorLog \${APACHE_LOG_DIR}/error.log CustomLog \${APACHE_LOG_DIR}/access.log combined </VirtualHost> EOF # Puis on active la configuration: a2ensite "$dom.conf" if apache2ctl configtest | grep 'Syntax OK'; then systemctl restart apache2 fi
A partir de là si l'accès HTTP est ouvert sur l'extérieur, on devrait voir le nom du site et des informations PHP en allant sur http://sub.mondomaine.com
serveur nginx
Note: si vous utilisez le server nginx et que vous avez besoin de PHP, il ne vient pas sous la forme d'un module conjoint contrairement à libapache2-mod-php
. Vous pouvez installer le service PHP indépendant,
apt-get install nginx php-fpm
puis ajouter à la configuration de votre site le paragraphe suivant:
(...) location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php7.0-fpm.sock; } (...)
Un "virtualhost" unique pour tous les sous-domaines
On peut mettre dans son DNS des entrées de type "wildcard": par exemple * IN CNAME tecrd.com.
ou bien * IN A 11.22.33.44
.
De cette manière tous les sous-domaines <nom>.tecrd.com
qui n'auraient pas déjà une entrée DNS explicite seront automatiquement renvoyés vers le serveur. Ce comportement "par défaut" est pratique: on pourra quand même faire pointer mx.tecrd.com
vers un serveur mail indépendant, avec le "joker" ou "wildcard" qui ne gère que les cas par défaut).
Du coté de la configuration Apache sur le serveur, on peut soit avoir un VirtualHost par sous-domaine (comme ci-dessus), soit mettre là aussi une configuration "wildcard" qui va associer un sous-domaine à un DocumentRoot
(base de l'arborescence pour ce sous-domaine).
Ce type de configuration DNS+Apache permet de gérer un nouveau sous-domaine sans devoir toucher ni au DNS, ni à Apache.
pour HTTPS il restera néamoins à re-générer le certificat Letsencrypt, afin de lui dire de couvrir le ou les nouveaux sous-domaines servi, ou bien d'avoir acheté un "wildcard certificate" en premier lieu, ce qui est assez couteux.
Voilà un exemple de configuration /etc/apache/site-available/999-wildcard-subdomains.conf
(n'oubliez pas le ".conf"!). Il gère automatiquement les sous-domaines en "wildcard". Pensez à l'activer une fois en place avec a2ensite
et relancer Apache:
<VirtualHost *:80> ServerAlias *.tecrd.com VirtualDocumentRoot /var/www/%1/ </VirtualHost>
Ici, http://test.tecrd.com sera donc servi par le dossier /var/www/test
, et variante.tecrd.com
par /var/www/variante
Astuce: si l'on a comme convention site1.dev.tecrd.com, site2.dev.tecrd.com, site3.prod.tecrd.com… on peut préférer utiliser deux niveaux de dossiers /var/www/dev
et /var/www/prod
:
<VirtualHost *:80> ServerAlias *.*.tecrd.com VirtualDocumentRoot /var/www/%2/%1/html </VirtualHost>
Plus de détails sur ce site (en anglais).
Certificat HTTPS avec Letsencrypt
Préparation
C'est devenu simplissime avec Letsencrypt (un consortium solide qui founit des certificats gratuits tout à fait valables):
apt install certbot python3-certbot-nginx # nginx pour nginx, logique # Ou pour la toute dernière version: # add-apt-repository ppa:certbot/certbot # apt install python-certbot-apache # a) si vous utilisez Apache2 # apt install python-certbot-nginx # b) si vous utilisez nginx
Il faudra vérifier la validité du ServerName mondomaine.com;
dans /etc/apache2/sites-available/your_domain.conf
, puis recharger éventuellement apache avec systemctl reload apache2
il est recommandé d'écrire le "your_domain.conf" sous une forme exacte, par exemple "sub.domain.com.conf". On évite des ambiguités ainsi et on permet l'automatisation des certificats (ci-dessous).
Création et installation
On peut alors laisser le robot de Letsencrypt faire son travail ( penser à lui fournir l'ensemble des domaines à protéger, il est généralement plus compliqué de le faire domaine par domaine):
certbot --apache -d mondomaine.com -d www.mondomaine.com -d sub.mondomaine.com -d autredomaine.com
Le système de validation va tenter de poser un fichier spécial temporairement à la racine du site web, que les serveurs de Letsencrypt doivent pouvoir consulter ensuite. C'est ainsi qu'ils peuvent valider que l'on est bien le propriétaire du serveur et du domaine. Si l'opération se passe mal, c'est la première chose à regarder!
Note: si vous avez de nombreux domaines, pensez à sauvegarder la ligne complète pour la prochaine fois ou vous voudriez en ajouter un.
serveur web NGINX
On peut systématiser et couvrir tous les sites-available
s'ils sont bien formés (nom FQDN complet) et en rajoutant les alias de noms de domaines, par exemple avec une commande "sportive", et l'automatiser pour renouvellement toutes les semaine en créant /etc/cron.weekly/letsencrypt
et le contenu suivant
#!/bin/sh # vive linux # Retirez le -q des options de certbot pour une version avec confirmation xargs -a <(sed -n 's/^\s*[^#]server_name\s*\([^;]*\)\s*;.*/ -d \1/p' /etc/nginx/sites-enabled/*|sort -u) certbot --nginx
Voilà en outre un exemple de définition de site avant le passage de certbot (toujours pour nginx!):
# Template for a generic nginx-server internet domain # Replace "tm.tecrd.com" with your domain name, # enable/disable parts, then run certbot with "-d tm.tecrd.com" # # certbot --dry-run --nginx $(grep server_name /etc/nginx/sites-available/*|sed 's/.*server_name \(.*\)\s*;/\1/'| tr ' ' '\n'|sort -u|grep .|xargs -L 1 echo -n " -d") # # jeremie@tecrd.com 20200325-083612 server { server_name tm.tecrd.com ; root /home/www/tm.tecrd.com; listen 80; } server { server_name tm.tecrd.com ; root /home/www/tm.tecrd.com; index index.html index.php; # If you need some server-controlled access filtering # auth_basic "Time flows here"; # auth_basic_user_file /home/www/.htusers; location / { try_files $uri $uri/ =404; } location ~ /\.ht { deny all; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php7.3-fpm.sock; } # If you need to proxy some ports: # location /sub/ { # rewrite ^\/sub\/(.*) /$1 break; # proxy_pass http://localhost:3333; # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection 'upgrade'; # proxy_set_header Host $host; # proxy_cache_bypass $http_upgrade; # } listen 443 ssl; # managed by Certbot }
Remarque: le robot letsencrypt fonctionne en "posant" un fichier de validation dans un dossier .well-known
à la racine du site, puis tente d'y accéder en HTTP depuis l'extérieur. S'il y a une configuration qui l'en empêche, on peut le rediriger et se faciliter la vie avec une clause dédiée de ce type (dans le "listen" HTTP port 80). Si le root est var/www/html (par défaut):
location /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; }
Redirection forcée vers HTTPS
Pour rediriger une URL particulière vers sa version HTTPS:
<VirtualHost *:80> ServerName wiki.mondomaine.com Redirect permanent / https://wiki.mondomaine.com/ </VirtualHost>
On peut le faire globalement aussi pour tout le site (vérifier que tous les services sont OK avec ça!):
<VirtualHost *:80> [...] <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} </IfModule> </VirtualHost>
Avec Letsencrypt, attention avec les VirtualHost en "wildcard" (cf
VirtualDocumentRoot
). Si l'on active la redirection http vers https (option 2 lors de l'installation), le patch effectué par le robot certbot
est ineffectif: il faut réécrire la condition sous cette forme (enlever le =
et remplacer *
par .*
), par exemple:
RewriteCond %{SERVER_NAME} .*.mondomaine.com
HSTS
C'est une protection implémentée côté client: s'il voit cet en-tête venant du serveur, c'est le navigateur client qui refusera alors de se connecter autrement qu'en HTTPS vers le serveur. Il faut faire attention à cette option: elle est sécurisante, mais elle est très "collante" (tout service HTTP qui n'a pas de version HTTPS correspondante deviendra inaccessible, c'est aussi le but).
Activer l'envoi d'en-tête:
a2enmod headers systemctl restart apache2
Et dans la configuration apache /etc/apache2/sites-enabled/*
:
<VirtualHost *:443> ServerName cloud.nextcloud.com <IfModule mod_headers.c> Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains" </IfModule> </VirtualHost>
Puis relancer le serveur avec systemctl restart apache2
.
Du coté client, pour se débarrasser de ce comportement il faut aller dans la configuration de son ou de ses navigateurs et leur faire oublier qu'ils ont vu l'offre HSTS (et généralement effacer tout l'historique de consultation pour éviter un recyclage des adresses HTTPS!).
Services multiples sur un site ou un domaine
Exceptions
Cas réel: une installation mattermost préconfigurée chez scaleway aura tendance à "occuper" tout le domaine.
Si l'on veut pouvoir ajouter un dokuwiki dans un sous dossier wiki/ par exemple, il faudra ajouter une exception dans la configuration du navigateur:
(ici Nginx)
rewrite ^/wiki/?$ /wiki/index.php; location /wiki { root /var/www/html/wiki/; location ~ /(data|conf|bin|inc)/ { deny all; } }
Reverse proxy
Dans certains cas, on peut aussi rediriger un "dossier" web vers un autre serveur.
L'intéret est alors d'utiliser un nom de domaine comme portail, mais qui redirige ensuite de façon totalement transparente pour l'utilisateur certaines URL vers d'autres services ou serveurs du réseau local.
Sous Apache par exemple, on redirigera le chemin /mattermost
dans un virtual host existant vers l'IP interne (10.5.82.219, telle qu'indiquée par ifconfig
) et le port 8065 du service mattermost ainsi:
ProxyPreserveHost On ProxyPass /mattermost http://10.5.82.219:8065/ ProxyPassReverse /mattermost http://10.5.82.219:8065/
Autre exemple sous nginx, pour dédier l'URL http://talk.tecrd.com
(champ A ou CNAME requis) vers le service mattermost local, on créerait un /etc/nginx/sites-enabled/talk.tecrd.com
dont le contenu serait le suivant:
server { server_name talk.tecrd.com; root /home/www/talk.tecrd.com; location / { proxy_pass http://10.5.82.219:8065; } }
on remarquera ici que la redirection est en HTTP (interne), mais qu'il faudra blinder l'accès externe en HTTPS. Cela ne posera pas de problème car c'est bien le certificat associé au nom de domaine exposé sur l'extérieur qui sera utilisé (il n'y a pas de problème de sécurité à "redescendre" d'HTTPS vers HTTP une fois entré sur le serveur).
Sous nginx la configuration finale serait par exemple:
server { server_name talk.tecrd.com; listen 80; return 301 https://talk.tecrd.com; } server { server_name talk.tecrd.com ; root /home/www/talk.tecrd.com; location / { proxy_pass http://127.0.0.1:8065; } listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/admin.tecrd.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/admin.tecrd.com/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot }
Exceptions pour letsencrypt
Lorsqu'un nom de domaine est redirigé sur un service que le robot certbot
ne peut gérer, il ne sera pas possible d'installer ou de renouveler le certificat HTTPS. C'est par exemple le cas de Mattermost qui accapare l'URL. Une option est alors de rajouter une règle spécifiquement pour le robot de letsencrypt.
Sous NGINX
Dans une configuration /etc/nginx/sites-available/forum.monsite.com
, par exemple, il faudra ajouter un bloc tel que:
location ^~ /.wel-known/acme-challenge { root /var/www/nginx/forum.monsite.com; default_type "text/plain"; }
Et prévoir la zone d'accueil de validation ainsi:
mkdir -p /var/www/nginx/forum.monsite.com/.wel-known/ chown -R www-data:www-data /var/www/nginx/forum.monsite.com
On peut tester cela ainsi:
echo testok > /var/www/nginx/forum.monsite.com/.wel-known/acme-challenge chown www-data:www-data /var/www/nginx/forum.monsite.com/.wel-known/acme-challenge curl http://forum.monsite.com/.wel-known/acme-challenge rm /var/www/nginx/forum.monsite.com/.wel-known/acme-challenge
Sous Apache
Exemple complet de proxying vers un Nextcloud restreint au LAN, qui tourne dans un docker sur le port local 12345, et qui est associé à un sous domaine dédiéfiles.monsite.com
.
L'exécution du script certbot
de Let's Encrypt se chargera du reste de la configuration HTTPS. On peut les tester avec certbot --apache --dry-run certonly -d files.monsite.com
.
à noter le
DocumentRoot
qui ne sert ici qu'au test du challenge (il peut se trouver n'importe où www-data
pourra écrire temporairement).
<VirtualHost 0.0.0.0:80> ServerName files.monsite.com DocumentRoot /var/www/files.monsite.com ErrorLog ${APACHE_LOG_DIR}/nextcloud_error.log CustomLog ${APACHE_LOG_DIR}/nextcloud_access.log combined ProxyPreserveHost On ProxyPass /.well-known/acme-challenge ! ProxyPass / http://127.0.0.1:12345/ ProxyPassReverse / http://127.0.0.1:12345/ <Location "/.well-known"> Satisfy Any Allow from all </Location> <LocationMatch "^/(?!\.well-known/)"> Order Deny,Allow Deny from all Allow from 127.0.0.1 Allow from 192.168.1 </LocationMatch> </VirtualHost>