https证书自动续订(renew)问题

https证书自动续订(renew)问题

之前写过一篇文章:为你的网站配置免费的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.comwww.xiebruce.com就是指定域名,而*.xiebruce.com即为通配符证书。通配符证书的好处,就在于,如果你后面又增加了几个网站子域名,比如aaa.xiebruce.combbb.xiebruce.com,你就可以直接用通配符证书,否则你每增加一个子域名,就要申请一次证书,特别麻烦。

既然通配符证书简单,是不是只申请通配符证书就行了呢?是的,但要注意通配符证书是不包括自解析的,比如本博客的域名为xiebruce.topwww.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:
Xnip2019-06-06_11-20-19.jpg

在你的~/.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_KeyAli_Secret,而CloudFlare要的是CF_EmailCF_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文件夹,文件夹里就是获取到的证书):
Xnip2019-02-15_00-02-12.jpg

理论上我们在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确实可以申请证书,但是限制比较多,建议最好还是用域名申请证书。

打赏
订阅评论
提醒
guest

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
()
x

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

https证书自动续订(renew)问题