コンテナ化されたNode.jsアプリケーションをNginx、Let'sEncrypt、DockerComposeで保護する方法

提供:Dev Guides
移動先:案内検索

序章

Node.jsアプリケーションの柔軟性とセキュリティを強化する方法は複数あります。 Nginx のようなリバースプロキシを使用すると、リクエストの負荷分散、静的コンテンツのキャッシュ、トランスポート層セキュリティ(TLS)の実装が可能になります。 サーバーで暗号化されたHTTPSを有効にすると、アプリケーションとの間の通信が安全に保たれます。

コンテナにTLS/SSLを使用してリバースプロキシを実装するには、ホストオペレーティングシステムで直接作業する場合とは異なる一連の手順が必要です。 たとえば、サーバーで実行されているアプリケーションの Let's Encrypt から証明書を取得する場合は、必要なソフトウェアをホストに直接インストールします。 コンテナを使用すると、別のアプローチを取ることができます。 Docker Compose を使用すると、アプリケーション、Webサーバー、およびCertbotクライアントのコンテナーを作成して証明書を取得できます。 これらの手順に従うことで、コンテナ化されたワークフローのモジュール性と移植性を活用できます。

このチュートリアルでは、Docker Composeを使用して、Nginxリバースプロキシを使用してNode.jsアプリケーションをデプロイします。 アプリケーションに関連付けられているドメインのTLS/SSL証明書を取得し、 SSLLabsから高いセキュリティ評価を受けていることを確認します。 最後に、 cron ジョブを設定して証明書を更新し、ドメインのセキュリティを維持します。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • Ubuntu 18.04サーバー、sudo権限を持つ非rootユーザー、およびアクティブなファイアウォール。 これらの設定方法のガイダンスについては、この初期サーバー設定ガイドを参照してください。
  • サーバーにインストールされているDockerとDockerCompose。 Dockerのインストールに関するガイダンスについては、 Ubuntu18.04にDockerをインストールして使用する方法のステップ1と2に従ってください。 Composeのインストールに関するガイダンスについては、 Ubuntu18.04にDockerComposeをインストールする方法のステップ1に従ってください。
  • 登録されたドメイン名。 このチュートリアルでは、全体を通してexample.comを使用します。 Freenom で無料で入手するか、選択したドメインレジストラを使用できます。
  • 次の両方のDNSレコードがサーバー用に設定されています。 DigitalOceanアカウントに追加する方法の詳細については、このDigitalOcean DNSの概要をフォローしてください(使用している場合)。
    • サーバーのパブリックIPアドレスを指すexample.comのAレコード。
    • サーバーのパブリックIPアドレスを指すwww.example.comのAレコード。

ステップ1—ノードアプリケーションのクローン作成とテスト

最初のステップとして、Composeを使用してアプリケーションイメージを構築するために使用するDockerfileを含むNodeアプリケーションコードを使用してリポジトリのクローンを作成します。 最初に、リバースプロキシやSSLを使用せずに、 docker runコマンドを使用してアプリケーションをビルドし、実行することで、アプリケーションをテストできます。

root以外のユーザーのホームディレクトリで、 DigitalOceanCommunityGitHubアカウントからnodejs-image-demoリポジトリのクローンを作成します。 このリポジトリには、Dockerを使用してNode.jsアプリケーションを構築する方法で説明されているセットアップのコードが含まれています。

リポジトリをnode_projectというディレクトリに複製します。

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

node_projectディレクトリに移動します。

cd  node_project

このディレクトリには、 Docker node:10imageと現在のプロジェクトディレクトリの内容を使用してノードアプリケーションを構築するための手順を含むDockerfileがあります。 次のように入力すると、Dockerfileの内容を確認できます。

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" ]

これらの手順では、プロジェクトコードを現在のディレクトリからコンテナにコピーし、npm installを使用して依存関係をインストールすることにより、ノードイメージを構築します。 また、プロジェクトのリストされた依存関係を含むpackage.jsonpackage-lock.jsonのコピーをアプリケーションの他の部分のコピーから分離することにより、Dockerのキャッシュとイメージレイヤーを利用します。コード。 最後に、この手順では、コンテナが非ルート node ユーザーとして実行され、アプリケーションコードとnode_modulesディレクトリに適切な権限が設定されていることを指定しています。

