Retour au blog

Comment sécuriser et mettre à l'échelle une application Django avec Docker, Nginx et Let’s Encrypt

Comment sécuriser et mettre à l'échelle une application Django avec Docker, Nginx et Let’s Encrypt

Des millions d'utilisateurs se connectent à Internet pour accéder à des informations à des fins diverses, notamment l'apprentissage, le divertissement, les actualités et le partage de l’évolution de leur vie avec leurs amis. Par conséquent, lors du déploiement d'une application, il est dans votre intérêt de mettre en œuvre une infrastructure hautement sécurisée et évolutive pour votre application. Le cloud offre différentes manières de sécuriser et de faire évoluer une Django application. La mise à l'échelle horizontale est une méthode qui vous permet d'exécuter plusieurs copies de votre application. Cela garantit qu'elle est plus tolérante aux pannes et hautement disponible. Cela augmente également ses performances pour traiter plusieurs requêtes simultanément.

Mettre à l'échelle horizontalement une application Django

Vous pouvez mettre à l'échelle horizontalement une application Django en provisionnant plusieurs serveurs d'application qui exécutent l'application Django et son serveur HTTP WSGI (comme Gunicorn ou uWSGI). Vous devrez ensuite mettre en place une infrastructure pour distribuer les requêtes entrantes sur ces serveurs d'application. Un répartiteur de charge et un proxy inverse comme Nginx peuvent aider votre infrastructure à distribuer le trafic. Nginx peut déployer des certificats SSL garantissant des connexions sécurisées à votre application via HTTPS. Enfin, Nginx peut également fournir une mise en cache du contenu statique afin de minimiser la charge sur votre serveur.

Configurer ces différents composants séparément et s'assurer qu'ils communiquent peut être une tâche ardue. Heureusement, l'utilisation de Docker simplifie le processus de configuration et garantit que les différents composants se comportent de la même manière, quel que soit l'endroit où ils sont déployés.

Ce que vous allez faire dans ce guide

Dans ce guide, vous apprendrez à mettre à l'échelle horizontalement une application Django conteneurisée, servie avec un serveur HTTP WSGI Gunicorn. Vous provisionnerez deux serveurs d'application, chacun ayant Docker installé, exécutant la même copie d'un conteneur d'application Django et Gunicorn.

Vous sécuriserez également votre application avec un Let’s Encrypt certificat SSL en provisionnant et en configurant un troisième serveur proxy qui exécutera un conteneur de proxy inverse Nginx et un conteneur client Certbot. Certbot est un package qui aide à gérer les certificats SSL de l'autorité de certification Let’s Encrypt. Il récupère le certificat, configure les blocs de serveur Nginx avec l'emplacement du certificat et gère les renouvellements automatiques. Il le fait en configurant une tâche cron pour vérifier périodiquement si le certificat est sur le point d'expirer et doit être renouvelé. En gardant votre certificat SSL à jour, votre site Web aura toujours une note de sécurité élevée sur SSL Labs.

Le troisième serveur proxy se situe devant votre architecture distribuée et reçoit tout le trafic externe entrant. Ensuite, il distribue le trafic vers vos serveurs d'application. Les serveurs d'application se trouvent derrière un pare-feu, permettant uniquement au serveur proxy d'y accéder.

Ce tutoriel est le deuxième d'une série de trois tutoriels traitant de Django, Docker et Kubernetes. Vous devez d'abord suivre les étapes décrites dans le tutoriel sur Création d'une application Django et Gunicorn avec Docker sur Ubuntu. Dans ce tutoriel, nous avons configuré le code de base du projet, un Dockerfile, et connecté l'application à MinIo Simple Storage Service (S3) pour servir nos fichiers statiques.

Prérequis

Pour suivre ce tutoriel, vous aurez besoin de ce qui suit :

  1. Quatre serveurs Ubuntu 20.04 :

