web-dev-qa-db-ja.com

Node.jsイメージに基づくDockerコンテナーでLet's Encryptを使用する方法

Node.js image に基づいて、Dockerコンテナで Express -ベースのWebサイトを実行しています。そのイメージに基づくコンテナーで Let's Encrypt を使用するにはどうすればよいですか?

21
jsejcksn

私が最初に行ったことは、シンプルなExpressベースのDockerイメージを作成することです。

私は次のapp.jsを使用しています、それらはドキュメントのExpressの hello world example から取得しました:

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

また、同じドキュメントでpackages.jsonを実行した後、次のnpm initファイルができました。

{
  "name": "exampleexpress",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.14.0"
  }
}

次のDockerfileを作成しました。

FROM node:onbuild
EXPOSE 3000
CMD node app.js

docker buildステップを実行したときの出力は次のとおりです。簡潔にするために、npm installの出力のほとんどを削除しました。

$ docker build -t exampleexpress .
Sending build context to Docker daemon 1.262 MB
Step 1 : FROM node:onbuild
# Executing 3 build triggers...
Step 1 : COPY package.json /usr/src/app/
Step 1 : RUN npm install
 ---> Running in 981ca7cb7256
npm info it worked if it ends with ok
<snip>
npm info ok
Step 1 : COPY . /usr/src/app
 ---> cf82ea76e369
Removing intermediate container ccd3f79f8de3
Removing intermediate container 391d27f33348
Removing intermediate container 1c4feaccd08e
Step 2 : EXPOSE 3000
 ---> Running in 408ac1c8bbd8
 ---> c65c7e1bdb94
Removing intermediate container 408ac1c8bbd8
Step 3 : CMD node app.js
 ---> Running in f882a3a126b0
 ---> 5f0f03885df0
Removing intermediate container f882a3a126b0
Successfully built 5f0f03885df0

このイメージを実行すると、次のようになります。

$ docker run -d --name helloworld -p 3000:3000 exampleexpress
$ curl 127.0.0.1:3000
Hello World!

これをクリーンアップするには、次のようにします:docker rm -f helloworld


非常に基本的なExpressベースのWebサイトをDockerコンテナーで実行していますが、TLSはまだ設定されていません。 expressjsのドキュメントをもう一度見ると、TLSを使用する場合の セキュリティのベストプラクティス はnginxを使用することです。

新しいコンポーネント(nginx)を導入したいので、2番目のコンテナーでそれを行います。

Nginxを操作するにはいくつかの証明書が必要になるため、先に進み、letsencryptクライアントで証明書を生成してみましょう。 Dockerでのletsencryptの使用方法に関するletsencryptのドキュメントは、次の場所にあります。 http://letsencrypt.readthedocs.io/en/latest/using.html#running-with-docker

次のコマンドを実行して、初期証明書を生成します。公衆インターネットに接続されており、letsencryptサーバーからポート80/443に到達できるシステムでこれを実行する必要があります。また、DNS名を設定し、これを実行するボックスをポイントする必要があります。

export LETSENCRYPT_EMAIL=<youremailaddress>
export DNSNAME=www.example.com

docker run --rm \
    -p 443:443 -p 80:80 --name letsencrypt \
    -v "/etc/letsencrypt:/etc/letsencrypt" \
    -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
    quay.io/letsencrypt/letsencrypt:latest \
    certonly -n -m $LETSENCRYPT_EMAIL -d $DNSNAME --standalone --agree-tos

LETSENCRYPT_EMAILDNSNAMEの値は必ず置き換えてください。メールアドレスは有効期限の通知に使用されます。


次に、この新しく生成された証明書を利用するnginxサーバーをセットアップしましょう。まず、TLS用に構成されたnginx構成ファイルが必要です。

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /dev/stdout  main;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  _;
        return 301 https://$Host$request_uri;
    }

    server {
        listen              443 ssl;
        #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        server_name         www.example.com;
        ssl_certificate     /etc/letsencrypt/live/www.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;

        location ^~ /.well-known/ {
            root   /usr/share/nginx/html;
            allow all;
        }

        location / {
            proxy_set_header Host $Host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_pass http://expresshelloworld:3000;
        }
    }
}

この構成ファイルを、次のDockerfileを使用して独自のカスタムnginxイメージに配置できます。

FROM nginx:Alpine
COPY nginx.conf /etc/nginx/nginx.conf

これは次のコマンドでビルドできます:docker build -t expressnginx .

次に、カスタムネットワークを作成して、Dockerのサービス検出機能を利用できるようにします。

docker network create -d bridge expressnet

これで、helloworldコンテナーとnginxコンテナーを起動できます。

docker run -d \
    --name expresshelloworld --net expressnet exampleexpress
