使用 acme.sh 在单台 Linux / Windows 服务器或者集群上自动化申请和更新 https 证书

acme.sh 是一个自动化申请和更新 https 证书的脚本,非常地好用;但在使用过程中也发现了一些不足,Ricky 在此提供一个解决方案。

什么是 acme.sh ?

acme.sh 实现了 acme 协议,可以从 letsencrypt 生成免费的证书,详情请看 acme.sh 在 github 上的首页和 github 上的中文介绍

如何安装和使用 acme.sh ?

如果您看 github 上的中文介绍您也能了解这部分的内容,但我这里会说一些 github 上所没有提及的(比如安装失败提示 Download error 时该如何解决)。

1 、安装 acme.sh(以 CentOS Linux 6 / 7 为例):

安装 acme.sh 的命令只有一条:

[root@host ~]# curl  https://get.acme.sh | sh

但 Ricky 建议您这么安装:

[root@host ~]# yum -y install nss
[root@host ~]# yum -y update nss
[root@host ~]# curl  https://get.acme.sh | sh
[root@host ~]# /root/.acme.sh/acme.sh  --upgrade  --auto-upgrade

( 1 )执行 /root/.acme.sh/acme.sh –upgrade –auto-upgrade 的目的是开启 acme.sh 的自动更新。目前由于 acme 协议和 letsencrypt CA 都在频繁的更新,因此 acme.sh 也需要经常更新以保持同步。

( 2 )对于一些版本比较旧的 CentOS Linux(如 CentOS Linux 6.2 )而言,如果不升级 nss 这个包,在安装 acme.sh 时可能会报以下 Download error 的错误:

[root@host ~]# curl  https://get.acme.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   705  100   705    0     0    355      0  0:00:01  0:00:01 --:--:--  2357
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  164k  100  164k    0     0  31028      0  0:00:05  0:00:05 --:--:-- 69529
[Thu Nov  8 21:52:26 CST 2018] Installing from online archive.
[Thu Nov  8 21:52:26 CST 2018] Downloading https://github.com/Neilpang/acme.sh/archive/master.tar.gz
[Thu Nov  8 21:52:27 CST 2018] Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: 35
[Thu Nov  8 21:52:27 CST 2018] Download error.
[root@host ~]#

执行 yum -y install nss 和 yum -y update nss 后上述问题即可解决,再次安装 acme.sh 就不会报错了:

[root@host ~]# curl  https://get.acme.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   705  100   705    0     0    227      0  0:00:03  0:00:03 --:--:--  2365
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  164k  100  164k    0     0   4887      0  0:00:34  0:00:34 --:--:-- 15699
[Thu Nov  8 21:55:02 CST 2018] Installing from online archive.
[Thu Nov  8 21:55:02 CST 2018] Downloading https://github.com/Neilpang/acme.sh/archive/master.tar.gz
[Thu Nov  8 21:55:04 CST 2018] Extracting master.tar.gz
[Thu Nov  8 21:55:06 CST 2018] It is recommended to install socat first.
[Thu Nov  8 21:55:06 CST 2018] We use socat for standalone server if you use standalone mode.
[Thu Nov  8 21:55:06 CST 2018] If you don't use standalone mode, just ignore this warning.
[Thu Nov  8 21:55:06 CST 2018] Installing to /root/.acme.sh
[Thu Nov  8 21:55:06 CST 2018] Installed to /root/.acme.sh/acme.sh
[Thu Nov  8 21:55:06 CST 2018] Installing alias to '/root/.bashrc'
[Thu Nov  8 21:55:06 CST 2018] OK, Close and reopen your terminal to start using acme.sh
[Thu Nov  8 21:55:06 CST 2018] Installing alias to '/root/.cshrc'
[Thu Nov  8 21:55:07 CST 2018] Installing alias to '/root/.tcshrc'
[Thu Nov  8 21:55:07 CST 2018] Installing cron job
[Thu Nov  8 21:55:07 CST 2018] Good, bash is found, so change the shebang to use bash as preferred.
[Thu Nov  8 21:55:10 CST 2018] OK
[Thu Nov  8 21:55:10 CST 2018] Install success!
[root@host ~]#

