写在之前
本文记录了作者使用docker中的acme.sh为docker中的Nginx添加SSL证书的历程,分别使用了阿里云的解析服务与自建Bind9服务,申请了泛域名证书。
本篇将教你如何设置你的acme.sh从而可以与你的DNS服务器(阿里云解析或者自建的Bind9)进行交互,以及使用docker版的acme.sh自动完成对Nginx容器的证书部署。
acme.sh
acme.sh是github上的一个开源项目,写作本文时它已经收获了近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
来部署证书,然后将申请证书加入定时任务,在证书将要过期时自动完成下一次的证书申请。
环境:
- 安装了docker的Centos 7云主机,并且可以提供https服务(即万事具备,只欠证书)。
- 有一个可用的域名
- 阿里云解析或者自建的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配置文件、网站目录映射到当前文件夹下的html
和conf
中。如果是第一次运行,可以使用下面的命令复制得到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进行加密-阿里云解析篇
1. 向阿里云申请API
在这里简单的登录后,申请一个AccessKey ID
和Access 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申请证书
使用以下命令,docker中的acme.sh将与阿里云服务器交互,自动完成申请泛域名证书的过程。注意将Ali_Key
和Ali_Secret
替换为你在本节第一步申请的AccessKey ID
和Access 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自动部署证书
使用下面的命令,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解析篇
使用这节教程,你需要拥有一个自建的Bind9来提供DNS服务。对于自建的Bind9服务器,acme.sh使用nsupdate工具来完成对DNS的操作,从而验证域所有权。
1. 生成更新DNS的密钥
执行以下命令,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来进行Test
对于自建的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申请证书
先将我们的之前使用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自动部署证书
使用下面的命令,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位,如果您想申请其他长度密钥,可以加入--keylength
:。对于我:
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
|
有参考:
点击链接末尾的回车符可以跳转回引用处~