如何在基于 Node.js 镜像的 Docker 容器中使用 Let's Encrypt
Posted
技术标签:
【中文标题】如何在基于 Node.js 镜像的 Docker 容器中使用 Let\'s Encrypt【英文标题】: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 与基于该图像的容器一起使用?
【问题讨论】:
【参考方案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
那我个人做的是:
-
创建一个 Docker 卷来存储证书并使用上图生成证书
创建 Docker 用户定义网络 (https://docs.docker.com/engine/userguide/networking/#/user-defined-networks)
根据你的配置创建一个基于 nginx 的镜像(也许this 会有用)
根据您的映像创建一个 Nginx 容器,在其中安装卷并将其连接到网络(还将端口 80 和 443 转发到您想要的任何位置)
我将为您的 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_EMAIL
和 DNSNAME
的值。电子邮件地址用于到期通知。
现在,让我们设置一个 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。我在这里列出了我面临的挑战,以及我逐步实施的方式。
挑战:
-
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 服务器才能顺利进行续订)【讨论】:
以上是关于如何在基于 Node.js 镜像的 Docker 容器中使用 Let's Encrypt的主要内容,如果未能解决你的问题,请参考以下文章