Docker (docker-compose) 应用程序容器中的 LetsEncrypt 无法正常工作

LetsEncrypt in a Docker (docker-compose) app container not working

我正在为 rails 应用程序使用 docker-compose 来拥有应用程序和数据库容器。为了测试某些应用程序功能,我需要 SSL...所以我将使用 LetsEncrypt 与自签名。

应用使用nginx,服务器ubuntu 14.04 lts,以phusion passenger docker镜像为基础镜像(轻量级debian)

通常使用 LetsEncrypt,我 运行 通常 ./certbot-auto certonly --webroot -w /path/to/app/public -d www.example.com

我的服务器 运行s nginx(将应用程序传递给容器的代理),所以我跳进容器到 运行 certbot 命令没有问题。

但是,当我尝试转到 https://test-app.example.com 时,它不起作用。我不知道为什么。

现场错误(Chrome):

This site can’t be reached

The connection was reset.

Curl 给出了更好的错误:

curl: (35) Unknown SSL protocol error in connection to test-app.example.com

服务器 nginx app.conf

upstream test_app { server localhost:4200; }
server {
  listen 80;
  listen 443 default ssl;
  server_name test-app.example.com;

  # for SSL
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_dhparam /etc/ssl/dhparam.pem;
  ssl_prefer_server_ciphers on;
  ssl_ciphers 'ECDHE-RSA-blahblahblah-SHA';

  location / {
    proxy_set_header Host $http_host;
    proxy_pass http://test_app;
  }
}

容器的nginx app.conf

server {
  server_name _;
  root /home/app/test/public;

  ssl_certificate /etc/letsencrypt/live/test-app.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/test-app.example.com/privkey.pem;

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_dhparam /etc/ssl/dhparam.pem;
  ssl_prefer_server_ciphers on;
  ssl_ciphers 'ECDHE-RSA-blahblah-SHA';

  passenger_enabled on;
  passenger_user app;
  passenger_ruby /usr/bin/ruby2.3;
  passenger_app_env staging;

  location /app_test/assets/ {
    passenger_enabled off;
    alias /home/app/test/public/assets/;

    gzip_static on;
    expires +7d;
    add_header Cache-Control public;
    break;
  }
}

在我的 Dockerfile 中,我有:

# expose port
EXPOSE 80
EXPOSE 443

在我的 docker-compose.yml 文件中我有:

test_app_app:
  build: "."
  env_file: config/test_app-application.env
  links:
  - test_app_db:postgres
  environment:
    app_url: https://test-app.example.com
  ports:
  - 4200:80

docker ps 显示为:

Up About an hour    443/tcp, 0.0.0.0:4200->80/tcp 

我现在怀疑是因为服务器的 nginx - "front-facing" 服务器 - 没有证书,但我不能 运行 没有应用程序位置的 LetsEncrypt 命令。

我尝试了 运行在服务器上使用手动 LetsEncrypt 命令,但因为我可能暴露了端口 80,所以我得到了这个:socket.error: [Errno 98] Address already in use我错过了什么吗?

我该怎么办?

有趣的一个。

我倾向于同意这可能是因为没有获得证书。

首先阅读我最后的免责声明。我会尝试使用 DNS authentication.,恕我直言,对于 Docker 之类的东西,这是一个更好的方法。我想到了一些想法。最简单的回答你的问题的方法是 docker 入口点脚本,它首先获取证书然后启动 nginx:

#!/bin/bash
set -ea

#get cert
./certbot-auto certonly --webroot -w /path/to/app/public -d www.example.com

#start nginx
nginx

恕我直言,这是“不错”的解决方案,但并不是真正的“自动化”(这是让加密目标的一部分)。它并没有真正解决以后更新证书的问题。如果这不是您关心的问题,那么您就可以了。

您可以真正参与并创建一个入口点脚本来检测证书何时过期,然后重新运行命令以更新它,然后重新加载 nginx。

一个更复杂(但也更具可扩展性的解决方案)是创建一个 docker 生活中唯一目的是处理 lets_encrypt 证书和续订的图像,然后提供一种分发方式这些证书到其他容器,例如:nfs(或共享 docker 卷,如果你 真的 小心)。

对于将来阅读本文的任何人:这是在 compose hooks 成为一项可用功能之前编写的,这将是迄今为止处理此类问题的最佳方式。

请阅读此免责声明:

Docker 并不是最好的解决方案,恕我直言。 Docker 图片应该是静态数据。因为 lets encrypt 证书在 3 个月后过期,这意味着您的容器的保质期应为三个月或更短(或者,如我上面所说,用于更新)。 “没关系!”我听到你说。但这也意味着您每次启动容器时都会不断获得颁发的新证书(使用入口点方法)。至少,这意味着以前的证书每次都会被吊销。我不知道使用 Lets Encrypt 执行此操作会产生什么后果。他们可能只会在他们认为有可疑的事情发生之前给你这么多撤销。

我最常做的实际上是使用配置管理并使用 nginx 作为主机系统的“前端”。或者依赖一些其他机制来处理 SSL 终止。但这并没有回答您关于如何让 Lets Encrypt 与 docker 一起工作的问题。 :-)

希望对您有所帮助或为您指明更好的方向。 :-)

我知道我错过了一件小事。如问题中所述,由于服务器上的 nginx 是 'front-facing' nginx,容器的 nginx 专门用于应用程序,因此服务器的 nginx 需要了解 SSL。

答案非常简单。将证书复制过来! (感谢我客户的运营主管)

我在 docker 容器中 cat fullchain.pemprivkey.pem 并在服务器上的 /etc/ssl 中创建了相关文件。

在服务器的 /etc/nginx/sites-enabled/app.conf 我添加了:

  ssl_certificate /etc/ssl/test-app-fullchain.pem;
  ssl_certificate_key /etc/ssl/test-app-privkey.pem;

检查配置并重新启动 nginx。繁荣!工作起来很有魅力。 :)