node.js - 如何在基于 Node.js 镜像的 Docker 容器中使用 Let's Encrypt

标签 node.js docker https lets-encrypt

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

最佳答案

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

运行以下命令以生成初始证书。您需要在连接到公共(public)互联网的系统上运行它,并且可以从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

关于node.js - 如何在基于 Node.js 镜像的 Docker 容器中使用 Let's Encrypt,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39846649/

相关文章:

node.js - Sequelize DataTypes.STRING 的意外标记

jenkins - 运行Jenkins从站进行Dev/Test/Prod的优点/缺点?

node.js - HTTP 重定向到不同端口的 HTTPS

https 背后的 Grails 应用程序

javascript - 当 promise 在 Redux 中间件内解析时,为什么我无法调度操作?

node.js - 为什么要使用 SQL/NoSQL 数据库而不是编写自己的服务器端存储?

docker - 如何通过docker容器正确使用Silex 2,Doctrine和PDO?

ssl - 系统无法从 xxxx url 推断传输信息

javascript reSTLer 如何阻止/等待请求

postgresql - 在 Minikube 上挂载 Postgres 的主机目录 - 权限问题