このDockerfileとNodeイメージのベストプラクティスの詳細については、Dockerを使用してNode.jsアプリケーションを構築する方法のステップ3の完全な説明を参照してください。

SSLを使用せずにアプリケーションをテストするには、 dockerbuild-tフラグを使用してイメージをビルドおよびタグ付けできます。 画像をnode-demoと呼びますが、別の名前を付けることもできます。

docker build -t node-demo .

ビルドプロセスが完了すると、 dockerimagesを使用してイメージを一覧表示できます。

docker images

次の出力が表示され、アプリケーションイメージのビルドが確認されます。

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

次に、docker runでコンテナを作成します。 このコマンドには、次の3つのフラグが含まれます。

  • -p:これにより、コンテナーのポートが公開され、ホストのポートにマップされます。 ホストではポート80を使用しますが、そのポートで別のプロセスを実行している場合は、必要に応じてこれを自由に変更してください。 これがどのように機能するかについての詳細は、ポートバインディングに関するDockerドキュメントのこの説明を参照してください。
  • -d:これはコンテナーをバックグラウンドで実行します。
  • --name:これにより、コンテナーに覚えやすい名前を付けることができます。

次のコマンドを実行して、コンテナーを作成します。

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

dockerpsを使用して実行中のコンテナーを検査します。

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

これで、ドメインにアクセスしてセットアップをテストできます:http://example.comexample.comを独自のドメイン名に置き換えることを忘れないでください。 アプリケーションは次のランディングページを表示します。

アプリケーションのテストが完了したので、コンテナーを停止してイメージを削除できます。 docker psをもう一度使用して、CONTAINER IDを取得します。

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

dockerstopでコンテナを停止します。 ここにリストされているCONTAINER IDは、必ず独自のアプリケーションCONTAINER IDに置き換えてください。

docker stop 4133b72391da

docker system prune-aフラグを使用して、停止したコンテナーと、未使用のぶら下がっているイメージを含むすべてのイメージを削除できるようになりました。

docker system prune -a

出力でプロンプトが表示されたら、yと入力して、停止したコンテナーとイメージを削除することを確認します。 これにより、ビルドキャッシュも削除されることに注意してください。

アプリケーションイメージをテストしたら、DockerComposeを使用して残りのセットアップの構築に進むことができます。

ステップ2—Webサーバー構成の定義

アプリケーションDockerfileを配置したら、Nginxコンテナーを実行するための構成ファイルを作成できます。 ドメイン名、ドキュメントルート、プロキシ情報、およびCertbotのリクエストを.well-knownディレクトリに転送するためのロケーションブロックを含む最小限の構成から始めます。ここで、一時的なものが配置されます。ドメインのDNSがサーバーに解決されることを検証するファイル。

まず、構成ファイルの現在のプロジェクトディレクトリにディレクトリを作成します。

mkdir nginx-conf

nanoまたはお気に入りのエディターでファイルを開きます。

nano nginx-conf/nginx.conf

次のサーバーブロックを追加して、ユーザーリクエストをノードアプリケーションコンテナにプロキシし、Certbotのリクエストを.well-knownディレクトリに送信します。 example.com は必ず独自のドメイン名に置き換えてください。

〜/ 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;
        }
}

このサーバーブロックにより、Nginxコンテナをリバースプロキシとして起動し、ノードアプリケーションコンテナにリクエストを渡すことができます。 また、Certbotのwebrootプラグインを使用してドメインの証明書を取得することもできます。 このプラグインは、 HTTP-01検証メソッドに依存しています。このメソッドは、HTTPリクエストを使用して、Certbotが特定のドメイン名に応答するサーバーからリソースにアクセスできることを証明します。

編集が終了したら、ファイルを保存して閉じます。 Nginxサーバーとロケーションブロックアルゴリズムの詳細については、Nginxサーバーとロケーションブロック選択アルゴリズムについての記事を参照してください。

Webサーバー構成の詳細が整ったら、docker-compose.ymlファイルの作成に進むことができます。これにより、アプリケーションサービスと、証明書の取得に使用するCertbotコンテナーを作成できます。

