MaDoVi - Le wiki

Machines - Données - Virtualisation

Outils pour utilisateurs

Outils du site


services:web:tuto:nginx_proxy1

Nginx - Reverse Proxy Web

On souhaite exploiter ici la fonctionnalité de «reverse proxy» du serveur Nginx.

Dans le cadre du projet , ce service va nous permettre d'adresser les requêtes web reçues sur notre machine vers le site correspondant, fonction de l'URL.
On va positionner ce composant au sein d'un container LXC, ce qui correspond au schéma plus global proposé dans ce paragraphe.

Les manipulations listées ci-dessous pourront s'appliquer dans un environnement Linux quelconque – aux commandes près propres à la distribution choisie – une fois qu'on est à l'intérieur du CT. Vous pouvez ainsi ignorer les mentions au container si vous ne travaillez pas dans ce contexte, le reste demeure applicable…

1. Création du container LXC

On souhaite donc installer notre serveur proxy web dans un CT LXC. Ce container sera «non privilégié», et on va donc le créer depuis le compte admin. On va mettre….heuuu… Tiens, du Alpine Linux. Il s'agit d'une distribution a une très faible empreinte système: «Les cibles principales d'Alpine sont les conteneurs et presque tous les appareils embarqués».

  • Création:
    admin@wasabi:~$ lxc-create -n lxc-proxy -t download -- -d alpine -r 3.11 -a amd64
  • Lancement:
    admin@wasabi:~$ lxc-start -n lxc-proxy 
  • Contrôle:
    admin@wasabi:~$ lxc-ls -f
    NAME      STATE   AUTOSTART GROUPS IPV4      IPV6 UNPRIVILEGED 
    lxc-proxy RUNNING 0         -      10.0.3.50 -    true         

    Il tourne, il a l'adresse attendue, il est bien unprivileged.


2. Préparation & Installation

On commence par s'«attacher» au container pour faire ce qu'on a à faire dedans:

admin@wasabi:~$ lxc-attach -n lxc-proxy 
/ # 

