So sichern Sie eine containerisierte Node.js-Anwendung mit Nginx, Let’s Encrypt und Docker Compose

Einführung

Es gibt mehrere Möglichkeiten, die Flexibilität und Sicherheit IhrerNode.js-Anwendung zu verbessern. Die Verwendung vonreverse proxy wieNginx bietet Ihnen die Möglichkeit, Lastausgleichsanforderungen zu erstellen, statischen Inhalt zwischenzuspeichern undTransport Layer Security (TLS) zu implementieren. Durch Aktivieren von verschlüsseltem HTTPS auf Ihrem Server wird sichergestellt, dass die Kommunikation zu und von Ihrer Anwendung sicher bleibt.

Das Implementieren eines Reverse-Proxys mit TLS / SSL in Containern erfordert andere Verfahren als das direkte Arbeiten auf einem Host-Betriebssystem. Wenn Sie beispielsweise Zertifikate vonLet’s Encrypt für eine Anwendung erhalten, die auf einem Server ausgeführt wird, installieren Sie die erforderliche Software direkt auf Ihrem Host. Mit Containern können Sie einen anderen Ansatz wählen. MitDocker Compose können Sie Container für Ihre Anwendung, Ihren Webserver und dieCertbot client erstellen, mit denen Sie Ihre Zertifikate erhalten können. Wenn Sie diese Schritte ausführen, können Sie die Modularität und Portabilität eines containerisierten Workflows nutzen.

In diesem Lernprogramm stellen Sie eine Node.js-Anwendung mit einem Nginx-Reverseproxy mithilfe von Docker Compose bereit. Sie erhalten TLS / SSL-Zertifikate für die Ihrer Anwendung zugeordnete Domäne und stellen sicher, dass sie eine hohe Sicherheitsbewertung vonSSL Labs erhält. Schließlich richten Sie einencron-Job ein, um Ihre Zertifikate zu erneuern, damit Ihre Domain sicher bleibt.

Voraussetzungen

Um diesem Tutorial zu folgen, benötigen Sie:

  • Ein Ubuntu 18.04-Server, ein Nicht-Root-Benutzer mitsudo-Berechtigungen und eine aktive Firewall. Anleitungen zum Einrichten finden Sie inInitial Server Setup guide.

  • Docker und Docker Compose sind auf Ihrem Server installiert. Befolgen Sie die Schritte 1 und 2 vonHow To Install and Use Docker on Ubuntu 18.04, um Anleitungen zur Installation von Docker zu erhalten. Befolgen Sie die Anweisungen zur Installation von Compose in Schritt 1 vonHow To Install Docker Compose on Ubuntu 18.04.

  • Ein registrierter Domainname. In diesem Tutorial werden durchgehendexample.com verwendet. Sie können eine kostenlos beiFreenom erhalten oder den Domain-Registrar Ihrer Wahl verwenden.

  • Die beiden folgenden DNS-Einträge wurden für Ihren Server eingerichtet. Sie könnenthis introduction to DigitalOcean DNS folgen, um Details zum Hinzufügen zu einem DigitalOcean-Konto zu erhalten, wenn Sie dies verwenden:

    • Ein A-Datensatz mitexample.com, der auf die öffentliche IP-Adresse Ihres Servers verweist.

    • Ein A-Datensatz mitwww.example.com, der auf die öffentliche IP-Adresse Ihres Servers verweist.

[[Schritt 1 - Klonen und Testen der Knotenanwendung]] == Schritt 1 - Klonen und Testen der Knotenanwendung

Als ersten Schritt klonen wir das Repository mit dem Node-Anwendungscode, der die Docker-Datei enthält, mit der wir unser Anwendungsimage mit Compose erstellen. Wir können die Anwendung zuerst testen, indem wir sie mitdocker run command ohne Reverse Proxy oder SSL erstellen und ausführen.

Klonen Sie im Home-Verzeichnis Ihres Nicht-Root-Benutzers dienodejs-image-demo repository aus denDigitalOcean Community GitHub account. Dieses Repository enthält den Code aus dem inHow To Build a Node.js Application with Docker beschriebenen Setup.

Klonen Sie das Repository in ein Verzeichnis namensnode_project:

git clone https://github.com/do-community/nodejs-image-demo.git node_project

Wechseln Sie in das Verzeichnisnode_project:

cd  node_project

In diesem Verzeichnis befindet sich eine Docker-Datei, die Anweisungen zum Erstellen einer Knotenanwendung unter Verwendung derDocker node:10 image und des Inhalts Ihres aktuellen Projektverzeichnisses enthält. Sie können den Inhalt der Docker-Datei anzeigen, indem Sie Folgendes eingeben:

cat Dockerfile
OutputFROM node:10-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

USER node

RUN npm install

COPY --chown=node:node . .

EXPOSE 8080

CMD [ "node", "app.js" ]