ステップ3—Docker作成ファイルの作成

docker-compose.ymlファイルは、ノードアプリケーションやWebサーバーなどのサービスを定義します。 名前付きボリュームなどの詳細を指定します。これは、コンテナー間でSSLクレデンシャルを共有するために重要であり、ネットワークおよびポート情報も指定します。 また、コンテナの作成時に実行する特定のコマンドを指定することもできます。 このファイルは、サービスがどのように連携するかを定義する中心的なリソースです。

現在のディレクトリでファイルを開きます。

nano docker-compose.yml

まず、アプリケーションサービスを定義します。

〜/ node_project / docker-compose.yml

version: '3'

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

nodejsサービス定義には次のものが含まれます。

  • build:これは、contextdockerfileなど、Composeがアプリケーションイメージをビルドするときに適用される構成オプションを定義します。 Docker Hub などのレジストリの既存のイメージを使用する場合は、代わりに image命令を使用して、ユーザー名、リポジトリ、イメージタグに関する情報を使用できます。
  • context:これは、アプリケーションイメージビルドのビルドコンテキストを定義します。 この場合、それは現在のプロジェクトディレクトリです。
  • dockerfile:これは、Composeがビルドに使用するDockerfile(ステップ1で確認したDockerfile)を指定します。
  • imagecontainer_name:これらは画像とコンテナに名前を適用します。
  • restart:これは再起動ポリシーを定義します。 デフォルトはnoですが、コンテナが停止しない限り再起動するように設定しています。

セットアップは開発ではなく展開に重点を置いているため、このサービスにはバインドマウントが含まれていないことに注意してください。 詳細については、バインドマウントおよびボリュームに関するDockerのドキュメントを参照してください。

アプリケーションとWebサーバーコンテナ間の通信を有効にするために、再起動定義の下にapp-networkというブリッジネットワークも追加します。

〜/ node_project / docker-compose.yml

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

このようなユーザー定義のブリッジネットワークにより、同じDockerデーモンホスト上のコンテナー間の通信が可能になります。 これにより、アプリケーション内のトラフィックと通信が合理化されます。これは、同じブリッジネットワーク上のコンテナ間のすべてのポートを開き、ポートを外部に公開しないためです。 したがって、フロントエンドサービスを公開するために必要なポートのみを開くことを選択できます。

次に、webserverサービスを定義します。

〜/ 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

nodejsサービスに対して定義した設定の一部は同じままですが、次の変更も加えました。

  • image:これは、DockerHubから最新のAlpineベースNginxイメージをプルするようにComposeに指示します。 alpineイメージの詳細については、Dockerを使用してNode.jsアプリケーションを構築する方法のステップ3を参照してください。
  • ports:これによりポート80が公開され、Nginx構成で定義した構成オプションが有効になります。

また、次の名前付きボリュームとバインドマウントも指定しました。

  • web-root:/var/www/html:これにより、web-rootというボリュームにコピーされたサイトの静的アセットが、コンテナーの/var/www/htmlディレクトリに追加されます。
  • ./nginx-conf:/etc/nginx/conf.d:これにより、ホスト上のNginx構成ディレクトリがコンテナ上の関連ディレクトリにバインドマウントされ、ホスト上のファイルに加えた変更がコンテナに反映されるようになります。
  • certbot-etc:/etc/letsencrypt:これにより、ドメインに関連するLet'sEncryptの証明書とキーがコンテナの適切なディレクトリにマウントされます。
  • certbot-var:/var/lib/letsencrypt:これにより、Let'sEncryptのデフォルトの作業ディレクトリがコンテナの適切なディレクトリにマウントされます。

次に、certbotコンテナの構成オプションを追加します。 ドメインと電子メール情報を自分のドメイン名と連絡先電子メールに必ず置き換えてください。

〜/ 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 

この定義は、DockerHubからcertbot / certbotimageをプルするようにComposeに指示します。 また、名前付きボリュームを使用して、certbot-etcのドメイン証明書とキー、certbot-varのLet'sEncrypt作業ディレクトリ、web-rootのアプリケーションコードなど、Nginxコンテナとリソースを共有します。 ]。