安装成功后,在 crontab -l 里您能看到如下任务计划(以下任务计划每天凌晨 0 点 31 分执行一次,相关 crontab 操作请点击这里):

31 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

github 上还列举了几种安装方式,详情请点击这里

2 、生成证书:

生成证书最重要的一步就是要验证域名的所有权(验证这个域名到底是不是您的,不然任何人都可以去生成 www.baidu.com 、www.taobao.com 和 www.qq.com 这些网站的证书来做钓鱼网站了),验证域名的所有权主要有两种方式:

( 1 )通过 DNS 来验证(在 DNS 解析里添加一条 TXT 记录,第三方验证服务器会去查询这条 TXT 记录是否存在),在 DNS 解析里添加一条 TXT 记录又分为手动和自动两种:

  • 允许 acme.sh 使用域名解析商提供的 API 自动添加 TXT 记录(不一定支持所有域名解析商的 API ,也不是所有的域名解析商都有相关 API );
  • 手工添加 TXT 记录(麻烦,达不到自动化运维的要求)。

( 2 )在代码目录里放置一个验证文件(第三方验证服务器会通过 http 的方式来访问您的代码目录,看其中是否有这个验证文件)。

综上所述,“ 在代码目录里放置一个验证文件 ” 这种验证方式是最方便的,这种方式生成证书的命令如下:

[root@host ~]# /root/.acme.sh/acme.sh --issue -d mydomain.com -d www.mydomain.com --webroot /home/wwwroot/mydomain.com/

或者:

[root@host ~]# /root/.acme.sh/acme.sh --issue -d www.mydomain.com --webroot /home/wwwroot/mydomain.com/

其中:

  • mydomain.com 和 www.mydomain.com 是您的域名;
  • /home/wwwroot/mydomain.com/ 是您网站的代码根目录。

只需要指定域名,并指定域名所在的网站根目录,acme.sh 就会全自动地生成验证文件,并放到网站的根目录,然后自动完成验证,最后会聪明地删除验证文件,整个过程没有任何副作用。

验证完成后,证书就会签发下来了,但此时还需要执行一个安装证书的命令。

3 、安装证书:

前面证书生成以后,接下来需要把证书 copy 到真正需要用它的地方。

注意,默认生成的证书都放在安装目录下:~/.acme.sh/ ,请不要直接使用此目录下的文件,例如:不要直接让 Nginx / Apache 的配置文件使用这下面的文件,这里面的文件都是内部使用,而且目录结构可能会变化。

正确的使用方法是使用 –installcert 命令来安装证书,并指定目标位置,然后证书文件会被 copy 到相应的位置,例如:

[root@host ~]# /root/.acme.sh/acme.sh  --installcert  -d  www.mydomain.com   \
               --key-file   /usr/local/nginx/conf/cert/www.mydomain.com.key \
               --fullchain-file /usr/local/nginx/conf/cert/www.mydomain.com.pem \
               --reloadcmd  "/usr/local/nginx/sbin/nginx -s reload"

经实践证明,使用命令 /usr/local/nginx/sbin/nginx -s reload 是可以让 Nginx 重新加载新的证书的(实验的 Nginx 版本号为 1.15.6 ,当前最新版本)。

如果您是想让 Nginx 多监听一个 443 端口,那我建议您先关闭 Nginx 的进程后再开启(相关 Nginx 的操作请点击这里)。Nginx 上有关于 https 和 443 端口的配置我建议您手工配置即可(今后 acme.sh 会自动更新证书文件并重新加载 Nginx )。

4 、更新证书:

目前证书在 60 天以后会自动更新,您无需任何操作(还记得上文中提到的 crontab -l 里的任务计划么?)。今后有可能会缩短这个时间,不过都是自动的,您不用关心。

5 、删除证书:

执行以下删除命令,再删除掉相关文件夹即可:

[root@host ~]# /root/.acme.sh/acme.sh --remove -d www.mydomain.com
[root@host ~]# rm -rf /root/.acme.sh/www.mydomain.com

在使用过程中 Ricky 遇到的问题:

首先非常感谢您能耐心地看到这里,接下来才是本文的重点。

在使用 acme.sh 时,虽然 “ 在代码目录里放置一个验证文件 ” 这种验证方式是最方便的,但是相比于 “ 通过 DNS 来验证 ” 的验证方式来说它有一个明显的缺点 —— 不支持集群。

假设您有一个域名是 www.mydomain.com ,这个域名会首先解析到 1.1.1.1 这个 IP 地址上,而 1.1.1.1 实际上是一台负载均衡设备(比如 A10 、F5 或者 HAProxy ),负载均衡设备再往后才是两台 Web 服务器 2.2.2.2 和 3.3.3.3( 如 Tomcat 或者 PHP 等 )。如果您在 2.2.2.2 和 3.3.3.3 上均安装了 acme.sh 并分别执行了 “ 在代码目录里放置一个验证文件 ” 这种验证方式的生成证书的命令,那么这个时候会发生什么呢?

假设 2.2.2.2 上的 acme.sh 生成的验证文件是 123 ,而 3.3.3.3 上的 acme.sh 生成的验证文件是 456 ,当第三方验证服务器通过 http 的方式来访问您的验证文件 123 时,负载均衡设备可能会将此次请求转给 3.3.3.3 ,那么此时验证失败 ……

所以在使用集群的时候反而是 “ 通过 DNS 来验证 ” 会更方便一点。但如果我的域名解析商没有提供相关的 DNS API ,我也不想手工添加 TXT 记录,而我同时还用着集群,就没有解决方法了吗?有的。

通过观察发现,acme.sh 会在您代码目录的 /.well-known/acme-challenge/ 文件夹下放置验证文件,那么我们可以通过反向代理来完成验证,比如:

  1. 我们专门创建一台安装有 acme.sh 的服务器 4.4.4.4 ,然后让所有 http://www.mydomain.com/.well-known/acme-challenge/xxxx 的 http 请求都转到 4.4.4.4 这台服务器上;
  2. 此时证书会生成到 4.4.4.4 这台服务器上,我们再在 2.2.2.2 和 3.3.3.3 上放置一个 Linux Shell 脚本,该脚本会自动去 4.4.4.4 上下载证书、自动跟现有的证书进行 MD5 比对,如果发现证书文件已经更新则自动 reload Nginx 即可。

详细步骤如下:

1 、专门创建一台服务器 4.4.4.4 ,安装好  acme.sh 。
2 、在 4.4.4.4 上安装一个 Nginx ,并配置以下 server 域:
server {
    listen       8081;
    server_name  4.4.4.4;
    root /www/ssl/;    # 记得创建该文件夹,4.4.4.4 的证书会存储在这里
}

这么做的目的是为了能让 2.2.2.2 和 3.3.3.3 来 4.4.4.4 下载证书。

从安全的角度出发,您不应该将该 8081 端口公开到互联网上(防止证书被别人下走),应该使用防火墙对该端口做一个基于源 IP 地址的限制,仅允许 2.2.2.2 和 3.3.3.3 来访问该端口(如果服务器均处于内网则可忽略):

iptables -A INPUT -s 2.2.2.2 -p tcp --dport 8081 -j ACCEPT
iptables -A INPUT -s 3.3.3.3 -p tcp --dport 8081 -j ACCEPT
iptables -A INPUT -p tcp --dport 8081 -j DROP