Diese Anweisungen erstellen ein Knotenabbild, indem Sie den Projektcode aus dem aktuellen Verzeichnis in den Container kopieren und Abhängigkeiten mitnpm install installieren. Sie nutzen auch Dockerscaching and image layering, indem sie die Kopie vonpackage.json undpackage-lock.json, die die aufgelisteten Abhängigkeiten des Projekts enthält, von der Kopie des restlichen Anwendungscodes trennen. Schließlich geben die Anweisungen an, dass der Container als Nicht-Root-Benutzernodemit den entsprechenden Berechtigungen ausgeführt wird, die für den Anwendungscode und die Verzeichnissenode_modulesfestgelegt wurden.

Weitere Informationen zu diesen Best Practices für Dockerfile- und Node-Images finden Sie in der vollständigen Diskussion inStep 3 of How To Build a Node.js Application with Docker.

Um die Anwendung ohne SSL zu testen, können Sie das Image mitdocker build und dem Flag-t erstellen und kennzeichnen. Wir werden das Bildnode-demo nennen, aber Sie können es auch anders benennen:

docker build -t node-demo .

Sobald der Erstellungsprozess abgeschlossen ist, können Sie Ihre Bilder mitdocker images auflisten:

docker images

Sie sehen die folgende Ausgabe, die die Erstellung des Anwendungsimages bestätigt:

OutputREPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
node-demo           latest              23961524051d        7 seconds ago       73MB
node                10-alpine           8a752d5af4ce        3 weeks ago         70.7MB

Erstellen Sie als Nächstes den Container mitdocker run. Wir werden drei Flags in diesen Befehl aufnehmen:

  • -p: Dies veröffentlicht den Port auf dem Container und ordnet ihn einem Port auf unserem Host zu. Wir werden Port80 auf dem Host verwenden, aber Sie können dies nach Bedarf ändern, wenn auf diesem Port ein anderer Prozess ausgeführt wird. Weitere Informationen zur Funktionsweise finden Sie in dieser Diskussion in den Docker-Dokumenten zuport binding.

  • -d: Hiermit wird der Container im Hintergrund ausgeführt.

  • --name: Dadurch können wir dem Container einen einprägsamen Namen geben.

Führen Sie den folgenden Befehl aus, um den Container zu erstellen:

docker run --name node-demo -p 80:8080 -d node-demo

Überprüfen Sie Ihre laufenden Container mitdocker ps:

docker ps

Sie sehen die Ausgabe, die bestätigt, dass Ihr Anwendungscontainer ausgeführt wird:

OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
4133b72391da        node-demo           "node app.js"       17 seconds ago      Up 16 seconds       0.0.0.0:80->8080/tcp   node-demo

Sie können jetzt Ihre Domain besuchen, um Ihr Setup zu testen:http://example.com. Denken Sie daran,example.com durch Ihren eigenen Domainnamen zu ersetzen. Ihre Anwendung zeigt die folgende Zielseite an:

Application Landing Page

Nachdem Sie die Anwendung getestet haben, können Sie den Container anhalten und die Bilder entfernen. Verwenden Sie erneutdocker ps, um IhreCONTAINER ID zu erhalten:

docker ps
OutputCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
4133b72391da        node-demo           "node app.js"       17 seconds ago      Up 16 seconds       0.0.0.0:80->8080/tcp   node-demo

Stoppen Sie den Container mitdocker stop. Stellen Sie sicher, dass Sie die hier aufgeführtenCONTAINER ID durch Ihre eigene AnwendungCONTAINER ID ersetzen:

docker stop 4133b72391da

Sie können jetzt den gestoppten Container und alle Bilder, einschließlich nicht verwendeter und baumelnder Bilder, mitdocker system prune und dem Flag-a entfernen:

docker system prune -a

Geben Siey ein, wenn Sie in der Ausgabe dazu aufgefordert werden, zu bestätigen, dass Sie den gestoppten Container und die angehaltenen Bilder entfernen möchten. Beachten Sie, dass dadurch auch der Build-Cache gelöscht wird.

Nachdem Sie Ihr Anwendungsimage getestet haben, können Sie den Rest Ihres Setups mit Docker Compose erstellen.