Les ajouts/installations/configurations de base: outils, ssh, Nginx (l'outil de gestion de paquets s'appelle apk sous Alpine).

  • Commençons par les outils:
    / # /sbin/apk update
    / # /sbin/apk add attr dialog dialog-doc bash bash-doc bash-completion grep grep-doc
    / # /sbin/apk add util-linux util-linux-doc pciutils usbutils binutils findutils readline
    / # /sbin/apk add man man-pages lsof lsof-doc less less-doc nano nano-doc curl curl-doc
    / # /sbin/apk add sudo sudo-doc openssh nginx libressl 
    / # export PAGER=less
  • On prend bash comme interpréteur de commandes: il suffit de remplacer, dans le fichier /etc/passwd la fin de la ligne associée au compte root (la 1ière) par celle-ci (ash devient bash):
    # nano /etc/passwd
     
    root:x:0:0:root:/root:/bin/bash
    ...

    Ctrl+x et y pour enregistrer et sortir.

  • Lancer le service ssh (et l'ajouter au démarrage du container):
    / # /sbin/rc-update add sshd
    / # /etc/init.d/sshd start
    ssh-keygen: generating new host keys: RSA DSA ECDSA ED25519 
     * Starting sshd ...                                        [ ok ]
  • Ajout d'un utilisateur «monusr» avec droit root (sudo):
    / # /usr/sbin/adduser monusr
    Changing password for monusr
    New password: 
    Retype password: 
    passwd: password for monusr changed by root
     
    / # /usr/sbin/visudo
    ## ajouter la ligne suivante (attention: VI - i pour insérer, esc + :w (sauver) + :q (quitter))
    ##                                                               
    ## User privilege specification
    ##
    root ALL=(ALL) ALL       
    monusr ALL=(ALL) ALL   ## <<<=== ICI
  • On lève un truc bizarre dans l'utilisation de sudo: il nous colle un sudo: setrlimit(RLIMIT_CORE): Operation not permitted à chaque utilisation… Pour l'éviter, on crée un fichier de config avec le contenu suivant:
    / # nano /etc/sudo.conf
    Set disable_coredump false
  • On redémarre pour prise en compte, du coup on sort et on se retrouve avec le shell du serveur:
    / # /sbin/reboot
    admin@wasabi:~$
  • On va maintenant travailler à partir d'une connexion ssh:
    admin@wasabi:~$ ssh monusr@lxc-proxy
    ## accepter l'échange de clé
    monusr@lxc-proxy's password: 
    Welcome to Alpine!
     
    The Alpine Wiki contains a large amount of how-to guides and general
    information about administrating Alpine systems.
    See <http://wiki.alpinelinux.org/>.
     
    You can setup the system with the command: setup-alpine
     
    You may change this message by editing /etc/motd.
    lxc-proxy:~$
  • On se positionne en root pour toutes les manip. à suivre:
    lxc-proxy:~$ sudo -i
    lxc-proxy:~#

    Notez le changement de $ à #: on est bien administrateur dans notre container.

  • On fixe le fuseau horaire:
    lxc-proxy:~# setup-timezone -z Europe/Paris

Maintenant, on va configurer notre proxy, avec du SSL/TLS.


3. Configuration

La configuration de Nginx en mode proxy est très simple: elle se limite à la mise en œuvre du certification SSL/TLS et à son utilisation.

3.1. Configuration du serveur

La configuration du serveur livrée nativement lors de l'installation de Nginx ne devrait pas à être modifiée a priori:

lxc-proxy:~# nano /etc/nginx/nginx.conf
user nginx;
worker_processes 1; # use "auto" to use all available cores (high performance)
 
# Configures default error logger.
error_log /var/log/nginx/error.log warn; # Log warn, error, crit, alert, emerg
 
events {
    # The maximum number of simultaneous connections that can be opened by a worker process.
    worker_connections 1024; # increase if you need more connections
}
 
http {
    # server_names_hash_bucket_size controls the maximum length
    # of a virtual host entry (ie the length of the domain name).
    server_names_hash_bucket_size   64; # controls the maximum length of a virtual host entry (ie domain name)
    server_tokens                   off; # hide who we are, don't show nginx version to clients
    sendfile                        off; # can cause issues
 
    # nginx will find this file in the config directory set at nginx build time
    # Includes mapping of file name extensions to MIME types of responses
    include mime.types;
 
    # fallback in case we can't determine a type
    default_type application/octet-stream;
 
    # buffering causes issues, disable it
    # increase buffer size. still useful even when buffering is off
    proxy_buffering off;
    proxy_buffer_size 4k;
 
    # allow the server to close the connection after a client stops responding. Frees up socket-associated memory.
    reset_timedout_connection on;
 
    # Specifies the main log format.
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
            '$status $body_bytes_sent "$http_referer" '
            '"$http_user_agent" "$http_x_forwarded_for"';
 
    # Sets the path, format, and configuration for a buffered log write.
    # Buffer log writes to speed up IO, or disable them altogether
    access_log /var/log/nginx/access.log main buffer=16k;
    #access_log off;
 
    # include virtual hosts configs
    include conf.d/*.conf;
}

3.2. Certificat SSL/TLS et «routage»

☛ Certificat:

  • On commence par créer un répertoire dans lequel on se positionne :
    lxc-proxy:~# mkdir  /etc/nginx/ssl/
    lxc-proxy:~# cd /etc/nginx/ssl/
  • On génère une autorité de certification (CA) en spécifiant une passphrase (lxc-proxy ici, vous mettez ce que vous voulez, mais vous vous en souvenez ;-)):
    lxc-proxy:~# openssl genrsa -des3 -out server.key 4096
    Generating RSA private key, 4096 bit long modulus (2 primes)
    ...............................................................++++
    .....++++
    e is 65537 (0x010001)
    Enter pass phrase for server.key:lxc-proxy
    Verifying - Enter pass phrase for server.key:lxc-proxy
  • Puis on spécifie les informations de l’autorité de certification :
    lxc-proxy:~# openssl req -new -key server.key -out server.csr
    Enter pass phrase for server.key:
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:FR
    State or Province Name (full name) [Some-State]:Département
    Locality Name (eg, city) []:Village
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:Maison
    Organizational Unit Name (eg, section) []:Bureau
    Common Name (e.g. server FQDN or YOUR name) []:lxc-proxy.lxc
    Email Address []:root@wasabi.lan
     
    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:
    An optional company name []:
  • Et on crée une clé allant avec le certificat (ici durée de validité 1000 jours):
    lxc-proxy:~# cp server.key server.key.org
    lxc-proxy:~# openssl rsa -in server.key.org -out server.key
    lxc-proxy:~# openssl x509 -req -days 1000 -in server.csr -signkey server.key -out server.crt

☛ Configuration proxy:

  • On commence par renommer (revient à ne pas le prendre en compte) le fichier de configuration par défaut:
    lxc-proxy:~# mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.original
  • Ensuite, on veut rediriger de façon systématique les flux entrant en HTTP (port 80) vers HTTPS (port 443), et cela pour toutes les requêtes. Le plus simple est de définir un fichier qui sera inclus dans la configuration chaque fois qu'on en aura besoin.
    On crée ainsi ce fichier et son contenu:
    lxc-proxy:~# nano /etc/nginx/conf.d/redirect_http.inc
    location / {
            return 301 https://$host$request_uri;
    }
  • Puis, il s'agit de répercuter les entêtes comme il faut vers les serveurs cibles. De la même façon, on définit un fichier qui sera inclus là où ca va bien:
    lxc-proxy:~$ nano /etc/nginx/conf.d/proxy_set_header.inc 
    proxy_set_header    X-Forwarded-By       $server_addr:$server_port;
    proxy_set_header    X-Forwarded-For      $remote_addr;
    proxy_set_header    X-Forwarded-Proto    $scheme;
    proxy_set_header    Host                 $host;
  • Enfin, on crée un fichier de configuration qui va «intégrer» tout cela:
    lxc-proxy:~# nano /etc/nginx/conf.d/lxc-proxy.conf
    server {
        listen                  80;
        listen                  [::]:80;
        server_name             default_server;
        include                 conf.d/redirect_http.inc;
    }
     
    server {
        listen                  443 ssl http2;
        listen                  [::]:443 ssl http2;
        server_name             default_server;
        ssl_certificate         /etc/nginx/ssl/server.crt;
        ssl_certificate_key     /etc/nginx/ssl/server.key;
     
        location / {
            include             conf.d/proxy_set_header.inc;
            proxy_pass          http://lxc-web01/phpinfo.php;
        }
     
        location /adminer {
            include             conf.d/proxy_set_header.inc;
            proxy_pass          http://lxc-web01/adminer/;
        }
    }

Quelques «explications»:

  1. On défini un contenu entre balise server { … } pour chaque «tuyau» écouté
  2. Le premier correspond à l'écoute du port 80 (HTTP): le flux est redirigé vers HTTPS systématiquement (fichier inclus)
  3. Le second est donc la configuration principale:
    1. écoute sur le port 443 ssl (IPv4 et IPv6)
    2. les éléments de sécurité SSL sont indiqués: clé publique et certificat
    3. enfin, pour chaque location présente dans l'URL d'origine sont précisés les entêtes et le serveur cible.

Ce fichier est donc à revisiter chaque fois que vous souhaiter ajouter des services web, que ce soit dans un container web existant, dans un nouveau container, voire si vous souhaitez atteindre une autre machine. FIXME pas sûr celà….

A chaque fois que vous le ferez, vous pourrez:

  • Vérifier la nouvelle configuration:
    lxc-proxy:~$ sudo nginx -t
  • Redémarrer Nginx pour prise en compte:
    lxc-proxy:~$ sudo service nginx restart

4. Quelques compléments

☛ Démarrage avec l'hôte

Si vous souhaitez éviter de faire «à la main» le lancement du container après un arrêt/relance du serveur, il suffit de modifier sa configuration en ajoutant ou dé-commentant les lignes suivantes:

$ nano $(lxc-config lxc.lxcpath)/lxc-proxy/config
lxc.group = onboot
lxc.group = webserver
lxc.start.auto = 1

Pour autant que vous ayez suivi la partie “autostart:” dans la configuration des containers «non privilégiés»

☛ NATer - Ouvrir les accès

Dans la configuration ci-dessus, Nginx est prêt pour recevoir des requêtes depuis le réseau sur les ports HTTP 80 et HTTPS 443. Mais il s'agit du réseau auquel il appartient, à savoir le réseau de containers LXC en 10.0.3.xxx.

Pour le rendre accessible depuis le réseau local de votre installation, il va falloir que l'hôte expose ces ports sur le réseau via la commande iptables. Elle peut prendre les formes suivantes:

Ouvrir & fermer et Voir les @:ports NATés:

Terminal
Ouvrir:
sudo iptables -t nat -A PREROUTING -p tcp -i <interface> --dport <portHost> -j DNAT --to-destination <@IP:Port Destination>
Exemple:
sudo iptables -t nat -A PREROUTING -p tcp -i br0 --dport 80 -j DNAT --to-destination 10.0.3.50:80
sudo iptables -t nat -A PREROUTING -p tcp -i br0 --dport 443 -j DNAT --to-destination 10.0.3.50:443
 
Fermer:
sudo iptables -t nat -D PREROUTING -p tcp -i <interface> --dport <portHost> -j DNAT --to-destination <@IP:Port Destination>
Exemple:
sudo iptables -t nat -D PREROUTING -p tcp -i br0 --dport 80 -j DNAT --to-destination 10.0.3.50:80
sudo iptables -t nat -D PREROUTING -p tcp -i br0 --dport 443 -j DNAT --to-destination 10.0.3.50:443
 
Liste:
sudo iptables -t nat -L
sudo iptables -t nat -S

On peut utiliser ici ces ports-là sur l'hôte car il ne sont pas pris par un quelconque serveur HTTP/HTTPS sur notre machine.
S'il s'avère qu'un tel serveur est nécessaire à terme, il faudra utiliser d'autres ports.

A la première utilisation, votre navigateur vous remontera un alerte sur le fait que le certificat du proxy n'appartient pas à une «Autorité de certification» reconnue; vous pourrez alors valider une exception qui lèvera cette alerte pour les utilisations futures.

L'intérêt d'un proxy est donc pratique, en particulier dans le le cadre du projet : le proxy porte seul le certificat et le lien HTTPS y est imposé; les serveurs web «derrière» peuvent s'affranchir de cela, avec un lien plus simple en HTTP entre le proxy et eux.

services/web/tuto/nginx_proxy1.txt · Dernière modification: 08/03/2020 20:53 de Cram28