Si vous avez suivi les étapes du tutoriel prérequis, Création d'une application Django et Gunicorn avec Docker sur Ubuntu, vous disposez déjà de deux des quatre serveurs :

  • Le premier serveur exécutera l' instance de base de données PostgreSQL. Suivez les étapes 1 et 2 du tutoriel : Création d'une application Django et Gunicorn avec Docker sur Ubuntu pour configurer la base de données. Les configurations Postgres doivent être modifiées pour autoriser les connexions externes uniquement à partir des adresses IP de vos serveurs d'application.

  • Le deuxième et le troisième serveurs hébergeront les conteneurs pour le code de votre application. Vous devriez déjà avoir le deuxième serveur en cours d'exécution depuis le tutoriel prérequis. Nous allons modifier son pare-feu pour autoriser uniquement les connexions externes provenant de l'adresse IP du serveur proxy. Vous pouvez suivre les étapes 1 à 4 de ce tutoriel étape par étape pour vous aider à configurer votre serveur Ubuntu sur CloudSigma.

  • Le quatrième serveur sera le serveur proxy gérant l'équilibrage de charge et la distribution du trafic vers les deux conteneurs de serveurs d'applications.

  1. Docker doit être installé sur les deux serveurs d'applications et le serveur proxy.

    Après avoir suivi les étapes du tutoriel prérequis, vous devriez déjà avoir Docker installé sur l'un des serveurs. Vous pouvez suivre les étapes 1, 2 et 3 de notre tutoriel sur l'installation et le fonctionnement de Docker. N'oubliez pas d'ajouter l'utilisateur sudo créé ci-dessus au groupe Docker.

  2. Acquérir un nom de domaine enregistré et configurez ses enregistrements DNS pour pointer vers l'adresse IP publique du serveur proxy . À des fins de démonstration, nous utiliserons example_domain.com.
  1. Configurer un service de stockage d'objets S3. Nous avons utilisé MinIO comme service de stockage dans le tutoriel prérequis. Par conséquent, suivez les explications de l'Étape 5 du tutoriel prérequis pour configurer votre compartiment de stockage MinIO.

Étape 1 : Vérification du fonctionnement du premier serveur d'applications Django

Comme expliqué dans les Prérequis, ce guide fait suite au tutoriel sur la construction d'une application Django et Gunicorn avec Docker sur Ubuntu. Si vous venez de ce tutoriel et avez déjà mis en œuvre les étapes, vous devriez avoir le premier serveur en cours d'exécution. Le code de l'application est basé sur le tutoriel de l'application Polls de la documentation de Django. Il est important que vous lisiez attentivement ces étapes pour comprendre la configuration initiale. Si vous avez mis en œuvre les étapes du tutoriel, vous pouvez ignorer cette première étape.

Sinon, vous pouvez simplement cloner la branche Dockerisée sur votre serveur. Commencez par vous connecter à votre premier serveur d'applications et exécutez la commande git suivante pour cloner la branche django-polls-docker du dépôt django-polls:

Ensuite, accédez au répertoire django-polls :

cd django-polls

Dans ce répertoire, vous trouverez un Dockerfile utilisé par Docker pour construire l'image de l'application, le répertoire django-polls qui contient le code de l'application Python, et un fichier env contenant une liste de variables d'environnement qui seront transmises au conteneur au démarrage pour modifier son comportement. Dans le Dockerfile, nous définissons les dépendances des paquets Django via le fichier requirements.txt . De plus, nous devons déclarer un port 8000 à utiliser pour accepter le trafic entrant, et le configurer pour exécuter un serveur gunicorn avec 3 workers. Pour en savoir plus sur les instructions du Dockerfile, veuillez consulter l'Étape 7 du tutoriel la construction d'une application Django et Gunicorn avec Docker sur Ubuntu.

Vous pouvez construire l'image Docker à l'aide de la commande :

docker build -t django-polls:v1 .

Une fois que Docker a construit l'image, vous pouvez lister les images disponibles sur le serveur avec la commande suivante :

docker images

Voici la sortie lorsque nous avons exécuté la commande :

Django Application scrn 1

Ensuite, nous devons modifier le fichier env utilisé pour configurer l'environnement d'exécution. Ce fichier est transmis au conteneur Docker lors de son démarrage. Ouvrez le fichier env avec l'éditeur nano :

Le fichier env contient du texte de substitution que vous devez modifier et remplir avec vos valeurs correctes :

  • DJANGO_SECRET_KEY: Générez une valeur unique et imprévisible comme expliqué dans la documentation de Django. Vous pouvez utiliser cette commande pour générer une chaîne aléatoire et l'affecter à la variable :  python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'.

  • DJANGO_ALLOWED_HOSTS: cette valeur est utilisée pour sécuriser votre application contre les attaques par en-tête d'hôte HTTP. Vous pouvez la définir sur *, un caractère générique correspondant à tous les hôtes si vous êtes en mode développement. Lorsque vous déployez votre application en production, définissez cette valeur sur votre nom de domaine enregistré. Pour notre démonstration, il s'agit de example_domain.com.

  • DB_DATABASE: définissez cette valeur sur le nom de la base de données PostgreSQL que vous avez créée dans la section Prérequis, dans notre cas il s'agit de polls_db.

  • DB_USERNAME: définissez cette valeur sur le nom d'utilisateur que vous avez choisi pour votre base de données.

  • DB_PASSWORD: définissez cette valeur sur le mot de passe que vous avez choisi pour votre base de données.

  • DB_HOST: définissez cette valeur sur l'hôte qui exécute votre instance de base de données tel que vous l'avez configuré dans la section Prérequis. Ceci est expliqué dans les étapes 1 et 2 du tutoriel Création d'une application Django et Gunicorn avec Docker sur Ubuntu pour configurer la base de données.

  • DB_PORT: définissez cette valeur sur le port de votre base de données.