[[Schritt 2 - Definieren der Webserverkonfiguration] == Schritt 2 - Definieren der Webserverkonfiguration

Mit unserer Anwendung Dockerfile können wir eine Konfigurationsdatei erstellen, um unseren Nginx-Container auszuführen. Wir beginnen mit einer minimalen Konfiguration, die unseren Domänennamen,document root, Proxy-Informationen und einen Standortblock enthält, um die Anforderungen von Certbot an das Verzeichnis.well-knownzu leiten, wo eine temporäre Datei abgelegt wird, um dies zu überprüfen Das DNS für unsere Domain wird auf unserem Server aufgelöst.

Erstellen Sie zunächst im aktuellen Projektverzeichnis ein Verzeichnis für die Konfigurationsdatei:

mkdir nginx-conf

Öffnen Sie die Datei mitnano oder Ihrem bevorzugten Editor:

nano nginx-conf/nginx.conf

Fügen Sie den folgenden Serverblock hinzu, um Proxy-Benutzeranforderungen an Ihren Knotenanwendungscontainer zu senden und die Anforderungen von Certbot an das Verzeichnis.well-knownweiterzuleiten. Stellen Sie sicher, dass Sieexample.com durch Ihren eigenen Domainnamen ersetzen:

~/node_project/nginx-conf/nginx.conf

server {
        listen 80;
        listen [::]:80;

        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                proxy_pass http://nodejs:8080;
        }

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

Mit diesem Serverblock können wir den Nginx-Container als Reverse-Proxy starten, der Anforderungen an unseren Node-Anwendungscontainer weiterleitet. Außerdem können wir diewebroot pluginvon Certbot verwenden, um Zertifikate für unsere Domain zu erhalten. Dieses Plugin hängt vonHTTP-01 validation methodab, das mithilfe einer HTTP-Anforderung nachweist, dass Certbot von einem Server, der auf einen bestimmten Domänennamen reagiert, auf Ressourcen zugreifen kann.

Speichern und schließen Sie die Datei, nachdem Sie die Bearbeitung abgeschlossen haben. Weitere Informationen zu Nginx-Server- und Standortblockierungsalgorithmen finden Sie in diesem Artikel zuUnderstanding Nginx Server and Location Block Selection Algorithms.

Mit den Konfigurationsdetails des Webservers können wir mit der Erstellung unsererdocker-compose.yml-Datei fortfahren, mit der wir unsere Anwendungsdienste und den Certbot-Container erstellen können, mit dem wir unsere Zertifikate erhalten.

[[Schritt-3 -—- Erstellen der Docker-Compose-Datei]] == Schritt 3 - Erstellen der Docker-Compose-Datei

Die Dateidocker-compose.ymldefiniert unsere Dienste, einschließlich der Knotenanwendung und des Webservers. Es werden Details wie benannte Volumes angegeben, die für die gemeinsame Nutzung von SSL-Anmeldeinformationen zwischen Containern sowie für Netzwerk- und Portinformationen von entscheidender Bedeutung sind. Außerdem können wir bestimmte Befehle angeben, die beim Erstellen unserer Container ausgeführt werden sollen. Diese Datei ist die zentrale Ressource, die definiert, wie unsere Services zusammenarbeiten.

Öffnen Sie die Datei in Ihrem aktuellen Verzeichnis:

nano docker-compose.yml

Definieren Sie zunächst den Anwendungsservice:

~/node_project/docker-compose.yml

version: '3'

services:
  nodejs:
    build:
      context: .
      dockerfile: Dockerfile
    image: nodejs
    container_name: nodejs
    restart: unless-stopped

Die Service-Definition vonnodejsumfasst Folgendes:

  • build: Hiermit werden die Konfigurationsoptionen definiert, einschließlichcontext unddockerfile, die beim Erstellen des Anwendungsabbilds durch Compose angewendet werden. Wenn Sie ein vorhandenes Image aus einer Registrierung wieDocker Hub verwenden möchten, können Sie stattdessenimage instruction mit Informationen zu Ihrem Benutzernamen, Repository und Image-Tag verwenden.

  • context: Dies definiert den Build-Kontext für den Build des Anwendungsimages. In diesem Fall ist es das aktuelle Projektverzeichnis.

  • dockerfile: Dies gibt die Docker-Datei an, die Compose für den Build verwendet - die Docker-Datei, die Sie inStep 1 betrachtet haben.

  • image,container_name: Diese wenden Namen auf das Bild und den Container an.

  • restart: Dies definiert die Neustartrichtlinie. Der Standardwert istno, aber wir haben den Container so eingestellt, dass er neu gestartet wird, sofern er nicht gestoppt wird.

Beachten Sie, dass wir Bindmounts nicht in diesen Service einbeziehen, da sich unser Setup eher auf die Bereitstellung als auf die Entwicklung konzentriert. Weitere Informationen finden Sie in der Docker-Dokumentation zubind mounts undvolumes.

Um die Kommunikation zwischen der Anwendung und den Webserver-Containern zu ermöglichen, fügen wir unterhalb der Neustartdefinition ein Brückennetzwerk mit dem Namenapp-network hinzu:

~/node_project/docker-compose.yml

services:
  nodejs:
...
    networks:
      - app-network

Ein solches benutzerdefiniertes Bridge-Netzwerk ermöglicht die Kommunikation zwischen Containern auf demselben Docker-Daemon-Host. Dies rationalisiert den Verkehr und die Kommunikation in Ihrer Anwendung, da alle Ports zwischen Containern im selben Brückennetz geöffnet werden und keine Ports für die Außenwelt freigegeben werden. Auf diese Weise können Sie gezielt nur die Ports öffnen, die Sie zum Anzeigen Ihrer Frontend-Services benötigen.

Definieren Sie als Nächstes den Dienst vonwebserver:

~/node_project/docker-compose.yml

...
 webserver:
    image: nginx:mainline-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - web-root:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
    depends_on:
      - nodejs
    networks:
      - app-network

Einige der Einstellungen, die wir für den Dienstnodejsdefiniert haben, bleiben unverändert, wir haben jedoch auch die folgenden Änderungen vorgenommen:

  • image: Hiermit wird Compose angewiesen, die neuestenAlpine-basedNginx image vom Docker Hub abzurufen. Weitere Informationen zualpine Bildern finden Sie in Schritt 3 vonHow To Build a Node.js Application with Docker.

  • ports: Dadurch wird Port80 verfügbar gemacht, um die Konfigurationsoptionen zu aktivieren, die wir in unserer Nginx-Konfiguration definiert haben.

Wir haben auch die folgenden benannten Volumes und Bindungs-Mounts angegeben:

  • web-root:/var/www/html: Hiermit werden die statischen Assets unserer Site, die auf ein Volume namensweb-root kopiert wurden, zum Verzeichnis/var/www/html im Container hinzugefügt.

  • ./nginx-conf:/etc/nginx/conf.d: Dadurch wird das Nginx-Konfigurationsverzeichnis auf dem Host an das entsprechende Verzeichnis im Container gebunden, um sicherzustellen, dass alle Änderungen, die wir an Dateien auf dem Host vornehmen, im Container berücksichtigt werden.

  • certbot-etc:/etc/letsencrypt: Dadurch werden die relevanten Let's Encrypt-Zertifikate und -Schlüssel für unsere Domain in das entsprechende Verzeichnis im Container gemountet.

  • certbot-var:/var/lib/letsencrypt: Hiermit wird das Standardarbeitsverzeichnis von Let's Encrypt in das entsprechende Verzeichnis im Container eingefügt.

Fügen Sie als Nächstes die Konfigurationsoptionen für den Containercertbothinzu. Achten Sie darauf, die Domain- und E-Mail-Informationen durch Ihren eigenen Domainnamen und Ihre Kontakt-E-Mail-Adresse zu ersetzen:

~/node_project/docker-compose.yml

...
  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - web-root:/var/www/html
    depends_on:
      - webserver
    command: certonly --webroot --webroot-path=/var/www/html --email [email protected] --agree-tos --no-eff-email --staging -d example.com  -d www.example.com

Diese Definition weist Compose an, diecertbot/certbot image von Docker Hub abzurufen. Außerdem werden benannte Volumes verwendet, um Ressourcen für den Nginx-Container freizugeben, einschließlich der Domänenzertifikate und des Schlüssels incertbot-etc, des Arbeitsverzeichnisses Let's Encrypt incertbot-var und des Anwendungscodes inweb-root.

Wiederum haben wirdepends_on verwendet, um anzugeben, dass der Containercertbot gestartet werden soll, sobald der Dienstwebserverausgeführt wird.

Wir haben auch die Optioncommand hinzugefügt, die den Befehl angibt, der beim Starten des Containers ausgeführt werden soll. Es enthält den Unterbefehlcertonlymit den folgenden Optionen:

  • --webroot: Hiermit wird Certbot angewiesen, das Webroot-Plugin zu verwenden, um Dateien zur Authentifizierung im Webroot-Ordner abzulegen.

  • --webroot-path: Gibt den Pfad des Webroot-Verzeichnisses an.

  • --email: Ihre bevorzugte E-Mail-Adresse für die Registrierung und Wiederherstellung.

  • --agree-tos: Dies gibt an, dass SieACME’s Subscriber Agreement zustimmen.

  • --no-eff-email: Hiermit wird Certbot mitgeteilt, dass Sie Ihre E-Mail nicht mitElectronic Frontier Foundation (EFF) teilen möchten. Fühlen Sie sich frei, dies wegzulassen, wenn Sie es vorziehen würden.

  • --staging: Hiermit wird Certbot mitgeteilt, dass Sie die Staging-Umgebung von Let's Encrypt verwenden möchten, um Testzertifikate zu erhalten. Mit dieser Option können Sie Ihre Konfigurationsoptionen testen und mögliche Beschränkungen für Domänenanforderungen vermeiden. Weitere Informationen zu diesen Grenzwerten finden Sie unter Let's Encrypt'srate limits documentation.

  • -d: Hier können Sie Domainnamen angeben, die Sie auf Ihre Anfrage anwenden möchten. In diesem Fall haben wirexample.com undwww.example.com eingeschlossen. Stellen Sie sicher, dass Sie diese durch Ihre eigenen Domain-Einstellungen ersetzen.

Fügen Sie als letzten Schritt die Volume- und Netzwerkdefinitionen hinzu. Stellen Sie sicher, dass Sie den Benutzernamen hier durch Ihren eigenen Benutzer ohne Rootberechtigung ersetzen:

~/node_project/docker-compose.yml

...
volumes:
  certbot-etc:
  certbot-var:
  web-root:
    driver: local
    driver_opts:
      type: none
      device: /home/sammy/node_project/views/
      o: bind

networks:
  app-network:
    driver: bridge

Zu unseren benannten Volumes gehören unser Certbot-Zertifikat und die Arbeitsverzeichnis-Volumes sowie das Volume für die statischen Assets unserer Site,web-root. In den meisten Fällen ist der Standardtreiber für Docker-Volumes derlocal-Treiber, der unter Linux ähnliche Optionen wiemount command akzeptiert. Dank dessen können wir eine Liste von Treiberoptionen mitdriver_opts angeben, die das Verzeichnisviews auf dem Host, der die statischen Assets unserer Anwendung enthält, zur Laufzeit auf dem Volume bereitstellen. Der Verzeichnisinhalt kann dann zwischen Containern geteilt werden. Weitere Informationen zum Inhalt des Verzeichnissesviewsfinden Sie unterStep 2 of How To Build a Node.js Application with Docker.

Die Dateidocker-compose.ymlieht nach Abschluss folgendermaßen aus:

~/node_project/docker-compose.yml

version: '3'

services:
  nodejs:
    build:
      context: .
      dockerfile: Dockerfile
    image: nodejs
    container_name: nodejs
    restart: unless-stopped
    networks:
      - app-network

  webserver:
    image: nginx:mainline-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - web-root:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
    depends_on:
      - nodejs
    networks:
      - app-network

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - web-root:/var/www/html
    depends_on:
      - webserver
    command: certonly --webroot --webroot-path=/var/www/html --email [email protected] --agree-tos --no-eff-email --staging -d example.com  -d www.example.com

volumes:
  certbot-etc:
  certbot-var:
  web-root:
    driver: local
    driver_opts:
      type: none
      device: /home/sammy/node_project/views/
      o: bind

networks:
  app-network:
    driver: bridge

Mit den vorhandenen Dienstdefinitionen können Sie die Container starten und Ihre Zertifikatanforderungen testen.

[[Schritt 4 - Abrufen von SSL-Zertifikaten und Anmeldeinformationen]] == Schritt 4 - Abrufen von SSL-Zertifikaten und Anmeldeinformationen

Wir können unsere Container mitdocker-compose up starten, wodurch unsere Container und Services in der von uns angegebenen Reihenfolge erstellt und ausgeführt werden. Wenn unsere Domain-Anfragen erfolgreich sind, sehen wir den korrekten Exit-Status in unserer Ausgabe und die richtigen Zertifikate im Ordner/etc/letsencrypt/liveauf dem Containerwebserver.

Erstellen Sie die Services mitdocker-compose up und dem Flag-d, wodurch die Containernodejs undwebserver im Hintergrund ausgeführt werden:

docker-compose up -d

Sie sehen die Ausgabe, die bestätigt, dass Ihre Services erstellt wurden:

OutputCreating nodejs ... done
Creating webserver ... done
Creating certbot   ... done

Überprüfen Sie mitdocker-compose ps den Status Ihrer Dienste:

docker-compose ps

Wenn alles erfolgreich war, sollten Ihre Dienstenodejs undwebserverUp sein, und der Containercertbot wurde mit der Statusmeldung0 beendet:

Output  Name                 Command               State          Ports
------------------------------------------------------------------------
certbot     certbot certonly --webroot ...   Exit 0
nodejs      node app.js                      Up       8080/tcp
webserver   nginx -g daemon off;             Up       0.0.0.0:80->80/tcp

Wenn Sie in der SpalteState für die Dienstenodejs undwebserveretwas anderes alsUp oder fürcertboteinen anderen Exit-Status als0 sehen ) s Container, überprüfen Sie die Serviceprotokolle mit dem Befehldocker-compose logs:

