This page looks best with JavaScript enabled

全员docker化!使用docker中的acme.sh为docker中的Nginx添加SSL证书

 ·  ☕ 9 min read  ·  🔮 Yu · 👀... views

写在之前

本文记录了作者使用docker中的acme.sh为docker中的Nginx添加SSL证书的历程,分别使用了阿里云的解析服务与自建Bind9服务,申请了泛域名证书。
本篇将教你如何设置你的acme.sh从而可以与你的DNS服务器(阿里云解析或者自建的Bind9)进行交互,以及使用docker版的acme.sh自动完成对Nginx容器的证书部署。

acme.sh

acme.sh是github上的一个开源项目1,写作本文时它已经收获了近17K颗⭐!它可以自动为你的网站向Let’s Encrypt申请并部署SSL证书,同时自动创建定时任务,在证书将要过期时自动重新申请并部署,其还可以于Nginx或者Apache直接交互完成证书申请过程。

Docker

Docker是一个使用Go语言编写的开源项目。Docker使用起来类似虚拟机,使用它,可以方便的安装、升级服务,而不必担心麻烦的依赖、环境问题。

基本原理:

acme.sh是一个shell程序,我们将使用其docker版本。设置好与我们自己的DNS服务器(阿里云解析或者自建的Bind9)进行交互的密钥后,acme.sh就可以自动向Let’s Encrypt完成DNS验证进行证书申请。申请完成后,docker中的acme.sh将根据我们启动Nginx容器时设置的label来部署证书,然后将申请证书加入定时任务,在证书将要过期时自动完成下一次的证书申请。

环境:

  1. 安装了docker的Centos 7云主机,并且可以提供https服务(即万事具备,只欠证书)。
  2. 有一个可用的域名
  3. 阿里云解析或者自建的Bind9提供DNS服务(二选一),本文将分别介绍使用阿里云的解析服务与自建Bind9两种方法。

缘起:

开始操作!

启动要加密的Nginx的Docker

你的Nginx的docker必须带有label才能被后面的acme.sh识别,从而自动部署。像这样:

1
docker run --rm -it -d  --label=sh.acme.autoload.domain=example.com   nginx:latest

注意,这个容器没有映射任何端口到宿主机,不能正常使用,上面的例子只是说明要加入label,也要注意替换掉example.com。对于我,启动命令是这样的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
docker container run \
  --rm \
  --name mynginx_1 \
  --volume "$PWD/html":/usr/share/nginx/html \
  --volume "$PWD/conf":/etc/nginx \
  --label=sh.acme.autoload.domain=iyu.icu \
  -p 80:80 \
  -p 443:443 \
  -d \
  nginx

这会将docker的nginx配置文件、网站目录映射到当前文件夹下的htmlconf中。如果是第一次运行,可以使用下面的命令复制得到conf、并创建html测试,并且创建certs文件夹给后面acme部署证书做准备

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
docker container run \
  -d \
  --rm \
  --name mynginx \
  nginx
docker container cp mynginx:/etc/nginx .
mv nginx conf
mkdir ./conf/certs
mkdir html
echo "<h1>Hello World</h1>" >> ./html/test.html
docker stop mynginx

在nginx.conf中配置证书文件路径

