【问题标题】:How to use Let's Encrypt with Docker container based on the Node.js image如何在基于 Node.js 镜像的 Docker 容器中使用 Let's Encrypt
【发布时间】:2017-02-12 06:51:58
【问题描述】:

我正在基于Node.js image 的Docker 容器中运行基于Express 的网站。如何将Let's Encrypt 与基于该图像的容器一起使用?

【问题讨论】:

    标签: node.js docker https lets-encrypt


    【解决方案1】:

    根据您的设置,有多种方法可以实现此目的。一种流行的方法是在 Docker 容器前设置 nginx,并完全在 nginx 配置中处理证书。

    nginx 配置可以包含“usptreams”(您的 Docker 容器)和“服务器”列表,它们基本上将请求映射到特定的上游。作为该映射的一部分,您还可以处理 SSL。

    您可以使用certbot 来帮助您进行设置。

    【讨论】:

    • 我正在寻找更多的分步指南来实现我所描述的。
    • 您不太可能实现这一点 - 有太多变体,Stack Overflow 的答案并不是“循序渐进的指南”。如果您描述您尝试过的内容并提出具体问题,您可能会得到更好的回应,
    • 本指南linode.com/docs/web-servers/nginx/use-nginx-reverse-proxy 完全涵盖了这个主题:“使用 NGINX 作为反向代理”。目前,我有一个在 8082 端口上运行的 Docker 容器,它前面有 NGINX,它通过 Certbot 向互联网提供 HTTPS。真的很容易设置
    【解决方案2】:

    你可以看看这里: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 证书的正确路径并代理到正确的 URL,例如 http://my-app:3210),您应该可以在 https 中访问您的应用程序。

    【讨论】:

      【解决方案3】:

      我做的第一件事是创建一个简单的基于 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!');
      });
      

      在同一个文档中运行npm init 后,我还得到了以下packages.json 文件:

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


      现在,我已经在 Docker 容器中运行了非常基本的基于 express 的网站,但它还没有设置任何 TLS。再看 expressjs 文档,security best practice 使用 TLS 时就是使用 nginx。

      由于我想引入一个新组件(nginx),我将使用第二个容器来实现。

      由于 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 LETSENCRYPT_EMAIL=me@example.com
      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
      

      【讨论】:

        【解决方案4】:

        前端 - NGINX - 监听 443 端口,并代理到后端

        后端 - 你是 docker 容器

        【讨论】:

          【解决方案5】:

          我最近使用 nginx 加密实现了 https。我在这里列出了我面临的挑战,以及我逐步实施的方式。

          挑战

          1. Docker 文件系统是短暂的。这意味着每次构建后,存储或在容器内生成的证书都会消失。所以在容器内生成证书是非常棘手的。

          克服它的步骤

          以下指南与您拥有的应用程序类型无关,因为它仅涉及 nginx 和 docker。

          • 首先在您的服务器上安装 nginx(不是在容器上,而是直接在服务器上。)您可以follow this guide 使用 certbot 为您的域生成证书。
          • 现在停止这个 nginx 服务器并开始构建您的应用程序。在您的容器上安装 nginx,并在您的 docker 容器上打开端口 80、443。 (如果在 ec2 实例上使用 aws open,默认情况下 aws 仅打开端口 80)

          • 接下来运行您的容器并将包含证书文件的卷直接安装在容器上。我已经回答了a question here 如何做同样的事情。

          • 这将在您的应用上启用 https。如果您无法观察,并且正在使用 chrome,请尝试 clearing dns cache for chrome

          自动续订流程:

          • Let's encrypt 证书有效期仅为 3 个月。在上述指南中,还设置了配置自动续订的步骤。但是您必须至少每 3 个月停止并重新启动您的容器,以确保安装在您的 docker 容器上的证书是最新的。 (您必须重新启动我们在第一步中设置的 nginx 服务器才能顺利进行续订)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-09-25
            • 2018-04-29
            • 1970-01-01
            • 2016-08-16
            • 1970-01-01
            • 2019-08-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多