Logo Khaganat
Traductions de cette page?:

Cette page est noté comme étant en brouillon. Votre participation pour l'améliorer est requise !

Ceci est une ancienne révision du document !


Nginx

Installation

Pour Debian, installez les paquets de base :

sudo apt install nginx-full

Pour PHP :

sudo apt install php php-gd php-imagick php-curl php-intl php-fpm

Ces paquets installent les autres dépendances utiles.

Pour php, les modules complémentaires sont ceux qui nous sont utiles sur Khaganat, mais suivant les CMS ils peuvent être inutiles, ou avoir besoin d'être complétés.

Aperçu de Nginx

La configuration de Nginx et des sites se trouve dans /etc/nginx.

nginx
├── conf.d
├── custom
│   ├── acme_http-01.conf
│   ├── apache_compat.conf
│   ├── headers.conf
│   ├── headers_nocsp.conf
│   └── tls.conf
├── fastcgi.conf
├── fastcgi_params
├── koi-utf
├── koi-win
├── mime.types
├── modules-available
├── modules-enabled
│   ├── 50-mod-http-auth-pam.conf -> /usr/share/nginx/modules-available/mod-http-auth-pam.conf
│   ├── 50-mod-http-dav-ext.conf -> /usr/share/nginx/modules-available/mod-http-dav-ext.conf
│   ├── 50-mod-http-echo.conf -> /usr/share/nginx/modules-available/mod-http-echo.conf
│   ├── 50-mod-http-geoip.conf -> /usr/share/nginx/modules-available/mod-http-geoip.conf
│   ├── 50-mod-http-image-filter.conf -> /usr/share/nginx/modules-available/mod-http-image-filter.conf
│   ├── 50-mod-http-subs-filter.conf -> /usr/share/nginx/modules-available/mod-http-subs-filter.conf
│   ├── 50-mod-http-upstream-fair.conf -> /usr/share/nginx/modules-available/mod-http-upstream-fair.conf
│   ├── 50-mod-http-xslt-filter.conf -> /usr/share/nginx/modules-available/mod-http-xslt-filter.conf
│   ├── 50-mod-mail.conf -> /usr/share/nginx/modules-available/mod-mail.conf
│   └── 50-mod-stream.conf -> /usr/share/nginx/modules-available/mod-stream.conf
├── nginx.conf
├── proxy_params
├── scgi_params
├── sites-available
│   ├── default
│   └── test.conf
├── sites-enabled
│   └── test.conf -> ../sites-available/test.conf
├── snippets
│   ├── fastcgi-php.conf
│   └── snakeoil.conf
├── uwsgi_params
└── win-utf
  • Le fichier principal de configuration est nginx.conf, il fixe les paramètres globaux et c'est utile de l'adapter à ses besoins (backend ou proxy ⇒ pas les mêmes contraintes).
  • Tout comme pour Apache, le dossier sites-enabled contient les liens symboliques vers les fichiers de configuration des différents sites situés dans sites-available. Ces autres fichiers de configuration sont inclus depuis le fichier de configuration principal nginx.conf grâce à la directive include /etc/nginx/sites-enabled/*.

La spécificité de Khaganat est la création du dossier custom qui contient des fichiers de configuration destinés à être inclus au cas par cas dans la configuration de chaque site. Les fichiers de custom sont les suivants :

  • acme_http-01.conf : Configuration nécessaire pour la résolution du challenge HTTP-01 lors d'un renouvellement de certificat initié par ACMEd.
  • apache_compat.conf : Interdit l'accès aux fichiers dont le nom commence par .ht (notamment .htaccess et .htpasswd) afin de ne pas faire fuiter les éventuels résidus de fichiers de configuration spécifiques à Apache.
  • headers.conf : Ajoute tous les headers HTTP classiques dont un site a normalement besoin. :!: Attention, ceci inclut des CSP très sévères.
  • headers_nocsp.conf : Identique à headers.conf mais n'inclut aucune CSP afin de laisser la libre configuration de ces dernières.
  • tls.conf : Configuration TLS.

Ajouter un domaine ou sous-domaine

Créez un fichier de configuration pour votre nom de domaine dans /etc/nginx/sites-available/monsite.conf.

Un exemple de fichier est disponible dans /etc/nginx/sites-available/default. Copiez-le, adaptez-le, puis enregistrez-le avec un nom compréhensible.

Vous pouvez vérifier que vous n'avez pas fait d'erreur de syntaxe avec la commande suivante :

nginx -t

Ou vous pouvez voir la configuration lue par nginx (en plus de la tester) avec cette commande :

nginx -T | less

Activez ensuite le site en créant un lien symbolique dans /etc/nginx/sites-enabled/ :

cd /etc/nginx/sites-enabled/
sudo ln -s ../sites-available/monsite.conf

Puis dites à nginx de prendre en compte la nouvelle configuration en le redémarrant :

sudo service nginx restart

Rediriger les sous-domaines non existant vers une adresse précise

Créez un fichier de configuration redirigeant tout ce qui ne matche pas :

# En cas d'erreur de sous-domaine, renvoie sur example.org.
server {
    listen 10.0.0.10:80 default_server;
    listen 10.0.0.10:443 ssl default_server;
    # Toutes requêtes non définie par ailleurs
    server_name _;
    include snippets/snakeoil.conf;
        access_log off;
        error_log  off;
    return 301 https://example.org/404.html;
}

Le snippets snakeoil ici est pour le https, c'est un certificat autosigné, c'est mieux que “rien”, mais ça fera des erreurs dans les navigateurs des visiteuses.

SSL Passthrough sur proxy Nginx

Un proxy est un outil qui peut faire beaucoup de choses, ou très peu. Dans notre cas, l'utilité principale est qu'en ayant une seule ipv4, on containerise les diverses applications histoire de limiter les dégâts si un des logiciels est troué. Donc tout arrive sur le serveur dédié, les flux sur les ports 80 et 443 sont redirigés vers le container du proxy, qui suivant les noms de domaine demandés renvoie vers un des containers ou un autre.

Il est assez simple de gérer les certificats ssl au niveau du proxy, mais cela a pour conséquence que le trafic entre le proxy et les containers passe “en clair”. Une attaque MITM à ce stade ne fais pas vraiment partie de notre modèle de menace, cependant certains CMS fonctionnent mal s'ils ne sont pas en https. Pour contourner ce problème, on peut donc dire au proxy de rediriger le flux mais sans s'occuper de ssl : il ne va rien déchiffrer et laisse ça au “backend”. Cela passe par l'usage de “stream” plutôt que de “http” au niveau de nginx.

On ne peux pas avoir à la fois des services sans ssl passthrough et d'autres avec. Soit tout ce qui passe sur 443 est redirigé vers les bons services (stream), soit c'est notre proxy qui gère les certificats ssl et transmet ensuite le flux en clair (site).

Ce type de configuration est riche en prise de tête. Ce n'est pas la peine d'essayer de le complexifier encore plus…

Pour notre exemple :

  • Le proxy Nginx reçoit tout le trafic (HTTP/HTTPS) pour monsite.org et autresite.net. Son ip locale est 192.168.1.100.
  • Suivant le nom de domaine demandé, il redirige le flux vers les bonnes ip en backend.
  • monsite.org a comme IP interne : 192.168.1.207 et autresite.net a 192.168.1.208. Et les deux sont avec Apache, sinon ce n'est pas drôle.

Ici “le proxy” fait référence à la VM qui centralise les flux avant de les redispatcher et “le backend” à toute VM/Container derrière, qui reçoit ce que le proxy a transmis.

Sur le proxy

On modifie /etc/nginx/nginx.conf pour ajouter la ligne suivante (attention, pas dans le bloc “http” ; plutôt à la suite de include /etc/nginx/modules-enabled/*.conf; par exemple :

include /etc/nginx/stream-enabled/*.conf;

Et on crée deux dossiers dont celui qui est lu :

sudo mkdir -p /etc/nginx/stream-enabled
sudo mkdir -p /etc/nginx/stream-avalaible

On suit avec “stream-*” une logique similaire aux dossiers “site-*”, cela permet d'activer/désactiver une configuration suivant le besoin. Et c'est parce que notre proxy gère beaucoup de domaines, on peut faire ça dans un seul fichier sur /etc/nginx/conf.d/stream/mystream.conf aussi (dans ce cas pas besoin de l'include dans nginx.conf, parait-il). Faudrait d'ailleurs que je prévois de découper, sans doute… on va voir à l'usage.

Voici à quoi ressemble un fichier de stream :

/etc/nginx/stream-available/nosites443.conf
# Table de routage : nom de domaine -> IP backend
stream {
	map $ssl_preread_server_name $backend {
		monsite.org 192.168.1.207:443;
		autresite.net  192.168.1.208:443;
		# Optionnel : où on redirige si le nom de domaine n'existe pas ?
		default        192.168.1.207:443;
	}
 
# HTTPS - SSL Passthrough
	server {
		listen 192.168.1.100:443;
		# Lit les nom de domaine sans déchiffrer le reste
		ssl_preread on;
		# Route vers la VM correspondante
		proxy_pass $backend;
		# Transmet l'IP réelle des visiteuses
		proxy_protocol on;
	}
}
  • map indique la correspondance entre les noms de domaine, et les ip où se trouvent réellement les sites.
  • listen : on écoute sur le port 443 du proxy
  • Et le reste est commenté ;)

Nous devons aussi déclarer le flux sur le port 80 ; mais là ça ne peut pas être du “stream”, on revient au mode de proxy plus traditionnel. Donc, on déclare les sites sur /sites-enabled :

/etc/nginx/sites-available/nosites80.conf
    server {
        listen 80;
        server_name monsite.org;
 
        location / {
            proxy_pass http://192.168.1.207:80;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
 
    server {
        listen 80;
        server_name autresite.net.fr;
 
        location / {
            proxy_pass http://192.168.1.208:80;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

Ici c'est vraiment très basique, mais il serait possible de rediriger dès maintenant vers une connexion sécurisée (443) sauf ce qui est nécessaire à l'obtention d'un certificat ssl

/etc/nginx/sites-available/nosites80.conf
server {
    listen 80;
    server_name monsite.org;
 
    # Rediriger tout vers HTTPS (sauf /.well-known/)
    location /.well-known/acme-challenge/ {
        proxy_pass http://192.168.1.207:80;
        proxy_set_header Host $host;
    }
 
    location / {
        return 301 https://$host$request_uri;
    }
}

Mais je préfère faire simple et gérer les ennuis côté apache, parce que les possibilités de bizarreries restent élevées avec ce genre de configuration.

On valide tout ça :

sudo ln -s /etc/nginx/sites-available/nosites80.conf /etc/nginx/sites-enabled/nosites80.conf
sudo ln -s /etc/nginx/stream-avalaible/nosites443.conf /etc/nginx/stream-enabled/nosites443.conf
sudo nginx -t
sudo systemctl restart nginx

Sur le backend (Apache)

Je pars du principe que le backend est avec Apache. Ça pourrait aussi être du nginx, mais je vous laisse faire la doc.

On commence par /etc/apache2/conf-available/remoteip.conf :

/etc/apache2/conf-available/remoteip.conf
# Déclare le proxy afin de recevoir les vraies ip des clients.
# Remplacer l'ip par celle du proxy !
RemoteIPInternalProxy 192.168.1.206
RemoteIPTrustedProxy 192.168.1.206
RemoteIPHeader X-Forwarded-For

Puis

 
sudo a2enmod remoteip ssl

Et on configure ensuite notre site, quasiment comme s'il était sur un dédié :

/etc/apache2/sites-available/org.monsite.conf
<VirtualHost *:80>
	ServerName monsite.org
	DocumentRoot /var/www/org.monsite
 
	# Redirection vers 443 sauf pour la demande de certificat
	RewriteEngine On
	RewriteCond %{REQUEST_URI} !.well-known/acme-challenge
	RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [R=301,L]
</VirtualHost>
 
<VirtualHost *:443>
	ServerName monsite.org
	# Important avec le ssl passthrough !!!
	RemoteIPProxyProtocol On
 
	DocumentRoot /var/www/org.monsite
	<Directory /var/www/org.monsite/ >
		Options FollowSymLinks MultiViews
		AllowOverride All
		Require all granted
	</Directory>
 
	SSLEngine On
	SSLCertificateFile /etc/letsencrypt/live/monsite.org/fullchain.pem
	SSLCertificateKeyFile /etc/letsencrypt/live/monsite.org/privkey.pem
</VirtualHost>
 
Sur le VirtualHost le point vraiment essentiel est de mettre le paramètre ''RemoteIPProxyProtocol On'' dans la partie 443. Non, on ne peux pas la mettre sur remoteip.conf, même entre les balises ''<IfModule mod_ssl.c>'' ; ça s'applique même sur 80 et ça fait planter (sur le 80 uniquement, ce qui n'est pas grave jusqu'au moment où on renouvelle le certificat). 
 
On valide tout ça 
 
<code>
sudo a2ensite org.monsite
sudo apachectl -t
sudo service restart

Exemples de configuration

Proxy sans ssl passthrough

Exemple simple:

server {
    listen      ip.du.pro.xy:80;
    server_name exemple.khaganat.net;
 
    location / {
        proxy_pass http://IP.DE.LA.VMàjoindre;
        include /etc/nginx/proxy.conf;
    }
 
}

Création d'une conf commune :

nano /etc/nginx/proxy.conf
proxy_redirect          off;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    50m;
client_body_buffer_size 512k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffers           32 4k;

Exemple plus complet:

server {
    listen      192.168.20.10:80;
    server_name "exemple.net";
 
    return      301 https://exemple.net$request_uri;
}
 
server {
    listen      192.168.20.10:443 ssl http2;
    server_name "exemple.net";
 
    ssl_certificate /etc/letsencrypt/live/exemple.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/exemple.net/privkey.pem;
 
    location / {
        proxy_set_header    Host $host;
        proxy_set_header    X-Real-IP $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto $scheme;
        proxy_buffering     off;
        proxy_pass          http://IP.DE.LA.VMàjoindre/;
    }
}

D'autres exemples (ajouter des liens vers les autres tuto) :

Demander un certificat let'encrypt

Voir sur_nginx.

Sources et liens

En cas de besoin, la documentation d'nginx est très complète. La lecture des articles suivants est recommandée :

CC Attribution-Share Alike 4.0 International Driven by DokuWiki
fr/nginx.1751970549.txt.gz · Dernière modification : de zatalyz

Licences Mentions légales Accueil du site Contact Inclusion