Enregistrez et fermez le fichier une fois que vous avez terminé les modifications. Avec nos identifiants de base de données en place, nous pouvons créer le schéma de la base de données en exécutant le conteneur et en remplaçant la commande CMD définie dans le Dockerfile. Vous trouverez plus d'informations sur le point d'entrée du Dockerfile dans la documentation officielle. Ensuite, exécutez la commande suivante :

Dans cette commande, nous exécutons l'image django-polls:v1 et lui passons le fichier env modifié précédemment. La partie : sh -c "python manage.py makemigrations && python manage.py migrate crée le schéma de base de données défini par le code de l'application. Si vous exécutez la commande pour la première fois, vous devriez voir une sortie similaire indiquant la création du schéma de base de données :

Django Application scrn 2

Une fois le schéma créé, nous pouvons créer le superutilisateur Django. Exécutez la commande suivante pour démarrer le conteneur avec un shell interactif :

La commande démarre le conteneur avec une invite de shell que vous pouvez utiliser pour interagir avec le shell Python. Créons un utilisateur avec la commande suivante :

Suivez les invites pour fournir un nom d'utilisateur, une adresse e-mail et un mot de passe. Saisissez à nouveau le mot de passe et appuyez sur Entrée pour créer l'utilisateur. Quittez le shell et arrêtez le conteneur en appuyant sur CTRL+D.

Ensuite, nous devons exécuter à nouveau le conteneur, en remplaçant la commande par défaut par la commande Django collectstatic. La commande générera les fichiers statiques de l'application et les téléchargera sur le stockage cloud MinIO :

La commande génère et télécharge le fichier vers votre service de stockage d'objets configuré. Voici la sortie :

object storage

Vous pouvez maintenant exécuter l'application sans spécifier de commande supplémentaire pour remplacer la commande CMD par défaut définie dans le Dockerfile :

Django Application scrn 3

Docker exécute la commande par défaut définie dans le Dockerfile, démarre le conteneur avec le serveur gunicorn, expose le port du conteneur 8000, et le mappe sur le port d'Ubuntu 80. Vous pouvez maintenant afficher l'interface de l'application dans votre navigateur en accédant à l'adresse IP du premier serveur dans votre barre d'adresse : http://FIRST_SERVER_IP.

Vous obtiendrez une Page 404 non trouvée car nous n'avons rien défini pour le / chemin. Accédez à http://FIRST_SERVER_IP/polls pour voir l'interface des sondages :

Django Application image 1

Visitez l'interface d'administration pour créer des sondages : http://FIRST_SERVER_IP/admin:

polls

Fournissez les identifiants que vous avez définis avec la commande createsuperuser ci-dessus pour accéder à l'interface d'administration :

polls administration

Si vous affichez le code source de la page, vous remarquerez que les fichiers statiques sont récupérés depuis le bucket de stockage comme défini. Après avoir confirmé que le conteneur sert l'application comme prévu, vous pouvez arrêter le conteneur en appuyant sur CTRL+C dans le terminal.

Ensuite, nous devons maintenir le conteneur en cours d'exécution en mode détaché afin de pouvoir quitter la session SSH du premier serveur. Cela laissera le conteneur s'exécuter en arrière-plan. Exécutez la commande suivante :

Le drapeau -d lance le conteneur en mode détaché afin qu'il puisse continuer à s'exécuter en arrière-plan. Le drapeau --rm nettoie le système de fichiers du conteneur après sa fermeture. Nous donnons un nom au conteneur, polls, afin de pouvoir le voir lorsque nous listons les conteneurs.

Quittez la session SSH de votre premier serveur et accédez à http://FIRST_SERVER_IP/polls dans votre navigateur pour confirmer qu'il fonctionne comme prévu. Si vous pouvez voir l'interface des sondages, alors votre premier serveur d'application a été configuré avec succès. Configurons le deuxième serveur d'application à l'étape suivante.

Étape 2 : Configuration du deuxième serveur d'application

Nous allons cloner la branche dockerisée de l'application que nous avons créée dans le tutoriel Création d'une application Django et Gunicorn avec Docker sur Ubuntu . Vous trouverez plus de détails sur les commandes que nous utiliserons ici dans ce tutoriel, ou la version résumée à l' Étape 1.