docker-compose logs service_name

Sie können jetzt überprüfen, ob Ihre Anmeldeinformationen mitdocker-compose exec im Containerwebserver bereitgestellt wurden:

docker-compose exec webserver ls -la /etc/letsencrypt/live

Wenn Ihre Anfrage erfolgreich war, sehen Sie die Ausgabe wie folgt:

Outputtotal 16
drwx------ 3 root root 4096 Dec 23 16:48 .
drwxr-xr-x 9 root root 4096 Dec 23 16:48 ..
-rw-r--r-- 1 root root  740 Dec 23 16:48 README
drwxr-xr-x 2 root root 4096 Dec 23 16:48 example.com

Nachdem Sie wissen, dass Ihre Anforderung erfolgreich ist, können Sie die Dienstdefinition voncertbotbearbeiten, um das Flag von--stagingzu entfernen.

Öffnen Siedocker-compose.yml:

nano docker-compose.yml

Suchen Sie den Abschnitt der Datei mit der Dienstdefinitioncertbotund ersetzen Sie das Flag--staging in der Optioncommand durch das Flag--force-renewal, das Certbot mitteilt, dass Sie dies möchten Fordern Sie ein neues Zertifikat mit denselben Domänen wie ein vorhandenes Zertifikat an. Die Service-Definition voncertbotollte nun folgendermaßen aussehen:

~/node_project/docker-compose.yml

...
  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - web-root:/var/www/html
    depends_on:
      - webserver
    command: certonly --webroot --webroot-path=/var/www/html --email [email protected] --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com
...

Sie können jetztdocker-compose up ausführen, um dencertbot-Container und seine relevanten Volumes neu zu erstellen. Wir werden auch die Option--no-deps einfügen, um Compose mitzuteilen, dass das Starten des Diensteswebserverübersprungen werden kann, da er bereits ausgeführt wird:

docker-compose up --force-recreate --no-deps certbot

In der Ausgabe wird angezeigt, dass Ihre Zertifikatanforderung erfolgreich war:

Outputcertbot      | IMPORTANT NOTES:
certbot      |  - Congratulations! Your certificate and chain have been saved at:
certbot      |    /etc/letsencrypt/live/example.com/fullchain.pem
certbot      |    Your key file has been saved at:
certbot      |    /etc/letsencrypt/live/example.com/privkey.pem
certbot      |    Your cert will expire on 2019-03-26. To obtain a new or tweaked
certbot      |    version of this certificate in the future, simply run certbot
certbot      |    again. To non-interactively renew *all* of your certificates, run
certbot      |    "certbot renew"
certbot      |  - Your account credentials have been saved in your Certbot
certbot      |    configuration directory at /etc/letsencrypt. You should make a
certbot      |    secure backup of this folder now. This configuration directory will
certbot      |    also contain certificates and private keys obtained by Certbot so
certbot      |    making regular backups of this folder is ideal.
certbot      |  - If you like Certbot, please consider supporting our work by:
certbot      |
certbot      |    Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
certbot      |    Donating to EFF:                    https://eff.org/donate-le
certbot      |
certbot exited with code 0

