如何使用基于Node.js映像的Docker容器encryption

我在基于Node.js映像的Docker容器中运行基于Express的网站。 如何使用基于该图像的容器进行encryption ?

我做的第一件事就是创build一个简单的基于expression式的Docker镜像。

我正在使用下面的app.js ,从他们的文档中的express的hello world示例中获取:

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

我已经创build了以下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容器中运行了基本的基于快递的网站,但尚未设置任何TLS。 再次查看expressjs文档,使用TLS时的安全最佳实践是使用nginx。

因为我想介绍一个新的组件(nginx),我会用第二个容器来做。

由于nginx需要一些证书来处理,我们继续用letsencrypt客户端生成这些证书。 有关如何在Docker中使用letsencrypt的letsencrypt文档可以在这里find: 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 

确保replaceLETSENCRYPT_EMAILDNSNAME的值。 电子邮件地址用于过期通知。


现在,我们build立一个将使用这个新生成的证书的nginx服务器。 首先,我们需要一个为TLSconfiguration的nginxconfiguration文件:

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

我们可以把这个configuration文件放到我们自己定制的nginx镜像中,使用下面的Dockerfile:

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

这可以使用以下命令docker build -t expressnginx .docker build -t expressnginx .

接下来,我们将创build一个自定义networking,以便我们可以利用Docker的服务发现function:

 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是否正确docker logs expressnginx

nginxconfiguration文件应该将端口80上的任何请求redirect到端口443.我们可以通过运行以下命令来testing:

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

在这一点上,我们也应该能够成功的TLS连接,并看到我们的Hello World! 回复:

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

现在,build立更新过程。 上面的nginx.conf提供了用于webrootvalidation方法的letsencrypt。well-knownpath的规定。 如果您运行以下命令,它将处理更新。 通常情况下,你可以在某种cron上运行这个命令,这样你的证书在到期之前会被更新:

 export LETSENCRYPT_EMAIL=jefferya@programmerq.net 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 

取决于您的设置,有很多方法来实现这一点。 一种stream行的方法是在你的Docker容器前设置nginx ,并在你的nginxconfiguration中完全处理证书。

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

你可以使用certbot来帮助你设置。

你可以看看这里: https : //certbot.eff.org/docs/using.html?highlight=docker#running-with-docker

那么我亲自做的是:

  1. 创buildDocker卷以存储证书并使用上面的图像生成证书
  2. 创build一个Docker用户定义的networking( https://docs.docker.com/engine/userguide/networking/#/user-defined-networks
  3. 用你的configuration创build基于nginx的图像(也许这将是有用的)
  4. 创build一个基于你的图像的Nginx容器,在其中安装卷并连接到networking(也可以将端口80和443转发到任何你想要的)
  5. 我将为您的node.js应用程序创build一个容器,并将其连接到同一个networking

现在,如果您正确configuration了nginx(指向TLS证书的正确path并将代理指向正确的URL,例如http:// my-app:3210 ),您应该可以通过https访问您的应用。

我最近实现了https,让我们使用nginx进行encryption。 我列出了我遇到的挑战,以及我在这里逐步实施的方式。

挑战

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

克服它的步骤

下面的指南是独立于你有的应用程序的types,因为它只涉及nginx和docker。

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

  • 接下来运行您的容器,并直接在容器上安装包含证书文件的卷。 我在这里回答了一个关于如何做同样的问题。

  • 这将启用您的应用程序的HTTPS。 Incase你无法观察,并且正在使用chrome尝试清除chns的dnscaching

自动更新过程:

  • 我们encryption证书的有效期只有3个月。 在上面的指导中,还configuration了自动续订的步骤。 但是,您必须至less每3个月停止并重新启动您的容器,以确保装载在docker集装箱上的证书是最新的。 (您将不得不重新启动我们在第一步中设置的nginx服务器,以使更新顺利进行)

前端 – NGINX – 其中监听443端口,并代理beck结束

后端 – 你的docker集装箱