Vous devriez avoir le deuxième serveur en cours d'exécution, avoir ajouté un utilisateur sudo non-root et installé Docker comme expliqué dans la section Prérequis.

L'étape suivante consiste à configurer ce serveur pour se connecter à l'instance du serveur PostgreSQL. Comme expliqué à l'étape 1 du tutoriel Création d'une application Django et Gunicorn avec Docker sur Ubuntu, vous devez autoriser l'adresse IP du deuxième serveur à travers le ufw et les configurations de PostgreSQL.

First, log into the PostgreSQL database server instance with your non-root sudo user. To add the ufw rule, execute the following command:

Ensuite, exécutez cette commande et ajoutez l'adresse IP du deuxième serveur au fichier d'authentification client de PostgreSQL :

Lisez les commentaires pour en savoir plus sur les configurations. Ensuite, ajoutez cette ligne sous la section des hôtes, en spécifiant votre adresse IP :

Enregistrez et fermez le fichier lorsque vous avez terminé l'édition.

Ensuite, redémarrez le service PostgreSQL pour que les modifications prennent effet :

Déconnectez-vous de l'instance du serveur de base de données PostgreSQL et procédez à la configuration de la deuxième instance du serveur d'application.

Connectez-vous au deuxième serveur d'application avec ssh. Ensuite, clonez la branche django-polls-branche du dépôt django-polls avec la commande suivante :

Déplacez-vous dans le répertoire django-polls :

Après cela, construisez l'image avec la commande suivante :

Une fois le processus de construction de l'image terminé, modifiez le fichier env avec les valeurs de configuration comme expliqué à l' Étape 1. Ouvrez le fichier avec nano :

Remplacez les textes de substitution par les valeurs réelles que vous avez ajoutées à l'Étape 1. N'oubliez pas de modifier la variable DJANGO_ALLOWED_HOSTS de manière appropriée. Enregistrez et fermez le fichier lorsque vous avez terminé. Mettez à jour vos identifiants MinIO dans le fichier env  comme vous l'avez fait à l'étape précédente.

Vous pouvez maintenant exécuter le conteneur de l'application en mode détaché avec la commande suivante :

La commande démarre le conteneur et le maintient en arrière-plan. Quittez la session SSH du deuxième serveur d'application et accédez à http://SECOND_SERVER_IP/polls dans votre navigateur pour confirmer qu'il fonctionne comme prévu. Vous devriez pouvoir visualiser l'interface des sondages si tout s'est déroulé comme prévu.

Vous avez maintenant deux serveurs d'application exécutant la même copie de votre application. À l'étape suivante, vous configurerez le conteneur Nginx pour qu'il serve de proxy inverse.

Étape 3 : Configuration du conteneur Docker Nginx

Nginx est l'un des logiciels de serveur web open-source les plus populaires au monde. Il est chargé de garantir la disponibilité et l'évolutivité des sites à fort trafic sur Internet. Il garantit la sécurité et il est très polyvalent. Vous pouvez l'utiliser pour le proxy inverse, la mise en cache et l'équilibrage de charge. Nous avons configuré notre application pour utiliser un service de stockage d'objets distinct afin de gérer ses fichiers statiques et multimédias. Par conséquent, nous n'utiliserons pas les fonctionnalités de mise en cache de Nginx. À la place, nous utiliserons les capacités de proxy inverse et d'équilibrage de charge de Nginx. Le serveur frontal Nginx recevra le trafic entrant et le distribuera aux serveurs d'application back-end. Ensuite, il assurera une communication sécurisée entre le client et le serveur en sécurisant le trafic à l'aide de certificats SSL obtenus auprès de Let’s Encrypt.

Il existe plusieurs façons de mettre en œuvre le proxy inverse et l'équilibrage de charge avec Nginx. L'une d'elles consiste à configurer le proxy inverse Nginx séparément du serveur d'application back-end, comme nous l'avons fait dans ce tutoriel. Cette configuration est flexible et vous permet de mettre à l'échelle à la fois la couche proxy Nginx et la couche application. Vous pouvez ajouter plusieurs proxys Nginx ou implémenter un équilibreur de charge cloud. Une autre façon d'implémenter le proxy inverse consiste à utiliser l'un des serveurs d'application back-end comme proxy Nginx. Ensuite, vous pouvez relayer les requêtes entrantes localement et vers d'autres serveurs d'application. En option, vous pouvez configurer un conteneur Nginx sur tous les serveurs d'application back-end et définir un équilibreur de charge cloud frontal pour recevoir le trafic entrant et le distribuer aux serveurs d'application back-end.