编辑你docker中的ningx配置文件,对于我是这样的,注意根据你的情况更改高亮行。19、20行对应的是我们将要放入加密证书的路径,其与后面acme.sh的docker时配置的选项必须匹配!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#/etc/conf/conf.d/blog.iyu.icu.conf 
server {
    listen       80;
    server_name  blog.iyu.icu;

    #80跳转到443
    rewrite ^(.*)$ https://${server_name}$1 permanent;

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

server {
    listen 443 ssl http2;
    server_name  blog.iyu.icu;

    ssl_certificate          /etc/nginx/certs/iyu.icu/full.pem;
    ssl_certificate_key      /etc/nginx/certs/iyu.icu/key.pem;

    ssl_session_timeout  5m;

    #开启HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    #适时移除TLSv1.2
    ssl_protocols TLSv1.3 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    location / {
        root   /usr/share/nginx/html/zzo_blog/blog.iyu.icu;
        index  index.html index.htm;
    }

}

使用acme.sh的docker进行加密-阿里云解析篇2

1. 向阿里云申请API

这里简单的登录后,申请一个AccessKey IDAccess Key Secret,妥善保存。

2. 首先创建acme.sh容器

下面的命令将在当前目录创建一个out目录存放一些证书文件,但我们将不会直接使用这些文件

1
2
3
4
5
6
docker run --rm  -itd \
    -v "$(pwd)/out":/acme.sh \
    --net=host \
    --name=acme.sh   \
    -v /var/run/docker.sock:/var/run/docker.sock \
    neilpang/acme.sh daemon

3. 使用acme.sh申请证书3

使用以下命令,docker中的acme.sh将与阿里云服务器交互,自动完成申请泛域名证书的过程。注意将Ali_KeyAli_Secret替换为你在本节第一步申请的AccessKey IDAccess Key Secret,并将expam.com替换为你的域名。如果没用报错,且后续弹出success之类的信息,那么恭喜你,申请就完成了!

1
2
3
4
docker  exec \
    -e Ali_Key="sdjkljlkjfljlfsdbsdfsdfoiwje" \
    -e Ali_Secret="jsnljkljlfdlsdflaklkjflsaa"  \
    acme.sh --dns dns_ali -d expam.com -d "*.expam.com"

4. 使用acme.sh自动部署证书2

使用下面的命令,acme.sh会自动将申请的证书部署到你的Nginx服务器,然后使你的服务器重载配置文件。注意高亮行要替换为你自己的信息(label要与你之前建立Nginx容器时相匹配、证书路径要与你之前写在配置文件中的路径相匹配、域名也要替换成自己的)

1
2
3
4
5
6
7
8
docker  exec \
    -e DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=iyu.icu  \
    -e DEPLOY_DOCKER_CONTAINER_KEY_FILE=/etc/nginx/certs/iyu.icu/key.pem   \
    -e DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/certs/iyu.icu/cert.pem"  \
    -e DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/certs/iyu.icu/ca.pem"   \
    -e DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/certs/iyu.icu/full.pem"  \
    -e DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"   \
    acme.sh --deploy -d iyu.icu  --deploy-hook docker

5. 完成!

现在打开你的网站,浏览器会显示你的网站证书有效!

该证书有3个月的有效期,快到期时,acme.sh将自动重新申请并部署证书与重载Nginx。

使用acme.sh的docker进行加密-自建Bind9解析篇24

使用这节教程,你需要拥有一个自建的Bind9来提供DNS服务。对于自建的Bind9服务器,acme.sh使用nsupdate工具来完成对DNS的操作,从而验证域所有权。

1. 生成更新DNS的密钥35

执行以下命令,dnssec-keygen工具将为我们生成密钥,密钥放在/etc/named/keys/update.key,确保您已经创建好相关文件夹。

1
2
3
4
5
6
7
8
b=$(dnssec-keygen -a hmac-sha512 -b 512 -n USER -K /tmp foo)
cat > /etc/named/keys/update.key <<EOF
key "update" {
    algorithm hmac-sha512;
    secret "$(awk '/^Key/{print $2}' /tmp/$b.private)";
};
EOF
rm -f /tmp/$b.{private,key}
上面的命令将使用entropy available on your system,类似根据系统行为来产生随机数,而不是直接使用伪随机数。对于空闲的系统,这将持续很久。这时你可以在另外一个窗口执行几个任务,比如find /,在密钥生成完后取消任务即可。

2. 在你的Bind9配置文件include你的证书,并允许更新

在你的Bind9配置文件简单添加以下行,include你的证书

include "/etc/named/keys/update.key";

在你Bind9对应的域名配置文件中写入如下内容来允许使用update这个名称的key进行更新。

zone "example.com" {
    type master;
    allow-update { key "update"; };
};

先检查一下Bind9的配置文件是否正确:named-checkconf

然后执行以下命令使你的配置生效:service named restart

3. 使用nsupdate来进行Test4

对于自建的Bind9服务器,acme.sh使用nsupdate工具来完成对DNS的操作,从而验证域所有权,所以我们需要先测试一下使用nsupdate能否对我们的DNS进行操作。

先确保你已经安装了nsupdate,以下命令将使用nsupdate工具向Bind9服务器的iyu.icu域添加一条A记录。status: NOERROR则测试添加成功。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[root@host ~]# nsupdate -k /etc/named/keys/update.key -v
> server iyu.icu
> debug yes
> zone iyu.icu
> update add mytest.iyu.icu 600 A 233.233.233.233
> show
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;iyu.icu.                       IN      SOA

;; UPDATE SECTION:
mytest.iyu.icu.         600     IN      A       233.233.233.233

> send
Sending update to 173.242.114.3#53
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:   7842
;; flags:; ZONE: 1, PREREQ: 0, UPDATE: 1, ADDITIONAL: 1
;; ZONE SECTION:
;iyu.icu.                       IN      SOA

;; UPDATE SECTION:
mytest.iyu.icu.         600     IN      A       233.233.233.233

;; TSIG PSEUDOSECTION:
update.                 0       ANY     TSIG    hmac-sha512. 1585046604 300 64 0qoDKLJLShAvf1WAsjzZQBDsHzTxGMnfTzqDNAtA2IONCbhedf2tYgNC 9ruazu9U/edqT2J/wcaL3X0t4eMrnQ== 7842 NOERROR 0


Reply from update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:   7842
;; flags: qr; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 1
;; ZONE SECTION:
;iyu.icu.                       IN      SOA

;; TSIG PSEUDOSECTION:
update.                 0       ANY     TSIG    hmac-sha512. 1585046604 300 64 +E2HbxEJ6lj/Pt/qeFUVXDz96YfQ9F0fzXOCeETyxyKq1ETcT62jOL8o 1rCn8YKcOvHUSm+1ofQo49O8VVgWNy== 7842 NOERROR 0


> [Ctrl-D]

再让我们用host命令测试一下:

1
2
3
4
5
6
7
[root@host ~]# host mytest.iyu.icu iyu.icu
Using domain server:
Name: iyu.icu
Address: 173.242.114.3#53
Aliases:

mytest.iyu.icu has address 233.233.233.233

可以看到服务器成功返回了我们之前添加的记录233.233.233.233!接下来的步骤与之前使用阿里云解析相差无几,可以认为我们前面几步是做了阿里云为我们做的事情。

然后把这条记录删除掉:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[root@host ~]# nsupdate -k /etc/named/keys/update.key -v
> server iyu.icu
> debug yes
> zone iyu.icu
> update delete mytest.iyu.icu A
> show
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;iyu.icu.                       IN      SOA

;; UPDATE SECTION:
mytest.iyu.icu.         0       ANY     A

> send
Sending update to 173.242.114.3#53
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:  61286
;; flags:; ZONE: 1, PREREQ: 0, UPDATE: 1, ADDITIONAL: 1
;; ZONE SECTION:
;iyu.icu.                       IN      SOA

;; UPDATE SECTION:
mytest.iyu.icu.         0       ANY     A

;; TSIG PSEUDOSECTION:
update.                 0       ANY     TSIG    hmac-sha512. 1585090128 300 64 MqZNjyT4pY62xe7RfOnctvN6Tj26qwMBlXGmTK6aUgCmUgfcRVO3hXsc ZaI5ZTNTvx5GQK/++Kg9B9j87bmsQg== 61286 NOERROR 0


Reply from update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:  61286
;; flags: qr; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 1
;; ZONE SECTION:
;iyu.icu.                       IN      SOA

;; TSIG PSEUDOSECTION:
update.                 0       ANY     TSIG    hmac-sha512. 1585090128 300 64 XjN3U0SZjAU//2FqFqsCa/wzr5P8XMr6Ef/lPC3igbQ3X52Ii619ApmG UELbPXTGtP0KVgA/nY40tZGvoDq0pw== 61286 NOERROR 0


> [Ctrl-D]

可以看到,现在这条记录已经蒸发了,再次查询服务器显示没有记录~

1
2
3
4
5
6
7
[root@host ~]# host mytest.iyu.icu iyu.icu
Using domain server:
Name: iyu.icu
Address: 173.242.114.3#53
Aliases:

Host mytest.iyu.icu not found: 3(NXDOMAIN)

现在,万事具备只欠东风

6. 首先创建acme.sh容器

下面的命令将在当前目录创建一个out目录存放一些证书文件,但我们将不会直接使用这些文件

1
2
3
4
5
6
docker run --rm  -itd \
    -v "$(pwd)/out":/acme.sh \
    --net=host \
    --name=acme.sh   \
    -v /var/run/docker.sock:/var/run/docker.sock \
    neilpang/acme.sh daemon

7. 使用acme.sh申请证书3

先将我们的之前使用dnssec-keygen工具生成的密钥放入当前路径下的out文件夹,后续才能传递给acmd.sh

1
cp /etc/named/keys/update.key ./out/

使用以下命令,docker中的acme.sh将与我们自建的Bind9服务器交互,自动完成DNS验证、申请泛域名证书的过程。注意替换域名和NS服务器的域名为你自己的域名。如果没有报错,且后续弹出success之类的信息,那么恭喜你,申请就完成了!

1
2
3
4
5
docker  exec \
    -e NSUPDATE_SERVER="ns1.iyu.icu" \
    -e NSUPDATE_KEY="/acme.sh/update.key" \
    -e NSUPDATE_ZONE="iyu.icu" \
    acme.sh --issue --dns dns_nsupdate -d iyu.icu -d "*.iyu.icu" 

8. 使用acme.sh自动部署证书2

使用下面的命令,acme.sh会自动将申请的证书部署到你的Nginx服务器,然后是你的服务器重载配置文件。注意高亮行要替换为你自己的信息(label要与你之前建立Nginx容器时相匹配、证书路径要与你之前写在配置文件中的路径相匹配、域名也要替换成自己的)

1
2
3
4
5
6
7
8
docker  exec \
    -e DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=iyu.icu  \
    -e DEPLOY_DOCKER_CONTAINER_KEY_FILE=/etc/nginx/certs/iyu.icu/key.pem   \
    -e DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/certs/iyu.icu/cert.pem"  \
    -e DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/certs/iyu.icu/ca.pem"   \
    -e DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/certs/iyu.icu/full.pem"  \
    -e DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"   \
    acme.sh --deploy -d iyu.icu  --deploy-hook docker

9. 完成!

现在打开你的网站,浏览器会显示你的网站证书有效!

该证书有3个月的有效期,快到期时,acme.sh将自动重新申请并部署证书与重载Nginx。

最终测试

如果你想测试自己的Nginx提供的SSL服务是否安全,可以进入SSL Server Test (Powered by Qualys SSL Labs)测试,如果你采用了和我相同的Nginx.conf配置文件,您应该会收到以下结果:

A+

PS:

您可能会发现我的RSA密钥是4096位的,acme.sh默认是2048位,如果您想申请其他长度密钥,可以加入--keylength6:。对于我:

1
2
3
4
5
docker  exec \
    -e NSUPDATE_SERVER="ns1.iyu.icu" \
    -e NSUPDATE_KEY="/acme.sh/update.key" \
    -e NSUPDATE_ZONE="iyu.icu" \
    acme.sh --issue --dns dns_nsupdate -d iyu.icu -d "*.iyu.icu" --keylength 4096

有参考:

点击链接末尾的回车符可以跳转回引用处~


  1. acmesh-official/acme.sh: A pure Unix shell script implementing ACME client protocol 访问日期:2020年3月24日
    https://github.com/acmesh-official/acme.sh ↩︎

  2. deploy to docker containers · acmesh-official/acme.sh Wiki 访问日期:2020年3月24日
    https://github.com/acmesh-official/acme.sh/wiki/deploy-to-docker-containers ↩︎

  3. dnsapi · acmesh-official/acme.sh Wiki 访问日期 2020年3月24日
    https://github.com/acmesh-official/acme.sh/wiki/dnsapi ↩︎

  4. Let’s Encrypt wildcard certificates, acme.sh and automated DNS verification – The ongoing struggle 访问日期 2020年3月24日
    https://strugglers.net/~andy/blog/2018/03/19/lets-encrypt-wildcard-certificates-acme-sh-and-automated-dns-verification/#put-the-new-zone-into-the-bind-config ↩︎

  5. Bug #963368 “dnssec-keygen takes forever to generate a keyfile” : Bugs : bind9 package : Ubuntu 访问日期 2020年3月24日
    https://bugs.launchpad.net/ubuntu/+source/bind9/+bug/963368 ↩︎

  6. Options and Params · acmesh-official/acme.sh Wiki 访问日期 2020年3月26日
    https://github.com/acmesh-official/acme.sh/wiki/Options-and-Params ↩︎


Yu
WRITTEN BY
Yu
🎓 College Students 📐Physics 💾 Programmer