建议:

  1. 如果您有自己的运维平台的话,可以自动获得所有服务器的 IP 地址,然后定时对该服务器的防火墙配置进行刷新即可。
  2. 建议在 8081 端口使用 https 协议,而不是 http 协议。
3 、配置反向代理:

( 1 )如果您的负载均衡设备 1.1.1.1 具有反向代理的功能,那么可以在负载均衡设备上配置,以 HAProxy 为例:

acl dns_check path_reg -i ^/.well-known/acme-challenge/
use_backend dns_check_backend if dns_check

backend dns_check_backend
        mode    http
        reqideny      ^[^:\ ]*\ .*/(root\.exe\?|cmd\.exe\?|default\.ida\?)
        balance roundrobin
        server local 4.4.4.4:8081 maxconn 30000 check

        contimeout     10000
        srvtimeout      5000
        fullconn 99999
        redispatch
        retries 3

        option forwardfor
        option httpclose

( 2 )如果您的负载均衡设备不支持反向代理,那么可以在 2.2.2.2 和 3.3.3.3 上安装一个 Nginx ,然后这么配置:

server {
    listen       80;
    server_name  _;

    ......

    location ~ "/.well-known/acme-challenge/" {
       proxy_pass http://4.4.4.4:8081;
    }

    location / {
        ......
    }
}

( 3 )如果 2.2.2.2 和 3.3.3.3 是 Windows 服务器,且使用的是 Apache ,那需要这么配置:

首先需要加载三个 Apache 的模块(在 Apache 的配置文件 httpd.conf 中,将下述三行前面的 # 号删除掉即可):

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so

mod_proxy_connect.so 模块是用于 https 协议的,如果您需要反向代理到使用 https 协议的网站(如:https://acme-server.com:8081/ ),不加载这个模块的话,Apache 的反向代理是不生效的(别问我为什么知道,Ricky 本人亲自踩过这个坑)。

然后在 Apache 的配置文件 httpd.conf 中,添加如下配置即可:

<IfModule proxy_module>
<IfModule proxy_http_module>
<IfModule proxy_connect_module>

#
# Reverse Proxy
#
SSLProxyEngine on
ProxyPass "/.well-known/acme-challenge/" "http://4.4.4.4:8081/.well-known/acme-challenge/"
ProxyPassReverse "/.well-known/acme-challenge/" "http://4.4.4.4:8081/.well-known/acme-challenge/"

ProxyRequests Off

<Proxy *>
      Require all granted
</Proxy>

</IfModule>
</IfModule>
</IfModule>

这样,所有 http://www.mydomain.com/.well-known/acme-challenge/xxxx 的 http 请求都转到 4.4.4.4 这台服务器上了。

4 、在 4.4.4.4 上生成和安装证书:

请使用如下生成证书的命令:

[root@host ~]# /root/.acme.sh/acme.sh  --issue  -d www.mydomain.com  --webroot /www/ssl/

请使用如下安装证书的命令:

[root@host ~]# /root/.acme.sh/acme.sh  --installcert  -d  www.mydomain.com   \
               --key-file   /www/ssl/www.mydomain.com.key \
               --fullchain-file /www/ssl/www.mydomain.com.pem \
               --reloadcmd  "echo $THREE_LEVEL_DOMAIN - cert update . >> /www/ssl/log/cert.log"

这样,证书就会被安装到 /www/ssl/ 目录,方便 2.2.2.2 和 3.3.3.3 前来下载证书,同时 reloadcmd 命令也被设置成一个往 /www/ssl/log/cert.log 文件写入日志的命令。

5 、在 2.2.2.2 和 3.3.3.3 上使用脚本来定时更新证书并重新加载服务:

( 1 )如果证书需要配置在 CentOS Linux 服务器上,则使用 Linux Shell 脚本来定时更新证书并重新加载服务:

假设您是将证书配置在 2.2.2.2 和 3.3.3.3 的 Nginx 上的,那么我们需要将证书下载到 2.2.2.2 和 3.3.3.3 上,同时今后证书还能自动更新,并自动重新加载 Nginx(即使用一个 Linux Shell 脚本来实现)。2.2.2.2 和 3.3.3.3 的 Nginx 上如何配置证书呢?请看文章《 CentOS Linux 6 / 7 编译安装 Nginx 》的 “ 2、修改 Nginx 的配置文件,让其支持 https ” 部分。

如果 1.1.1.1 使用的是 HAProxy ,那么在 HAProxy 上配置证书也是可以的,请看文章《 CentOS Linux 6 / 7 编译安装 HAProxy 》的 “ 1 、修改 HAProxy 的配置文件,让其支持 https ” 部分。同时,如下脚本需要进行相应的修改。

编写脚本,在命令行界面输入:

[root@host ~]# vi /root/update_cert.sh

键入小写字母 i ,进入编辑模式,将以下脚本复制粘贴进去(请根据实际需要进行相应地修改):

#!/bin/bash

if [ ! -f "update_cert.list" ] ; then
        echo update_cert.list not found !
        exit
fi
cert_of_list=`cat update_cert.list`

cd /usr/local/nginx/conf/cert/
is_reload="false"

for cert in $cert_of_list ; do
        wget http://4.4.4.4:8081/$cert.pem -O $cert.pem.new 
        wget http://4.4.4.4:8081/$cert.key -O $cert.key.new

        if [ -s "$cert.pem.new" -a -s "$cert.key.new" ] ; then

                if [ -f "$cert.pem" -a -f "$cert.key" ] ; then

                        pem_file_old=`md5sum $cert.pem | awk -F ' ' '{print $1}'`
                        pem_file_new=`md5sum $cert.pem.new | awk -F ' ' '{print $1}'`

                        key_file_old=`md5sum $cert.key | awk -F ' ' '{print $1}'`
                        key_file_new=`md5sum $cert.key.new | awk -F ' ' '{print $1}'`

                        if [ "$pem_file_old" != "$pem_file_new" -o "$key_file_old" != "$key_file_new" ] ; then
                                mv -f $cert.pem.new $cert.pem
                                mv -f $cert.key.new $cert.key

                                is_reload="true"

                                echo $(date "+%F %H:%M") - $cert changed ! >> /root/update_cert.log
                        else
                                rm -f $cert.pem.new $cert.key.new

                                #echo
                                #echo pem_file_old=$pem_file_old , pem_file_new=$pem_file_new
                                #echo key_file_old=$key_file_old , key_file_new=$key_file_new
                                echo $(date "+%F %H:%M") - $cert no changed . >> /root/update_cert.log
                        fi
                else
                        mv -f $cert.pem.new $cert.pem
                        mv -f $cert.key.new $cert.key

                        echo $(date "+%F %H:%M") - $cert new cert ! >> /root/update_cert.log
                fi
        else
                echo $(date "+%F %H:%M") - $cert download error ! >> /root/update_cert.log
        fi
done

if [ "$is_reload" == "true" ] ; then
        /usr/local/nginx/sbin/nginx -s reload
        echo $(date "+%F %H:%M") - nginx reloaded ! >> /root/update_cert.log
else
        echo $(date "+%F %H:%M") - nginx not reloaded . >> /root/update_cert.log
fi

按一次 ESC 键退出编辑模式,然后键入 “ :wq ” 保存并退出。

其中:

  1. update_cert.list 是配置文件,里面存放的是要更新证书的域名的名称(一个域名一行);这里用的是相对路径,所以该文件和 Linux Shell 脚本处在同一个目录下;请执行以下命令来写入配置:
    [root@host ~]# echo "www.mydomain.com" > /root/update_cert.list
  2. update_cert.log 是日志文件;这里用的是相对路径,所以该文件和 Linux Shell 脚本处在同一个目录下。
  3. /usr/local/nginx/conf/cert/ 是存放证书的目录,这里用的是绝对路径。
  4. /usr/local/nginx/sbin/nginx -s reload 是重新加载 Nginx 的命令,这个命令只是重新加载配置文件,不重启进程;如果证书是配置在 HAProxy 上的,那么这里需要改成重新加载 HAProxy 的命令。
  5. 请注意上述脚本中的 is_reload 变量是在什么时候才等于 true 的,请根据实际需要进行相应地修改。

( 2 )如果证书需要配置在 Windows 服务器上,则使用 Python 脚本来定时更新证书并重新加载服务:

Windows 服务器自然是无法使用 Linux Shell 脚本的,但 Linux 服务器可以使用 Python 脚本,那为什么 Linux 服务器不使用 Python 脚本呢?这纯属个人爱好问题,因为 Ricky 觉得没必要,毕竟 Linux 服务器下的 Linux Shell 脚本挺好用的。如果您喜欢,也可以在 Windows 服务器上使用 BAT 批处理脚本而不是 Python 脚本。

要想在 Windows 中使用 Python 脚本,自然需要先安装 Python ,安装 Python 的过程这里就忽略了。

Python 脚本代码如下所示(脚本文件名为:update_cert.pyw ):

# -*- coding: UTF-8 -*-
from pathlib import Path as __path
from os import rename, remove, popen
from hashlib import md5 as __md5
from urllib.request import urlretrieve
from urllib.error import HTTPError
from time import strftime, localtime

list_filename = 'update_cert.list'
log_filename = 'update_cert.log'
url = r'http://4.4.4.4:8081/%s.%s'
cert_dir = r'cert'
restart_server_cmd = r'C:\xampp\apache\bin\httpd -k restart'

cert_filename_template = cert_dir + r'\%s.%s'
log_file = None

def log_file_write(record):
    global log_file
    if log_file == None or log_file.closed or 'a' not in log_file.mode:
        log_file = open(log_filename, 'a')
    log_file.write('%s - %s\n' % (strftime("%Y-%m-%d %H:%M:%S", localtime()), record))
    log_file.flush()

def md5sum(filename):
    if not __path(filename).is_file():  return '0'
    md5_value = __md5()
    with open(filename, 'rb') as f:
        while True:
            data = f.read(2048)
            if not data:
                break
            md5_value.update(data)
    return md5_value.hexdigest()

def processing_a_single_cert(domain_name):
    download_pem_filename = cert_filename_template % (domain_name, 'pem.new')
    download_key_filename = cert_filename_template % (domain_name, 'key.new')
    try:
        urlretrieve(url % (domain_name, 'pem'), filename = download_pem_filename)
        urlretrieve(url % (domain_name, 'key'), filename = download_key_filename)
    except HTTPError:
        log_file_write('%s download error !' % domain_name)
        return False

    pem_filename = cert_filename_template % (domain_name, 'pem')
    key_filename = cert_filename_template % (domain_name, 'key')
    if __path(pem_filename).is_file() and __path(key_filename).is_file():
        if md5sum(pem_filename) != md5sum(download_pem_filename) or md5sum(key_filename) != md5sum(download_key_filename):
            remove(pem_filename)
            remove(key_filename)
            rename(download_pem_filename, pem_filename)
            rename(download_key_filename, key_filename)
            log_file_write('%s changed !' % domain_name)
            return True
        else:
            remove(download_pem_filename)
            remove(download_key_filename)
            log_file_write('%s no changed .' % domain_name)
            return False
    else:
        if __path(pem_filename).is_file():remove(pem_filename)
        if __path(key_filename).is_file():remove(key_filename)
        rename(download_pem_filename, pem_filename)
        rename(download_key_filename, key_filename)
        log_file_write('%s new cert !' % domain_name)
        return False

def restart_server():
    return  popen(restart_server_cmd).read().strip()
    
if __name__ == '__main__':
    cert_path = __path(cert_dir)
    if cert_path.is_file(): remove(cert_dir)
    if not cert_path.exists(): cert_path.mkdir()
    if __path(list_filename).is_file():
        is_reload = False
        with open(list_filename, 'r') as f:
            for domain_name in f.readlines():
                is_reload = processing_a_single_cert(domain_name.strip())
        if is_reload:
            if return_str := restart_server() == '':
                log_file_write('Apache reloaded !')
            else:
                log_file_write('Apache reloaded ! CMD return = %s' % return_str)
        else:
             log_file_write('Apache not reloaded .')
    else:
        log_file_write('%s not found !' % list_filename)

    if log_file != None and not log_file.closed:
        log_file.close()

这里需要注意的是:该脚本使用了海象运算符 := ,所以该脚本至少需要运行在 Python 3.8.0 上;目前 Ricky 使用的 Python 版本为 3.8.2 ,也是目前 Python 的最新版本。

其中:

  1. update_cert.list 是配置文件,里面存放的是要更新证书的域名的名称(一个域名一行,如:www.mydomain.com );这里用的是相对路径,所以该文件和 Python 脚本处在同一个目录下。
  2. update_cert.log 是日志文件;这里用的是相对路径,所以该文件和 Python 脚本处在同一个目录下。
  3. cert 是存放证书的目录,这里用的是相对路径,所以该目录和 Python 脚本处在同一个目录下。
  4. C:\xampp\apache\bin\httpd -k restart 是重新加载 Apache 的命令,这个命令只是重新加载配置文件,不重启进程,具体如下图所示:

    -k restart : tell running Apache to do a graceful restart
    -k restart : tell running Apache to do a graceful restart

Python 脚本代码结构如下图所示:

Python 脚本代码结构
Python 脚本代码结构
6 、将脚本添加到任务计划:

( 1 )将 Linux Shell 脚本 update_cert.sh 添加到 crontab 任务计划,并手工执行一次该脚本:

[root@host ~]# echo "51 3 * * * sh /root/update_cert.sh" >> /var/spool/cron/root
[root@host ~]# sh /root/update_cert.sh

51 3 * * * 表示每天 3 点 51 分执行一次命令。不知道 crontab 怎么使用?请点击这里

( 2 )将 Python 脚本 update_cert.pyw 添加到 Windows 任务计划程序,并手工执行一次该脚本:

以 Windows Server 2012 R2 Datacenter 操作系统为例,先按 Windows 微标键 + R ,在弹出的窗口中输入 “ taskschd.msc ” ,然后点击 “ 确定 ” ,具体如下图所示:

先按 Windows 微标键 + R ,在弹出的窗口中输入 “ taskschd.msc ” ,然后点击 “ 确定 ”
先按 Windows 微标键 + R ,在弹出的窗口中输入 “ taskschd.msc ” ,然后点击 “ 确定 ”

接着会弹出一个名为 “ 任务计划程序 ” 的窗口,在窗口右边的侧边栏中点击 “ 创建基本任务 ” ,具体如下图所示:

接着会弹出一个名为 “ 任务计划程序 ” 的窗口,在窗口右边的侧边栏中点击 “ 创建基本任务 ”
接着会弹出一个名为 “ 任务计划程序 ” 的窗口,在窗口右边的侧边栏中点击 “ 创建基本任务 ”

接着会弹出一个名为 “ 创建基本任务向导 ” 的窗口,在此处填写 “ 名称 ” 和 “ 描述 ” 后点击 “ 下一步 ” ,具体如下图所示:

接着会弹出一个名为 “ 创建基本任务向导 ” 的窗口,在此处填写 “ 名称 ” 和 “ 描述 ” 后点击 “ 下一步 ”
接着会弹出一个名为 “ 创建基本任务向导 ” 的窗口,在此处填写 “ 名称 ” 和 “ 描述 ” 后点击 “ 下一步 ”

接下来设置的是触发器,选择 “ 每天 ” 即可,然后点击 “ 下一步 ” ,具体如下图所示:

接下来设置的是触发器,选择 “ 每天 ” 即可,然后点击 “ 下一步 ”
接下来设置的是触发器,选择 “ 每天 ” 即可,然后点击 “ 下一步 ”

接下来设置的是每日具体触发的时间,Ricky 这里设置的是凌晨 5 点 30 分触发,每隔 1 天发生一次,然后点击 “ 下一步 ” ,具体如下图所示:

接下来设置的是每日具体触发的时间,Ricky 这里设置的是凌晨 5 点 30 分触发,每隔 1 天发生一次,然后点击 “ 下一步 ”
接下来设置的是每日具体触发的时间,Ricky 这里设置的是凌晨 5 点 30 分触发,每隔 1 天发生一次,然后点击 “ 下一步 ”

接下来设置的是操作,选择 “ 启动程序 ” 即可,然后点击 “ 下一步 ” ,具体如下图所示:

接下来设置的是操作,选择 “ 启动程序 ” 即可,然后点击 “ 下一步 ”
接下来设置的是操作,选择 “ 启动程序 ” 即可,然后点击 “ 下一步 ”

接下来设置的是具体要启动哪些程序,具体参数设置如下所示,请根据实际需要做相应修改:

  • 程序或脚本需设置为 pythonw.exe 的绝对路径:“ C:\Users\Administrator\AppData\Local\Programs\Python\Python38\pythonw.exe ” ;
  • 添加参数(可选)需设置为 Python 脚本的名称:“ update_cert.pyw ” ;
  • 起始于(可选)需设置为 Python 脚本所在的目录:“ E:\xampp\SSL ” 。

上述设置完成后请点击 “ 下一步 ” ,具体如下图所示:

接下来设置的是具体要启动哪些程序,设置完成后请点击 “ 下一步 ”
接下来设置的是具体要启动哪些程序,设置完成后请点击 “ 下一步 ”

接下来没有什么是需要设置的,直接点击 “ 完成 ” 即可,具体如下图所示:

接下来没有什么是需要设置的,直接点击 “ 完成 ” 即可
接下来没有什么是需要设置的,直接点击 “ 完成 ” 即可

最后我们手工执行一次该脚本。其实您在 Python 的 IDLE 编辑器中跑一下脚本就可以了,那如何在 Windows 的 CMD 命令提示符窗口中运行该脚本呢?

以 Windows Server 2012 R2 Datacenter 操作系统为例,先按 Windows 微标键 + R ,在弹出的窗口中输入 “ cmd ” ,然后点击 “ 确定 ” ,具体如下图所示:

先按 Windows 微标键 + R ,在弹出的窗口中输入 “ cmd ” ,然后点击 “ 确定 ”
先按 Windows 微标键 + R ,在弹出的窗口中输入 “ cmd ” ,然后点击 “ 确定 ”

接下来在弹出的窗口中输入以下三行命令即可:

cd /D E:
cd E:\xampp\SSL
C:\Users\Administrator\AppData\Local\Programs\Python\Python38\pythonw.exe update_cert.pyw

具体如下图所示:

接下来在弹出的窗口中输入这三行命令即可
接下来在弹出的窗口中输入这三行命令即可

现在,证书就会同步到 2.2.2.2 和 3.3.3.3 上了!

总结:

至此,acme.sh 脚本算是完美了。如果您没有使用集群,但是域名比较多,也可以使用上述方法来部署 acme.sh ,这样 acme.sh 只需要在一台服务器上安装即可(不用每台服务器都安装,方便统一管理)。

这篇文章对你有帮助吗?

相关文章

2条评论

  1. 2020 年 03 月 08 日 13:35:10 ,对文章做了大量的改动,其中最主要的就是添加了对 Apache 和 Windows 服务器的支持。

  2. jearton

    webroot方式生成证书,需要确保公网能访问到这台服务器

发表评论?

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