Commençons à configurer le serveur proxy. Connectez-vous au quatrième serveur Ubuntu que vous aviez configuré pour être utilisé comme proxy Nginx et créez un répertoire de configuration :

Ouvrez un fichier de configuration avec nano à l'intérieur du répertoire :

Ensuite, ajoutez la configuration suivante au fichier :

Dans ce fichier de configuration, nous spécifions les server, upstream, et location blocs afin d'indiquer à Nginx de rediriger les requêtes HTTP vers HTTPS et de répartir les requêtes entre les deux serveurs d'application configurés à l'Étape 1 et à l'Étape 2. Vous trouverez des informations générales sur la structure du fichier de configuration Nginx dans leur documentation officielle.

Nous avons étudié les fichiers de configuration de la documentation de l'image Nginx de Docker Hub, Certbot, et Gunicorn pour concevoir ce fichier de configuration Nginx minimal. Bien que ce ne soit qu'à des fins de démonstration et pour faire fonctionner notre installation, vous êtes libre d'explorer et d'expérimenter avec d'autres configurations en suivant les guides Nginx.

Le bloc upstream est utilisé pour définir le groupe de serveurs qui traiteront les requêtes entrantes. Un nom est attribué au groupe et est appelé par la proxy_pass directive. Nous avons nommé le bloc django et spécifié les adresses IP des deux serveurs d'application backend :

Nous avons également défini 3 blocs server. Le premier bloc server capture toutes les requêtes ne correspondant pas à votre domaine et renvoie un 444 code (ferme la connexion sans envoyer de réponse au client, refusant ainsi les requêtes malveillantes ou mal formées). Une requête HTTP directe vers l'adresse IP de votre serveur est gérée par ce bloc puisqu'il est défini comme le default_server:

Le deuxième bloc server gère les requêtes HTTP entrantes (port 80) et les redirige vers HTTPS (port 443) en utilisant une redirection HTTP 301:

Le troisième bloc server gère maintenant les requêtes. Il comporte plusieurs directives, et nous définirons leur importance ci-dessous.

Nous avons deux directives définissant les chemins vers le certificat TLS et la clé fournis par Certbot. Les certificats sont montés dans le conteneur Nginx lorsque nous le démarrons :

Ensuite, nous avons les paramètres de sécurité SSL par défaut recommandés par Certbot. Vous pouvez en savoir plus dans la documentation officielle de Nginx sur le module ngx_http_ssl_module. Mozilla propose également plus d'informations sur la sécurité côté serveur. La valeur ssl_ciphers dans le fichier conf est tirée de la page de Mozilla :

Dans les deux directives suivantes, nous définirons la taille maximale autorisée du corps de la requête client et configurerons le délai d'expiration pour les connexions keep-alive avec le client. Nginx fermera les connexions avec le client après le nombre de secondes que vous aurez défini dans la directive keepalive_timeout. Vous trouverez plus d'informations sur les configurations Nginx pour le déploiement de Gunicorn dans la documentation officielle :

Dans le fichier de configuration, nous avons également défini deux blocs location. Le premier bloc gère le proxying des requêtes comme défini avec les directives proxy. Les requêtes entrantes sont redirigées vers les serveurs upstream django définis précédemment :

Vous trouverez plus d'informations sur les directives de proxy dans Nginx Module ngx_http_proxy_module et la documentation sur le déploiement d'un serveur Gunicorn.

Dans le second bloc location, nous définissons un chemin : /well-known/acme-challenge/. Il est généralement utilisé par Certbot pour vérifier votre nom de domaine auprès de Let’s Encrypt avant de générer ou de renouveler un certificat SSL :

C'est tout pour le fichier de configuration Nginx. Vous pouvez maintenant enregistrer et fermer le fichier une fois l'édition terminée.

Le fichier de configuration que vous venez de définir peut être utilisé pour exécuter un conteneur Nginx. Cependant, cela échouera car nous n'avons pas encore généré les certificats SSL de Let’s Encrypt. Dans ce tutoriel, nous utiliserons la nginx:1.20.2 version 1.20.2 de l'image Docker depuis le dépôt officiel d'images Nginx sur Docker Hub.

Vous pouvez exécuter la commande ci-dessous pour télécharger l'image et vérifier que tout fonctionne correctement :