ここでも、depends_onを使用して、webserverサービスの実行後にcertbotコンテナーを開始するように指定しました。

コンテナの起動時に実行するコマンドを指定するcommandオプションも含まれています。 これには、certonlyサブコマンドと次のオプションが含まれています。

  • --webroot:これは、認証のためにwebrootプラグインを使用してファイルをwebrootフォルダーに配置するようにCertbotに指示します。
  • --webroot-path:これはwebrootディレクトリのパスを指定します。
  • --email:登録と復旧のためのご希望のメールアドレス。
  • --agree-tos:これは、ACMEのサブスクライバー契約に同意することを指定します。
  • --no-eff-email:これは、電子メールを Electronic Frontier Foundation (EFF)と共有したくないことをCertbotに通知します。 必要に応じて、これを省略してください。
  • --staging:これは、Let'sEncryptのステージング環境を使用してテスト証明書を取得することをCertbotに通知します。 このオプションを使用すると、構成オプションをテストして、ドメイン要求の制限を回避できます。 これらの制限の詳細については、Let'sEncryptのレート制限のドキュメントを参照してください。
  • -d:これにより、リクエストに適用するドメイン名を指定できます。 この場合、example.comwww.example.comが含まれています。 これらを独自のドメイン設定に置き換えてください。

最後のステップとして、ボリュームとネットワークの定義を追加します。 ここでのユーザー名は、root以外のユーザーに置き換えてください。

〜/ 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

名前付きボリュームには、Certbot証明書と作業ディレクトリのボリューム、およびサイトの静的アセットのボリュームweb-rootが含まれます。 ほとんどの場合、Dockerボリュームのデフォルトドライバーはlocalドライバーであり、Linuxではmountコマンドと同様のオプションを受け入れます。 このおかげで、アプリケーションの静的アセットを含むホスト上のviewsディレクトリを実行時にボリュームにマウントするdriver_optsを使用してドライバーオプションのリストを指定できます。 その後、ディレクトリの内容をコンテナ間で共有できます。 viewsディレクトリの内容の詳細については、Dockerを使用してNode.jsアプリケーションを構築する方法のステップ2を参照してください。

docker-compose.ymlファイルは、終了すると次のようになります。

〜/ 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  

サービス定義が整ったら、コンテナを起動して証明書要求をテストする準備が整います。

ステップ4—SSL証明書とクレデンシャルを取得する

コンテナはdocker-composeup で開始できます。これにより、指定した順序でコンテナとサービスが作成および実行されます。 ドメイン要求が成功すると、出力に正しい終了ステータスが表示され、webserverコンテナの/etc/letsencrypt/liveフォルダに適切な証明書がマウントされます。

docker-compose up-dフラグを使用してサービスを作成します。これにより、nodejsおよびwebserverコンテナーがバックグラウンドで実行されます。

docker-compose up -d

サービスが作成されたことを確認する出力が表示されます。

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

docker-compose ps を使用して、サービスのステータスを確認します。

docker-compose ps

すべてが成功した場合、nodejsおよびwebserverサービスはUpである必要があり、certbotコンテナは0ステータスメッセージで終了します。 :

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

nodejsおよびwebserverサービスのState列にUp以外のものが表示された場合、または0以外の終了ステータスが表示された場合certbotコンテナーの場合は、必ず docker-composelogsコマンドを使用してサービスログを確認してください。

docker-compose logs service_name

これで、 docker-compose exec を使用して、資格情報がwebserverコンテナーにマウントされていることを確認できます。

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

リクエストが成功した場合、次のような出力が表示されます。

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

リクエストが成功することがわかったので、certbotサービス定義を編集して、--stagingフラグを削除できます。

docker-compose.ymlを開きます:

nano docker-compose.yml

certbotサービス定義のあるファイルのセクションを見つけ、commandオプションの--stagingフラグを--force-renewalフラグに置き換えます。これによりCertbotに通知されます。既存の証明書と同じドメインを持つ新しい証明書を要求すること。 certbotサービス定義は次のようになります。

〜/ 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
...

これで、docker-compose upを実行して、certbotコンテナーとそれに関連するボリュームを再作成できます。 また、--no-depsオプションを含めて、webserverサービスがすでに実行されているため、開始をスキップできることをComposeに通知します。

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

