轶哥小站

妄图改变世界的全栈程序员。

Docker搭配免费SSL证书

MySQL定时备份程序中我有提到一个基于Docker快速生成SSL证书的方式。该文章整理了该方案的详细教程。

基于Let's Encrypt免费SSL证书。

官方介绍

docker-letsencrypt-nginx-proxy-companion

官方示例

使用方法

非docker-compose模式下。

第一步:执行下述两条命令

docker run --detach \
    --name nginx-proxy \
    --publish 80:80 \
    --publish 443:443 \
    --volume /etc/nginx/certs \
    --volume /etc/nginx/vhost.d \
    --volume /usr/share/nginx/html \
    --volume /var/run/docker.sock:/tmp/docker.sock:ro \
    jwilder/nginx-proxy

docker run --detach \
    --name nginx-proxy-letsencrypt \
    --volumes-from nginx-proxy \
    --volume /var/run/docker.sock:/var/run/docker.sock:ro \
    jrcs/letsencrypt-nginx-proxy-companion

第二步:启动项目时加上相关环境变量

以Nginx静态资源站点为例:

docker run -itd --restart always \
      -v 你的静态资源文件:/usr/share/nginx/html \
    --env "VIRTUAL_PORT=80" \
    --env "VIRTUAL_HOST=你的域名" \
    --env "LETSENCRYPT_HOST=你的域名" \
    nginx

此时,直接访问LETSENCRYPT_HOST中的域名,显示证书正常即可(可能有一分钟左右延迟)。

第三步:重复第二步

是不是很简单,几乎零配置,连修改nginx配置的工夫都省了。

环境变量说明

VIRTUAL_PORT:您的项目监听的端口。这里有个坑,某些情况下监听端口不一定成功。建议每次暴露内部端口,例如增加-p 80参数,确保监听一定成功。详见Node.js示例。

VIRTUAL_HOST:站点域名,可多个,以英文逗号分割。

LETSENCRYPT_HOST:申请SSL证书的域名,可多个,以英文逗号分割。

LETSENCRYPT_EMAIL:通知邮箱,选填。(很多情况下填了也没用)

docker-compose示例参考:https://github.com/nginx-proxy/docker-letsencrypt-nginx-proxy-companion/blob/master/docs/Docker-Compose.md

更多示例用法

Node.js 项目

docker run -itd --name appointment-service --restart always \
    -m 1024m \
    --link mysql:mysql \
    -p 80 \
    --env "VIRTUAL_PORT=80" \
    --env "VIRTUAL_HOST=appointment.api.ykfz.pw" \
    --env "LETSENCRYPT_HOST=appointment.api.ykfz.pw" \
    -v /etc/localtime:/etc/localtime:ro \
    -v /root/site/appointment-service:/root \
    -w /root \
    --restart=always node:13.7.0 npm start

说明:-p 80参数将明确暴露容器内80端口,随机转发到宿主机某一端口。缺少该参数将可能导致监听失败出现502报错

泛解析用法

由于申请免费通配符证书需要ACMEv2支持,以上方法暂不适用于通配符域名(原因)。

解决思路:

  1. 参考另外一篇博文配置自动续期的免费通配符SSL证书
  2. 未测试的方案:https://github.com/adferrand/docker-letsencrypt-dns
  3. 推荐搭配acme.sh实现该需求。

思路3具体操作如下:

  1. 按照acme.sh说明安装acme.sh。通常只需要执行curl https://get.acme.sh | sh命令。
  2. 根据https://github.com/acmesh-official/acme.sh/wiki/dnsapi 寻找你的域名DNS提供商,参照说明配置API账号。例如腾讯云则参考DNSPod配置方式,阿里云则参考第十一条Aliyun的配置方式。如果您使用的DNS解析提供商不在该列表,可以在github搜搜看。
  3. 生成SSL证书。例如acme.sh --issue --dns dns_dp -d *.app.ykfz.pw

将得到:

[2020年 03月 07日 星期六 21:40:19 CST] Your cert is in  /root/.acme.sh/*.app.ykfz.pw/*.app.ykfz.pw.cer
[2020年 03月 07日 星期六 21:40:19 CST] Your cert key is in  /root/.acme.sh/*.app.ykfz.pw/*.app.ykfz.pw.key
[2020年 03月 07日 星期六 21:40:19 CST] The intermediate CA cert is in  /root/.acme.sh/*.app.ykfz.pw/ca.cer
[2020年 03月 07日 星期六 21:40:19 CST] And the full chain certs is there:  /root/.acme.sh/*.app.ykfz.pw/fullchain.cer
  1. 下载此代码https://gist.github.com/yi-ge/6c13dfddabd128a630bea9481ac8fb98nginx.tmpl文件或参考jtegtmeier提供的配置方法:https://github.com/nginx-proxy/nginx-proxy/commit/07655df85884b4ee7937a422ccd33b413b584a02

感谢jtegtmeier提供的方案!

  1. 以类似下述命令的方式启动nginx-proxy
docker run --detach \
    --name nginx-proxy \
    --publish 80:80 \
    --publish 443:443 \
    --volume /etc/nginx/certs \
    --volume /etc/nginx/vhost.d \
    --volume /usr/share/nginx/html \
    --volume /var/run/docker.sock:/tmp/docker.sock:ro \
    -v `pwd`/nginx.tmpl:/app/nginx.tmpl \
    -v /root/.acme.sh:/root/.acme.sh \
    jwilder/nginx-proxy

增加了acme路径映射,请确保nginx.tmpl在执行文件的目录下。

  1. 增加环境变量配置并启动容器。
docker run -itd -m 1024m \
  --restart=always \
  --name appointment-ui \
  -v /root/site/appointment-ui:/usr/share/nginx/html \
  --env "VIRTUAL_HOST=*.app.ykfz.pw" \
  --env "CERT_PATHNAME=/root/.acme.sh/*.app.ykfz.pw/*.app.ykfz.pw.cer" \
  --env "PRIVATE_KEY_PATHNAME=/root/.acme.sh/*.app.ykfz.pw/*.app.ykfz.pw.key" \
  --env "CHAIN_PATHNAME=/root/.acme.sh/*.app.ykfz.pw/fullchain.cer" \
  nginx     

请参考第二步得到的证书,对应设置证书路径。

Custom pathnames are set on the virtual hosts using these environment variables:

  • CERT_PATHNAME=/path/to/cert
  • PRIVATE_KEY_PATHNAME=/path/to/key
  • CHAIN_PATHNAME=/path/to/chain

注意,此处有个坑。请检查crontab配置,在自动更新SSL证书的配置下方增加nginx重启指令。

例如:

49 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
55 0 * * * /usr/bin/docker restart nginx-proxy

如果未使用nginx-proxy(即使用的是本地nginx),则参考添加如下命令:

17 0 1 * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
22 0 1 * * /use/sbin/nginx -s reload

配置完毕。

总结: 免费泛解析SSL证书必须修改DNS进行验证,每个季度一次。无论是手工修改DNS还是通过DNS API,都请做一个TODO List,在3个月后检查一下自己的网站是否正常运行。

评论功能已被暂时关闭,请通过 a@wyr.me 联系我。

  上一篇
下一篇 (Windows应用以管理员权限开机自启(开源))