Cette commande crée un conteneur nommé nginx et mappe les ports 80 et 443 entre le système hôte et le conteneur. L'option --rm supprime tous les conteneurs intermédiaires après une construction réussie. Nous utilisons l'option -v pour monter le fichier de configuration dans le conteneur à l'emplacement /etc/nginx/conf.d/nginx.conf qui est le répertoire de configuration par défaut de Nginx. Il est monté en mode lecture seule à l'aide de l'option ro pour empêcher le conteneur Nginx de le modifier. Nous définissons le répertoire webroot par défaut et le montons en tant que /var/www/html. Nous terminons en demandant à Docker d'utiliser l'image nginx:1.20.2 pour cette construction. Obtenons le certificat TLS/SSL et la clé de Let’s Encrypt à l'étape suivante.

Étape 4 : Obtention du certificat SSL/TLS de Let’s Encrypt et configuration du renouvellement automatique de Certbot

Certbot permet d'obtenir des certificats TLS gratuits auprès de Let’s Encrypt ainsi que de gérer leur renouvellement automatique avant leur expiration. Cela améliore la sécurité de vos sites web et garantit qu'ils sont servis via HTTPS. Afin de maintenir notre architecture conteneurisée, nous utiliserons l'image Docker Certbot pour récupérer les certificats SSL/TLS et configurer le renouvellement automatique. Assurez-vous d'avoir installé Docker sur votre serveur proxy conformément aux instructions de la section Prérequis.

Vous devez également disposer d'un enregistrement DNS A pour votre nom de domaine enregistré pointant vers l'adresse IP de votre serveur proxy. Vous pouvez le vérifier en exécutant l'image Docker certbot et en transmettant l'option --staging :

La commande va télécharger l'image Certbot et l'exécuter en mode interactif. Cela signifie qu'elle s'ouvrira avec un shell, vous permettant de saisir certaines informations. Elle mappe le port 80 de l'hôte au port 80 à l'intérieur du conteneur. Nous utilisons l'option -v pour monter deux répertoires de l'hôte dans le conteneur : /etc/letsencrypt/ et /var/lib/letsencrypt/. L'option --standalone indique que nous voulons que l'image Certbot s'exécute sans utiliser Nginx. Enfin, nous avons le --staging qui permettra à Certbot de s'exécuter sur les serveurs de staging et de valider votre nom de domaine.

Saisissez votre adresse e-mail et acceptez les Conditions d'utilisation lorsque vous y êtes invité. Voici le résultat d'une validation réussie :

PROCHAINES ÉTAPES :

Le certificat devra être renouvelé avant son expiration. Certbot peut renouveler automatiquement le certificat en arrière-plan, mais vous devrez peut-être prendre des mesures pour activer cette fonctionnalité. Consultez ce lien pour obtenir des instructions.

Vous pouvez afficher le certificat à l'aide de la commande cat :

La commande ci-dessus devrait afficher votre certificat dans le terminal. Une fois que vous avez confirmé que Certbot a généré votre certificat, vous pouvez maintenant tester la configuration Nginx que vous aviez créée à l' Étape 3. Exécutez la commande Docker ci-dessous pour démarrer le conteneur Nginx :

Dans cette commande, nous avons utilisé l'option -v pour monter l'emplacement des répertoires de certificats SSL/TLS Let's Encrypt.

Lorsque le conteneur est opérationnel, ouvrez la page web dans votre navigateur : http://example_domain.com. Vous verrez probablement un avertissement indiquant que le site web n'est pas sécurisé :

C'est parce que nous n'avions généré que des certificats de staging/test et non des certificats de production de Let's Encrypt. Obtenons les certificats de production en exécutant la commande Certbot suivante sans l'option --staging :

Dans l'invite, confirmez que vous souhaitez renouveler et remplacer le certificat existant en tapant 2 et appuyez sur ENTRÉE. Cela devrait générer un certificat prêt pour la production. Vous pouvez maintenant exécuter le conteneur Nginx et tout devrait fonctionner correctement :

Une fois le conteneur opérationnel, ouvrez à nouveau la page web dans votre navigateur : http://example_domain.com . Notez que votre navigateur est redirigé vers HTTPS même si vous saisissez HTTP. Cela signifie que notre serveur dans la configuration Nginx ainsi que les certificats SSL/TLS générés fonctionnent correctement. Accédez à la polls route http://example_domain.com/polls car nous n'avons pas de route définie pour le chemin d'accueil /. Vous devriez voir l'interface des sondages :

Jusqu'à présent, vous avez configuré avec succès une architecture prête pour la production. Vous avez implémenté deux serveurs backend qui traiteront les requêtes entrantes transmises par le serveur proxy. Le serveur proxy gérera l'équilibrage de charge et la sécurisation du trafic à l'aide des certificats TLS fournis.