証明書要求が成功したことを示す出力が表示されます。

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

証明書を配置したら、Nginx構成を変更してSSLを含めることができます。

手順5—Webサーバーの構成とサービス定義を変更する

Nginx構成でSSLを有効にするには、HTTPSへのHTTPリダイレクトを追加し、SSL証明書とキーの場所を指定する必要があります。 また、 Perfect ForwardSecrecyに使用するDiffie-Hellmanグループの指定も含まれます。

webserverサービスを再作成してこれらの追加を含めるので、ここで停止できます。

docker-compose stop webserver

次に、現在のプロジェクトディレクトリにDiffie-Hellmanキーのディレクトリを作成します。

mkdir dhparam

opensslコマンドを使用してキーを生成します。

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

キーの生成には少し時間がかかります。

関連するDiffie-HellmanおよびSSL情報をNginx構成に追加するには、最初に、前に作成したNginx構成ファイルを削除します。

rm nginx-conf/nginx.conf

ファイルの別のバージョンを開きます。

nano nginx-conf/nginx.conf

次のコードをファイルに追加して、HTTPをHTTPSにリダイレクトし、SSLクレデンシャル、プロトコル、およびセキュリティヘッダーを追加します。 example.comを独自のドメインに置き換えることを忘れないでください。

〜/ 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;
}

HTTPサーバーブロックは、.well-known/acme-challengeディレクトリへのCertbot更新要求のWebルートを指定します。 また、ルートディレクトリへのHTTPリクエストをHTTPSに転送するrewriteディレクティブも含まれています。

HTTPSサーバーブロックは、sslおよびhttp2を有効にします。 HTTP / 2がHTTPプロトコルを反復処理する方法と、それがWebサイトのパフォーマンスにもたらすメリットの詳細については、 Ubuntu18.04でHTTP/2サポートを使用してNginxをセットアップする方法の概要を参照してください。 このブロックには、最新のSSLプロトコルと暗号を使用し、OSCPステープリングがオンになっていることを確認するための一連のオプションも含まれています。 OSCPステープリングを使用すると、最初の TLSハンドシェイク中に、認証局からタイムスタンプ付きの応答を提供できます。これにより、認証プロセスを高速化できます。

このブロックは、SSLとDiffie-Hellmanの資格情報とキーの場所も指定します。

最後に、プロキシパス情報をこのブロックに移動しました。これには、 try_files ディレクティブを含むロケーションブロック、エイリアス化されたNode.jsアプリケーションコンテナへのリクエストのポインティング、およびそのエイリアスのロケーションブロックが含まれます。 SSLLabsSecurityHeadersサーバーテストサイトなどでA評価を取得できるようにするセキュリティヘッダー。 これらのヘッダーには、 X-Frame-OptionsX-Content-Type-OptionsReferrer PolicyContent-Security-Policy 、およびX-XSS-ProtectionHTTP Strict Transport Security (HSTS)ヘッダーはコメント化されています。これは、影響を理解し、「プリロード」機能を評価した場合にのみ有効にしてください。

編集が終了したら、ファイルを保存して閉じます。

webserverサービスを再作成する前に、HTTPSに関連するポート情報やDiffie-Hellmanボリューム定義など、docker-compose.ymlファイルのサービス定義にいくつか追加する必要があります。

ファイルを開きます。

nano docker-compose.yml

webserverサービス定義で、次のポートマッピングとdhparamという名前のボリュームを追加します。

〜/ 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

次に、dhparamボリュームをvolumes定義に追加します。

〜/ node_project / docker-compose.yml

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

web-rootボリュームと同様に、dhparamボリュームは、ホストに格納されているDiffie-Hellmanキーをwebserverコンテナーにマウントします。

編集が終了したら、ファイルを保存して閉じます。

webserverサービスを再作成します。

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

docker-compose psでサービスを確認してください。

docker-compose ps

nodejsおよびwebserverサービスが実行されていることを示す出力が表示されます。

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

最後に、ドメインにアクセスして、すべてが期待どおりに機能していることを確認できます。 ブラウザをhttps://example.comに移動し、example.comを自分のドメイン名に置き換えてください。 次のランディングページが表示されます。

