https证书自动续订(renew)问题
Table of Contents
之前写过一篇文章:为你的网站配置免费的HTTPS支持,看本文之前,建议先看一下这篇文章。
前面文章讲的方法主要是无法自动续订通配符证书,其原因是通配符证书需要向域名服务商添加一个txt记录,其实用前面文章中的certbot renew
也是可以的,只是要指定更新域名服务商txt记录的shell脚本,该脚本需要调用域名服务商提供的接口,如果你熟悉shell,完全可以自己写,如果你不想麻烦,已经有人帮写好了一个工具:acme.sh。
配置步骤
再简单说一下配置步骤:
- 1.获取https证书(即ssl证书,前面说了,http是用ssl或tls来加密数据包的,所以我们要先获取ssl证书)
- 2.在http服务器(如nginx、apache等)中配置使用ssl证书
- 3.过期前自动更新证书(证书是有时效的,比如letsencrypt的证书有效期是90天)
http证书的类型
https证书有指定域名的证书和通配符证书,指定域名如:xiebruce.com
和www.xiebruce.com
就是指定域名,而*.xiebruce.com
即为通配符证书。通配符证书的好处,就在于,如果你后面又增加了几个网站子域名,比如aaa.xiebruce.com
、bbb.xiebruce.com
,你就可以直接用通配符证书,否则你每增加一个子域名,就要申请一次证书,特别麻烦。
既然通配符证书简单,是不是只申请通配符证书就行了呢?是的,但要注意通配符证书是不包括自解析的,比如本博客的域名为xiebruce.top
或www.xiebruce.top
,有www的,可以用*.xiebruce.top
通配符证书,但没有www的是无法使用通配符证书的,因为即使*
是空,那也只能匹配.xiebruce.top
,最前面多了一个点,所以无法匹配。
所以我们需要给通配符证书同时设置两个域名(一个证书是可以设置多个域名的),一个当然就是通配域名*.xiebruce.top
,另一个就是不包括在通配域名中的xiebruce.top
。
另外如果你的域名的顶级域名是.cf
, .ga
, .gq
, .ml
, .tk
,并且接入了cloudflare,那么是无法获取通配符证书的。
获取https证书的方法
获取数字证书一般都是用ACME(Automatic Certificate Management Environment )自动证书管理环境工具来获取,这样的工具有多种,比如:
- certbot
- certbot-auto
- acme.sh
- certbot-letencrypt-wildcardcertificates-alydns-au
鉴于大部分情况下都需要使用通配符证书,所以前面两个并不合适(无法自动更新通配符证书),如果是阿里云买的域名或国外买的域名,可以用第三个acme.sh
,最后一个可用于阿里、腾讯云、GoDaddy的域名,因为我的是阿里云买的域名,我这里使用第三个,即acme.sh
(后面两个工具都是国人写的)。
安装ssl证书获取工具(acme.sh)
acme.sh的github最初是https://github.com/Neilpang/acme.sh,但现在它会自动跳转到https://github.com/acmesh-official/acme.sh,好像是“Neilpang”加入了官方开发组。
安装acme.sh:
curl https://get.acme.sh | sh
安装.acme.sh会自动安装到~/.acme.sh
目录中(注意它是一个目录,不是单个文件),也就是说,如果你是用root用户安装,那么会安装到/root/.acme.sh
目录中,如果是用普通用户,比如你的用户名是zhangsan
,那么它将会被安装到/home/zhangsan/.acme.sh
目录中。
安装中出现的这三句红色的提示不用管它,只有你用standalone模式获取证书时才需要安装socat(但我们不会用这种方式获取):
[2019年 02月 15日 星期五 01:04:08 CST] It is recommended to install socat first.
[2019年 02月 15日 星期五 01:04:08 CST] We use socat for standalone server if you use standalone mode.
[2019年 02月 15日 星期五 01:04:08 CST] If you don't use standalone mode, just ignore this warning.
安装完它会自动在.bashrc
或.zshrc
的最后添加以下命令(这是添加环境变量,这样你就可以直接使用acme.sh
命令了):
. "/home/xiebruce/.acme.sh/acme.sh.env"
你只要source一下就可以使用了,如果你用的是bash:
source ~/.bashrc
如果你使用的是zsh:
source ~/.zshrc
source之后,试一下(如果正常,则会出来帮助选项):
acme.sh -h
设置默认CA(证书颁发机构)为letsencrypt
acme.sh --set-default-ca --server letsencrypt
最初默认CA是letsencrypt,后来改为了ZeroSSL,但由于ZeroSSL每个账号只能免费获取3个证书,对我来说远远不够(因为我有免费域名,无法获取通配符证书),所以我需要修改为letsencrypt。
DNS方式获取通配符证书
获取证书有两种:
- DNS-01:申请证书过程中,证书申请工具(比如acme.sh)会向解析域名的网站添加一个TXT记录
_acme-challenge.example.com
用于验证域名(验证完会自动删除),所以这依赖于域名解析网站,这种方式可以申请通配符证书; - HTTP-01:不依赖于域名解析网站,只需要证书申请工具即可或结合web服务器(如nginx),但无法申请通配符证书。
一般域名都可以用DNS-01方式获取,只有少数不支持,目前我知道的是freenom注册的cf,ml,tk,ga,gq这些域名,在Cloudflare上都无法用DNS-01方式。
建议:能申请通配符证书一定要申请通配符证书,因为假如你后面又添加了新域名,那么就不用重新申请证书了,直接就能用,但由于申请通配符证书必须使用DNS-01方式,所以相当于是建议你使用DNS-01方式申请证书,当然如果你的域名是freenom注册的cf,ml,tk,ga,gq这些域名,那就没办法,只能用HTTP-01方式申请非通配符证书。
阿里云中获取证书:
先从阿里云中获取“AccessKey ID”和“Access Key Secret”:https://ak-console.aliyun.com/#/accesskey。
在你的~/.bashrc
或.zshrc
文件中添加阿里云的“AccessKey ID”和“Access Key Secret”(添加后source一下,跟前面一样,不再赘述):
export Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export Ali_Secret="jlsdflanljkljlfdsaklkjflsa"
设置默认ca为letsencrypt(因为不设置的话, 它默认已经是ZeroSSL,而)
acme.sh --set-default-ca --server letsencrypt
如果不修改默认ca,那就要用ZeroSSL的方法:Using ZeroSSL.com CA。
然后获取*.xiebruce.top
的通配符证书,注意要加上xiebruce.top
,而且要它放第一个(否则生成的证书目录名会被命名成有*号的那个,目录名有*号不太好),因为它是无法匹配通配符证书的:
# 申请rsa证书
acme.sh --issue --dns dns_ali -d xiebruce.top -d '*.xiebruce.top'
# --keylength ec-256 表示申请ecc证书(还有ec-384,ec-521也是ecc证书长度,但我们用ec-256就行了)
# 建议申请ecc证书,因为ecc证书速度更快,据说可以快几十倍
acme.sh --issue --dns dns_ali -d xiebruce.top -d '*.xiebruce.top' --keylength ec-256
另外,它会等待120秒(这个时间以后有可能会改,而且可以手动用--dnssleep xx
来指定,xx是整数,代表等待多少秒),原因是要等阿里云的txt记录解析生效,其实不需要这么久,不过为了保证成功率,还是等久一点好。
注意,如果是namesilo,因为它是每15分钟(900秒)向外同步一次,所以我们要设置--dnssleep=910
,超过900,这样容易让它识别到添加的txt记录
acme.sh --issue --dns dns_namesilo --dnssleep 910 -d xiebruce.top -d '*.xiebruce.top'
其它平台的具体可以看这里dnsapi。
从CloudFlare获取证书:
点击CloudFlare的右上角头像→点击下拉菜单中的My Profile
,滚到差不多到底部,你就可以看到一个“Global API Key”和一个“Origin CA Key”,“Global API Key”就是我们要的Key:
在你的~/.bashrc
或.zshrc
文件中添加CloudFlare的这两个变量(任何位置都行):
export CF_Email="[email protected]"
export CF_Key="85302dsiorw3oisjdkf899234sdf72dfdb46"
然后命令是一样的,只不过--dns
指定的api为dns_cf
(查看~/.acme.sh/dnsapi/
目录,里面有所有支持的api):
acme.sh --issue --dns dns_cf -d xiebruce.top -d '*.xiebruce.top'
# 申请ecc证书
acme.sh --issue --dns dns_cf -d xiebruce.top -d '*.xiebruce.top' --keylength ec-256
至于为什么阿里云要的是Ali_Key
和Ali_Secret
,而CloudFlare要的是CF_Email
和CF_Key
?查看~/.acme.sh/dnsapi/
下的api文件,打开对应的文件,比如阿里的就是dns_ali.sh
,CloudFlare的就是dns_cf.sh
,打开文件自然能看到里面需要什么变量。
HTTP方式获取证书
http方式不需要依赖域名所在网站或者域名解析网站,在自己服务器上就能获取,它有standalone(独立运行模式)和webroot模式(借助web服务器,如nginx,apache,caddy等等)。
如果你的顶级域名是.cf
, .ga
, .gq
, .ml
, .tk
(这些都是freenom的免费域名),并且接入了cloudflare,那么cloudflare是关闭了这些域名的DNS获取方式的接口的,所以你无法使用前面说的方式获取证书,就必须用这里介绍的webroot模式。
webroot模式:
webroot模式在申请之前需要用nginx配置监听80端口,设置一个location,并且保证root就是执行acme.sh时的webroot值,因为acme.sh获取证书时,会在你指定的nginx root目录下创建一个文件(带多层文件夹)
/.well-known/acme-challenge/
假设你nginx的root值为root /data/wwwroot/well-know
,则acme.sh创建的文件路径为(最后那个文件名不是固定的)
/data/wwwroot/well-know/.well-known/acme-challenge/z0cTIz2zcORh47L0EwrSXKKoJxKMUTFK0EJIMrRWYaA
然后letsencrypt会向你服务器的这个路径发起一个http请求(访问你服务器的80端口),来访问你服务器中的z0cTIz2zcORh47L0EwrSXKKoJxKMUTFK0EJIMrRWYaA
这个文件,并验证它的正确性,验证完成后,acme.sh会把它创建的文件夹删除掉(清理现场,恢复原样,就当它没有创建过一样)。
为了保证letsencrypt一定能通,可以自己在webroot下创建/.well-known/acme-challenge/
这个目录,并在里面放个index.html文件,这样方便测试,必须保证所有server_name都能访问到index.html文件
# 80端口,用于acme.sh获取tls证书以及跳转到443
server {
listen 80;
server_name aa.test.com bb.test.com cc.test.com;
access_log /data/wwwlogs/aa.test.com_nginx.access.log combined;
# access_log /data/wwwlogs/aa.test.com_nginx.access.log combined buffer=1k;
error_log /data/wwwlogs/aa.test.com_nginx.error.log error;
root /data/wwwroot/well-know;
index index.html;
# 验证时,letsencript会向你的服务器发起一个如下所示的http请求
# 172.71.142.131 - - [25/Aug/2022:20:42:51 +0000] "GET /.well-known/acme-challenge/z0cTIz2zcORh47L0EwrSXKKoJxKMUTFK0EJIMrRWYaA HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server;+https://www.letsencrypt.org)"
# 可以看到它请求的是:/.well-known/acme-challenge/z0cTIz2zcORh47L0EwrSXKKoJxKMUTFK0EJIMrRWYaA
# 基于这个规则,acme.sh(或certbot)的--webroot模式,acme.sh会自动在你指定的root目录下创建这个文件:
# /.well-known/acme-challenge/z0cTIz2zcORh47L0EwrSXKKoJxKMUTFK0EJIMrRWYaA
# 所以我们要配置一个location规则,让该请求能正常访问到acme.sh创建的文件
# 参考:https://eff-certbot.readthedocs.io/en/stable/using.html#webroot
location ^~ /.well-known/acme-challenge/ {
default_type text/plain;
}
# 其它请求都跳转到https
location / {
return 301 https://$host$request_uri;
}
}
acme.sh使用webroot方式获取证书
# 指定你要获取的证书的域名,并用--webroot指定nginx中的那个root值
acme.sh --issue -d test.com -d aa.test.com -d bb.test.com -d cc.test.com --webroot /data/wwwroot/well-know/
# 申请ecc证书(推荐,速度快)
acme.sh --issue -d test.com -d aa.test.com -d bb.test.com -d cc.test.com --webroot /data/wwwroot/well-know/ --keylength ec-256
# 如果你用的nginx服务器, 或者反代, acme.sh还可以智能的从nginx的配置中自动完成验证, 你不需要指定网站根目录,这种跟webroo方式选一种就行,但这种有时候会报找不到nginx配置,所以我还是建议用--webroot
acme.sh --issue -d test.com -d aa.test.com -d bb.test.com -d cc.test.com --nginx
# 安装证书并重启使用证书的进程
acme.sh --install-cert -d xiebruce.cf \
--key-file /usr/local/nginx/letsencrypt/test.com/private.pem \
--fullchain-file /usr/local/nginx/letsencrypt/test.com/fullchain.pem \
--reloadcmd "systemctl restart nginx"
# 如果申请的是ecc证书,安装时需要加上“--ecc”选项
acme.sh --install-cert --ecc -d xiebruce.cf \
--key-file /usr/local/nginx/letsencrypt/test.com/private.pem \
--fullchain-file /usr/local/nginx/letsencrypt/test.com/fullchain.pem \
--reloadcmd "systemctl restart nginx"
获取证书这一步,只要前面nginx那边能保证80端口访问到对应文件,location设置正确,那么获取证书是没问题的。
standalone模式:如果你没有nginx,acme.sh还可以假装自己是一个web服务器,它会监听80端口,所以如果你用standalone模式,必须保证80端口没有被占用,但由于一般你申请证书都会用到web服务器(nginx,apache,caddy之类的),所以standalone模式一般比较少用
acme.sh --issue -d example.cf -d 'aa.test.com' --standalone
注意:webroot模式和standalone模式本质上是一样的都属于http-01
验证类型,只不过webroot模式用web服务器(如nginx,apache,caddy等)监听80端口来验证,而standalone模式由acme.sh自己监听80端口。
而http-01验证类型是不支持申请通配符证书的,会出现如下报错:
[Fri Nov 11 20:12:04 CST 2022] Error, can not get domain token entry *.xiebruce.top for http-01
[Fri Nov 11 20:12:04 CST 2022] The supported validation types are: dns-01 , but you specified: http-01
[Fri Nov 11 20:12:04 CST 2022] Please add ‘–debug’ or ‘–log’ to check more details.
[Fri Nov 11 20:12:04 CST 2022] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
如果要申请通配符证书,必须用dns-01类型,也就是用--dns dns_cf
这种指定你的dns解析服务商的方式来申请才可以申请通配符证书。
安装证书
上边的获取证书,是放到了~/.acme.sh
目录中的(实际上是在该目录中生成了一个xiebruce.top
文件夹,文件夹里就是获取到的证书):
理论上我们在nginx中引用它即可,实际上也可以,但最好不要直接引用它,而是先把它拷贝到另一个目录中比如nginx要用就拷贝到nginx目录中,或者你喜欢放在/etc
下或/usr/local/etc
下也行,我是放到了nginx的目录中,但不需要自己手动拷贝,直接用以下命令:
acme.sh --install-cert -d 'xiebruce.top' \
--key-file /usr/local/nginx/letsencrypt/private.pem \
--fullchain-file /usr/local/nginx/letsencrypt/fullchain.pem \
--reloadcmd "nginx -s reload"
# 如果申请的是ecc证书,则安装时,需要加个“--ecc”
acme.sh --install-cert --ecc -d 'xiebruce.top' \
--key-file /usr/local/nginx/letsencrypt/private.pem \
--fullchain-file /usr/local/nginx/letsencrypt/fullchain.pem \
--reloadcmd "nginx -s reload"
解析:
--install-cert
:表示安装证书,其实就是把证书文件拷贝到指定目录。
--ecc
:当申请的时候添加了--keylength ec-256
(或ec-384,ec-521)时,安装的时候就要添加--ecc
来安装;
-d
:指定拷贝哪个域名的证书,不过由于我们获取的时候,写了两个域名,一个是xiebruce.top
,一个是通配*.xiebruce.top
,写哪个好呢?就写xiebruce.top
,因为生成的文件夹名字是xiebruce.top
。
--key-file
:指定证书私钥(本例是xiebruce.top.key
文件)的目标路径(即指定要拷到哪里,从哪里拷不用指定,因为肯定是在acme.sh
的工作目录中,该工具会自动去找的)。
--fullchain-file
:fullchain是所有证书文件,该选项指定拷贝fullchain.cer
文件到哪里。
--reloadcmd
:重载的命令,因为证书改变后,nginx如果不重新加载配置,新证书是不会生效的。
注意:要保证--key-file
和--fullchain-file
指向的目录存在,如果不存在,则要先创建,否则拷贝失败,比如我是用的openresty所以我放在以下目录,你自己的可以看情况,你自己喜欢放哪就放哪:
mkdir /usr/local/openresty/nginx/letsencrypt
自动续订(renew)
把以下命令加入到定时任务中即可实现证书过期时自动续订证书(事实上安装的时候已经自动帮你加了,所以你不用手动加了):
0 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" >> /var/log/acme.sh-auto-renew.log
解析:
0 0 * * *
:表示每天的0点执行一次。
/root/.acme.sh/acme.sh --cron
:其实就是acme.sh --cron
,只不过在定时任务中要把acme.sh
的绝对路径写出来,该命令会把你所有的证书都renew一遍,并且会自动install并reload nginx,也就是说,renew获取到新证书后,它会自动执行前面的安装命令最后会执行reloadcmd
指定的重载nginx的命令。有人可能会有疑问,这样是不是nginx每天都会重载一次?不会的,你单独执行一下acme.sh --cron
命令看看结果:
[2019年 02月 15日 星期五 00:40:43 CST] ===Starting cron===
[2019年 02月 15日 星期五 00:40:43 CST] Renew: 'xiebruce.top'
[2019年 02月 15日 星期五 00:40:43 CST] Skip, Next renewal time is: 2019年 04月 15日 星期一 15:45:22 UTC
[2019年 02月 15日 星期五 00:40:43 CST] Add '--force' to force to renew.
[2019年 02月 15日 星期五 00:40:43 CST] Skipped xiebruce.top
[2019年 02月 15日 星期五 00:40:43 CST] ===End cron===
看到其中的“Skip, Next renewal time is”了吗?没有到续订日期,它会跳过,而且还给出下次续订的日期,所以只有到了续订日期,才会真正续订并reload nginx,否则相当于没有执行。
--home "/root/.acme.sh"
:这个是指定acme.sh
的工具目录,其实不指定应该也是可以的。
>> /var/log/acme.sh-auto-renew.log
把前面命令执行输出的结果存到指定的日期文件中。
关于证书所属用户和所属组问题
acme.sh github中有以下描述
The ownership and permission info of existing files are preserved. You can pre-create the files to define the ownership and permission.
意思就是说当一个文件已经存在时,它的所属用户/组和它的权限,不会因为被再次覆盖而改变,比如有以下两个文件
-rw-r--r-- 1 www www 7 Sep 5 23:35 aa/test.txt
-rwxrwxrwx 1 root root 7 Sep 5 23:35 bb/test.txt
我用root用户把“bb/test.txt”复制到“aa/”文件夹中,去覆盖aa文件夹中的test.txt,结果aa文件夹中的test.txt权限并没有改变
> cp bb/test.txt aa/
-rw-r--r-- 1 www www 7 Sep 5 23:38 aa/test.txt
这是一个非常重要的特性,这意味着,无论acme.sh用什么用户运行,更新证书时都不会改变证书的所有者和所属组以及权限(当然我们也无需修改权限)。
重要:所以,如果你希望证书文件与nginx运行用户和组一致(一般是www:www
),那么你只需要在第一次获取证书后,把它用户和组改成www:www
,以后更新的时候,它就一直会保持这个用户和组,无论acme.sh用什么权限更新都一样(或者干脆自己先创建两个空文件,把权限调好,这样连第一次获取证书也不用设置权限和所属用户/组了)。
多台机同一个网站问题
一般情况下都会在负载均衡那台机做一个就够了,不需要每台机都做,如果要多台机做,那就用一台机来更新,然后同步到其他机器。
使用证书
本文主要讲如何使用acme.sh
安装证书以及自动续订证书,至于获取到证书以后如何使用,请参考:为你的网站配置免费的HTTPS支持,主要就是在nginx中引用两个证书文件。
ip也可以申请证书
其实不用域名,ip也能申请证书,比如:https://1.1.1.1,可以在HiCA申请(国内的一个证书申请网站),而且也支持acme.sh申请(-d
后面换成ip就行),但用ip申请证书有些问题:比如ipv4和ipv6需要分开申请,如果你要同时支持v4和v6,而使用证书的工具软件不支持同时加载v4和v6,那就会遇到这个问题,而且HiCA只能申请一个免费ip证书,总之,ip确实可以申请证书,但是限制比较多,建议最好还是用域名申请证书。