docker run -d -p 80:80 -p 443:443 \
    --name expressnginx --net expressnet \
    -v /etc/letsencrypt:/etc/letsencrypt \
    -v /usr/share/nginx/html:/usr/share/nginx/html \
    expressnginx

docker logs expressnginxの出力を見て、nginxが適切に起動したことを再確認します。

Nginx設定ファイルは、ポート80のすべてのリクエストをポート443にリダイレクトする必要があります。次のコマンドを実行することでテストできます。

curl -v http://www.example.com/

この時点で、正常なTLS接続を確立し、Hello World!レスポンスが返されることも確認できます。

curl -v https://www.example.com/

次に、更新プロセスを設定します。上記のnginx.confには、webroot検証方法用のletsencrypt .well-knownパスが用意されています。次のコマンドを実行すると、更新が処理されます。通常、このコマンドはある種のcronで実行し、証明書が期限切れになる前に更新されるようにします。

export [email protected]
export DNSNAME=www.example.com

docker run --rm --name letsencrypt \
    -v "/etc/letsencrypt:/etc/letsencrypt" \
    -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
    -v "/usr/share/nginx/html:/usr/share/nginx/html" \
    quay.io/letsencrypt/letsencrypt:latest \
    certonly -n --webroot -w /usr/share/nginx/html -d $DNSNAME --agree-tos
36
programmerq

設定によっては、これを実現する多くの方法があります。一般的な方法の1つは、Dockerコンテナーの前に nginx をセットアップし、nginx構成内で完全に証明書を処理することです。

Nginx設定には、「usptreams」(Dockerコンテナー)と「サーバー」のリストを含めることができ、これらは基本的にリクエストを特定のアップストリームにマッピングします。そのマッピングの一部として、SSLを処理することもできます。

certbot を使用して、これを設定することができます。

5
duncanhall

私は最近nginxを使用して暗号化しようとhttpsを実装しました。ここでは、私が直面した課題と、実装した方法を順を追って説明します。

チャレンジ

  1. Dockerファイルシステムは短命です。つまり、ビルドを作成するたびに、保存されている証明書、またはコンテナ内で生成された証明書が消えてしまいます。そのため、コンテナー内で証明書を生成するのは非常に困難です。

それを克服するための手順

以下のガイドは、nginxとdockerのみを含むため、お使いのアプリの種類に依存しません。

  • 最初にサーバーにnginxをインストールします(コンテナーではなくサーバーに直接インストールします)。 このガイドに従ってください certbotを使用してドメインの証明書を生成できます。
  • 次に、このnginxサーバーを停止して、アプリのビルドを開始します。コンテナーにnginxをインストールし、Dockerコンテナーのポート80、443を開きます。 (ec2インスタンスでaws openを使用している場合、デフォルトではaws openはポート80のみです)

  • 次にコンテナーを実行し、証明書ファイルを含むボリュームをコンテナーに直接マウントします。私は答えました ここの質問 同じことをする方法について。

  • これにより、アプリでhttpsが有効になります。観察できず、chrome try chromeのDNSキャッシュをクリアする を使用している場合

自動更新プロセス:

  • 証明書を暗号化しましょう。有効期間は3か月のみです。上記のガイドでは、自動更新を設定する手順も設定されています。ただし、Dockerコンテナーにマウントされている証明書が最新であることを確認するには、少なくとも3か月ごとにコンテナーを停止して再起動する必要があります。 (更新をスムーズに行うには、最初のステップで設定したnginxサーバーを再起動する必要があります)
4
Penkey Suresh

あなたはここを見るかもしれません: https://certbot.eff.org/docs/using.html?highlight=docker#running-with-docker

それから私が個人的にすることは:

  1. Dockerボリュームを作成して証明書を保存し、上記のイメージで証明書を生成します
  2. Dockerユーザー定義ネットワークを作成する( https://docs.docker.com/engine/userguide/networking/#/user-defined-networks
  3. あなたの設定でnginxに基づいてイメージを作成します(多分 this が役に立つでしょう)
  4. イメージに基づいてNginxコンテナーを作成し、そのボリュームをマウントしてネットワークに接続します(ポート80と443も必要に応じて転送します)。
  5. 私はあなたのnode.jsアプリのコンテナを作成し、それを同じネットワークに接続します

これで、nginxを正しく構成した場合(TLS証明書の正しいパスをポイントし、 http:// my-app:321 のように正しいURLにプロキシする)、httpsでアプリにアクセスできるはずです。 。

2
Paul Trehiou

フロントエンド-NGINX-443ポートをリッスンし、エンドをプロキシするプロキシ

バックエンド-Dockerコンテナ

1
Denis Lisitskiy