Wenn Ihre Zertifikate vorhanden sind, können Sie Ihre Nginx-Konfiguration so ändern, dass sie SSL enthält.

[[Schritt 5 - Ändern der Webserverkonfiguration und der Dienstdefinition] == Schritt 5 - Ändern der Webserverkonfiguration und der Dienstdefinition

Wenn Sie SSL in unserer Nginx-Konfiguration aktivieren, müssen Sie eine HTTP-Umleitung zu HTTPS hinzufügen und unser SSL-Zertifikat und die Schlüsselpositionen angeben. Dazu gehört auch die Angabe unserer Diffie-Hellman-Gruppe, die wir fürPerfect Forward Secrecy verwenden.

Da Sie den Dienstwebserverneu erstellen, um diese Ergänzungen einzuschließen, können Sie ihn jetzt beenden:

docker-compose stop webserver

Erstellen Sie als Nächstes ein Verzeichnis in Ihrem aktuellen Projektverzeichnis für Ihren Diffie-Hellman-Schlüssel:

mkdir dhparam

Generieren Sie Ihren Schlüssel mitopenssl command:

sudo openssl dhparam -out /home/sammy/node_project/dhparam/dhparam-2048.pem 2048

Es dauert einige Augenblicke, bis der Schlüssel generiert ist.

Um die relevanten Diffie-Hellman- und SSL-Informationen zu Ihrer Nginx-Konfiguration hinzuzufügen, entfernen Sie zunächst die zuvor erstellte Nginx-Konfigurationsdatei:

rm nginx-conf/nginx.conf

Öffnen Sie eine andere Version der Datei:

nano nginx-conf/nginx.conf

Fügen Sie der Datei den folgenden Code hinzu, um HTTP an HTTPS umzuleiten und SSL-Anmeldeinformationen, Protokolle und Sicherheitsheader hinzuzufügen. Denken Sie daran,example.com durch Ihre eigene Domain zu ersetzen:

~/node_project/nginx-conf/nginx.conf

server {
        listen 80;
        listen [::]:80;
        server_name example.com www.example.com;

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

        location / {
                rewrite ^ https://$host$request_uri? permanent;
        }
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name example.com www.example.com;

        server_tokens off;

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

        ssl_buffer_size 8k;

        ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;

        ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
        ssl_prefer_server_ciphers on;

        ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;

        ssl_ecdh_curve secp384r1;
        ssl_session_tickets off;

        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8;

        location / {
                try_files $uri @nodejs;
        }

        location @nodejs {
                proxy_pass http://nodejs:8080;
                add_header X-Frame-Options "SAMEORIGIN" always;
                add_header X-XSS-Protection "1; mode=block" always;
                add_header X-Content-Type-Options "nosniff" always;
                add_header Referrer-Policy "no-referrer-when-downgrade" always;
                add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
                # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
                # enable strict transport security only if you understand the implications
        }

        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
}

Der HTTP-Serverblock gibt die Webroot für Certbot-Erneuerungsanforderungen an das Verzeichnis.well-known/acme-challengean. Es enthält auch einrewrite directive, das HTTP-Anforderungen an das Stammverzeichnis an HTTPS weiterleitet.

Der HTTPS-Serverblock aktiviertssl undhttp2. Weitere Informationen darüber, wie HTTP / 2 in HTTP-Protokollen iteriert und welche Vorteile es für die Leistung der Website haben kann, finden Sie in der Einführung zuHow To Set Up Nginx with HTTP/2 Support on Ubuntu 18.04. Dieser Block enthält auch eine Reihe von Optionen, um sicherzustellen, dass Sie die aktuellsten SSL-Protokolle und -Verschlüsselungen verwenden und die OSCP-Heftung aktiviert ist. Mit dem OSCP-Heften können Sie während der erstenTLS handshake eine Antwort mit Zeitstempel voncertificate authority anbieten, wodurch der Authentifizierungsprozess beschleunigt werden kann.

Der Block gibt auch Ihre SSL- und Diffie-Hellman-Anmeldeinformationen und die Schlüsselpositionen an.

Schließlich haben wir die Proxy-Pass-Informationen in diesen Block verschoben, einschließlich eines Standortblocks mit der Anweisungtry_files, der Anforderungen an unseren Alias-Node.js-Anwendungscontainer verweist, und eines Standortblocks für diesen Alias, der Sicherheitsheader enthält Auf diese Weise können wirA Bewertungen für Dinge wie die ServertestseitenSSL Labs undSecurity Headerserhalten. Diese Überschriften umfassenX-Frame-Options,X-Content-Type-Options,Referrer Policy,Content-Security-Policy undX-XSS-Protection. Der HeaderHTTP Strict Transport Security (HSTS) ist auskommentiert. Aktivieren Sie diese Option nur, wenn Sie die Auswirkungen verstanden und die“preload” functionality bewertet haben.

Speichern und schließen Sie die Datei, nachdem Sie die Bearbeitung abgeschlossen haben.

Bevor Sie den Dienstwebserverneu erstellen, müssen Sie der Dienstdefinition in Ihrerdocker-compose.yml-Datei einige Dinge hinzufügen, einschließlich relevanter Portinformationen für HTTPS und einer Diffie-Hellman-Datenträgerdefinition.

Öffne die Datei:

nano docker-compose.yml

Fügen Sie in der Dienstdefinition vonwebserverdie folgende Portzuordnung und das benannte Volume vondhparamhinzu:

~/node_project/docker-compose.yml

...
 webserver:
    image: nginx:latest
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - web-root:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - dhparam:/etc/ssl/certs
    depends_on:
      - nodejs
    networks:
      - app-network

Fügen Sie als Nächstes das Volumen vondhparamzu den Definitionen vonvolumeshinzu:

~/node_project/docker-compose.yml

...
volumes:
  ...
  dhparam:
    driver: local
    driver_opts:
      type: none
      device: /home/sammy/node_project/dhparam/
      o: bind

Ähnlich wie das Volume vonweb-rootwird auf dem Volume vondhparamder auf dem Host gespeicherte Diffie-Hellman-Schlüssel in den Container vonwebservereingehängt.

Speichern und schließen Sie die Datei, wenn Sie mit der Bearbeitung fertig sind.

Erstellen Sie den Dienstwebserverneu:

docker-compose up -d --force-recreate --no-deps webserver

Überprüfen Sie Ihre Dienste mitdocker-compose ps:

docker-compose ps

Die Ausgabe sollte anzeigen, dass Ihre Dienstenodejs undwebserverausgeführt werden:

Output  Name                 Command               State                     Ports
----------------------------------------------------------------------------------------------
certbot     certbot certonly --webroot ...   Exit 0
nodejs      node app.js                      Up       8080/tcp
webserver   nginx -g daemon off;             Up       0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

Schließlich können Sie Ihre Domain besuchen, um sicherzustellen, dass alles wie erwartet funktioniert. Navigieren Sie in Ihrem Browser zuhttps://example.com, und ersetzen Sieexample.com durch Ihren eigenen Domainnamen. Sie sehen die folgende Zielseite:

Application Landing Page

Sie sollten auch das Schlosssymbol in der Sicherheitsanzeige Ihres Browsers sehen. Wenn Sie möchten, können Sie zuSSL Labs Server Test landing page oderSecurity Headers server test landing page navigieren. Mit den von uns enthaltenen Konfigurationsoptionen sollte Ihre Site für beide eine Bewertung vonAerhalten.

[[Schritt 6 - Erneuern von Zertifikaten]] == Schritt 6 - Erneuern von Zertifikaten

Let 's Encrypt-Zertifikate sind 90 Tage gültig. Sie sollten daher einen automatischen Erneuerungsprozess einrichten, um sicherzustellen, dass sie nicht verfallen. Eine Möglichkeit, dies zu tun, besteht darin, einen Job mit dem Planungsdienstprogrammcronzu erstellen. In diesem Fall planen wir einencron-Job mithilfe eines Skripts, das unsere Zertifikate erneuert und unsere Nginx-Konfiguration neu lädt.

Öffnen Sie ein Skript namensssl_renew.sh in Ihrem Projektverzeichnis:

nano ssl_renew.sh

Fügen Sie dem Skript den folgenden Code hinzu, um Ihre Zertifikate zu erneuern und Ihre Webserverkonfiguration neu zu laden:

~/node_project/ssl_renew.sh

#!/bin/bash

/usr/local/bin/docker-compose -f /home/sammy/node_project/docker-compose.yml run certbot renew --dry-run \
&& /usr/local/bin/docker-compose -f /home/sammy/node_project/docker-compose.yml kill -s SIGHUP webserver

Zusätzlich zur Angabe des Speicherorts unsererdocker-compose-Binärdatei geben wir auch den Speicherort unsererdocker-compose.yml-Datei an, umdocker-compose-Befehle auszuführen. In diesem Fall verwenden wirdocker-compose run, um einencertbot-Container zu starten und die in unserer Service-Definition angegebenencommand mit einem anderen zu überschreiben: dem Unterbefehlrenew, mit dem Zertifikate erneuert werden das sind kurz vor dem Ablauf. Wir haben hier die Option--dry-runeingefügt, um unser Skript zu testen.

Das Skript verwendet danndocker-compose kill, umSIGHUP signal an den Containerwebserver zu senden und die Nginx-Konfiguration neu zu laden. Weitere Informationen zur Verwendung dieses Prozesses zum Neuladen Ihrer Nginx-Konfiguration finden Sie unterthis Docker blog post on deploying the official Nginx image with Docker.

Schließen Sie die Datei, wenn Sie mit der Bearbeitung fertig sind. Mach es ausführbar:

chmod +x ssl_renew.sh

Öffnen Sie als Nächstes Ihrerootcrontab-Datei, um das Erneuerungsskript in einem bestimmten Intervall auszuführen:

sudo crontab -e

Wenn Sie diese Datei zum ersten Mal bearbeiten, werden Sie aufgefordert, einen Editor auszuwählen:

crontab

no crontab for root - using an empty one
Select an editor.  To change later, run 'select-editor'.
  1. /bin/ed
  2. /bin/nano        <---- easiest
  3. /usr/bin/vim.basic
  4. /usr/bin/vim.tiny
Choose 1-4 [2]:
...

Fügen Sie am Ende der Datei die folgende Zeile hinzu:

crontab

...
*/5 * * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1

Dadurch wird das Auftragsintervall auf alle fünf Minuten festgelegt, sodass Sie testen können, ob Ihre Erneuerungsanforderung wie beabsichtigt funktioniert hat. Wir haben auch eine Protokolldateicron.log erstellt, um relevante Ausgaben des Jobs aufzuzeichnen.

Überprüfen Sie nach fünf Minutencron.log, um festzustellen, ob die Erneuerungsanforderung erfolgreich war oder nicht:

tail -f /var/log/cron.log

Sie sollten sehen, dass die Ausgabe eine erfolgreiche Erneuerung bestätigt:

Output- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** 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/example.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Killing webserver ... done

Sie können jetzt diecrontab-Datei ändern, um ein tägliches Intervall festzulegen. Um das Skript beispielsweise jeden Tag mittags auszuführen, müssen Sie die letzte Zeile der Datei folgendermaßen ändern:

crontab

...
0 12 * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1

Sie möchten auch die Option--dry-run aus Ihremssl_renew.sh-Skript entfernen:

~/node_project/ssl_renew.sh

#!/bin/bash

/usr/local/bin/docker-compose -f /home/sammy/node_project/docker-compose.yml run certbot renew \
&& /usr/local/bin/docker-compose -f /home/sammy/node_project/docker-compose.yml kill -s SIGHUP webserver

Durch den Job voncronwird sichergestellt, dass Ihre Let's Encrypt-Zertifikate nicht verfallen, indem Sie sie erneuern, wenn sie berechtigt sind. Sie können auchset up log rotation with the Logrotate utility verwenden, um Ihre Protokolldateien zu drehen und zu komprimieren.

Fazit

Sie haben Container verwendet, um eine Node-Anwendung mit einem Nginx-Reverse-Proxy einzurichten und auszuführen. Sie haben auch SSL-Zertifikate für die Domäne Ihrer Anwendung gesichert und einencron-Job eingerichtet, um diese Zertifikate bei Bedarf zu erneuern.

Wenn Sie mehr über Let's Encrypt-Plugins erfahren möchten, lesen Sie bitte unsere Artikel zur Verwendung vonNginx plugin oderstandalone plugin.

Weitere Informationen zu Docker Compose finden Sie in den folgenden Ressourcen:

DasCompose documentation ist auch eine großartige Ressource, um mehr über Anwendungen mit mehreren Containern zu erfahren.