Cependant, vous devez garder à l'esprit que les certificats Let’s Encrypt expirent dans 90 jours. Ainsi, vous devez les renouveler avant la limite des 90 jours. Étant donné que le conteneur Nginx sera en cours d'exécution, vous devez utiliser le mode webroot au lieu du mode standalone lorsque vous exécutez la commande certbot pour le renouvellement du certificat. Rappelez-vous que vous aviez spécifié le répertoire /var/www/html/.well-known/acme-challenge/ dans le fichier de configuration Nginx à l' Étape 3. Certbot utilisera ce chemin pour stocker les fichiers de validation. De plus, le client Let’s Encrypt appellera ce chemin avec des requêtes de validation lorsque vous tenterez de renouveler les certificats. Une fois l'exécution de la commande de renouvellement terminée, vous pouvez recharger Nginx pour appliquer les modifications.

Arrêtez le conteneur en appuyant sur CTRL+C dans votre terminal, et relançons-le en mode détaché avec le drapeau -d :

Cela laissera le conteneur Nginx s'exécuter en arrière-plan. Testons la procédure de renouvellement de certificat avec le drapeau --dry-run en exécutant la commande ci-dessous :

Dans cette commande, nous avons spécifié le plugin --webroot ainsi que le chemin à utiliser pour les requêtes de validation avec le drapeau -w. Nous spécifions également le drapeau --dry-run pour vérifier la procédure de renouvellement automatique sans réellement provisionner de certificat.

Vous devriez voir une sortie similaire en cas de simulation réussie :

Chaque fois que vous renouvelez un certificat pour votre application en cours d'exécution, vous devez recharger Nginx pour que le conteneur commence à utiliser le nouveau certificat. La commande Docker suivante recharge le conteneur nginx (rappelez-vous que nous avons nommé le conteneur nginx) :

La commande envoie un signal Unix HUP au processus Nginx s'exécutant à l'intérieur du conteneur Docker nginx. Cela amène Nginx à recharger ses configurations et à commencer à utiliser les certificats renouvelés.

Puisque nous avons installé TLS/SSL sur notre serveur proxy et que notre site Web est servi en HTTPS, nous devons maintenant sécuriser nos serveurs d'applications backend pour autoriser uniquement les requêtes provenant du serveur proxy.

Étape 5 : Sécuriser les serveurs Django backend contre les accès externes

Le serveur proxy que vous avez implémenté dans ce tutoriel gère la terminaison SSL, où il décrypte la connexion SSL et transmet les paquets non chiffrés aux serveurs d'applications backend. Étant donné que nous allons sécuriser les serveurs backend contre tout accès externe, ce niveau de sécurité devrait convenir dans la plupart des cas. Cependant, si vous déployez des applications qui transmettent des données sensibles telles que des informations bancaires ou des données de santé, vous devriez alors implémenter un chiffrement de bout en bout.

Dans ce tutoriel, les serveurs Gunicorn du backend sont protégés par Nginx car ils ne sont pas destinés à être directement exposés à l'extérieur. Le serveur proxy Nginx est comme une passerelle vers les serveurs backend, empêchant les clients externes d'accéder directement aux serveurs d'applications backend. Vous devez vous assurer que toutes les requêtes passent par le serveur proxy. Cela étant dit, il se trouve que Docker présente un problème où il contourne le ufw règles de pare-feu et ouvre des ports vers l'extérieur, ce qui peut rendre votre infrastructure vulnérable. C'est d'ailleurs évident puisque nous avons configuré nos serveurs d'applications à l' Étape 1 et à l' Étape 2 sans autoriser le port 80 dans les règles ufw. Cependant, vous pouvez toujours accéder aux pages web lorsque vous visitez l'une ou l'autre des adresses IP publiques du serveur dans le navigateur. Une façon de résoudre ce problème consiste à utiliser iptables directement sans passer par ufw. Vous pouvez lire les documents officiels de Docker et iptables pour en savoir plus. Une autre méthode recommandée consiste à utiliser des pare-feux cloud.

Modifions les configurations de l'UFW pour bloquer l'accès externe à tous les ports qui auraient pu être ouverts par Docker. Lorsque nous avons mappé le port de l'hôte 80 au port du conteneur Docker 8000 avec le drapeau -p 80:8000 de la commande Docker, nous avons également ouvert par inadvertance le port 80 sur la machine hôte. Vous pouvez désactiver cet accès en modifiant la configuration de l'UFW comme décrit dans le dépôt ufw-docker README.

Faisons la modification pour le premier serveur d'applications Django. Connectez-vous au serveur et ouvrez le fichier situé à /etc/ufw/after.rules avec nano en tant qu'utilisateur sudo :