ブラウザのセキュリティインジケータにも鍵のアイコンが表示されます。 必要に応じて、SSLLabsサーバーテストのランディングページまたはセキュリティヘッダーサーバーのテストランディングページに移動できます。 含まれている構成オプションは、両方でAの評価をサイトに与えるはずです。

ステップ6—証明書の更新

Let's Encryptの証明書は90日間有効なので、自動更新プロセスを設定して、証明書が失効しないようにする必要があります。 これを行う1つの方法は、cronスケジューリングユーティリティを使用してジョブを作成することです。 この場合、証明書を更新してNginx構成を再読み込みするスクリプトを使用して、cronジョブをスケジュールします。

プロジェクトディレクトリでssl_renew.shというスクリプトを開きます。

nano ssl_renew.sh

次のコードをスクリプトに追加して、証明書を更新し、Webサーバー構成を再ロードします。

〜/ node_project / ssl_renew.sh

#!/bin/bash

COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"

cd /home/sammy/node_project/
$COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af

このスクリプトは、最初にdocker-composeバイナリをCOMPOSEという変数に割り当て、--no-ansiオプションを指定します。このオプションはANSI制御なしでdocker-composeコマンドを実行します。文字。 次に、dockerバイナリでも同じことを行います。 最後に、~/node_projectディレクトリに移動し、次のdocker-composeコマンドを実行します。

  • docker-compose run:これにより、certbotコンテナーが起動し、certbotサービス定義で提供されるcommandがオーバーライドされます。 ここでは、certonlyサブコマンドを使用する代わりに、renewサブコマンドを使用しています。これにより、有効期限が近づいている証明書が更新されます。 スクリプトをテストするために、ここに--dry-runオプションを含めました。
  • docker-compose kill :これにより、SIGHUPシグナルwebserverコンテナーに送信され、Nginx構成が再ロードされます。 このプロセスを使用してNginx構成をリロードする方法の詳細については、Dockerを使用した公式のNginxイメージのデプロイに関するこのDockerブログ投稿を参照してください。

次に、 docker system prune を実行して、未使用のコンテナーとイメージをすべて削除します。

編集が終了したら、ファイルを閉じます。 実行可能にする:

chmod +x ssl_renew.sh

次に、 root crontabファイルを開いて、指定した間隔で更新スクリプトを実行します。

sudo crontab -e 

このファイルを初めて編集する場合は、エディターを選択するように求められます。

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]: 
...

ファイルの最後に、次の行を追加します。

crontab

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

これにより、ジョブ間隔が5分ごとに設定されるため、更新要求が意図したとおりに機能したかどうかをテストできます。 また、ジョブからの関連する出力を記録するために、ログファイルcron.logを作成しました。

5分後、cron.logをチェックして、更新要求が成功したかどうかを確認します。

tail -f /var/log/cron.log

更新が成功したことを確認する出力が表示されます。

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

crontabファイルを変更して、1日の間隔を設定できるようになりました。 たとえば、毎日正午にスクリプトを実行するには、ファイルの最後の行を次のように変更します。

crontab

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

また、ssl_renew.shスクリプトから--dry-runオプションを削除することもできます。

〜/ node_project / ssl_renew.sh

#!/bin/bash

COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"

cd /home/sammy/node_project/
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af

cronジョブは、Let's Encrypt証明書が適格なときに更新することで、証明書が失効しないようにします。 Logrotateユーティリティを使用してログローテーションを設定し、ログファイルをローテーションおよび圧縮することもできます。

結論

コンテナを使用して、Nginxリバースプロキシを使用してNodeアプリケーションをセットアップして実行しました。 また、アプリケーションのドメインのSSL証明書を保護し、cronジョブを設定して、必要に応じてこれらの証明書を更新します。

Let's Encryptプラグインの詳細については、Nginxプラグインまたはスタンドアロンプラグインの使用に関する記事をご覧ください。

次のリソースを参照して、DockerComposeの詳細を確認することもできます。

構成ドキュメントは、マルチコンテナアプリケーションについてさらに学ぶための優れたリソースでもあります。