Introduction
Nous allons voir dans cet article comment mettre en place une connexion sécurisée (via https
) avec des certificats valides d’une application que nous développons localement.
Lors de la phase de développement d’une application il peut, pour diverses raisons, être nécessaire d’avoir une connection sécurisée (en https), comme par exemple avoir des cookies ayant le flag secure
, qui interdit l’accès aux cookies hors connexion chiffrée.
Il est évidement possible de gérer cette problématique de la manière suivante :
cookies.set('MON_COOKIE', 'MA_VALEUR', { secure: process.env.NODE_ENV === 'production', });
Ceci fonctionne, mais il en résulte deux problèmes majeurs:
- Il y peut y avoir unefaille de sécuritési la variable d’environnement
NODE_ENV
est mal configurée. En effet sur l’environnement deproduction
si la valeur deNODE_ENV
n’est pasproduction
, le flagsecure
du cookie sera alorsfalse
. - Nous avons un comportement différent selon les environnements.
Mise en place
Dans un premier temps nous allons avoir besoin d’une application, dans notre exemple nous allons utiliser ReactJS.
Créons notre application et lançons la :
$ npx create-react-app local-proxy $ cd local-proxy $ npm start
Voici le résultat (http://localhost:3000), pour le moment rien de compliqué :
Nginx proxy
J’ai choisi de mettre en place un proxy nginx pour plusieurs raisons :
- Facilité de configuration, notamment pour ajouter des certificats pour le SSL
- Se rapprocher au maximum de ce que nous pourrions potentiellement avoir en production
- Possibilité de
proxifier
facilement un BFF (Backend For Frontend) si nous en avons a un qui tourne localement, afin d’avoir un nom de domaine unique.
Pour cela, nous allons dans un premier temps écrire un fichier de configuration nginx afin de faire un reverse proxy
vers notre application en local. Nous allons ensuite créer un nom de domaine dans notre fichier /etc/hosts
et enfin utiliser Docker pour démarrer un server nginx sans avoir à l’installer sur notre machine.
Configuration nginx
Voici à quoi ressemble le fichier nginx.conf
pour le moment :
user nginx; worker_processes 5; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 4096; } http { server { listen 80 default_server; # On accepte les connections sur le port 80 listen [::]:80 default_server; # Pour les addresses IPv6 access_log /var/log/nginx/access.log combined; error_log /var/log/nginx/error.log debug; # C'est ici que l'on va configurer le reverse proxy, # tel que c'est définit ici, toutes les pages seront redirigées # vers l'addresse qui est définit dans l'option "proxy_pass" # qui correspond à notre application ReactJS location / { proxy_pass ernal:3000; proxy_set_header Host $http_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; } } }
Le mot clé host.docker.internal
à la ligne 24, permet de cibler la machine hôte du container docker.
/!\ Attention, dans mon cas je suis sous MacOS, Il se peut que selon le système d’exploitation utilisé, ce mot clé ne fonctionne pas, je vous invite alors à explorer la documentation de docker afin de savoir comment cibler la machine hôte selon votre système d’exploitation.
Création d’un nom de domaine local
Dans cette étape, nous allons créer un nom de domaine spécifique à l’application, cela peut fonctionner avec https://localhost
, mais en ayant un FQDN
destiné à l’application, nous savons qu’il y a une configuration spécifique (derrière un proxy ou autre). Le revers de la médaille, c’est qu’il faudra refaire le proxy pour une appli différente.
Nous allons donc créer le nom de domaine local.myapp.com
, que seule notre machine pourra résoudre, notre application sera alors accessible via cette url.
Pour cela éditons le fichier /etc/hosts
(vous devez avoir les droits administrateur pour pouvoir l’éditer), pour ajouter à la fin, la ligne suivante :
127.0.0.1 local.myapp.com
Nous vérifions que notre machine réussit bien à résoudre ce nom de domaine :
$ ping local.myapp.com PING local.myapp.com (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.036 ms 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.103 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.103 ms
Dockerfile
Nous allons maintenant écrire un Dockerfile
afin de construire une image de notre proxy nginx :
FROM nginx:1.19.10 COPY nginx.conf /etc/nginx/nginx.conf
Construisons maintenant notre image :
$ docker build -t local-proxy .
Et lançons le container avec cette image :
$ docker run -d -p 80:80 --name myapp-local local-proxy
Nous pouvons maintenant accéder à notre application avec l’URL :
Comme nous pouvons le constater, nous avons toujours une connection non sécurisée, modifions un peu le Dockerfile
ainsi que la configuration nginx pour corriger cela.
Activation de la connexion sécurisée
Avant de pouvoir activer ssl
dans notre proxy nginx, il nous faut des certificats valides, nous ne pouvons pas utiliser des certificats auto-signés car cela entrainerait des erreurs de sécurité, les fameux « Trust Errors ». Nous allons donc utiliser un outil pour générer des certificats valides.
L’outil mkcert
L’outil mkcert va automatiquement générer et installer sur votre machine une autorité de certification (CA, Certificate Authority en anglais) et l’utiliser pour générer des certificats “locally-trusted” et c’est exactement ce dont nous avons besoin pour notre cas.
Veuillez vous référer à la documentation de mkcert pour l’installer sur votre machine selon votre système d’exploitation.
Une fois l’outil installé, nous pouvons générer les certificats :
$ mkcert -install $ mkcert local.myapp.com
L’outil nous a généré deux fichiers local.myapp.com-key.pem
et local.myapp.com.pem
, qui correspondent à la clé privée ainsi qu’au certificat serveur, que nous allons fournir à notre proxy nginx.
Configuration Nginx
Une fois ces deux fichiers certificats générés, nous allons pouvoir reconfigurer nginx afin d’activer la connexion https
. Voici à quoi ressemble la nouvelle configuration:
user nginx; worker_processes 5; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 4096; } http { server { listen 443 ssl default_server; # On change le port de 80 à 443, le port par défaut pour https listen [::]:443 ssl default_server; # Ici on définit les paramètres pour l'activation du SSL ssl_certificate /opt/myapp/certs/local.myapp.com.pem; ssl_certificate_key /opt/myapp/certs/local.myapp.com-key.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; access_log /var/log/nginx/access.log combined; error_log /var/log/nginx/error.log debug; location / { proxy_pass ernal:3000; proxy_set_header Host $http_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; } } }
Nous avons passé, dans les attributs ssl_certificate
et ssl_certificate_key
, le chemin vers les deux fichiers que mkcert nous a généré, il faut donc que notre container docker ait accès à ces fichiers, nous devons donc modifier également le Dockerfile
:
FROM nginx:1.19.10 COPY nginx.conf /etc/nginx/nginx.conf RUN mkdir -p /opt/myapp/certs COPY local.myapp.com-key.pem /opt/myapp/certs/local.myapp.com-key.pem COPY local.myapp.com.pem /opt/myapp/certs/local.myapp.com.pem
Ceci fait, il faut reconstruire l’image mais avant nous devons stopper et supprimer le container local-proxy
lancé précédemment. Nous pouvons également supprimer l’ancienne image car elle ne nous servira plus à l’avenir, puis nous allons reconstruire et relancer le container en changeant le port binding de 80
à 443
:
$ docker stop myapp-local $ docker rm myapp-local $ docker rmi local-proxy $ docker build -t local-proxy . $ docker run -d -p 443:443 --name myapp-local local-proxy
Nous pouvons maintenant accéder a notre application via l’URL :
Conclusion
Notre application React est désormais accessible via une URL sécurisée avec un joli nom de domaine.
Ceci décrit une méthode parmi tant d’autres, d’autre méthodes proposeront une solution sans container docker mais cela ne remet pas en cause le principe général de cette solution.