One place for hosting & domains

      Skalieren und Sichern einer Django-Anwendung mit Docker, Nginx und Let’s Encrypt


      Einführung

      In cloudbasierten Umgebungen gibt es mehrere Möglichkeiten, eine Django-Anwendung zu skalieren und zu sichern. Durch horizontale Skalierung und die Ausführung mehrerer Kopien Ihrer Anwendung können Sie ein fehlertolerantes und hochverfügbares System aufbauen und gleichzeitig den Durchsatz erhöhen, sodass Anfragen gleichzeitig verarbeitet werden können. Eine Möglichkeit zur horizontalen Skalierung einer Django-Anwendung besteht darin, zusätzliche App-Server bereitzustellen, die Ihre Django-Anwendung und Ihren WSGI-HTTP-Server (wie Gunicorn oder uWSGI) ausführen. Um eingehende Anfragen über diesen Satz von App-Servern zu leiten und zu verteilen, können Sie einen Load Balancer und Reverse Proxy wie Nginx verwenden. Nginx kann auch statische Inhalte zwischenspeichern und Transport Layer Security-Verbindungen (TLS-Verbindungen) beenden, die zur Bereitstellung von HTTPS- und sicheren Verbindungen zu Ihrer Anwendung verwendet werden.

      Die Ausführung Ihrer Django-Anwendung und des Nginx-Proxys innerhalb von Docker-Containern stellt sicher, dass sich diese Komponenten unabhängig von der Umgebung, in der sie bereitgestellt werden, gleich verhalten. Darüber hinaus bieten Container viele Funktionen, die das Paketieren und Konfigurieren Ihrer Anwendung erleichtern.

      In diesem Tutorial skalieren Sie eine containerisierte Django- und Gunicorn-Umfrageanwendung horizontal, indem Sie zwei App-Server bereitstellen, die jeweils eine Kopie eines Django- und Gunicorn-Anwendungscontainers ausführen.

      Des Weiteren aktivieren Sie HTTPS, indem Sie einen dritten Proxy-Server bereitstellen und konfigurieren, auf dem ein Nginx Reverse-Proxy-Container und ein Certbot-Client-Container ausgeführt werden. Certbot stellt TLS-Zertifikate für Nginx von der Let’s Encrypt Zertifizierungsstelle bereit. Dadurch wird sichergestellt, dass Ihre Website von SSL Labs eine hohe Sicherheitsbewertung erhält. Dieser Proxy-Server empfängt alle externen Anfragen Ihrer Anwendung und sitzt vor den beiden upstream Django-App-Servern. Schließlich härten Sie dieses verteilte System ab, indem Sie den externen Zugriff auf nur den Proxy-Server beschränken.

      Voraussetzungen

      Um dieser Anleitung zu folgen, benötigen Sie:

      • Drei Ubuntu 18.04-Server:

        • Zwei Server werden App-Server sein, die zum Ausführen Ihrer Django- und Gunicorn-Anwendung verwendet werden.
        • Ein Server wird ein Proxy-Server sein, auf dem Nginx und Certbot ausgeführt werden.
        • Alle Server sollten einen Nicht-Root-Benutzer mit sudo-Berechtigungen und eine aktive Firewall haben. Eine Anleitung für das Setup finden Sie im Leitfaden für die Ersteinrichtung des Servers.
      • Auf allen drei Servern installiertes Docker. Eine Anleitung zur Installation von Docker finden Sie in den Schritten 1 und 2 von Installieren und Verwenden von Docker unter Ubuntu 18.04.

      • Einen registrierten Domänennamen. In diesem Tutorial wird durchgängig your_domain.com verwendet. Einen Domänennamen können Sie kostenlos bei Freenom erhalten oder Sie nutzen eine Domänenregistrierungsstelle Ihrer Wahl.

      • Einen DNS-A-Eintrag mit your-domain.com, der auf die öffentliche IP-Adresse Ihres Proxy-Servers verweist. Falls Sie ein DigitalOcean-Konto nutzen, können Sie in dieser Einführung in DigitalOcean-DNS im Einzelnen nachlesen, wie Sie ihn hinzufügen.

      • Einen S3-Objektspeicher-Bucket wie beispielsweise einen DigitalOcean Space zur Speicherung der statischen Dateien Ihres Django-Projekts und einen Satz von Zugriffsschlüsseln für diesen Space. Um zu erfahren, wie Sie einen Space erstellen können, lesen Sie die Produktdokumentation Erstellen von Spaces. Um zu erfahren, wie Sie Zugriffsschlüssel für Spaces erstellen können, lesen Sie Zugriff auf Spaces mit Zugriffsschlüsseln gemeinsam nutzen. Mit geringfügigen Änderungen können Sie jeden Objektspeicherdienst verwenden, der das Plugin django-storages verwendet.

      • Eine PostgreSQL-Server-Instanz, Datenbank und Benutzer für Ihre Django-Anwendung. Mit geringfügigen Änderungen können Sie jede Datenbank verwenden, die Django unterstützt.

      Schritt 1 – Konfigurieren des ersten Django-App-Servers

      Wir klonen zunächst das Django-Anwendungs-Repository auf den ersten App-Server. Dann konfigurieren und erstellen wir das Docker-Image und testen die Anwendung durch Ausführung des Django-Containers.

      Anmerkung: Wenn Sie von Erstellen einer Django- und Gunicorn-Anwendung mit Docker fortfahren, haben Sie Schritt 1 bereits abgeschlossen und können mit Schritt 2 fortfahren, um den zweiten App-Server zu konfigurieren.

      Beginnen Sie mit der Anmeldung beim ersten der beiden Django-App-Server und verwenden Sie git, um den Zweig polls-docker der Django Tutorial Umfrageanwendung GitHub-Repository zu klonen. Dieses Repository enthält Code für die Umfrage-Beispielanwendung der Django-Dokumentation. Der Zweig polls-docker enthält eine dockerisierte Version der Umfrageanwendung. Um zu erfahren, wie die Umfrageanwendung modifiziert wurde, um effektiv in einer containerisierten Umgebung zu arbeiten, lesen Sie bitte Erstellen einer Django- und Gunicorn-Anwendung mit Docker.

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Navigieren Sie in das Verzeichnis django-polls:

      cd django-polls
      

      Dieses Verzeichnis enthält den Python-Code der Django-Anwendung, ein Dockerfile, das Docker zum Erstellen des Container-Images verwendet, sowie eine Datei env, die eine Liste von Umgebungsvariablen enthält, die an die laufende Umgebung des Containers übergeben werden müssen. Prüfen Sie das Dockerfile mit cat:

      cat Dockerfile
      

      Output

      FROM python:3.7.4-alpine3.10 ADD django-polls/requirements.txt /app/requirements.txt RUN set -ex && apk add --no-cache --virtual .build-deps postgresql-dev build-base && python -m venv /env && /env/bin/pip install --upgrade pip && /env/bin/pip install --no-cache-dir -r /app/requirements.txt && runDeps="$(scanelf --needed --nobanner --recursive /env | awk '{ gsub(/,/, "nso:", $2); print "so:" $2 }' | sort -u | xargs -r apk info --installed | sort -u)" && apk add --virtual rundeps $runDeps && apk del .build-deps ADD django-polls /app WORKDIR /app ENV VIRTUAL_ENV /env ENV PATH /env/bin:$PATH EXPOSE 8000 CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi"]

      Dieses Dockerfile verwendet das offizielle Python 3.7.4 Docker-Image als Basis und installiert die Python-Paketanforderungen von Django und Gunicorn, wie sie in der Datei django-polls/requirements.txt definiert sind. Anschließend entfernt es einige unnötige Builddateien, kopiert den Anwendungscode in das Image und legt den Ausführungspfad PATH fest. Schließlich gibt es an, dass Port 8000 verwendet wird, um eingehende Container-Verbindungen zu akzeptieren und gunicorn mit 3 Workern ausgeführt wird, die Port 8000 abhören.

      Um mehr über die einzelnen Schritte in diesem Dockerfile zu erfahren, lesen Sie bitte Schritt 6 von Erstellen einer Django- und Gunicorn-Anwendung mit Docker.

      Erstellen Sie nun das Image mit docker build:

      Wir benennen das Image polls mit dem Flag -t und übergeben im aktuellen Verzeichnis als Build-Kontext den Satz von Daten, auf den beim Erstellen des Images verwiesen werden soll.

      Nachdem Docker das Image erstellt und mit Tags versehen hat, listen wir die verfügbaren Images mit docker images auf:

      docker images
      

      Sie sollten die polls-Images aufgelistet sehen:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE polls latest 80ec4f33aae1 2 weeks ago 197MB python 3.7.4-alpine3.10 f309434dea3a 8 months ago 98.7MB

      Bevor wir den Django-Container ausführen, müssen wir seine Betriebsumgebung mithilfe der im aktuellen Verzeichnis vorhandenen Datei env konfigurieren. Diese Datei wird an den Befehl docker run übergeben, der zum Ausführen des Containers verwendet wird, und Docker injiziert die konfigurierten Umgebungsvariablen in die Betriebsumgebung des Containers.

      Öffnen Sie die Datei env mit nano oder Ihrem bevorzugten Editor:

      nano env
      

      Wir werden die Datei wie nachfolgend beschrieben konfigurieren und Sie müssen einige zusätzliche Werte hinzufügen.

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Geben Sie die fehlenden Werte für die folgenden Schlüssel ein:

      • DJANGO_SECRET_KEY: Setzen Sie diesen auf einen eindeutigen, nicht vorhersagbaren Wert, wie in den Django-Dokumentationen beschrieben. Eine Methode zur Generierung dieses Wertes wird in Anpassen der Anwendungseinstellungen in dem Tutorial Skalierbare Django-Anwendung angeboten.
      • DJANGO_ALLOWED_HOSTS: Diese Variable sichert die Anwendung und verhindert HTTP-Host-Header-Angriffe. Setzen Sie diese Variable für Testzwecke auf *, einen Platzhalter, der auf alle Hosts zutrifft. In der Produktion sollten Sie diese Variable auf your_domain.com setzen. Um mehr über diese Django-Einstellungen zu erfahren, konsultieren Sie die Core-Einstellungen der Django-Dokumentation.
      • DATABASE_USERNAME: Setzen Sie diesen auf den in den vorbereitenden Schritten erstellten PostgreSQL Datenbankbenutzer.
      • DATABASE_NAME: Setzen Sie diesen auf polls oder den in den vorbereitenden Schritten erstellten Namen der PostgreSQL-Datenbank.
      • DATABASE_PASSWORD: Setzen Sie dieses auf das in den vorbereitenden Schritten erstellte Passwort für den PostgreSQL Benutzer.
      • DATABASE_HOST: Setzen Sie diesen Wert auf den Hostnamen Ihrer Datenbank.
      • DATABASE_PORT: Setzen Sie diesen Wert auf den Port Ihrer Datenbank.
      • STATIC_ACCESS_KEY_ID: Setzen Sie diesen Wert auf den Zugriffsschlüssel Ihres S3-Buckets oder Space.
      • STATIC_SECRET_KEY: Setzen Sie diesen Wert auf das Zugriffsschlüsselgeheimnis Ihres S3-Bucket oder Space
      • STATIC_BUCKET_NAME: Setzen Sie diesen auf Ihren S3-Bucket- oder Space-Namen.
      • STATIC_ENDPOINT_URL: Setzen Sie diese auf die entsprechenden S3-Bucket- oder Space-Endpunkt-URL, z.B. https://space-name.nyc3.digitaloceanspaces.com, wenn sich Ihr Space in der Region nyc3 befindet.

      Wenn Sie die Bearbeitung abgeschlossen haben, speichern und schließen Sie die Datei.

      Wir verwenden nun docker run, um den CMD-Satz in dem Dockerfile zu überschreiben und das Datenbankschema mit den Befehlen manage.py makemigrations und manage.py migrate zu erstellen:

      docker run --env-file env polls sh -c "python manage.py makemigrations && python manage.py migrate"
      

      Wir führen das Container-Image polls:latest aus, übergeben die von uns gerade modifizierte Umgebungsvariablendatei und überschreiben den Dockerfile-Befehl mit sh -c "python manage.py makemigrations && python manage.py migrate", wodurch das durch den Anwendungscode definierte Datenbankschema erstellt wird. Wenn Sie dies zum ersten Mal ausführen, sollten Sie Folgendes sehen:

      Output

      No changes detected Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying polls.0001_initial... OK Applying sessions.0001_initial... OK

      Dies zeigt an, dass das Datenbankschema erfolgreich erstellt wurde.

      Wenn Sie die Migration zu einem späteren Zeitpunkt ausführen, führt Django eine Nulloperation durch, es sei denn, das Datenbankschema wurde geändert.

      Als Nächstes führen wir eine weitere Instanz des Anwendungscontainers aus und verwenden darin eine interaktive Shell, um einen Administratorbenutzer für das Django-Projekt zu erstellen.

      docker run -i -t --env-file env polls sh
      

      Dadurch erhalten Sie eine Shell-Eingabeaufforderung innerhalb des laufenden Containers, die Sie zum Erstellen des Django-Benutzers verwenden können:

      python manage.py createsuperuser
      

      Geben Sie einen Benutzernamen, eine E-Mail-Adresse und ein Passwort für Ihren Benutzer ein. Drücken Sie nach dem Erstellen des Benutzers STRG+D, um den Container zu verlassen und zu beenden.

      Schließlich generieren wir die statischen Dateien für die Anwendung und laden sie mit collectstatic in den DigitalOcean Space hoch. Beachten Sie, dass dies möglicherweise einige Zeit dauern kann.

      docker run --env-file env polls sh -c "python manage.py collectstatic --noinput"
      

      Nachdem diese Dateien generiert und hochgeladen sind, erhalten Sie folgende Ausgabe.

      Output

      121 static files copied.

      Wir können die Anwendung nun ausführen:

      docker run --env-file env -p 80:8000 polls
      

      Output

      [2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0 [2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) [2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync [2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7 [2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8 [2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

      Hier führen wir den in dem Dockerfile definierten Standardbefehl gunicorn ---bind :8000 --workers 3 mysite.wsgi:application aus und stellen den Container-Port 8000 frei, sodass Port 80 auf dem Ubuntu-Server dem Port 8000 des Containers poll zugeordnet wird.

      Sie sollten nun über Ihren Webbrowser zu der Anwendung polls navigieren können, indem Sie http://APP_SERVER_1_IP in der URL-Leiste eingeben. Da für den Pfad / keine Route definiert ist, erhalten Sie wahrscheinlich einen 404 Page Not Found-Fehler, der zu erwarten ist.

      Warnung: Wenn Sie die UFW-Firewall mit Docker verwenden, umgeht Docker alle konfigurierten UFW-Firewallregeln, wie in diesem GitHub-Problem dokumentiert. Dies erklärt, warum Sie Zugriff auf Port 80 Ihres Servers haben, obwohl Sie in keinem vorbereitenden Schritt explizit eine UFW-Zugriffsregel erstellt haben. In Schritt 5 werden wir diese Sicherheitslücke schließen, indem wir die UFW-Konfiguration patchen. Wenn Sie UFW nicht verwenden und die Cloud Firewalls von DigitalOcean einsetzen, können Sie diese Warnung getrost ignorieren.

      Navigieren Sie zu http://APP_SERVER_1_IP/polls, um die Benutzeroberfläche der Umfrageanwendung zu sehen:

      Oberfläche der Umfrageanwendung

      Um die administrative Oberfläche anzuzeigen, besuchen Sie http://APP_SERVER_1_IP/admin. Sie sollten das Authentifizierungsfenster für den Administrator der Umfrageanwendung sehen:

      Authentifizierungsseite für Polls-Administrator

      Geben Sie den administrativen Benutzernamen und das Passwort ein, das Sie mit dem Befehl createsuperuser erstellt haben.

      Nach der Authentifizierung können Sie auf die administrative Oberfläche der Umfrageanwendung zugreifen:

      Administrative Hauptoberfläche von Polls

      Beachten Sie, dass statische Assets für die Anwendungen admin und polls direkt aus dem Objektspeicher bereitgestellt werden. Um dies zu bestätigen, konsultieren Sie Prüfen der statischen Dateizustellung von Spaces.

      Wenn Sie die Erkundung abgeschlossen haben, drücken Sie Strg+C im Terminalfenster, in dem der Docker-Container ausgeführt wird, um den Container zu beenden.

      Nachdem Sie nun bestätigt haben, dass der App-Container wie erwartet ausgeführt wird, können Sie ihn im getrennten (detached) Modus ausführen, wodurch er im Hintergrund ausgeführt wird und Ihnen ermöglicht, sich von Ihrer SSH-Sitzung abzumelden:

      docker run -d --rm --name polls --env-file env -p 80:8000 polls
      

      Das Flag -d weist Docker an, den Container im getrennten Modus auszuführen, das Flag -rm säubert das Dateisystem des Containers nach dem Verlassen des Containers und wir benennen den Container polls.

      Melden Sie sich von dem ersten Django App-Server ab und navigieren Sie zu http://APP_SERVER_1_IP/polls, um zu bestätigen, dass der Container wie erwartet ausgeführt wird.

      Nachdem Ihr erster Django-App-Server ausgeführt wird, können Sie nun Ihren zweiten Django-App-Server einrichten.

      Schritt 2 – Konfigurieren des zweiten Django-App-Servers

      Da viele der Befehle zur Einrichtung dieses Servers die gleichen sind wie im vorherigen Schritt, werden sie hier in abgekürzter Form dargestellt. Bitte lesen Sie Schritt 1 für weitere Informationen zu einem bestimmten Befehl in diesem Schritt.

      Beginnen Sie damit, sich bei dem zweiten Django-App-Server anzumelden.

      Klonen Sie den Zweig polls-docker des GitHub-Repositorys von django-polls:

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Navigieren Sie in das Verzeichnis django-polls:

      cd django-polls
      

      Erstellen Sie das Image mit docker build:

      Öffnen Sie die Datei env mit nano oder Ihrem bevorzugten Editor:

      nano env
      

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Geben Sie die fehlenden Werte wie in Schritt 1 ein. Wenn Sie die Bearbeitung abgeschlossen haben, speichern und schließen Sie die Datei.

      Führen Sie den App-Container anschließend im getrennten Modus aus:

      docker run -d --rm --name polls --env-file env -p 80:8000 polls
      

      Navigieren Sie zu http://APP_SERVER_2_IP/polls, um zu bestätigen, dass der Container wie erwartet ausgeführt wird. Sie können sich sicher von dem zweiten App-Server anmelden, ohne Ihren laufenden Container zu beenden.

      Da beide Django App-Container ausgeführt werden, können Sie mit der Konfiguration des Reverse-Proxy-Containers von Nginx fortfahren.

      Schritt 3 – Konfigurieren des Nginx Docker-Containers

      Nginx ist ein vielseitiger Webserver, der eine Reihe von Funktionen wie Reverse-Proxying, Load Balancing und Caching bietet. In diesem Tutorial haben wir die statischen Assets von Django in den Objektspeicher ausgelagert, sodass wir die Caching-Funktionen von Nginx nicht verwenden werden. Wir werden Nginx jedoch als Reverse-Proxy für unsere beiden Backend-Django-App-Server verwenden und eingehende Anfragen zwischen ihnen verteilen. Darüber hinaus wird Nginx TLS-Terminierung und -Umleitung unter Verwendung eines von Certbot bereitgestellten TLS-Zertifikats durchführen. Das bedeutet, dass es die Clients zwingen wird, HTTPS zu verwenden, und eingehende HTTPS-Anfragen an Port 443 umzuleiten. Anschließend entschlüsselt Nginx HTTPS-Anfragen und leitet sie an die vorgelagerten Django-Server weiter.

      In diesem Tutorial haben wir die Designentscheidung getroffen, die Nginx-Container von den Backend-Servern zu entkoppeln. Abhängig von Ihrem Anwendungsfall können Sie sich dafür entscheiden, den Nginx-Container auf einem der Django-App-Server auszuführen, das Proxying von Anfragen sowohl lokal als auch auf dem anderen Django-Server auszuführen. Eine weitere mögliche Architektur wäre die Ausführung von zwei Nginx-Containern, einer auf jedem Backend-Server, mit einem Cloud Load Balancer davor. Jede Architektur bietet andere Sicherheits- und Leistungsvorteile und Sie sollten Ihr System einem Lasttest unterziehen, um Engpässe aufzudecken. Die in diesem Tutorial beschriebene flexible Architektur ermöglicht es Ihnen, sowohl die Backend-Django-Anwendungsschicht als auch die Nginx-Proxying-Schicht zu skalieren. Sobald der einzelne Nginx-Container zum Engpass wird, können Sie auf mehrere Nginx-Proxys skalieren und einen Cloud Load Balancer oder schnellen L4 Load Balancer wie HAProxy hinzufügen.

      Da beide Django-App-Server ausgeführt werden, können wir mit dem Einrichten des Proxy-Servers beginnen. Melden Sie sich an Ihrem Proxy-Server an und erstellen Sie ein Verzeichnis namens conf:

      mkdir conf
      

      Erstellen Sie mit nano oder Ihrem bevorzugten Editor eine Konfigurationsdatei namens nginx.conf:

      nano conf/nginx.conf
      

      Fügen Sie die folgende Nginx-Konfiguration ein:

      conf/nginx.conf

      
      upstream django {
          server APP_SERVER_1_IP;
          server APP_SERVER_2_IP;
      }
      
      server {
          listen 80 default_server;
          return 444;
      }
      
      server {
          listen 80;
          listen [::]:80;
          server_name your_domain.com;
          return 301 https://$server_name$request_uri;
      }
      
      server {
          listen 443 ssl http2;
          listen [::]:443 ssl http2;
          server_name your_domain.com;
      
          # SSL
          ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
      
          ssl_session_cache shared:le_nginx_SSL:10m;
          ssl_session_timeout 1440m;
          ssl_session_tickets off;
      
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_prefer_server_ciphers off;
      
          ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
      
          client_max_body_size 4G;
          keepalive_timeout 5;
      
              location / {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_pass http://django;
              }
      
          location ^~ /.well-known/acme-challenge/ {
              root /var/www/html;
          }
      
      }
      

      Diese Blöcke upstream, server und location konfigurieren Nginx so, dass HTTP-Anfragen an HTTPS umgeleitet werden und sorgen für einen Lastausgleich zwischen den beiden in Schritt 1 und 2 konfigurierten Django-App-Servern. Um mehr über die Nginx-Konfigurationsdatei zu erfahren, lesen Sie bitte in diesem Artikel über das Verstehen der Nginx-Konfigurationsdateistruktur und der Konfigurationskontexte. Außerdem kann dieser Artikel zum Verstehen von Nginx-Server und Location-Block-Auswahlalgorithmen hilfreich sein.

      Diese Konfiguration wurde aus Beispielkonfigurationsdateien zusammengestellt, die von Gunicorn, Certbot und Nginx bereitgestellt wurden, und ist als eine minimale Nginx-Konfiguration gedacht, um diese Architektur betriebsbereit zu machen. Die Feineinstellung dieser Nginx-Konfiguration geht über den Umfang dieses Artikels hinaus, Sie können jedoch ein Tool wie NGINXConfig verwenden, um performante und sichere Nginx-Konfigurationsdateien für Ihre Architektur zu generieren.

      Der Block upstream definiert die Gruppe von Servern, die zum Proxying von Anfragen zur Verwendung der Anweisung proxy_pass verwendet werden:

      conf/nginx.conf

      upstream django {
          server APP_SERVER_1_IP;
          server APP_SERVER_2_IP;
      }
      . . .
      

      In diesem Block nennen wir den upstream django und schließen die IP-Adressen der beiden Django-App-Server ein. Wenn die App-Server auf DigitalOcean ausgeführt werden und VPC Networking aktiviert haben, sollten Sie hier ihre privaten IP-Adressen verwenden. Um zu erfahren, wie Sie VPC-Networking auf DigitalOcean aktivieren können, lesen Sie bitte Aktivieren von VPC-Networking auf vorhandenen Droplets.

      Der erste Block server erfasst Anfragen, die nicht Ihrer Domäne entsprechen und beendet die Verbindung. Beispielsweise würde eine direkte HTTP-Anfrage an die IP-Adresse Ihres Servers von diesem Block bearbeitet werden:

      conf/nginx.conf

      . . .
      server {
          listen 80 default_server;
          return 444;
      }
      . . .
      

      Der nächste Block server leitet HTTP-Anfragen an Ihre Domäne über eine HTTP 301-Umleitung an HTTPS um. Diese Anfragen werden dann vom letzten Block server bearbeitet:

      conf/nginx.conf

      . . .
      server {
          listen 80;
          listen [::]:80;
          server_name your_domain.com;
          return 301 https://$server_name$request_uri;
      }
      . . .
      

      Diese zwei Anweisungen definieren die Pfade zum TLS-Zertifikat und geheimen Schlüssel. Diese werden mit Certbot bereitgestellt und im nächsten Schritt in den Nginx-Container eingebunden.

      conf/nginx.conf

      . . .
      ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
      . . .
      

      Bei diesen Parametern handelt es sich um die von Certbot empfohlenen SSL-Sicherheitsstandards. Um mehr über sie zu erfahren, lesen Sie bitte das Modul ngx_http_ssl_module der Nginx-Dokumentation. Mozillas Sicherheits-/Serverseitiges TLS ist ein weiterer hilfreicher Leitfaden, den Sie für die Feinabstimmung Ihrer SSL-Konfiguration verwenden können.

      conf/nginx.conf

      . . .
          ssl_session_cache shared:le_nginx_SSL:10m;
          ssl_session_timeout 1440m;
          ssl_session_tickets off;
      
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_prefer_server_ciphers off;
      
          ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
      . . .
      

      Diese beiden Anweisungen aus Gunicorns Nginx-Beispielkonfiguration legen die maximal zulässige Größe des Client-Anfragekörpers fest und weisen das Timeout für Keep-Alive-Verbindungen mit dem Client zu. Nginx schließt Verbindungen mit dem Client nach keepalive_timeout-Sekunden.

      conf/nginx.conf

      . . .
      client_max_body_size 4G;
      keepalive_timeout 5;
      . . .
      

      Der erste Block location weist Nginx zum Proxying von Anfragen über HTTP an die upstream django-Server an. Er bewahrt zusätzlich Client HTTP-Header auf, die die Ursprungs-IP-Adresse, das zur Verbindung verwendete Protokoll und den Ziel-Host erfassen:

      conf/nginx.conf

      . . .
      location / {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header Host $http_host;
          proxy_redirect off;
          proxy_pass http://django;
      }
      . . .
      

      Um mehr über diese Anweisungen zu erfahren, lesen Sie bitte Bereitstellen von Gunicorn und das Modul ngx_http_proxy_module der Nginx-Dokumentation.

      Der letzte Block location erfasst Anfragen an den Pfad /well-known/acme-Challenge/, der von Certbot für HTTP-01-Challenges verwendet wird, um Ihre Domäne mit Let’s Encrypt zu verifizieren und TLS-Zertifikate bereitzustellen oder zu erneuern. Weitere Informationen über die von Certbot verwendete HTTP-01-Challenge finden Sie unter Challenge-Arten in der Let’s Encrypt-Dokumentation.

      conf/nginx.conf

      . . .
      location ^~ /.well-known/acme-challenge/ {
              root /var/www/html;
      }
      

      Wenn Sie die Bearbeitung abgeschlossen haben, speichern und schließen Sie die Datei.

      Sie können diese Konfigurationsdatei nun verwenden, um einen Nginx Docker-Container auszuführen. In diesem Tutorial verwenden wir das Image nginx:1.19.0, Version 1.19.0 des offiziellen Docker-Images, das von Nginx verwaltet wird.

      Wenn wir den Container zum ersten Mal ausführen, wird Nginx einen Fehler ausgeben und fehlschlagen, da wir die in der Konfigurationsdatei definierten Zertifikate noch nicht bereitgestellt haben. Wir werden jedoch trotzdem den Befehl zum Herunterladen des Nginx-Images lokal ausführen und testen, ob alles andere korrekt funktioniert:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Hier nennen wir den Container nginx und ordnen die Host-Ports 80 und 443 den jeweiligen Container-Ports zu. Das Flag -v bindet die Konfigurationsdatei unter /etc/nginx/conf.d/nginx.conf in den Nginx-Container ein, für dessen Laden das Nginx-Image vorkonfiguriert ist. Es wird im Modus ro oder „read-only“ eingebunden, sodass der Container die Datei nicht verändern kann. Das Web-Stammverzeichnis /var/www/html ist ebenfalls in den Container eingebunden. Schließlich weist nginx:1.19.0 Docker an, das Image nginx:1.19.0 aus Dockerhub zu ziehen und auszuführen.

      Docker wird das Image ziehen und ausführen, dann wird Nginx einen Fehler ausgeben, wenn es das konfigurierte TLS-Zertifikat und den geheimen Schlüssel nicht findet. Im nächsten Schritt stellen wir diese mithilfe eines dockerisierten Certbot-Clients und der Zertifizierungsstelle Let’s Encrypt bereit.

      Schritt 4 – Konfigurieren von Certbot und Let’s Encrypt-Zertifikaterneuerung

      Certbot ist ein Let’s Encrypt-Client, der von der Electronic Frontier Foundation entwickelt wurde. Er stellt kostenlose TLS-Zertifikate von der Let’s Encrypt-Zertifizierungsstelle zur Verfügung, mit denen Browser die Identität Ihrer Webserver überprüfen können. Da wir auf unserem Nginx-Proxy-Server Docker installiert haben, verwenden wir das Certbot Docker-Image zur Bereitstellung und Erneuerung der TLS-Zertifikate.

      Stellen Sie zunächst sicher, dass Sie über einen DNS-A-Eintrag verfügen, der der öffentlichen IP-Adresse des Proxy-Servers zugeordnet ist. Stellen Sie dann auf Ihrem Proxy-Server eine Staging-Version der Zertifikate unter Verwendung des Docker-Images certbot bereit:

      docker run -it --rm -p 80:80 --name certbot 
               -v "/etc/letsencrypt:/etc/letsencrypt" 
               -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
               certbot/certbot certonly --standalone --staging -d your_domain.com
      

      Dieser Befehl führt das certbot Docker-Image im interaktiven Modus aus und leitet Port 80 auf dem Host an den Container-Port 80 weiter. Er erstellt zwei Host-Verzeichnisse und bindet sie in die Container ein: /etc/letsencrypt/ und /var/lib/letsencrypt/. certbot wird im Modus standalone ohne Nginx ausgeführt und verwendet die Staging-Server von Let’s Encrypt, um die Domänenvalidierung durchzuführen.

      Geben Sie, wenn Sie dazu aufgefordert werden, Ihre E-Mail-Adresse ein und stimmen Sie den Nutzungsbedingungen zu. Wenn die Domänenvalidierung erfolgreich war, sollten Sie die folgende Ausgabe sehen:

      Output

      Obtaining a new certificate Performing the following challenges: http-01 challenge for stubb.dev Waiting for verification... Cleaning up challenges IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain.com/privkey.pem Your cert will expire on 2020-09-15. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal.

      Sie können das Zertifikat mit cat inspizieren:

      sudo cat /etc/letsencrypt/live/your_domain.com/fullchain.pem
      

      Mit dem bereitgestellten TLS-Zertifikat können wir die im vorherigen Schritt eingebundene Nginx-Konfiguration testen:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Dies ist derselbe Befehl, der in Schritt 3 ausgeführt wurde, mit dem Hinzufügen der beiden kürzlich erstellten Let’s Encrypt-Verzeichnisse:

      Sobald Nginx ausgeführt wird, navigieren Sie zu http://your_domain.com. Möglicherweise erhalten Sie in Ihrem Browser eine Warnung, dass die Zertifizierungsstelle ungültig ist. Dies ist zu erwarten, da wir Staging-Zertifikate und keine Produktions-Let’s Encrypt-Zertifikate bereitgestellt haben. Überprüfen Sie die URL-Leiste Ihres Browsers, um zu bestätigen, dass Ihre HTTP-Anfrage an HTTPS umgeleitet wurde.

      Drücken Sie zum Beenden von Nginx in Ihrem Terminal Strg+C und führen Sie den Client certbot erneut aus, diesmal ohne das Flag --staging:

      docker run -it --rm -p 80:80 --name certbot 
               -v "/etc/letsencrypt:/etc/letsencrypt" 
               -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
               certbot/certbot certonly --standalone -d your_domain.com
      

      Wenn Sie dazu aufgefordert werden, entweder das vorhandene Zertifikat beizubehalten oder es zu erneuern und zu ersetzen, drücken Sie 2 zum Erneuern und dann ENTER, um Ihre Wahl zu bestätigen.

      Wenn das Produktions-TLS-Zertifikat bereitgestellt ist, führen Sie den Nginx-Server erneut aus:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Navigieren Sie in Ihrem Browser zu http://your_domain.com. Bestätigen Sie in der URL-Leiste, dass die HTTP-Anfrage an HTTPS umgeleitet wurde. Da für die Umfrageanwendung keine Standardroute konfiguriert ist, sollten Sie den Django-Fehler Page not found (Seite nicht gefunden) sehen. Navigieren Sie zu https://your_domain.com/polls und Sie sehen die Standardoberfläche der Umfrageanwendung:

      Oberfläche der Umfrageanwendung

      Zu diesem Zeitpunkt haben Sie mit dem Certbot Docker-Client ein Produktions-TLS-Zertifikat bereitgestellt und externe Anfragen an die beiden Django-App-Server durch Reverse Proxying und Lastausgleich umgeleitet.

      Die Let’s Encrypt-Zertifikate laufen alle 90 Tage ab. Um sicherzustellen, dass Ihr Zertifikat gültig bleibt, sollten Sie es regelmäßig vor dessen geplantem Ablauf erneuern. Wenn Nginx ausgeführt wird, sollten Sie den Certbot-Client im Modus webroot anstelle des Modus standalone verwenden. Das bedeutet, dass Certbot die Validierung durch die Erstellung einer Datei im Verzeichnis /var/www/html/.well-known/acme-challenge/ durchführt, und die Validierungsanforderungen von Let’s Encrypt an diesen Pfad werden von der in der Nginx-Konfiguration in Schritt 3 definierten Regel location erfasst. Certbot wird dann die Zertifikate rotieren, und Sie können Nginx neu laden, sodass es dieses neu bereitgestellte Zertifikat verwendet.

      Es gibt mehrere Möglichkeiten, um dieses Verfahren zu automatisieren und die automatische Erneuerung von TLS-Zertifikaten geht über den Umfang dieses Tutorials hinaus. Ein ähnliches Verfahren unter Verwendung des Scheduling-Dienstprogramms cron finden Sie in Schritt 6 von Sichern einer containerisierten Node.js-Anwendung mit Nginx, Let’s Encrypt, und Docker Compose.

      Drücken Sie zum Beenden des Nginx-Containers in Ihrem Terminal Strg+C. Führen Sie ihn erneut im getrennten Modus aus, indem Sie das Flag -d anhängen:

      docker run --rm --name nginx -d -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
        -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Verwenden Sie mit im Hintergrund ausgeführtem Nginx den folgenden Befehl, um einen Probelauf des Zertifikatserneuerungsverfahrens auszuführen:

      docker run -it --rm --name certbot 
          -v "/etc/letsencrypt:/etc/letsencrypt" 
        -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
        -v "/var/www/html:/var/www/html" 
        certbot/certbot renew --webroot -w /var/www/html --dry-run
      

      Wir verwenden das Plugin --webroot, geben den Web-Stammpfad ein und verwenden das Flag --dry-run zum Überprüfen der ordnungsgemäßen Funktion, ohne die Zertifikatserneuerung tatsächlich durchzuführen.

      Wenn die Erneuerungssimulation erfolgreich ist, sollten Sie die folgende Ausgabe sehen:

      Output

      Cert not due for renewal, but simulating renewal for dry run Plugins selected: Authenticator webroot, Installer None Renewing an existing certificate Performing the following challenges: http-01 challenge for your_domain.com Using the webroot path /var/www/html for all unmatched domains. Waiting for verification... Cleaning up challenges - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed without reload, fullchain is /etc/letsencrypt/live/your_domain.com/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/your_domain.com/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

      In einer Produktionseinstellung sollten Sie nach der Erneuerung von Zertifikaten Nginx neu laden, damit die Änderungen wirksam werden. Führen Sie zum Neuladen von Nginx den folgenden Befehl aus:

      docker kill -s HUP nginx
      

      Dieser Befehl sendet ein HUP Unix-Signal an den Nginx-Prozess, der innerhalb des Docker-Containers nginx ausgeführt wird. Nach Empfang dieses Signals lädt Nginx seine Konfiguration und erneuerten Zertifikate neu.

      Wenn HTTPS aktiviert ist und alle Komponenten dieser Architektur ausgeführt werden, besteht der letzte Schritt darin, die Einrichtung zu sperren, indem der externe Zugriff auf die beiden Backend-App-Server verhindert wird; alle HTTP-Anfragen sollten über den Nginx-Proxy laufen.

      Schritt 5 – Verhindern des externen Zugriffs auf Django-App-Server

      In der in diesem Tutorial beschriebenen Architektur erfolgt die SSL-Terminierung am Nginx-Proxy. Das bedeutet, dass Nginx die SSL-Verbindung entschlüsselt und die Pakete unverschlüsselt an die Django-App-Server weitergeleitet werden. Für viele Anwendungsfälle ist diese Sicherheitsstufe ausreichend. Für Anwendungen mit Finanz- oder Gesundheitsdaten sollten Sie eventuell eine End-to-End-Verschlüsselung implementieren. Sie können dies tun, indem Sie verschlüsselte Pakete über den Load Balancer weiterleiten und auf den App-Servern entschlüsseln oder am Proxy neu verschlüsseln und auf den Django-App-Servern wieder entschlüsseln. Diese Techniken gehen über den Rahmen dieses Artikels hinaus. Um mehr zu erfahren, lesen Sie bitte End-to-End-Verschlüsselung.

      Der Nginx-Proxy fungiert als Gateway zwischen externem Datenverkehr und dem internen Netzwerk. Theoretisch sollten keine externen Clients direkten Zugriff auf die internen App-Server haben, und alle Anfragen sollten über den Nginx-Server laufen. Der Hinweis in Schritt 1 beschreibt kurz ein offenes Problem mit Docker, bei dem Docker standardmäßig die ufw-Firewalleinstellungen umgeht und Ports extern öffnet, die möglicherweise unsicher sind. Um dieses Sicherheitsproblem zu beheben wird empfohlen, bei der Arbeit mit Docker-fähigen Servern Cloud Firewalls zu verwenden. Weitere Informationen zum Erstellen von Cloud Firewalls mit DigitalOcean finden Sie unter Erstellen von Firewalls. Sie können iptables auch direkt manipulieren, anstatt ufw zu verwenden. Um mehr über die Verwendung von iptables mit Docker zu erfahren, lesen Sie bitte Docker und iptables.

      In diesem Schritt ändern wir die UFW-Konfiguration so, dass der externe Zugriff auf die von Docker geöffneten Host-Ports blockiert wird. Bei der Ausführung von Django auf den App-Servern haben wir das Flag -p 80:8000 an docker übergeben, das Port 80 auf dem Host an den Container-Port 8000 weiterleitet. Dadurch wurde Port 80 auch für externe Clients geöffnet, was Sie unter http://your_app_server_1_IP überprüfen können. Um den direkten Zugriff zu verhindern, ändern wir die UFW-Konfiguration mithilfe der im ufw-docker GitHub-Repository beschriebenen Methode.

      Beginnen Sie damit, sich bei dem ersten Django-App-Server anzumelden. Öffnen Sie dann die Datei /etc/ufw/after.rules mit superuser-Berechtigungen, indem Sie nano oder Ihren bevorzugten Editor verwenden:

      sudo nano /etc/ufw/after.rules
      

      Geben Sie bei Aufforderung Ihr Passwort ein und drücken Sie zur Bestätigung ENTER.

      Sie sollten die folgenden ufw-Regeln sehen:

      /etc/ufw/after.rules

      #
      # rules.input-after
      #
      # Rules that should be run after the ufw command line added rules. Custom
      # rules should be added to one of these chains:
      #   ufw-after-input
      #   ufw-after-output
      #   ufw-after-forward
      #
      
      # Don't delete these required lines, otherwise there will be errors
      *filter
      :ufw-after-input - [0:0]
      :ufw-after-output - [0:0]
      :ufw-after-forward - [0:0]
      # End required lines
      
      # don't log noisy services by default
      -A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input
      -A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input
      -A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input
      
      # don't log noisy broadcast
      -A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input
      
      # don't delete the 'COMMIT' line or these rules won't be processed
      COMMIT
      

      Scrollen Sie nach unten und fügen Sie den folgenden Block mit UFW-Konfigurationsregeln ein:

      /etc/ufw/after.rules

      . . .
      
      # BEGIN UFW AND DOCKER
      *filter
      :ufw-user-forward - [0:0]
      :DOCKER-USER - [0:0]
      -A DOCKER-USER -j RETURN -s 10.0.0.0/8
      -A DOCKER-USER -j RETURN -s 172.16.0.0/12
      -A DOCKER-USER -j RETURN -s 192.168.0.0/16
      
      -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
      
      -A DOCKER-USER -j ufw-user-forward
      
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
      
      -A DOCKER-USER -j RETURN
      COMMIT
      # END UFW AND DOCKER
      

      Diese Regeln schränken den öffentlichen Zugriff auf die von Docker geöffneten Ports ein und ermöglichen den Zugriff aus den privaten IP-Bereichen 10.0.0/8, 172.16.0.0/12 und 192.168.0.0/16. Wenn Sie VPC mit DigitalOcean verwenden, dann haben Droplets in Ihrem VPC-Netzwerk über die private Netzwerkschnittstelle Zugriff auf den offenen Port, externe Clients jedoch nicht. Weitere Informationen über VPC finden Sie in der offiziellen VPC-Dokumentation. Um mehr über die in diesem Snippet implementierten Regeln zu erfahren, lesen Sie bitte Funktionsweise in der ufw-docker README.

      Wenn Sie VPC nicht mit DigitalOcean verwenden und die öffentlichen IP-Adressen der App-Server in den Block upstream Ihrer Nginx-Konfiguration eingegeben haben, müssen Sie die UFW-Firewall explizit ändern, um den Datenverkehr vom Nginx-Server über Port 80 auf den Django-App-Servern zuzulassen. Eine Anleitung zur Erstellung von allow-Regeln mit der UFW-Firewall finden Sie unter UFW Grundlagen: Allgemeine Firewallregeln und -befehle.

      Wenn Sie die Bearbeitung abgeschlossen haben, speichern und schließen Sie die Datei.

      Starten Sie ufw neu, damit die neue Konfiguration übernommen wird:

      sudo systemctl restart ufw
      

      Navigieren Sie in Ihrem Webbrowser zu http://APP_SERVER_1_IP, um zu bestätigen, dass Sie über Port 80 nicht mehr auf die App-Server zugreifen können.

      Wiederholen Sie diesen Vorgang auf dem zweiten Django-App-Server.

      Melden Sie sich bei dem ersten App-Server ab oder öffnen Sie ein anderes Terminalfenster und melden Sie sich bei dem zweiten Django-App-Server an. Öffnen Sie dann die Datei /etc/ufw/after.rules mit superuser-Berechtigungen, indem Sie nano oder Ihren bevorzugten Editor verwenden:

      sudo nano /etc/ufw/after.rules
      

      Geben Sie bei Aufforderung Ihr Passwort ein und drücken Sie zur Bestätigung ENTER.

      Scrollen Sie nach unten und fügen Sie den folgenden Block mit UFW-Konfigurationsregeln ein:

      /etc/ufw/after.rules

      . . .
      
      # BEGIN UFW AND DOCKER
      *filter
      :ufw-user-forward - [0:0]
      :DOCKER-USER - [0:0]
      -A DOCKER-USER -j RETURN -s 10.0.0.0/8
      -A DOCKER-USER -j RETURN -s 172.16.0.0/12
      -A DOCKER-USER -j RETURN -s 192.168.0.0/16
      
      -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
      
      -A DOCKER-USER -j ufw-user-forward
      
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
      
      -A DOCKER-USER -j RETURN
      COMMIT
      # END UFW AND DOCKER
      

      Wenn Sie die Bearbeitung abgeschlossen haben, speichern und schließen Sie die Datei.

      Starten Sie ufw neu, damit die neue Konfiguration übernommen wird:

      sudo systemctl restart ufw
      

      Navigieren Sie in Ihrem Webbrowser zu http://APP_SERVER_2_IP, um zu bestätigen, dass Sie über Port 80 nicht mehr auf die App-Server zugreifen können.

      Navigieren Sie abschließend zu https://your_domain_here/polls, um zu bestätigen, dass der Nginx-Proxy weiterhin Zugriff auf die upstream Django-Server hat. Sie sollten die Standardoberfläche der Umfrageanwendung sehen.

      Zusammenfassung

      In diesem Tutorial haben Sie mit Docker-Containern eine skalierbare Django Umfrageanwendung eingerichtet. Wenn Ihr Datenverkehr steigt und die Last auf dem System zunimmt, können Sie jede Schicht separat skalieren: die Nginx-Proxying-Schicht, die Django-Backend-Anwendungsschicht und die PostgreSQL-Datenbankschicht.

      Beim Aufbau eines verteilten Systems müssen Sie oft mehrere Designentscheidungen treffen, und mehrere Architekturen können Ihrem Anwendungsfall gerecht werden. Die in diesem Tutorial beschriebene Architektur ist als flexible Blaupause für den Entwurf skalierbarer Anwendungen mit Django und Docker gedacht.

      Möglicherweise möchten Sie das Verhalten Ihrer Container steuern, wenn sie auf Fehler stoßen, oder Container automatisch ausführen, wenn Ihr System gestartet wird. Zu diesem Zweck können Sie einen Prozessmanager wie Systemd verwenden oder Neustartrichtlinien implementieren. Weitere Informationen hierzu finden Sie unter Automatisches Starten von Containern in der Docker-Dokumentation.

      Wenn Sie im großen Maßstab mit mehreren Hosts arbeiten, die dasselbe Docker-Image ausführen, kann es effizient sein, Schritte mit einem Konfigurations-Managementtool wie Ansible oder Chef zu automatisieren. Um mehr über das Konfigurationsmanagement zu erfahren, lesen Sie bitte Eine Einführung in das Konfigurationsmanagement und Automatisieren der Servereinrichtung mit Ansible: Ein DigitalOcean-Workshop-Kit.

      Anstatt auf jedem Host dasselbe Image zu erstellen, können Sie die Bereitstellung auch mithilfe einer Image-Registrierung wie Docker Hub rationalisieren, bei der Docker-Images zentral erstellt, gespeichert und an mehrere Server verteilt werden. Zusammen mit einer Image-Registrierung kann Ihnen eine kontinuierliche Integrations- und Bereitstellungspipeline dabei helfen, Images zu erstellen, zu testen und auf Ihre App-Server zu verteilen. Weitere Informationen zu CI/CD finden Sie unter Eine Einführung in die CI/CD Best Practices.



      Source link

      Cómo escalar y proteger una aplicación de Django con Docker, Nginx y Let’s Encrypt


      Introducción

      En los entornos basados en la nube, hay diversas maneras de escalar y proteger aplicaciones de Django. Al escalar horizontalmente y ejecutar varias copias de su aplicación, puede crear un sistema más tolerante a fallos y de alta disponibilidad, a la vez que aumenta su rendimiento para que se puedan procesar solicitudes de forma simultánea. Una manera de escalar horizontalmente una aplicación de Django es proporcionar servidores de aplicaciones adicionales que ejecuten su aplicación de Django y su servidor HTTP WSGI (como Gunicorn o uWSGI). Para dirigir y distribuir las solicitudes entrantes a través de este conjunto de servidores de aplicaciones, puede usar un equilibrador de carga y un proxy inverso como Nginx. Nginx también puede almacenar en caché contenido estático y detener las conexiones de Seguridad de la capa de transporte (TLS), que se utilizan para proporcionar conexiones HTTPS y seguras a su aplicación.

      Ejecutar su aplicación de Django y el proxy Nginx dentro de contenedores de Docker garantiza que estos componentes se comporten de la misma manera independientemente del entorno en el que se implementen. Además, los contenedores proporcionan muchas características que facilitan la creación de paquetes y la configuración de su aplicación.

      En este tutorial, escalará horizontalmente una aplicación Polls de Django y Gunicorn en un contenedor proporcionando dos servidores de aplicaciones que ejecutarán una copia de un contenedor de una aplicación de Django y Gunicorn.

      También habilitará HTTPS al proporcionar y configurar un tercer servidor proxy que ejecutará un contenedor de un proxy inverso Nginx y otro de un cliente Certbot. Certbot proporcionará certificados TLS para Nginx de la entidad de certificación Let’s Encrypt. Esto garantizará que su sitio reciba una calificación de seguridad alta de SSL Labs. Este servidor proxy recibirá todas las solicitudes externas de su aplicación y se ubicará frente a los dos servidores de aplicaciones de Django que preceden en la cadena. Por último, reforzará este sistema distribuido al restringir el acceso externo solo al servidor proxy.

      Requisitos previos

      Para seguir este tutorial, necesitará lo siguiente:

      • Tres servidores con Ubuntu 18.04:

        • Dos se utilizarán como servidores de aplicaciones y se utilizarán para ejecutar su aplicación de Django y Gunicorn.
        • Uno se usará como servidor proxy y se utilizará para ejecutar Nginx y Certbot.
        • Todos los usuarios deben tener un usuario no root con privilegios sudo y firewall activo. Para obtener información sobre cómo configurarlos, consulte esta guía de configuración inicial para servidores.
      • Docker instalado en los tres servidores. Para obtener orientación sobre la instalación de Docker, siga los pasos 1 y 2 de Cómo instalar y usar Docker en Ubuntu 18.04.

      • Un nombre de dominio registrado. En este tutorial, utilizaremos your_domain en todo momento. Puede obtener un ejemplar gratis en Freenom o utilizar el registrador de dominios que desee.

      • Un registro DNS A con your_domain.com orientado a la dirección IP pública de su servidor proxy. Puede seguir esta introducción al DNS de DigitalOcean para obtener información sobre cómo agregarlo a una cuenta de DigitalOcean, si usa una:

      • Un depósito de almacenamiento de objetos S3, como un Space de DigitalOcean, para almacenar los archivos estáticos de su proyecto de Django y un conjunto de claves de acceso para ese espacio. Para obtener información sobre cómo crear Spaces, consulte la documentación de Cómo crear Spaces. Para obtener información sobre cómo crear claves de acceso para Spaces, consulte Compartir el acceso a Spaces con claves de acceso. Con cambios menores, puede usar cualquier servicio de almacenamiento de objetos que admita el complemento django-storages

      • Una instancia de un servidor de PostgreSQL, una base de datos y un usuario para su aplicación de Django. Con cambios menores, puede usar cualquier base de datos que admita Django.

      Paso 1: Configurar el primer servidor de aplicaciones de Django

      Para comenzar, vamos a clonar el repositorio de aplicaciones de Django en el primer servidor de aplicaciones. A continuación, configuraremos y compilaremos la imagen de Docker de la aplicación y probaremos la aplicación ejecutando el contenedor de Django.

      Nota: Si continúa desde Cómo crear una aplicación de Django y Gunicorn con Docker, ya habrá completado el Paso 1, por lo que puede pasar directamente al Paso 2 para configurar el segundo servidor de aplicaciones.

      Comience iniciando sesión en el primero de los dos servidores de aplicaciones de Django. Utilice git para clonar la rama polls-docker del repositorio de GitHub de la aplicación Polls del tutorial de Django. Este repositorio contiene código para la aplicación Polls de muestra de la documentación de Django. La rama polls-docker contiene una versión con Docker de la aplicación Polls. Para obtener información sobre cómo se modificó la aplicación Polls para que funcione de forma eficaz en un entorno con contenedor, consulte Cómo crear una aplicación de Django y Gunicorn con Docker.

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Diríjase al directorio django-polls:

      cd django-polls
      

      Este directorio contiene el código de Python de la aplicación de Django, un Dockerfile que Docker utilizará para crear la imagen del contenedor, y un archivo env que contiene una lista de las variables de entorno que se van a pasar al entorno en ejecución del contenedor. Inspeccione el Dockerfile con cat:

      cat Dockerfile
      

      Output

      FROM python:3.7.4-alpine3.10 ADD django-polls/requirements.txt /app/requirements.txt RUN set -ex && apk add --no-cache --virtual .build-deps postgresql-dev build-base && python -m venv /env && /env/bin/pip install --upgrade pip && /env/bin/pip install --no-cache-dir -r /app/requirements.txt && runDeps="$(scanelf --needed --nobanner --recursive /env | awk '{ gsub(/,/, "nso:", $2); print "so:" $2 }' | sort -u | xargs -r apk info --installed | sort -u)" && apk add --virtual rundeps $runDeps && apk del .build-deps ADD django-polls /app WORKDIR /app ENV VIRTUAL_ENV /env ENV PATH /env/bin:$PATH EXPOSE 8000 CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi"]

      Este Dockerfile utiliza la imagen de Docker oficial de Python 3.7.4 como base e instala los requisitos del paquete de Python de Django y Gunicorn, tal como se define en el archivo django-polls/requirements.txt. A continuación, elimina algunos archivos de compilación innecesarios, copia el código de la aplicación en la imagen y establece el PATH de ejecución. Por último, declara que el puerto 8000 se utilizará para aceptar conexiones de contenedores entrantes y ejecuta gunicorn con 3 trabajadores, escuchando en el puerto 8000.

      Para obtener más información sobre cada uno de los pasos de este Dockerfile, consulte el Paso 6 de Cómo crear una aplicación de Django y Gunicorn con Docker.

      Ahora, compile la imagen con docker build:

      Nombramos la imagen polls con el indicador -t y pasamos el directorio actual como contexto de compilación, el conjunto de archivos a los que se debe hacer referencia al construir la imagen.

      Una vez que Docker haya compilado y etiquetado la imagen, enumere las imágenes disponibles utilizando docker images:

      docker images
      

      Debería ver la imagen polls enumerada:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE polls latest 80ec4f33aae1 2 weeks ago 197MB python 3.7.4-alpine3.10 f309434dea3a 8 months ago 98.7MB

      Antes de ejecutar el contenedor de Django, debemos configurar su entorno de ejecución utilizando el archivo env presente en el directorio actual. Este archivo se pasará al comando docker run que se utiliza para ejecutar el contenedor y Docker insertará las variables de entorno configuradas en el entorno en ejecución del contenedor.

      Abra el archivo env con nano o su editor favorito:

      nano env
      

      Configuraremos el archivo de esta manera y deberá agregar algunos valores adicionales como se indica a continuación.

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Complete los valores que faltan para las siguientes claves:

      • DJANGO_SECRET_KEY: establézcala en un valor único e impredecible, como se detalla en la documentación de Django. Se proporciona un método para generar esta clave en la sección Ajustar la configuración de la aplicación del tutorial Aplicaciones escalables de Django.
      • DJANGO_ALLOWED_HOSTS: esta variable asegura la aplicación y evita ataques a través del encabezado de host HTTP. Para propósitos de prueba, establézcala en *, un comodín que coincidirá con todos los hosts. En producción, debe establecerlo en your_domain.com. Para obtener más información sobre esta configuración de Django, consulte la sección Configuración principal en la documentación de Django.
      • DATABASE_USERNAME: establézcalo en el usuario de la base de datos de PostgreSQL creado en los pasos de requisitos previos.
      • DATABASE_NAME: establézcalo en polls o el nombre de la base de datos de PostgreSQL creado en los pasos de requisitos previos.
      • DATABASE_PASSWORD: establézcala en la contraseña de la base de datos de PostgreSQL creada en los pasos de requisitos previos.
      • DATABASE_HOST: establézcalo en el nombre de host de su base de datos.
      • DATABASE_PORT: Set establézcalo en el puerto de su base de datos.
      • STATIC_ACCESS_KEY_ID: establézcala en la clave de acceso de su cubo S3 o su Space.
      • STATIC_SECRET_KEY: establézcala en el secreto de la clave de acceso de su cubo S3 o su Space.
      • STATIC_BUCKET_NAME: establézcalo en el nombre de su cubo S3 o su Space.
      • STATIC_ENDPOINT_URL: establézcala en la URL de extremo correspondiente de su cubo S3 o su Space, por ejemplo, https://space-name.nyc3.digitaloceanspaces.com, si su Space está ubicado en la región nyc3.

      Una vez que haya finalizado la edición, guarde y cierre el archivo.

      Ahora, utilizaremos docker run para anular el CMD establecido en Dockerfile y crear el esquema de la base de datos utilizando los comandos manage.py makemigrations y manage.py migrate:

      docker run --env-file env polls sh -c "python manage.py makemigrations && python manage.py migrate"
      

      Ejecutamos la imagen del contenedor polls:latest, pasamos el archivo de variables de entorno que acabamos de modificar y anulamos el comando de Dockerfile con sh -c "python manage.py makemigrations && python manage.py migrate", lo que creará el esquema de la base de datos definido mediante el código de la aplicación. Si lo ejecuta por primera vez, debería ver lo siguiente:

      Output

      No changes detected Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying polls.0001_initial... OK Applying sessions.0001_initial... OK

      Esto indica que el esquema de la base de datos se ha creado correctamente.

      Si no está ejecutando migrate por primera vez, Django realizará un no-op a menos que el esquema de la base de datos haya cambiado.

      A continuación, ejecutaremos otra instancia del contenedor de la aplicación y utilizaremos una shell interactiva en su interior para crear un usuario administrativo para el proyecto de Django.

      docker run -i -t --env-file env polls sh
      

      Esto le proporcionará una línea de comandos de shell dentro del contenedor en ejecución que puede usar para crear el usuario de Django:

      python manage.py createsuperuser
      

      Ingrese un nombre de usuario, una dirección de correo electrónico y una contraseña para su usuario y, una vez que haya creado el usuario, presione CTRL+D para salir del contenedor y cerrarlo.

      Por último, generaremos los archivos estáticos de la aplicación y los subiremos al Space de DigitalOcean utilizando collectstatic. Tenga en cuenta que esta operación puede tardar un poco en completarse.

      docker run --env-file env polls sh -c "python manage.py collectstatic --noinput"
      

      Una vez que estos archivos se hayan generado y cargado, obtendrá el siguiente resultado.

      Output

      121 static files copied.

      Ahora, podemos ejecutar la aplicación:

      docker run --env-file env -p 80:8000 polls
      

      Output

      [2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0 [2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) [2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync [2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7 [2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8 [2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

      Aquí, ejecutamos el comando predeterminado definido en el Dockerfile, gunicorn --bind :8000 --workers 3 mysite.wsgi:application y exponemos el puerto del contenedor 8000 para que el puerto 80 del servidor de Ubuntu se asigne al puerto 8000 del contenedor polls.

      Ahora, debería poder navegar a la aplicación polls desde su navegador web al escribir http://APP_SERVER_1_IP en la barra de direcciones URL. Dado que no hay una ruta definida para la ruta / , es probable que reciba un error de 404 Page Not Found (Página no encontrada), lo que es de esperar.

      Advertencia: Al usar el firewall UFW con Docker, Docker omite cualquier regla de firewall configurada, tal como se documenta en este número de GitHub. Esto explica por qué tiene acceso al puerto 80 de su servidor, a pesar de no haber creado explícitamente una regla de acceso de UFW en ningún paso de los requisitos previos. Abordaremos este problema de seguridad en el Paso 5, al corregir la configuración de UFW. Si no usa UFW y está utilizando firewalls de DigitalOcean para la nube, puede ignorar de forma segura esta advertencia.

      Diríjase a http://APP_SERVER_1_IP/polls para ver la interfaz de la aplicación Polls:

      Interfaz de la aplicación Polls

      Para ver la interfaz administrativa, visite http://APP_SERVER_1_IP/admin. Debería ver la ventana de autenticación de administración de la aplicación Polls:

      Página de autenticación de administración de Polls

      Ingrese el nombre de usuario administrativo y la contraseña que creó con el comando createsuperuser.

      Después de la autenticación, podrá acceder a la interfaz administrativa de la aplicación Polls:

      Interfaz principal de administración de Polls

      Tenga en cuenta que los que los recursos estáticos de las aplicaciones admin y polls se entregan directamente desde el almacenamiento de objetos. Para confirmar esto, consulte Prueba de la entrega de archivos estáticos de Spaces.

      Cuando haya terminado de explorar, presione CTRL+C en la ventana de terminal que está ejecutando el contenedor de Docker para cerrar el contenedor.

      Ahora que ha confirmado que el contenedor de la aplicación se ejecuta de la manera prevista, puede ejecutarlo en modo separado, lo que lo ejecutará en segundo plano y le permitirá salir de su sesión SSH:

      docker run -d --rm --name polls --env-file env -p 80:8000 polls
      

      El indicador -d le indica a Docker que ejecute el contenedor en modo separado y el indicador -rm limpia el sistema de archivos del contenedor una vez que se sale de él. Denominamos polls al contenedor.

      Desconéctese del primer servidor de aplicaciones de Django y diríjase a http://APP_SERVER_1_IP/polls para confirmar que el contenedor se está ejecutando de la manera prevista.

      Ahora que su primer servidor de aplicaciones de Django está en ejecución, puede configurar el segundo.

      Paso 2: Configurar el segundo servidor de aplicaciones de Django

      Como muchos de los comandos que se utilizan para configurar este servidor serán los mismos que los que utilizamos en el paso anterior, se presentarán aquí de forma abreviada. Revise el Paso 1 para obtener más información sobre los comandos que se utilizan en este paso.

      Comience por iniciar sesión en el segundo servidor de aplicaciones de Django.

      Clone la rama polls-docker del repositorio de GitHub django-polls:

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Diríjase al directorio django-polls:

      cd django-polls
      

      Compile la imagen con docker build:

      Abra el archivo env con nano o su editor favorito:

      nano env
      

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Complete los valores que faltan como se indica en el Paso 1. Cuando haya terminado de editar, guarde y cierre el archivo.

      Por último, ejecute el contenedor de la aplicación en modo separado:

      docker run -d --rm --name polls --env-file env -p 80:8000 polls
      

      Diríjase a http://APP_SERVER_2_IP/polls para confirmar que el contenedor se está ejecutando de la manera prevista. Puede iniciar sesión de forma segura en el segundo servidor de aplicaciones sin cerrar el contenedor en ejecución.

      Con los dos contenedores de aplicaciones de Django en ejecución, puede configurar el contenedor del proxy inverso Nginx.

      Paso 3: Configurar el contenedor de Docker de Nginx

      Nginx es un servidor web versátil que ofrece varias características, como proxy inverso, equilibrio de carga y almacenamiento en caché. En este tutorial, hemos descargado los recursos estáticos de Django al almacenamiento de objetos, por lo que no utilizaremos las capacidades de almacenamiento en caché de Nginx. Sin embargo, utilizaremos Nginx como proxy inverso para nuestros dos servidores de aplicaciones de Django de backend y distribuiremos las solicitudes entrantes entre ellos. Además, Nginx realizará la terminación de TLS y el redireccionamiento utilizando un certificado TLS proporcionado por Certbot. Esto significa que obligará a los clientes a usar HTTPS, redireccionando las solicitudes HTTP entrantes al puerto 443. Luego, descifrará las solicitudes HTTPS y las redirigirá, a través del proxy, a los servidores de Django que preceden en la cadena.

      En este tutorial, hemos tomado la decisión de desacoplar los contenedores de Nginx de los servidores de backend. Dependiendo de su caso de uso, puede optar por ejecutar el contenedor de Nginx en cualquiera de los dos servidores de aplicaciones de Django, redirigiendo las solicitudes, a través del proxy, de forma local. Otra posible arquitectura sería ejecutar dos contenedores de Nginx, uno en cada servidor de backend, con un equilibrador de carga en la nube al frente. Cada arquitectura presenta diferentes ventajas de seguridad y desempeño; debe realizar una prueba de carga de su sistema para descubrir los posibles cuellos de botella. La arquitectura flexible que se describe en este tutorial le permite escalar tanto la capa de la aplicación de Django de backend como la capa del proxy de Nginx. Cuando el contenedor único de Nginx se convierta en un cuello de botella, puede escalar varios proxy de Nginx y agregar un equilibrador de carga en la nube o uno L4 rápido, como HAProxy.

      Con los dos servidores de aplicaciones de Django en ejecución, podemos comenzar a configurar el servidor proxy de Nginx. Inicie sesión en su servidor proxy y cree un directorio llamado conf:

      mkdir conf
      

      Cree un archivo de configuración denominado nginx.conf con nano o su editor favorito:

      nano conf/nginx.conf
      

      Pegue la siguiente configuración de Nginx:

      conf/nginx.conf

      
      upstream django {
          server APP_SERVER_1_IP;
          server APP_SERVER_2_IP;
      }
      
      server {
          listen 80 default_server;
          return 444;
      }
      
      server {
          listen 80;
          listen [::]:80;
          server_name your_domain.com;
          return 301 https://$server_name$request_uri;
      }
      
      server {
          listen 443 ssl http2;
          listen [::]:443 ssl http2;
          server_name your_domain.com;
      
          # SSL
          ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
      
          ssl_session_cache shared:le_nginx_SSL:10m;
          ssl_session_timeout 1440m;
          ssl_session_tickets off;
      
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_prefer_server_ciphers off;
      
          ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
      
          client_max_body_size 4G;
          keepalive_timeout 5;
      
              location / {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_pass http://django;
              }
      
          location ^~ /.well-known/acme-challenge/ {
              root /var/www/html;
          }
      
      }
      

      Los bloques upstream, server y location configuran Nginx para que redirija las solicitudes HTTP a HTTPS y equilibre su carga entre los dos servidores de aplicaciones de Django configurados en los pasos 1 y 2. Para obtener más información sobre la estructura de los archivos de configuración de Nginx, consulte el artículo Información sobre la estructura de los archivos y los contextos de configuración de Nginx. El artículo Información sobre algoritmos de selección de bloques de servidores y ubicación de Nginx también puede resultarle útil.

      Esta configuración se realizó a partir de archivos de configuración de muestra proporcionados por Gunicorn, Cerbot y Nginx y representa una configuración mínima de Nginx para poner en marcha esta arquitectura. Los ajustes de esta configuración de Nginx están fuera del alcance de este artículo, pero puede usar una herramienta como NGINXConfig para generar archivos de configuración de Nginx seguros y de buen rendimiento para su arquitectura.

      El bloque upstream define el grupo de servidores que se utiliza para redirigir las solicitudes mediante el proxy utilizando la directiva proxy_pass:

      conf/nginx.conf

      upstream django {
          server APP_SERVER_1_IP;
          server APP_SERVER_2_IP;
      }
      . . .
      

      En este bloque, lo denominamos django e incluimos las direcciones IP de los dos servidores de aplicaciones de Django. Si los servidores de aplicaciones se ejecutan en DigitalOcean y tienen habilitadas redes VPC, debe usar sus direcciones IP privadas aquí. Para obtener información sobre cómo habilitar las redes VPC en DigitalOcean, consulte Cómo habilitar redes VPC en Droplets existentes.

      El primer bloque server captura las solicitudes que no coinciden con su dominio y termina la conexión. Por ejemplo, este bloque manejaría una solicitud HTTP directa a la dirección IP de su servidor:

      conf/nginx.conf

      . . .
      server {
          listen 80 default_server;
          return 444;
      }
      . . .
      

      El siguiente bloque server redirige las solicitudes HTTP a su dominio a HTTPS utilizando un redireccionamiento HTTP 301. Luego, el bloque server final se encarga de manejar estas solicitudes:

      conf/nginx.conf

      . . .
      server {
          listen 80;
          listen [::]:80;
          server_name your_domain.com;
          return 301 https://$server_name$request_uri;
      }
      . . .
      

      Estas dos directivas definen las rutas al certificado TLS y la clave secreta. Se proporcionarán utilizando Certbot y se instalarán en el contenedor de Nginx en el siguiente paso.

      conf/nginx.conf

      . . .
      ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
      . . .
      

      Estos parámetros son los valores predeterminados de seguridad SSL recomendados por Certbot. Para obtener más información sobre ellos, consulte el Módulo ngx_http_ssl_module en la documentación de Nginx. La guía Seguridad/TLS del lado del servidor de Mozilla es otro recurso útil que puede usar para ajustar su configuración de SSL.

      conf/nginx.conf

      . . .
          ssl_session_cache shared:le_nginx_SSL:10m;
          ssl_session_timeout 1440m;
          ssl_session_tickets off;
      
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_prefer_server_ciphers off;
      
          ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
      . . .
      

      Estas dos directivas de la configuración de muestra de Nginx de Gunicorn establecen el tamaño máximo permitido del cuerpo de la solicitud del cliente y asignan el tiempo de espera para las conexiones persistentes con el cliente. Nginx cerrará las conexiones con el cliente una vez transcurridos los segundos de keepalive_timeout.

      conf/nginx.conf

      . . .
      client_max_body_size 4G;
      keepalive_timeout 5;
      . . .
      

      El primer bloque location le indica a Nginx que redirija, a través del proxy, las solicitudes a los servidores upstream django mediante HTTP. También preserva los encabezados HTTP del cliente que capturan la dirección IP de origen, el protocolo utilizado para la conexión y el host de destino:

      conf/nginx.conf

      . . .
      location / {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header Host $http_host;
          proxy_redirect off;
          proxy_pass http://django;
      }
      . . .
      

      Para obtener más información sobre estas directivas, consulte Implementación de Gunicorn y Module ngx_http_proxy_module en la documentación de Nginx.

      El bloque location final captura las solicitudes a la ruta /well-known/acme-challenge/ que utiliza Certbot en los desafíos de HTTP-01 para verificar su dominio con Let’s Encrypt y suministrar o renovar los certificados TLS.  Para obtener más información sobre el desafío HTTP-01 que utiliza Certbot, consulte Tipos de desafíos en la documentación de Let’s Encrypt.

      conf/nginx.conf

      . . .
      location ^~ /.well-known/acme-challenge/ {
              root /var/www/html;
      }
      

      Una vez que haya finalizado la edición, guarde y cierre el archivo.

      Ahora, puede usar este archivo de configuración para ejecutar un contenedor de Docker de Nginx. En este tutorial, utilizaremos la imagen nginx:1.19.0, versión 1.19.0, de la imagen de Docker oficial que mantiene Nginx.

      Cuando ejecutemos el contenedor por primera vez, Nginx arrojará un error y fallará, dado que aún no hemos proporcionado los certificados definidos en el archivo de configuración. De todos modos, ejecutaremos el comando para descargar la imagen de Nginx de forma local y probar que todo lo demás funcione correctamente:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Aquí, denominamos nginx al contenedor y asignamos los puertos del host 80 y 443 a los puertos de los contenedores respectivos. El indicador -v ubica el archivo config en el contenedor de Nginx en /etc/nginx/conf.d/nginx.conf, que la imagen de Nginx está preconfigurada para cargar. Se coloca en modo ro o de “solo lectura”, por lo que el contenedor no puede modificar el archivo. El directorio web root /var/www/html también se instala en el contenedor. Por último, nginx:1.19.0 le indica a Docker que extraiga y ejecute la imagen nginx:1.19.0 de Dockerhub.

      Docker extraerá y ejecutará la imagen y, luego, Nginx arrojará un error cuando no encuentre el certificado TLS configurado y la clave secreta. Los suministraremos en el siguiente paso utilizando un cliente de Cerbot con Docker y la entidad de certificación Let’s Encrypt.

      Paso 4: Configurar la renovación de certificados de Let’s Encrypt y Certbot

      Certbot es un cliente Let’s Encrypt desarrollado por Electronic Frontier Foundation. Proporciona certificados TLS gratuitos de la entidad de certificación Let’s Encrypt que permiten a los navegadores verificar la identidad de sus servidores web. Como tenemos Docker instalado en nuestro servidor proxy de Nginx, utilizaremos la imagen de Docker de Certbot para suministrar y renovar los certificados TLS.

      Comience por asegurarse de tener un registro DNS A asignado a la dirección IP pública del servidor proxy. A continuación, en su servidor proxy, proporcione una versión provisional de los certificados utilizando la imagen de Docker certbot:

      docker run -it --rm -p 80:80 --name certbot 
               -v "/etc/letsencrypt:/etc/letsencrypt" 
               -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
               certbot/certbot certonly --standalone --staging -d your_domain.com
      

      Este comando ejecuta la imagen de Docker certbot en modo interactivo y reenvía el puerto 80 del host al puerto 80 del contenedor. Crea y monta dos directorios de host en el contenedor: /etc/letsencrypt/ y /var/lib/letsencrypt/. certbot se ejecuta en modo standalone, sin Nginx, y utilizará los servidores staging de Let’s Encrypt para realizar la validación del dominio.

      Cuando se le solicite, ingrese su dirección de correo electrónico y acepte las Condiciones del servicio. Si la validación del dominio es correcta, debería ver el siguiente resultado:

      Output

      Obtaining a new certificate Performing the following challenges: http-01 challenge for stubb.dev Waiting for verification... Cleaning up challenges IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain.com/privkey.pem Your cert will expire on 2020-09-15. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal.

      Puede inspeccionar el certificado utilizando cat:

      sudo cat /etc/letsencrypt/live/your_domain.com/fullchain.pem
      

      Con el certificado TLS suministrado, podemos probar la configuración de Nginx que establecimos en el paso anterior:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Este es el mismo comando que ejecutamos en el Paso 3, con la adición de los dos directorios de Let’s Encrypt que acabamos de crear.

      Una vez que Nginx esté en ejecución, diríjase a http://your_domain.com. Puede recibir una advertencia en su navegador indicando que la entidad de certificación no es válida. Esto es de esperar dado que suministramos certificados provisionales, no certificados de producción de Let’s Encrypt. Compruebe la barra de direcciones URL de su navegador para confirmar que su solicitud HTTP se haya redireccionado a HTTPS.

      Presione CTRL+C en su terminal para salir de Nginx y volver a ejecutar el cliente certbot, omitiendo el indicador --staging:

      docker run -it --rm -p 80:80 --name certbot 
               -v "/etc/letsencrypt:/etc/letsencrypt" 
               -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
               certbot/certbot certonly --standalone -d your_domain.com
      

      Cuando se le solicite mantener el certificado existente o renovarlo y sustituirlo, presione 2 para renovarlo y, luego, presione ENTER para confirmar su elección.

      Con el certificado TLS de producción suministrado, vuelva a ejecutar el servidor Nginx:

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      En su navegador, diríjase a http://your_domain.com. En la barra de direcciones URL, confirme que la solicitud HTTP se haya redireccionado a HTTPS. Dado que la aplicación Polls no tiene una ruta predeterminada configurada, debería ver un error de Django de *Página no encontrada *. Diríjase a https://your_domain.com/polls para ver la interfaz estándar de la aplicación Polls:

      Interfaz de la aplicación Polls

      En este punto, ha suministrado un certificado TLS de producción utilizando el cliente de Docker Certbot y está redirigiendo las solicitudes externas a través del proxy inverso y equilibrando la carga entre los dos servidores de aplicaciones de Django.

      Los certificados de Let’s Encrypt caducan cada 90 días. Para asegurarse de que su certificado permanezca válido, debe renovarlo regularmente antes de su vencimiento programado. Con Nginx en ejecución, debe usar el cliente de Certbot en modo webroot en vez de standalone. Esto significa que Certbot realizará la validación creando un archivo en el directorio /var/www/html/.well-known/acme-challenge/ y la regla location definida en la configuración de Nginx realizada en el Paso 3 capturará las solicitudes de validación de Let’s Encrypt a esta ruta. A continuación, Certbot rotará los certificados, y usted podrá volver a cargar Nginx para que utilice este certificado recién suministrado.

      Hay varias formas de automatizar este procedimiento, pero la renovación automática de certificados TLS está fuera del alcance de este tutorial. Para obtener un proceso similar usando la utilidad de programación cron, consulte el paso 6 de Cómo proteger una aplicación de Node.js en un contenedor con Nginx, Let’s Encrypt y Docker Compose.

      En su terminal, presione CTRL+C para cerrar el contenedor de Nginx. Vuelva a ejecutarlo en modo separado al agregar el indicador -d:

      docker run --rm --name nginx -d -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
        -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Con Nginx ejecutándose en segundo plano, utilice el siguiente comando para realizar una ejecución de prueba del procedimiento de renovación de certificados:

      docker run -it --rm --name certbot 
          -v "/etc/letsencrypt:/etc/letsencrypt" 
        -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
        -v "/var/www/html:/var/www/html" 
        certbot/certbot renew --webroot -w /var/www/html --dry-run
      

      Utilizamos el complemento --webroot, especificamos la ruta web root y utilizamos el indicador --dry-run para verificar que todo funciona correctamente sin realizar la renovación de certificados real.

      Si la simulación de la renovación se realiza correctamente, debería ver el siguiente resultado:

      Output

      Cert not due for renewal, but simulating renewal for dry run Plugins selected: Authenticator webroot, Installer None Renewing an existing certificate Performing the following challenges: http-01 challenge for your_domain.com Using the webroot path /var/www/html for all unmatched domains. Waiting for verification... Cleaning up challenges - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed without reload, fullchain is /etc/letsencrypt/live/your_domain.com/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/your_domain.com/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

      En un entorno de producción, después de renovar los certificados, debe volver a cargar Nginx para que los cambios surtan efecto. Para volver a cargar Nginx, ejecute el siguiente comando:

      docker kill -s HUP nginx
      

      Este comando enviará una señal HUP Unix al proceso de Nginx que se está ejecutando en el contenedor de Docker nginx. Al recibir esta señal, Nginx volverá a cargar su configuración y sus certificados renovados.

      Con HTTPS habilitado y todos los componentes de esta arquitectura en ejecución, el paso final es bloquear la configuración al evitar el acceso externo a los dos servidores de aplicaciones backend; todas las solicitudes HTTP deben pasar por el proxy de Nginx.

      Paso 5: Prevención de acceso externo a servidores de aplicaciones de Django

      En la arquitectura que se describe en este tutorial, la terminación de SSL se produce en el proxy de Nginx. Esto significa que Nginx descifra la conexión SSL y los paquetes se redirigen, a través del proxy, a los servidores de aplicaciones de Django no cifrados. Para muchos casos de uso, este nivel de seguridad es suficiente. Para las aplicaciones que incluyen datos financieros o de salud, es conveniente implementar cifrado de extremo a extremo. Puede hacerlo al reenviar paquetes cifrados a través del equilibrador de carga y descifrarlos en los servidores de aplicaciones o volver a cifrarlos en el proxy y descifrarlos nuevamente en los servidores de aplicaciones de Django. Estas técnicas están fuera del alcance de este artículo, pero puede consultar el documento Cifrado de extremo a extremo para obtener más información.

      El proxy de Nginx actúa como una puerta de enlace entre el tráfico externo y la red interna. En teoría, ningún cliente externo debería tener acceso directo a los servidores de aplicaciones internos y todas las solicitudes deberían pasar a través del servidor de Nginx. La nota del Paso 1 describe brevemente un problema abierto con Docker en el que Docker omite la configuración de firewall ufw de manera predeterminada y abre los puertos de forma externa, lo que puede ser peligroso. Para solucionar este problema de seguridad, se recomienda usar firewalls para la nube al trabajar con servidores con Docker. Para obtener más información sobre la creación de firewalls para la nube con DigitalOcean, consulte Cómo crear firewalls. También puede manipular iptables directamente en lugar de usar ufw. Para obtener más información sobre el uso de iptables con Docker, consulte Docker e iptables.

      En este paso, modificaremos la configuración de UFW para bloquear el acceso externo a los puertos del host que abre Docker. Al ejecutar Django en los servidores de aplicaciones, pasamos el indicador -p 80:8000 a docker, que reenvía el puerto 80 del host al puerto 8000 del contenedor. Esto también abrió el puerto 80 a clientes externos, lo que puede verificar al dirigirse a http://your_app_server_1_IP. Para evitar el acceso directo, modificaremos la configuración de UFW utilizando el método que se describe en el repositorio de GitHub ufw-docker.

      Comience por iniciar sesión en el primer servidor de aplicaciones de Django. A continuación, abra el archivo /etc/ufw/after.rules con privilegios de superusuario, utilizando nano o su editor favorito:

      sudo nano /etc/ufw/after.rules
      

      Cuando se le solicite, ingrese su contraseña y, luego, presione ENTER para confirmar.

      Debería ver las siguientes reglas ufw:

      /etc/ufw/after.rules

      #
      # rules.input-after
      #
      # Rules that should be run after the ufw command line added rules. Custom
      # rules should be added to one of these chains:
      #   ufw-after-input
      #   ufw-after-output
      #   ufw-after-forward
      #
      
      # Don't delete these required lines, otherwise there will be errors
      *filter
      :ufw-after-input - [0:0]
      :ufw-after-output - [0:0]
      :ufw-after-forward - [0:0]
      # End required lines
      
      # don't log noisy services by default
      -A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input
      -A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input
      -A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input
      
      # don't log noisy broadcast
      -A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input
      
      # don't delete the 'COMMIT' line or these rules won't be processed
      COMMIT
      

      Pegue el siguiente bloque de reglas de configuración de UFW en la parte inferior:

      /etc/ufw/after.rules

      . . .
      
      # BEGIN UFW AND DOCKER
      *filter
      :ufw-user-forward - [0:0]
      :DOCKER-USER - [0:0]
      -A DOCKER-USER -j RETURN -s 10.0.0.0/8
      -A DOCKER-USER -j RETURN -s 172.16.0.0/12
      -A DOCKER-USER -j RETURN -s 192.168.0.0/16
      
      -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
      
      -A DOCKER-USER -j ufw-user-forward
      
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
      
      -A DOCKER-USER -j RETURN
      COMMIT
      # END UFW AND DOCKER
      

      Estas reglas restringen el acceso público a los puertos que abre Docker y permiten el acceso a los intervalos de IP privadas 10.0.0.0/8, 172.16.0.0/12 y 192.168.0.0/16. Si usa VPC con DigitalOcean, entonces, las Droplets de su red VPC tendrán acceso al puerto abierto a través de la interfaz de red privada, pero los clientes externos no lo tendrán. Para obtener más información sobre las VPC, consulte la documentación oficial de las VPC. Para obtener más información sobre las reglas implementadas en este fragmento de código, consulte ¿Cómo funciona? en el archivo README de ufw-docker.

      Si no está utilizando VPC con DigitalOcean y ha introducido las direcciones IP públicas de los servidores de aplicaciones en el bloque upstream de su configuración de Nginx, deberá modificar explícitamente el firewall UFW para que permita tráfico del servidor de Nginx a través del puerto 80 de los servidores de aplicaciones de Django. Para obtener información sobre la creación de reglas allow con el firewall de UFW, consulte Aspectos básicos de UFW: reglas y comandos comunes de firewall.

      Una vez que haya finalizado la edición, guarde y cierre el archivo.

      Reinicie ufw para que tome la nueva configuración:

      sudo systemctl restart ufw
      

      Diríjase a http://APP_SERVER_1_IP en su navegador web para confirmar que ya no pueda acceder al servidor de aplicaciones a través del puerto 80.

      Repita este proceso en el segundo servidor de aplicaciones de Django.

      Cierre sesión en el primer servidor de aplicaciones o abra otra ventana de terminal, e inicie sesión en el segundo servidor de aplicaciones de Django. A continuación, abra el archivo /etc/ufw/after.rules con privilegios de superusuario, utilizando nano o su editor favorito:

      sudo nano /etc/ufw/after.rules
      

      Cuando se le solicite, ingrese su contraseña y, luego, presione ENTER para confirmar.

      Pegue el siguiente bloque de reglas de configuración de UFW en la parte inferior:

      /etc/ufw/after.rules

      . . .
      
      # BEGIN UFW AND DOCKER
      *filter
      :ufw-user-forward - [0:0]
      :DOCKER-USER - [0:0]
      -A DOCKER-USER -j RETURN -s 10.0.0.0/8
      -A DOCKER-USER -j RETURN -s 172.16.0.0/12
      -A DOCKER-USER -j RETURN -s 192.168.0.0/16
      
      -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
      
      -A DOCKER-USER -j ufw-user-forward
      
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
      
      -A DOCKER-USER -j RETURN
      COMMIT
      # END UFW AND DOCKER
      

      Una vez que haya finalizado la edición, guarde y cierre el archivo.

      Reinicie ufw para que tome la nueva configuración:

      sudo systemctl restart ufw
      

      Diríjase a http://APP_SERVER_2_IP en su navegador web para confirmar que ya no pueda acceder al servidor de aplicaciones a través del puerto 80.

      Por último, diríjase a https://your_domain_here/polls para confirmar que el proxy de Nginx siga teniendo acceso a los servidores de Django que preceden en la cadena. Debería ver la interfaz predeterminada de la aplicación de Polls.

      Conclusión

      En este tutorial, configuró una aplicación Polls de Django escalable utilizando contenedores de Docker. A medida que su tráfico y la carga en el sistema aumenten, puede escalar cada capa de forma separada: la capa de redireccionamiento mediante proxy de Nginx, la capa de aplicaciones de backend de Django y la capa de la base de datos de PostgreSQL.

      Crear un sistema distribuido suele implicar tener que tomar varias decisiones de diseño, y encontrará varias arquitecturas disponibles para satisfacer su caso de uso. La arquitectura que se describe en este tutorial se presenta como un plan flexible para diseñar aplicaciones escalables con Django y Docker.

      Probablemente desee controlar el comportamiento de sus contenedores cuando detecten errores o ejecutar contenedores de forma automática al iniciar su sistema. Para hacerlo, puede usar un administrador de procesos como Systemd o implementar directivas de reinicio. Para obtener más información al respecto, consulte Iniciar contenedores de forma automática en la documentación de Docker.

      Al trabajar a gran escala con varios hosts ejecutando la misma imagen de Docker, puede resultar más eficaz automatizar los pasos utilizando una herramienta de administración de configuración como Ansible o Chef. Para obtener más información sobre la administración de configuración, consulte Introducción a la administración de configuración y Configuración de la automatización con Ansible: Un kit del taller de DigitalOcean.

      En lugar de compilar la misma imagen en cada host, también puede simplificar la implementación utilizando un registro de imágenes como Docker Hub, que compila, almacena y distribuye imágenes de Docker en varios servidores. Además de un registro de imágenes, una canalización de integración e implementación continua puede ayudarlo a compilar, probar e implementar imágenes en sus servidores de aplicaciones. Para obtener más información sobre CI/CD, consulte Introducción a las prácticas recomendadas de CI/CD.



      Source link

      Comment dimensionner et sécuriser une application Django avec Docker, Nginx et Let’s Encrypt


      Introduction

      Dans les environnements basés sur le cloud, il existe de multiples façons de faire évoluer et de sécuriser une application Django. En dimensionnant horizontalement et en exécutant plusieurs copies de votre application, vous pouvez construire un système plus tolérant aux défauts et très disponible, tout en augmentant également son débit, afin que les demandes puissent être traitées simultanément. Une manière de faire évoluer à l’échelle horizontale une application Django consiste à fournir des serveurs d’application supplémentaires qui exécutent votre application Django et son serveur HTTP WSGI (comme Gunicorn ou uWSGI). Pour acheminer et distribuer les demandes entrantes sur cet ensemble de serveurs d’application, vous pouvez utiliser un équilibreur de charge et un proxy inverse comme Nginx. Nginx peut également mettre en cache le contenu statique et mettre fin aux connexions TLS (Transport Layer Security Security), utilisées pour fournir des connexions HTTPS, et sécuriser les connexions à votre application.

      L’exécution de votre application Django et du proxy Nginx à l’intérieur des conteneurs Docker garantit que ces composants se comportent de la même manière quel que soit l’environnement dans lequel ils sont déployés. De plus, les conteneurs fournissent de nombreuses fonctionnalités qui facilitent l’emballage et la configuration de votre application.

      Dans ce tutoriel, vous allez à mettre l’échelle horizontalement une application de sondages pour Django et Gunicorn conteneurisé en fournissant deux serveurs d’application qui exécuteront chacune une copie d’un conteneur d’application Django et Gunicorn.

      Vous allez également activer HTTPS, en fournissant et en configurant un troisième serveur proxy qui exécutera un conteneur proxy inverse Nginx et un conteneur client Certbot. Certbot fournira des certificats TLS pour Nginx à partir de l’autorité de certification Let’s Encrypt. Cela garantira que votre site bénéficie d’une cote de sécurité élevée de la part de SSL Labs. Ce serveur proxy recevra toutes les demandes externes de votre application et sera en face des deux serveurs d’application Django en amont. Enfin, vous allez renforcer ce système distribué en limitant l’accès externe au serveur proxy uniquement.

      Conditions préalables

      Pour suivre ce tutoriel, vous aurez besoin de :

      • Trois serveurs Ubuntu 18.04 :

        • Deux serveurs seront des serveurs d’application, utilisés pour exécuter vos applications Django et Gunicorn.
        • Un serveur sera un serveur proxy utilisé pour exécuter Nginx et Certbot.
        • Tous devraient avoir un non-root user avec des privilèges sudo et un pare-feu actif. Pour savoir comment les configurer, veuillez consulter le présent Guide de configuration initiale du serveur.
      • Docker installé sur les trois serveurs. Pour obtenir de l’aide sur l’installation de Docker, suivez les Étapes 1 et 2 de Comment installer et utiliser Docker sur Ubuntu 18.04.

      • Un nom de domaine enregistré. Ce tutoriel utilisera your_domain.comtout au long. Vous pouvez en obtenir un gratuitement sur Freenom ou utiliser le registre de domaine de votre choix.

      • Un enregistrement DNS A avec your_domain.com pointant sur l’adresse IP publique de votre serveur proxy. Vous pouvez suivre cette introduction au DNS DigitalOcean pour plus de détails sur la façon de l’ajouter à un compte DigitalOcean, si c’est ce que vous utilisez.

      • Un object storage bucket S3 comme un espace DigitalOceanpour stocker les fichiers statiques de votre projet Django et un ensemble de clés d’accès pour cet espace. Pour apprendre à créer un espace, consultez la documentation produit Comment créer des espaces. Pour apprendre à créer des clés d’accès pour les espaces, consultez Partager l’accès aux espaces avec les clés d’accès. Avec des modifications mineures, vous pouvez utiliser n’importe quel service de stockage d’objets que le plugin django-storages prend en charge.

      • Une instance serveur PostgreSQL, une base de données et un utilisateur pour votre application Django. Avec des modifications mineures, vous pouvez utiliser n’importe quelle base de données que Django prend en charge.

      Étape 1 — Configuration du premier serveur d’application Django

      Pour commencer, nous allons cloner le référentiel d’application Django sur le premier serveur d’application. Ensuite, nous allons configurer et construire l’image de l’application Docker, et tester l’application en exécutant le conteneur Django.

      Remarque : si vous continuez en suivant Comment construire une application Django et Gunicorn avec Docker, vous aurez déjà terminé Étape 1 et pouvez passer à l’étape 2 pour configurer le deuxième serveur d’application.

      Commencez par vous connecter au premier des deux serveurs d’application Django et utilisez git pour cloner la branche polls-docker du référentiel GitHub du tutoriel des applications Django Polls. Ce référentiel contient le code pour l’application de sondages dans la documentation de Django. La branche de polls-docker contient une version Dockerisée de l’application de sondage. Pour savoir comment l’application de sondage a été modifiée pour fonctionner efficacement dans un environnement conteneurisé, consultez Comment construire une application Django et Gunicorn avec Docker.

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Naviguez dans le répertoire django-polls :

      cd django-polls
      

      Ce répertoire contient le code Python de l’application Django, un Dockerfile que Docker utilisera pour construire l’image du conteneur, ainsi qu’un fichier env contenant une liste de variables d’environnement à passer dans l’environnement d’exécution du conteneur. Inspectez le Dockerfile à l’aide de cat :

      cat Dockerfile
      

      Output

      FROM python:3.7.4-alpine3.10 ADD django-polls/requirements.txt /app/requirements.txt RUN set -ex && apk add --no-cache --virtual .build-deps postgresql-dev build-base && python -m venv /env && /env/bin/pip install --upgrade pip && /env/bin/pip install --no-cache-dir -r /app/requirements.txt && runDeps="$(scanelf --needed --nobanner --recursive /env | awk '{ gsub(/,/, "nso:", $2); print "so:" $2 }' | sort -u | xargs -r apk info --installed | sort -u)" && apk add --virtual rundeps $runDeps && apk del .build-deps ADD django-polls /app WORKDIR /app ENV VIRTUAL_ENV /env ENV PATH /env/bin:$PATH EXPOSE 8000 CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi"]

      Ce Dockerfile utilise l’image Docker officielle de Python 3.7.4 comme base, et installe les exigences du paquet Python de Django et Gunicorn, telles que définies dans le fichier django-polls/requirements.txt. Il supprime ensuite quelques fichiers de construction inutiles, copie le code de l’application dans l’image, et définit le PATH d’exécution. Enfin, il déclare que le port 8000 sera utilisé pour accepter les connexions de conteneurs entrantes, et exécute gunicorn avec 3 travailleurs, en écoutant sur le port 8000.

      Pour en savoir plus sur chacune des étapes de ce Dockerfile, consultez l’Étape 6 de Comment construire une application Django et Gunicorn avec Docker.

      Maintenant, construisez l’image à l’aide de docker build :

      Nous nommons l’image polls en utilisant le drapeau -t et passons dans le répertoire courant comme contexte de construction, l’ensemble de fichiers à faire référence lors de la construction de l’image.

      Après que Docker ait construit et étiqueté l’image, listez les images disponibles à l’aide de docker images :

      docker images
      

      Vous devriez voir l’image polls listée :

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE polls latest 80ec4f33aae1 2 weeks ago 197MB python 3.7.4-alpine3.10 f309434dea3a 8 months ago 98.7MB

      Avant de lancer le conteneur Django, nous devons configurer son environnement d’exécution à l’aide du fichier env présent dans le répertoire actuel. Ce fichier sera transmis dans la commande docker run utilisée pour exécuter le conteneur, et Docker injectera les variables d’environnement configurées dans l’environnement d’exécution du conteneur.

      Ouvrez le fichier env avec nano ou votre éditeur préféré :

      nano env
      

      Nous allons configurer le fichier comme ceci, et vous devrez ajouter quelques valeurs supplémentaires comme indiqué ci-dessous.

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Remplissez les valeurs manquantes pour les clés suivantes :

      • DJANGO_SECRET_KEY : définissez cette valeur à une valeur unique et imprévisible, comme indiqué dans les docs de Django. Une méthode de génération de cette clé est fournie dans Ajustement des paramètres du tutoriel sur les applications Django dimensionnables.
      • DJANGO_ALLOWED_HOSTS: : cette variable sécurise l’application et prévient les attaques d’en-tête d’hôte HTTP. Pour les besoins de test, définissez cette variable à *, un joker qui correspondra à tous les hôtes. En production, vous devriez la définir sur your_domain.com. Pour en savoir plus sur ce paramètre Django, consultez les paramètres de base dans les docs Django.
      • DATABASE_USERNAME : définissez ce paramètre sur l’utilisateur de la base de données PostgreSQL créé dans les étapes préalables.
      • DATABASE_NAME : définissez ce paramètres sur polls ou le nom de la base de données PostgreSQL créée dans les étapes préalables.
      • DATABASE_PASSWORD : définissez ce paramètre sur le mot de passe de l’utilisateur PostgreSQL créé dans les étapes préalables.
      • DATABASE_HOST : définissez ce paramètre sur le nom d’hôte de votre base de données.
      • DATABASE_PORT : définissez ce paramètre sur le port de votre base de données.
      • STATIC_ACCESS_KEY_ID : définissez ce paramètre sur votre bucket S3 ou sur la clé d’accès à l’espace.
      • STATIC_ACCESS_KEY_ID : définissez ce paramètre sur votre bucket S3 ou sur la clé secrète d’accès à l’espace.
      • STATIC_BUCKET_NAME : définissez ce paramètre sur votre bucket S3 ou votre nom d’espace.
      • STATIC_ENDPOINT_URL : définissez ce paramètre sur le bucket S3 approprié ou l’URL de votre espace, par exemple https://space-name.nyc3.digitaloceanspaces.com si votre espace se trouve dans la région nyc3.

      Une fois que vous avez terminé de le modifier, enregistrez et fermez le fichier.

      Nous allons maintenant utiliser docker run pour remplacer le paramètre CMD dans le Dockerfile et créer le schéma de base de données à l’aide des commandes manage.py et manage.py migrate :

      docker run --env-file env polls sh -c "python manage.py makemigrations && python manage.py migrate"
      

      Nous lançons le container d’images polls:latest, nous passons dans le fichier variable d’environnement que nous venons de modifier, et remplacons la commande Dockerfile par sh -c "python manage.py makemigrations python manage.py image", qui créera le schéma de base de données défini par le code de l’application. Si vous exécutez cette opération pour la première fois, vous devriez voir ce qui suit :

      Output

      No changes detected Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying polls.0001_initial... OK Applying sessions.0001_initial... OK

      Cela indique que le schéma de base de données a été créé avec succès.

      Si vous exécutez migrate une, fois de plus, Django effectuera un no-op à moins que le schéma de base de données ait changé.

      Ensuite, nous allons exécuter une autre instance du conteneur de l’application et utiliser un shell interactif à l’intérieur de celui-ci pour créer un utilisateur administratif pour le projet Django.

      docker run -i -t --env-file env polls sh
      

      Vous obtiendrez une invite shell à l’intérieur du conteneur en cours d’exécution que vous pouvez utiliser pour créer l’utilisateur Django :

      python manage.py createsuperuser
      

      Entrez un nom d’utilisateur, une adresse email et un mot de passe pour votre utilisateur, et après avoir créé l’utilisateur, appuyez sur CTRL+D pour quitter le conteneur et le fermer.

      Enfin, nous allons générer les fichiers statiques pour l’application et les télécharger sur l’espace DigitalOcean à l’aide de collectstatic. Notez que cela peut prendre un peu de temps.

      docker run --env-file env polls sh -c "python manage.py collectstatic --noinput"
      

      Une fois que ces fichiers sont générés et téléchargés, vous obtiendrez la sortie suivante.

      Output

      121 static files copied.

      Nous pouvons maintenant exécuter l’application :

      docker run --env-file env -p 80:8000 polls
      

      Output

      [2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0 [2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) [2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync [2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7 [2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8 [2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

      Ici, nous exécutons la commande par défaut définie dans le Dockerfile, gunicorn --bind :8000 --workers 3 mysite.wsgi:application, et exposons le port de conteneur 8000 afin que le port 80 sur le serveur Ubuntu soit mappé sur le port 8000 du conteneur polls.

      Vous devriez maintenant pouvoir naviguez jusqu’à l’application de sondages à l’aide de votre navigateur web en tapant : http://APP_SERVER_1_IP dans la barre d’URL. Comme il n’y a pas de route définie pour le chemin d’accès / , vous obtiendrez probablement une erreur de recherche 404 Page Not Found, qui est prévisible.

      Attention : Lorsque vous utilisez le pare-feu UFW avec Docker, Docker contourne les règles de pare-feu UFW, comme indiqué dans ce numéro de GitHub. Cela explique pourquoi vous avez accès au port 80 de votre serveur, même si vous n’avez pas explicitement créé de règle d’accès UFW dans aucune étape préalable. Dans l’Étape 5, nous aborderons cette faille de sécurité en corrigeant la configuration d’UFW. Si vous n’utilisez pas UFW et que vous utilisez les pare-feu Cloud de DigitalOcean, vous pouvez ignorer cet avertissement.

      Naviguez sur http://APP_SERVER_1_IP/polls pour voir l’interface de l’application de sondage :

      Interface des applications de sondage

      Pour voir l’interface administrative, allez à http://APP_SERVER_1_IP/admin. Vous devriez voir la fenêtre d’authentification de l’application de sondage :

      Page Auth admin des sondages

      Entrez le nom d’utilisateur administratif et le mot de passe que vous avez créé avec la commande createsuperuser.

      Après avoir été authentifié, vous pouvez accéder à l’interface administrative de l’application de sondage :

      Interface principale de l'administration de sondages

      Notez que les actifs statiques pour les applications d’administration et de sondage sont livrées directement depuis le stockage d’objets. Pour confirmer ceci, consultez Testing Spaces Static File Delivery.

      Lorsque vous avez terminé d’explorer, appuyez sur CTRL+C dans la fenêtre de terminal en exécutant le conteneur de Docker pour terminer le conteneur.

      Maintenant que vous avez confirmé que le conteneur d’application fonctionne comme prévu, vous pouvez l’exécuter en mode détaché, qui l’exécutera en arrière-plan et vous permettra de vous déconnecter de votre session SSH :

      docker run -d --rm --name polls --env-file env -p 80:8000 polls
      

      Le drapeau -d demande à Docker d’exécuter le conteneur en mode détaché, le drapeau -rm nettoie le système de fichiers du conteneur après la fermeture du conteneur, et nous donnons un nom aux sondages de conteneur.

      Déconnectez-vous du premier serveur d’application Django, et naviguez vers http://APP_SERVER_1_IP/polls pour confirmer que le conteneur fonctionne comme prévu.

      Maintenant que votre premier serveur d’application Django est opérationnel, vous pouvez configurer votre deuxième serveur d’application Django.

      Étape 2 — Configuration du deuxième serveur d’application Django

      Comme beaucoup de commandes pour configurer ce serveur seront les mêmes que celles de l’étape précédente, elles seront présentées ici sous forme abrégée. Veuillez consulter l’Étape 1 pour plus d’informations sur une commande particulière de cette étape.

      Commencez par vous connecter au deuxième serveur d’application Django.

      Clonez la branche polls-docker du référentiel GitHub django-polls :

      • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

      Naviguez dans le répertoire django-polls :

      cd django-polls
      

      Maintenant, construisez l’image à l’aide de docker build :

      Ouvrez le fichier env avec nano ou votre éditeur préféré :

      nano env
      

      django-polls/env

      DJANGO_SECRET_KEY=
      DEBUG=True
      DJANGO_ALLOWED_HOSTS=
      DATABASE_ENGINE=postgresql_psycopg2
      DATABASE_NAME=polls
      DATABASE_USERNAME=
      DATABASE_PASSWORD=
      DATABASE_HOST=
      DATABASE_PORT=
      STATIC_ACCESS_KEY_ID=
      STATIC_SECRET_KEY=
      STATIC_BUCKET_NAME=
      STATIC_ENDPOINT_URL=
      DJANGO_LOGLEVEL=info
      

      Remplissez les valeurs manquantes comme à l’Étape 1. Lorsque vous avez terminé les modifications, enregistrez et fermez le fichier.

      Enfin, exécutez le conteneur d’application en mode détaché :

      docker run -d --rm --name polls --env-file env -p 80:8000 polls
      

      Naviguez jusqu’à http://APP_SERVER_2_IP/polls pour confirmer que le conteneur fonctionne comme prévu. Vous pouvez vous déconnecter en toute sécurité du deuxième serveur d’application sans mettre fin à votre conteneur en cours d’exécution.

      Une fois les conteneurs d’application Django opérationnels, vous pouvez passer à la configuration du conteneur proxy inverse Nginx.

      Étape 3 — Configuration du conteneur Docker Nginx

      Nginx est un serveur web polyvalent qui offre un certain nombre de fonctionnalités, dont le proxy inverse, l’équilibrage de la charge et la mise en cache. Dans ce tutoriel, nous avons déchargé les ressources statiques de Django pour le stockage d’objets, nous n’utiliserons donc pas les capacités de mise en cache de Nginx. Cependant, nous utiliserons Nginx comme proxy inverse sur nos deux serveurs d’application Django de backend et distribuerons les demandes entrantes entre eux. En outre, Nginx effectuera la terminaison et la redirection TLS à l’aide d’un certificat TLS fourni par Certbot. Cela signifie qu’il forcera les clients à utiliser HTTPS, redirigeant les requêtes HTTPS vers le port 443. Il déchiffrera ensuite les requêtes HTTPS, puis les enverra par proxy aux serveurs Django en amont.

      Dans ce tutoriel, nous avons pris la décision de découpler les conteneurs Nginx des serveurs de backend. En fonction de votre cas d’utilisation, vous pouvez choisir d’exécuter le conteneur Nginx sur l’un des serveurs d’application Django, en envoyant les requêtes par proxy localement et ainsi que vers l’autre serveur Django. Une autre architecture possible serait d’exécuter deux conteneurs Nginx, un sur chaque serveur de backend avec un load balancer en cloud en avant. Chaque architecture présente différents avantages de sécurité et de performance, et vous devriez tester la charge de votre système pour découvrir les goulets d’étranglement. L’architecture flexible décrite dans ce tutoriel vous permet d’évaluer à la fois la couche d’application Django de backend et la couche proxy Nginx. Une fois que le conteneur Nginx unique devient un goulot d’étranglement, vous pouvez échelonner sur plusieurs proxies Nginx, et ajouter un load balancer en cloud ou un load balancer L4 rapide comme HAProxy.

      Une fois les deux serveurs d’applications Django en place, nous pouvons commencer à configurer le serveur proxy Nginx. Connectez-vous à votre serveur proxy et créez un répertoire appelé conf :

      mkdir conf
      

      Créez un fichier de configuration appelé nginx.conf à l’aide de nano ou de votre éditeur préféré :

      nano conf/nginx.conf
      

      Collez dans la configuration Nginx suivante :

      conf/nginx.conf

      
      upstream django {
          server APP_SERVER_1_IP;
          server APP_SERVER_2_IP;
      }
      
      server {
          listen 80 default_server;
          return 444;
      }
      
      server {
          listen 80;
          listen [::]:80;
          server_name your_domain.com;
          return 301 https://$server_name$request_uri;
      }
      
      server {
          listen 443 ssl http2;
          listen [::]:443 ssl http2;
          server_name your_domain.com;
      
          # SSL
          ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
          ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
      
          ssl_session_cache shared:le_nginx_SSL:10m;
          ssl_session_timeout 1440m;
          ssl_session_tickets off;
      
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_prefer_server_ciphers off;
      
          ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
      
          client_max_body_size 4G;
          keepalive_timeout 5;
      
              location / {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_pass http://django;
              }
      
          location ^~ /.well-known/acme-challenge/ {
              root /var/www/html;
          }
      
      }
      

      Ces blocs upstream, server et location configurent Nginx pour rediriger les requêtes HTTP vers HTTPS, et et équilibrent la charge entre les deux serveurs d’applications Django configurés aux Étapes 1 et 2. Pour en savoir plus sur la structure du fichier de configuration Nginx, consultez cet article sur la compréhension de la structure du fichier de configuration Nginx et les contextes de configuration. En outre, cet article sur La compréhension des algorithmes de sélection des serveurs Nginx et des blocs de localisation peut être utile.

      Cette configuration a été assemblée à partir d’exemplesde fichiers de configuration fournis par Gunicorn, Cerbot et Nginx et est conçue comme une configuration Nginx minimale pour que cette architecture soit opérationnelle. L’adaptation de cette configuration Nginx dépasse la portée de cet article, mais vous pouvez utiliser un outil comme NGINXConfig pour générer des fichiers de configuration Nginx performants et sécurisés pour votre architecture.

      Le bloc upstream définit le groupe de serveurs utilisés pour les demandes de proxy à l’aide de la directive proxy_pass :

      conf/nginx.conf

      upstream django {
          server APP_SERVER_1_IP;
          server APP_SERVER_2_IP;
      }
      . . .
      

      Dans ce bloc, nous nommons le upstream django et incluons les adresses IP des deux serveurs d’application Django. Si les serveurs d’application fonctionnent sur DigitalOcean et que le réseau VPC est activé, vous devriez utiliser leurs adresses IP privées ici. Pour apprendre à activer le VPC Networking sur DigitalOcean, consultez Comment activer le VPC Networking sur les droplets existants.

      Le premier bloc server capture les demandes qui ne correspondent pas à votre domaine et met fin à la connexion. Par exemple, une requête HTTP directe vers l’adresse IP de votre serveur serait traitée par ce bloc :

      conf/nginx.conf

      . . .
      server {
          listen 80 default_server;
          return 444;
      }
      . . .
      

      Le bloc server suivant redirige les requêtes HTTP, vers votre domaine, à l’aide d’un redirect HTTP 301 redirect. Ces demandes sont ensuite traitées par le bloc server final :

      conf/nginx.conf

      . . .
      server {
          listen 80;
          listen [::]:80;
          server_name your_domain.com;
          return 301 https://$server_name$request_uri;
      }
      . . .
      

      Ces deux directives définissent les chemins d’accès au certificat TLS et à la clé secrète. Ceux-ci seront fournis à l’aide de Certbot et montés dans le conteneur Nginx à l’étape suivante.

      conf/nginx.conf

      . . .
      ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
      . . .
      

      Ces paramètres sont les valeurs par défaut de la sécurité SSL recommandées par Certbot. Pour en savoir plus sur ces questions, consultez le Module ngx_http_ssl_module dans les docs Nginx. Security/Server Side TLS de Mozilla est un autre guide utile que vous pouvez utiliser pour ajuster la configuration SSL.

      conf/nginx.conf

      . . .
          ssl_session_cache shared:le_nginx_SSL:10m;
          ssl_session_timeout 1440m;
          ssl_session_tickets off;
      
          ssl_protocols TLSv1.2 TLSv1.3;
          ssl_prefer_server_ciphers off;
      
          ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
      . . .
      

      Ces deux directives d’exemple de configuration Nginx de Gunicorn définissent la taille maximale autorisée du corps de requête client et attribuent le timeout pour les connexions keep-alive avec le client. Nginx fermera les connexions avec le client après keepalive_timeout secondes.

      conf/nginx.conf

      . . .
      client_max_body_size 4G;
      keepalive_timeout 5;
      . . .
      

      Le premier bloc location permet à Nginx d’accéder aux demandes de proxy vers les serveurs upstream django sur HTTP. Il préserve également les en-têtes HTTP du client qui capturent l’adresse IP d’origine, le protocole utilisé pour la connexion et l’hôte cible :

      conf/nginx.conf

      . . .
      location / {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header Host $http_host;
          proxy_redirect off;
          proxy_pass http://django;
      }
      . . .
      

      Pour en savoir plus sur ces directives, consultez Deploying Gunicorn et Module ngx_http_proxy_module à partir des docs Deploying

      Le bloc location final capture les demandes vers le chemin /well-known/acme-challenge/ utilisé par Certbot pour les défis HTTP-01 pour vérifier votre domaine avec Let’s Encrypt et fournir ou renouveler les certificats TLS. Pour plus d’informations sur le défi HTTP-01 utilisé par Certbot, consultez les Types de défi dans les docs Let’s Encrypt.

      conf/nginx.conf

      . . .
      location ^~ /.well-known/acme-challenge/ {
              root /var/www/html;
      }
      

      Une fois que vous avez terminé de le modifier, enregistrez et fermez le fichier.

      Vous pouvez maintenant utiliser ce fichier de configuration pour exécuter un conteneur Docker Nginx. Dans ce tutoriel, nous utiliserons l’image nginx:1.19.0, version 1.19.0 de l’image Docker officielle maintenue par Nginx.

      Lorsque nous exécutons le conteneur pour la première fois, Nginx lancera une erreur et échouera car nous n’avons pas encore fourni les certificats définis dans le fichier de configuration. Cependant, nous allons toujours exécuter la commande pour télécharger l’image Nginx localement et tester que tout le reste fonctionne correctement :

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Ici, nous nommons le conteneur nginx et cartographions les ports hôtes 80 et 443 vers les ports conteneur respectifs. Le drapeau -v monte le fichier de configuration dans le conteneur Nginx à /etc/nginx/conf.d/nginx.conf, que l’image Nginx est préconfigurée pour charger. Il est monté en mode ro ou “read only”, de sorte que le conteneur ne peut pas modifier le fichier. Le répertoire racine web /var/www/html est également monté dans le conteneur. Enfin, nginx:1.19.0 demande à Docker de tirer et d’exécuter l’image nginx:1.19.0 depuis Dockerhub.

      Docker tirera et exécutera l’image, puis Nginx lancera une erreur lorsqu’il ne trouvera pas le certificat TLS configuré et la clé secrète. Dans la prochaine étape, nous allons les fournir à l’aide d’un client Certbot Dockerizé et de l’autorité de certification Let’s Encrypt.

      Étape 4 — Configuration de Cerbot et renouvellement du certificat Let’s Encrypt

      Certbot est un client Let’s Encrypt développé par la Electronic Frontier Foundation. Il fournit des certificats TLS gratuits depuis l’autorité de certification Let’s Encrypt qui permet aux navigateurs de vérifier l’identité de vos serveurs web. Comme Docker a été installé sur notre serveur proxy Nginx, nous utiliserons l’image Certbot Docker pour fournir et renouveler les certificats TLS.

      Commencez par vous assurer que vous disposez d’un enregistrement DNS A mappé à l’adresse IP publique du serveur proxy. Ensuite, sur votre serveur proxy, proposez une version de mise en scène des certificats à l’aide de l’image certbot Docker :

      docker run -it --rm -p 80:80 --name certbot 
               -v "/etc/letsencrypt:/etc/letsencrypt" 
               -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
               certbot/certbot certonly --standalone --staging -d your_domain.com
      

      Cette commande exécute l’image certbot Docker en mode interactif, et achemine le port 80 de l’hôte au port 80 du conteneur. Il crée et monte deux répertoires hôtes dans le conteneur : /etc/letsencrypt/ et /var/lib/letsencrypt/ certbot est exécuté en mode stand alone, sans Nginx, et utilisera les serveurs staging de Let’s Encrypt pour effectuer la validation du domaine.

      Lorsque vous y êtes invité, entrez votre adresse email et acceptez les conditions de service. Si la validation du domaine a réussi, vous devriez voir la sortie suivante :

      Output

      Obtaining a new certificate Performing the following challenges: http-01 challenge for stubb.dev Waiting for verification... Cleaning up challenges IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain.com/privkey.pem Your cert will expire on 2020-09-15. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal.

      Vous pouvez inspecter le certificat à l’aide de cat :

      sudo cat /etc/letsencrypt/live/your_domain.com/fullchain.pem
      

      Une fois le certificat TLS fourni, nous pouvons tester la configuration Nginx assemblée à l’étape précédente :

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      C’est la même commande exécutée à l’Étape 3, avec l’ajout des deux répertoires Let’s Encrypt récemment créés.

      Une fois que Nginx est opérationnel, naviguez jusqu’à http://your_domain.com. Vous pouvez recevoir un avertissement dans votre navigateur signalant que l’autorité de certification est invalide. C’est normal car nous avons fourni des certificats de mise en scène et non pas des certificats Let’s Encrypt. Vérifiez la barre d’URL de votre navigateur pour confirmer que votre requête HTTP a été redirigée vers HTTPS.

      Entrez CTRL+C dans votre terminal pour quitter Nginx et exécutez à nouveau le client certbot, cette fois en omettant le drapeau --staging flag :

      docker run -it --rm -p 80:80 --name certbot 
               -v "/etc/letsencrypt:/etc/letsencrypt" 
               -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
               certbot/certbot certonly --standalone -d your_domain.com
      

      Lorsque vous êtes invité à conserver le certificat existant ou à le renouveler et à le remplacer, tapez 2 pour le renouveler et ensuite ENTER (ENTRÉE) pour confirmer votre choix.

      Une fois le certificat de production TLS fourni, exécutez à nouveau le serveur Nginx :

      docker run --rm --name nginx -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
          -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Dans votre navigateur, naviguez vers http://your_domain.com. Dans la barre d’URL, vérifiez que la requête HTTP a été redirigée vers HTTPS. Comme l’application de sondage n’a pas de route configurée par défaut, vous devriez voir une erreur Django Page not found. Naviguez vers https://your_domain.com/polls et vous verrez l’interface standard de l’application de sondage :

      Interface des applications de sondage

      Vous avez maintenant fourni un certificat TLS de production à l’aide du client Certbot Docker, et vous effectuez un proxying inverse et un équilibrage de charge des requêtes externes vers les deux serveurs d’application Django.

      Les certificats Let’s Encrypt expirent tous les 90 jours. Pour vous assurer que votre certificat reste valide, vous devriez le renouveler régulièrement avant son expiration prévue. Avec Nginx en cours d’exécution, vous devriez utiliser le client Certbot en mode webroot au lieu d’un mode standalone. Cela signifie que Certbot effectuera la validation en créant un fichier dans le répertoire /var/www/html/.well-known/acme-challenge/ et les demandes de validation Let’s Encrypt vers ce chemin seront captées par la règle location définie dans la configuration Nginx à l’Étape 3. Certbot effectuera ensuite la rotation des certificats, et vous pouvez recharger Nginx afin qu’il utilise ce certificat qui vient d’être fourni.

      Il existe de multiples façons d’automatiser cette procédure et le renouvellement automatique des certificats TLS va au-delà de la portée de ce tutoriel. Pour effectuer un processus similaire à l’aide de l’utilitaire de planification cron, consultez l’Étape 6 de Comment sécuriser une application Node.js avec Nginx, Let’s Encrypt et Docker Compose.

      Dans votre terminal, entrez sur CTRL+C pour terminer le conteneur Nginx. Exécutez-le à nouveau en mode détaché en apposant le drapeau -d :

      docker run --rm --name nginx -d -p 80:80 -p 443:443 
          -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro 
          -v /etc/letsencrypt:/etc/letsencrypt 
          -v /var/lib/letsencrypt:/var/lib/letsencrypt 
        -v /var/www/html:/var/www/html 
          nginx:1.19.0
      

      Avec Nginx en cours d’exécution en arrière-plan, utilisez la commande suivante pour effectuer un essai de la procédure de renouvellement du certificat :

      docker run -it --rm --name certbot 
          -v "/etc/letsencrypt:/etc/letsencrypt" 
        -v "/var/lib/letsencrypt:/var/lib/letsencrypt" 
        -v "/var/www/html:/var/www/html" 
        certbot/certbot renew --webroot -w /var/www/html --dry-run
      

      Nous utilisons le plugin --webroot, spécifions le chemin racine web, et utilisons le drapeau --dry-run pour vérifier que tout fonctionne correctement sans réellement effectuer le renouvellement du certificat.

      Si la simulation de renouvellement réussit, vous devriez voir la sortie suivante :

      Output

      Cert not due for renewal, but simulating renewal for dry run Plugins selected: Authenticator webroot, Installer None Renewing an existing certificate Performing the following challenges: http-01 challenge for your_domain.com Using the webroot path /var/www/html for all unmatched domains. Waiting for verification... Cleaning up challenges - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed without reload, fullchain is /etc/letsencrypt/live/your_domain.com/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/your_domain.com/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

      Dans un environnement de production, après avoir renouvelé les certificats, vous devez recharger Nginx pour que les modifications prennent effet. Pour recharger Nginx, exécutez la commande suivante :

      docker kill -s HUP nginx
      

      Cette commande enverra un signal Unix HUP au processus Nginx en cours d’exécution dans le conteneur Docker nginx. À la réception de ce signal, Nginx rechargera sa configuration et les certificats renouvelés.

      Une fois HTTPS activé, et tous les composants de cette architecture opérationnels, la dernière étape consiste à verrouiller la configuration en empêchant l’accès externe aux deux serveurs d’application backend ; toutes les requêtes HTTP devraient passer par le proxy Nginx.

      Étape 5 — Prévention de l’accès externe aux serveurs d’application Django

      Dans l’architecture décrite dans ce tutoriel, la terminaison SSL se produit au niveau du proxy Nginx. Cela signifie que Nginx décrypte la connexion SSL, et que les paquets sont acheminés vers les serveurs d’application Django non cryptés. Pour de nombreux cas d’utilisation, ce niveau de sécurité est suffisant. Pour les applications impliquant des données financières ou sanitaires, vous souhaiterez peut-être mettre en place le cryptage de bout en bout. Vous pouvez le faire en envoyant des paquets cryptés via le répartiteur de charge et en les décryptant sur les serveurs d’application, ou en les cryptant à niveau du proxy et en décryptant une fois de plus sur les serveurs d’application Django. Ces techniques vont au-delà de la portée de cet article, mais pour en savoir plus veuillez consulter le Cryptage de bout en bout.

      Le proxy Nginx agit comme une passerelle entre le trafic externe et le réseau interne. En théorie, aucun client externe ne doit avoir un accès direct aux serveurs d’application internes, et toutes les demandes doivent passer par le serveur Nginx. La remarque de l’Étape 1 décrit brièvement un problème ouvert avec Docker où Docker contourne les paramètres du pare-feu ufw par défaut et ouvre les ports à l’extérieur, ce qui peut se révéler dangereux. Pour répondre à cette préoccupation de sécurité, il est recommandé d’utiliser les pare-feu en cloud lors de l’utilisation de serveurs Docker. Pour obtenir plus d’informations sur la création de pare-feu en cloud avec DigitalOcean, consultez Comment créer des pare-feu. Vous pouvez également manipuler directement les iptables au lieu d’utiliser ufw. Pour en savoir plus sur l’utilisation d’iptables avec Docker, consultez Docker et iptables.

      Au cours de cette étape, nous allons modifier la configuration d’UFW pour bloquer l’accès externe aux ports hôtes ouverts par Docker. En exécutant Django sur les serveurs d’application, nous avons passé le drapeau -p 80:8000 à docker, qui achemine le port 80 de l’hôte au port de conteneur 8000. Cela a également ouvert le port 80 à des clients externes, ce que vous pouvez vérifier en visitant http://your_app_server_1_IP. Pour éviter un accès direct, nous allons modifier la configuration d’UFW à l’aide de la méthode décrite dans le référentiel GitHub ufw-docker.

      Commencez par vous connecter au premier serveur d’application Django. Ensuite, ouvrez le fichier /etc/ufw/after.rules avec des privilèges super-utilisateur, à l’aide de nano ou de votre éditeur préféré :

      sudo nano /etc/ufw/after.rules
      

      Entrez votre mot de passe lorsque vous y serez invité, et appuyez sur ENTER pour confirmer.

      Vous devriez voir les règles ufw suivantes :

      /etc/ufw/after.rules

      #
      # rules.input-after
      #
      # Rules that should be run after the ufw command line added rules. Custom
      # rules should be added to one of these chains:
      #   ufw-after-input
      #   ufw-after-output
      #   ufw-after-forward
      #
      
      # Don't delete these required lines, otherwise there will be errors
      *filter
      :ufw-after-input - [0:0]
      :ufw-after-output - [0:0]
      :ufw-after-forward - [0:0]
      # End required lines
      
      # don't log noisy services by default
      -A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input
      -A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input
      -A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input
      -A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input
      
      # don't log noisy broadcast
      -A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input
      
      # don't delete the 'COMMIT' line or these rules won't be processed
      COMMIT
      

      Faites défiler vers le bas, et collez dans le bloc suivant des règles de configuration UFW :

      /etc/ufw/after.rules

      . . .
      
      # BEGIN UFW AND DOCKER
      *filter
      :ufw-user-forward - [0:0]
      :DOCKER-USER - [0:0]
      -A DOCKER-USER -j RETURN -s 10.0.0.0/8
      -A DOCKER-USER -j RETURN -s 172.16.0.0/12
      -A DOCKER-USER -j RETURN -s 192.168.0.0/16
      
      -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
      
      -A DOCKER-USER -j ufw-user-forward
      
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
      
      -A DOCKER-USER -j RETURN
      COMMIT
      # END UFW AND DOCKER
      

      Ces règles limitent l’accès du public aux ports ouverts par Docker, et permettent un accès à partir des plages IP privées 10.0.0/8, 172.16.0.0/12 et 192.168.0.0/16. Si vous utilisez VPC avec DigitalOcean, alors les Droplets de votre réseau VPC aura accès au port ouvert sur l’interface réseau privée, mais les clients externes ne l’auront pas. Pour plus d’informations sur VPC, consultez la documentation officielle de VPC. Pour en savoir plus sur les règles appliquées dans ce snippet, consultez Comment ça marche ? du fichier README ufw-docker.

      Si vous n’utilisez pas VPC avec DigitalOcean et que vous avez entré les adresses IP publiques des serveurs d’application dans le bloc upstream de votre configuration Nginx, vous devrez modifier explicitement le pare-feu UFW pour autoriser le trafic depuis le serveur Nginx par le port 80 sur les serveurs d’application Django. Pour obtenir des conseils sur la création de règles allow avec le pare-feu UFW, consultez les Essentiels  d’UFW: Règles et commandes communes du pare-feu.

      Quand vous avez terminé de le modifier, enregistrez et fermez le fichier.

      Redémarrez ufw afin qu’il adopte la nouvelle configuration :

      sudo systemctl restart ufw
      

      Naviguez vers http://APP_SERVER_1_IP dans votre navigateur web pour vérifier que vous ne pouvez plus accéder au serveur d’application sur le port 80.

      Répétez ce processus sur le deuxième serveur d’application Django.

      Déconnectez-vous du premier serveur d’application ou ouvrez une autre fenêtre de terminal, et connectez-vous au deuxième serveur d’application Django. Ensuite, ouvrez le fichier /etc/ufw/after.rules avec des privilèges super-utilisateur, à l’aide de nano ou de votre éditeur préféré :

      sudo nano /etc/ufw/after.rules
      

      Entrez votre mot de passe lorsque vous y serez invité, et appuyez sur ENTER pour confirmer.

      Faites défiler vers le bas, et collez dans le bloc suivant des règles de configuration UFW :

      /etc/ufw/after.rules

      . . .
      
      # BEGIN UFW AND DOCKER
      *filter
      :ufw-user-forward - [0:0]
      :DOCKER-USER - [0:0]
      -A DOCKER-USER -j RETURN -s 10.0.0.0/8
      -A DOCKER-USER -j RETURN -s 172.16.0.0/12
      -A DOCKER-USER -j RETURN -s 192.168.0.0/16
      
      -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
      
      -A DOCKER-USER -j ufw-user-forward
      
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
      -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
      
      -A DOCKER-USER -j RETURN
      COMMIT
      # END UFW AND DOCKER
      

      Quand vous avez terminé de le modifier, enregistrez et fermez le fichier.

      Redémarrez ufw afin qu’il adopte la nouvelle configuration :

      sudo systemctl restart ufw
      

      Naviguez vers http://APP_SERVER_2_IP dans votre navigateur web pour vérifier que vous ne pouvez plus accéder au serveur d’application sur le port 80.

      Enfin, naviguez jusqu’à https://your_domain_here/polls pour confirmer que le proxy Nginx a toujours accès aux serveurs Django en amont. Vous devriez voir l’interface de ‘application de sondage.

      Conclusion

      Dans ce tutoriel, vous avez mis en place une application de sondage Django évolutive à l’aide de conteneurs Docker. Au fur et à mesure que votre trafic et la charge sur le système grandissent, vous pouvez faire évoluer chaque couche séparément : la couche proxy Nginx, la couche d’application backend Django et la couche de base de données PostgreSQL.

      Lorsque vous construisez un système distribué, vous devez souvent faire face à de multiples décisions de conception, et plusieurs architectures peuvent satisfaire votre cas d’utilisation. L’architecture décrite dans ce tutoriel est conçue comme un modèle flexible pour la conception d’applications évolutives avec Django et Docker.

      Vous pouvez vouloir contrôler le comportement de vos conteneurs lorsqu’ils rencontrent des erreurs, ou exécuter automatiquement les conteneurs lorsque votre système démarre. Pour ce faire, vous pouvez utiliser un gestionnaire de processus comme Systemd ou mettre en œuvre des politiques de redémarrage. Pour plus d’informations à ce propos, consultez la section Démarrer les conteneurs automatiquement dans la documentation Docker.

      Lorsque vous travaillez à l’échelle avec plusieurs hôtes exécutant la même image Docker, il peut être plus efficace d’automatiser les étapes à l’aide d’un outil de gestion de configuration comme Ansible ou Chef. Pour en savoir plus sur la gestion de la configuration, consultez Une Introduction à la gestion de la configuration et Configuration de serveur automatisée avec Ansible : un kit d’atelier DigitalOcean.

      Au lieu de construire la même image sur chaque hôte, vous pouvez également rationaliser le déploiement à l’aide d’un registre d’images comme Docker Hub, qui construit, stocke et distribue des images Docker à plusieurs serveurs de manière centralisée. En plus d’un registre d’images, un pipeline d’intégration et de déploiement continu peut vous aider à construire, tester et déployer des images sur vos serveurs d’application. Pour plus d’informations sur les CI/CD, veuillez consulter Une Introduction aux meilleures pratiques de CI/CD.



      Source link