Le fichier contient les règles ufw suivantes :

Ajoutez le bloc de lignes de configuration UFW suivant au bas du fichier :

Les règles que vous avez ajoutées empêchent l'accès public aux ports ouverts par Docker. De plus, elles permettent l'accès depuis les 10.0.0.0/8, 172.16.0.0/12, et 192.168.0.0/16 plages d'adresses IP privées. Vous pouvez lire plus de détails sur ces règles dans le ufw-docker README. Enregistrez et fermez les fichiers lorsque vous avez terminé les modifications. Cette configuration devrait fonctionner si vous aviez configuré un réseau de cloud privé virtuel (VPC), avec vos trois serveurs dans le VPC, puis spécifié les IP privées des serveurs Django dans la directive upstream du fichier de configuration Nginx.

Cependant, nous avons utilisé des IP publiques et nous n'avons peut-être pas de VPC. Ainsi, vous devez ajouter une règle à ufw pour autoriser le trafic depuis le serveur proxy Nginx via le port 80 des deux serveurs d'application Django. Vous pouvez ajouter une règle d'autorisation à ufw spécifiant l'IP du serveur d'origine vers le port 80 en utilisant la commande suivante :

Une fois les modifications terminées, redémarrez votre serveur d'application Django pour que les changements prennent effet, car l'exécution de sudo ufw reload semble ne pas appliquer les modifications :

Lorsque le serveur a redémarré, lancez le conteneur comme vous l'avez fait à l'Étape 1 ou à l'Étape 2:

Ensuite, essayez de visiter l'IP du premier serveur Django dans le navigateur pour voir s'il présente l'interface Polls : http://FIRST_SERVER_IP/polls. Cela va échouer. Maintenant, déconnectez-vous du premier serveur et répétez les étapes effectuées ici pour le second serveur. Ouvrez le fichier /etc/ufw/after.rules avec nano en tant qu'utilisateur sudo :

Comme vous l'avez fait précédemment, faites défiler vers le bas et ajoutez le bloc de configurations UFW :

Enregistrez et fermez le fichier une fois que vous avez ajouté le bloc ci-dessus.

Ensuite, ajoutez une règle d'autorisation à ufw en spécifiant l'IP du serveur d'origine vers le port 80 en utilisant la commande suivante :

Redémarrez votre serveur pour que les modifications prennent effet :

Lorsque le serveur est de nouveau opérationnel, relancez le conteneur avec la commande :

Testez si vous pouvez afficher l'interface des sondages en vous rendant directement à l'adresse IP du second serveur : http://SECOND_SERVER_IP/polls. Cela devrait également échouer.

Cette architecture est maintenant prête à être testée. Vous pouvez visiter https://example_domain_here/polls pour afficher l'interface de sondages par défaut depuis votre navigateur. Cela signifie que le serveur proxy Nginx a toujours accès aux serveurs backend Django.

Conclusion

Dans ce guide, nous vous avons montré comment implémenter une infrastructure évolutive à l'aide de conteneurs Docker. L'infrastructure comprend un serveur de base de données PostgreSQL distinct, deux serveurs d'applications backend et un serveur proxy Nginx pour équilibrer la charge et distribuer le trafic entre les deux serveurs. Bien que nous ayons basé notre application sur l'application Django Polls, vous pouvez personnaliser cette architecture pour diverses applications en utilisant différents frameworks, comme Node.js, Laravel, etc.

Il s'agit d'une directive de base pour vous aider à démarrer. Quelques améliorations que vous pouvez ajouter consistent à héberger votre image sur un dépôt d'images comme Docker Hub permettant une distribution facile de l'image sur plusieurs serveurs. Vous pouvez également ajouter des pipelines d'intégration et de déploiement continus pour créer, tester et déployer automatiquement des images sur les serveurs d'applications dès qu'un événement se produit. Par exemple, un événement pourrait être l'envoi de nouveau code vers une branche spécifiée sur un dépôt git. Vous pouvez également automatiser ce qui se passe lorsque le conteneur rencontre une erreur. La documentation officielle de Docker fournit une bonne directive sur le Démarrage automatique des conteneurs en cas d'erreurs ou de redémarrage du système.

Bonne programmation !

author

Hark Labs

Auteur · CloudSigma

Preslav Dobrev est un designer créatif chez CloudSigma, axé sur une identité commerciale cohérente à travers des canaux marketing traditionnels et innovants. Il excelle à fusionner la vision artistique avec le marketing stratégique pour créer des récits de marque percutants.

Commentaires

Aucun commentaire pour l'instant. Soyez le premier.