一些说明
什么时候会用acme?
- 在你想为服务开启https但是又不想花钱的时候
- 在你证书到期又不想手动去替换的时候。
这里的nginx只是个web服务的示例,你也可以使用apache或者其他服务。
(本文中acme=acme.sh,后续申请证书的网址demo为
xxx.com)
准备工作
docker带来维护的便利,在同一个实例(机器)中可以多次部署而不污染宿主机环境。
1. acme.sh
相关链接: Git链接|How to issue a cert|Run acme.sh in docker|docker-acme部署证书到其他容器
- acme申请的免费证书通常是3个月有效期,每60天自动续期。
- 在docker中使用的话,要将acme作为一个docker守护进程运行,然后你就可以用
docker exec acme.sh来执行acme所有命令了,并且用于续期的crontab也运行在容器内而不是宿主机。
acme的两种证书校验模式
- HTTP-01挑战:需要服务器有web服务(通常是nginx或apache,并且需要监听http的80端口,如果你没有web服务,可以使用acme.sh脚本自带的web服务,添加—standalone即可)。缺点是不能做泛域名证书申请,并需要使用80端口的服务。
- DNS-01挑战:需要DNS服务商提供api去生成校验所需的DNS txt记录(临时,校验完会删除),可以生成泛域名证书。缺点是需要服务商提供api(现在大部分DNS服务商都有提供)。ps:cloudflare不支持tk,ml,ga等免费域名使用api去做DNS自动化挑战(即freenom的免费域名都不能使用cloudflare的api进行DNS-01挑战),所以如果你要用cloudflare管理freenom免费域名的DNS,申请https证书的时候就只能使用HTTP-01挑战。
2. nginx
如果你使用付费域名,acme使用DNS-01挑战,那么nginx的配置就非常简单了(因为不需要定制80端口的一条规则,供acme.sh的HTTP-01挑战校验)。
所以这里本文只举使用HTTP-01挑战的例子:
- nginx配置文件
# default.conf
server {
listen 80;
listen [::]:80;
server_name xxx.com;
location /.well-known/acme-challenge {
# acme.sh HTTP-01挑战时,acme.sh会在你指定的路径下生成一个随机文件,供证书颁发机构访问,以证明你对网站的控制权,这里我们使用/var/www/acme-challenge
root /var/www/acme-challenge;
}
}- 上述配置中没有https 443端口的相关配置,因为此时证书还没有申请下来,如果强行加上443的相关配置nginx会报错。
- 此时nginx容器的
/var/www/acme-challenge目录要和acme.sh容器共享。由acme.sh容器生成文件,nginx容器提供外部访问服务。
开始
使用docker-compose组装
都使用docker了,那多容器肯定使用docker-compose了
一. 申请证书
按照上述内容,加入acme.sh和nginx两个服务:
version: '3.4'
services:
acme.sh:
image: neilpang/acme.sh
container_name: acme.sh
command: daemon # docker守护进程
volumes:
- ./acme.sh:/acme.sh # 生成证书的默认位置,映射到宿主机,可能你在其他应用会用到
- ./_acme-challenge:/var/www/acme-challenge # acme和nginx的共享目录
nginx:
image: nginx
ports:
- "80:80"
- "443:443"
restart: always
volumes:
- ./web:/var/www/html
- ./nginx:/etc/nginx/conf.d
# 配合acme.sh的证书申请校验
- ./_acme-challenge:/var/www/acme-challenge # acme和nginx的共享目录
# 可以看到,上述volums配置中,两个容器的`/var/www/acme-challenge`目录实际上是宿主机上的同一个位置,这样在acme.sh生成校验文件的时候,外界通过nginx服务也能访问到校验文件。注意:上述docker-compose.yml并不完整,只是为了展示关键步骤。以下3步可以直接执行,也可以在docker-compose.yml写完整之后(见后文<部署证书>)再执行:
- 在
docker-compose.yml文件所在目录执行:docker-compose up -d,将acme和nginx容器启动。 - 注册账号:
docker exec acme.sh --register-account -m xxxx@gmail.com - 申请证书,二选一:
- 使用http-01申请证书(http-01的方式校验的话,必须加-w参数指定生成校验文件的路径):
docker exec acme.sh --issue -d xxx.com -w /var/www/acme-challenge - 使用dns挑战的方式申请证书,更多细节见x-ui使用acme.sh实现https证书签发和自动续期
- 使用http-01申请证书(http-01的方式校验的话,必须加-w参数指定生成校验文件的路径):
export CF_Key="9af71xx941344" # 这是cf全局key
export CF_Email="abc@foxmail.com"
docker exec acme.sh --issue --dns dns_cf -d xxx.com -d *.xxx.com
# 添加*.xxx.com是申请泛域名证书
二、部署证书
想要把证书部署到nginx,需要在docker-compose.yml增加部分内容(参考文章顶部acme相关链接,主要是指定label和使用label):
version: '3.4'
services:
acme.sh:
image: neilpang/acme.sh
container_name: acme.sh
command: daemon
volumes:
- ./acme.sh:/acme.sh
- /var/run/docker.sock:/var/run/docker.sock # 映射宿主机的docker通信,acme.sh会用此来通知docker守护服务重启nginx服务
- ./_acme-challenge:/var/www/acme-challenge
environment:
- DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=xxx.com
# 指定acme.sh部署证书文件位置,nginx的配置里要同步
- DEPLOY_DOCKER_CONTAINER_KEY_FILE="/acme/key.pem"
- DEPLOY_DOCKER_CONTAINER_CERT_FILE="/acme/cert.pem"
- DEPLOY_DOCKER_CONTAINER_CA_FILE="/acme/ca.pem"
- DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/acme/full.pem"
# 到期续签新证书之后,acme.sh会通知docker守护进程重启nginx
- DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
nginx:
image: nginx
labels:
- sh.acme.autoload.domain=xxx.com
ports:
- "80:80"
- "443:443"
restart: always
volumes:
- ./web:/var/www/html
- ./nginx:/etc/nginx/conf.d
- ./_acme-challenge:/var/www/acme-challenge如上使用最终版docker-compose.yml,在执行申请证书里的3个指令之后,执行如下指令:
4. 部署证书到nginx容器:docker exec acme.sh --deploy -d xxx.com --deploy-hook docker
5. 修改nginx配置,加上https的配置:
server {
listen 80;
listen [::]:80;
server_name xxx.com;
# 续签证书用
location /.well-known/acme-challenge {
root /var/www/acme-challenge;
}
# 重写http流量到https协议上
location / {
rewrite ^/(.*)$ https://$host/$1 permanent;
}
}
# https
server {
listen 443 ssl http2 reuseport;
server_name xxx.com;
root /var/www/html;
# 指定秘钥
ssl_certificate /acme/cert.pem;
ssl_certificate_key /acme/key.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
}
- 重启nginx容器:
docker restart 你的nginx容器名或者docker-compose down && docker-compose up -d - 之后每60天acme会自动续期证书,续期完成后会重启你的nginx服务(不重启新证书不生效)。
结语
nginx在一个应用里扮演的角色只是一个领路人,acme带来的https只是基础建设。所以这两个容器只是你服务的一个基础部分,你可以基于此做很多很多事情,比如x-ui使用acme.sh实现https证书签发和自动续期,当然,都是在docker里的。 如果你同时需要端口转发、ddns等服务,可以考虑lucky使用lucky进行ddns、端口转发、https证书acme续签