我们将以通配符证书为例(使用DNS验证),并假定使用Cloudflare作为DNS服务商。进行自动安装SSL证书

步骤:

安装acme.sh

配置Cloudflare  API密钥(用于DNS验证)

申请通配符证书

配置证书自动部署到Nginx/Apache等服务器

设置自动续期

注意:通配符证书只能通过DNS验证方式申请。

假设我们的域名是example.com,需要申请*.example.com和example.com的证书。

使用acme.sh实现Let's  Encrypt证书自动续期

acme.sh是轻量级的ACME协议客户端,专为自动化证书管理设计。以下是完整配置指南。

一、安装与基础配置

1.  一键安装

bash

方法1:官方一键安装(推荐)

curl  https://get.acme.sh  |  sh  -s  email=my@example.com

方法2:指定安装目录(无root权限)

curl  https://get.acme.sh  |  sh  -s  email=my@example.com  --install  \

    --home  ~/.acme.sh  \

    --config-home  ~/.acme.sh/config  \

    --cert-home  ~/.acme.sh/certs

方法3:从GitHub安装(中国大陆可用)

git  clone  https://github.com/acmesh-official/acme.sh.git

cd  acme.sh

./acme.sh  --install  -m  my@example.com

安装后加载环境变量

source  ~/.bashrc    或  source  ~/.zshrc

2.  配置默认参数

bash

查看当前配置

acme.sh  --info

设置默认CA(推荐ZeroSSL,速度更快)

acme.sh  --set-default-ca  --server  zerossl

或者使用Let's  Encrypt(默认)

acme.sh  --set-default-ca  --server  letsencrypt

配置证书存储路径

export  CERT_HOME="/etc/ssl/acme"

mkdir  -p  $CERT_HOME

acme.sh  --set-default-cert-home  $CERT_HOME

配置账户邮箱(用于注册)

acme.sh  --update-account  --accountemail  my@example.com

启用自动升级

acme.sh  --upgrade  --auto-upgrade

二、证书申请与验证

1.  HTTP验证方式(适合单域名)

bash

创建webroot目录

mkdir  -p  /var/www/html/.well-known/acme-challenge

申请证书(webroot验证)

acme.sh  --issue  -d  example.com  -d  www.example.com  \

    --webroot  /var/www/html  \

    --keylength  ec-256    #  使用ECC密钥(更安全)

或者使用standalone模式(自动启动临时web服务器)

acme.sh  --issue  -d  example.com  \

    --standalone  \

    --listen-v4  \

    --httpport  88    #  使用非标准端口

2.  DNS验证方式(支持通配符)

bash

配置DNS  API凭证(以Cloudflare为例)

export  CF_Email="admin@example.com"

export  CF_Key="your_cloudflare_api_key"

申请通配符证书

acme.sh  --issue  -d  "*.example.com"  -d  example.com  \

    --dns  dns_cf  \

    --keylength  2048    #  RSA密钥(兼容性更好)

或者使用配置文件方式

cat  >  ~/.acme.sh/account.conf  <<  EOF

SAVED_CF_Email='admin@example.com'

SAVED_CF_Key='your_cloudflare_api_key'

EOF

使用配置文件申请

acme.sh  --issue  -d  "*.example.com"  \

    --dns  dns_cf  \

    --dnssleep  120    #  等待DNS传播时间

3.  支持的DNS提供商列表

bash

查看支持的DNS提供商

acme.sh  --list-dns

常用DNS提供商配置示例

1.  Cloudflare

export  CF_Token="your_api_token"

acme.sh  --issue  -d  example.com  --dns  dns_cf

2.阿里云

export  Ali_Key="LTAIxxxxxxxx"

export  Ali_Secret="xxxxxxxx"

acme.sh  --issue  -d  example.com  --dns  dns_ali

3.  DNSPod

export  DP_Id="12345"

export  DP_Key="xxxxxxxx"

acme.sh  --issue  -d  example.com  --dns  dns_dp

4.华为云

export  HUAWEICLOUD_Username="username"

export  HUAWEICLOUD_Password="password"

export  HUAWEICLOUD_ProjectID="project_id"

acme.sh  --issue  -d  example.com  --dns  dns_huaweicloud

5.  自定义脚本

acme.sh  --issue  -d  example.com  \

    --dns  dns_myapi  \

    --dnssleep  900

三、证书安装与部署

1.  安装到Nginx

bash

方法1:直接安装(acme.sh自动配置)

acme.sh  --install-cert  -d  example.com  \

    --key-file  /etc/nginx/ssl/example.com/key.pem  \

    --fullchain-file  /etc/nginx/ssl/example.com/cert.pem  \

    --reloadcmd  "systemctl  reload  nginx"

方法2:使用部署钩子

acme.sh  --install-cert  -d  example.com  \

    --deploy-hook  nginx

方法3:创建符号链接(推荐,便于管理)

acme.sh  --install-cert  -d  example.com  \

    --cert-file  /etc/ssl/acme/example.com/cert.pem  \

    --key-file  /etc/ssl/acme/example.com/key.pem  \

    --fullchain-file  /etc/ssl/acme/example.com/fullchain.pem  \

    --reloadcmd  "ln  -sf  /etc/ssl/acme/example.com/*.pem  /etc/nginx/ssl/  &&  systemctl  reload  nginx"

2.  Nginx配置模板

nginx

nginx-acme.conf  -  自动生成的配置模板

ssl_certificate  /etc/ssl/acme/__DOMAIN__/fullchain.pem;

ssl_certificate_key  /etc/ssl/acme/__DOMAIN__/key.pem;

ssl_trusted_certificate  /etc/ssl/acme/__DOMAIN__/chain.pem;

OCSP装订配置

ssl_stapling  on;

ssl_stapling_verify  on;

resolver  8.8.8.8  8.8.4.4  valid=300s;

resolver_timeout  5s;

HSTS头

add_header  Strict-Transport-Security  "max-age=63072000;  includeSubDomains;  preload"  always;

创建Nginx配置文件

cat  >  /etc/nginx/snippets/ssl-acme.conf  <<  'EOF'

ssl_session_cache  shared:SSL:10m;

ssl_session_timeout  10m;

ssl_session_tickets  off;

ssl_protocols  TLSv1.2  TLSv1.3;

ssl_ciphers  ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

ssl_prefer_server_ciphers  off;

EOF

3.  Apache配置

bash

安装到Apache

acme.sh  --install-cert  -d  example.com  \

    --cert-file  /etc/ssl/acme/example.com/cert.pem  \

    --key-file  /etc/ssl/acme/example.com/key.pem  \

    --fullchain-file  /etc/ssl/acme/example.com/fullchain.pem  \

    --reloadcmd  "systemctl  reload  apache2"

Apache  SSL配置模板

cat  >  /etc/apache2/sites-available/example.com-ssl.conf  <<  EOF

<VirtualHost  *:443>

        ServerName  example.com

        ServerAlias  www.example.com

        

        SSLEngine  on

        SSLCertificateFile  /etc/ssl/acme/example.com/fullchain.pem

        SSLCertificateKeyFile  /etc/ssl/acme/example.com/key.pem

        启用HTTP/2

        Protocols  h2  http/1.1

        HSTS头

        Header  always  set  Strict-Transport-Security  "max-age=63072000;  includeSubDomains"

        DocumentRoot  /var/www/html

</VirtualHost>

EOF

4.  多服务部署

bash

!/bin/bash

multi-service-deploy.sh

DOMAIN="example.com"

CERT_DIR="/etc/ssl/acme/$DOMAIN"

1.  申请证书

acme.sh  --issue  -d  $DOMAIN  -d  www.$DOMAIN  \

    --dns  dns_cf  \

    --keylength  ec-256

2.  部署到Nginx

acme.sh  --deploy  -d  $DOMAIN  \

    --deploy-hook  nginx

3.  部署到Postfix(邮件服务器)

acme.sh  --deploy  -d  $DOMAIN  \

    --deploy-hook  postfix

  4.  部署到Dovecot

acme.sh  --deploy  -d  $DOMAIN  \

    --deploy-hook  dovecot

5.  部署到HAProxy

acme.sh  --deploy  -d  $DOMAIN  \

    --deploy-hook  haproxy

6.  创建PEM格式(适合多种服务)

cat  $CERT_DIR/fullchain.pem  $CERT_DIR/key.pem  >  $CERT_DIR/combined.pem

chmod  600  $CERT_DIR/combined.pem

四、自动续期配置

1.  续期基础配置

bash

查看所有证书及其状态

acme.sh  --list

手动续期单个证书

acme.sh  --renew  -d  example.com  --force

续期所有证书

acme.sh  --renew-all  --force

测试续期(不实际执行)

acme.sh  --renew  -d  example.com  --test

设置自动续期阈值(默认提前30天)

acme.sh  --set-auto-renew  --days  20    #  提前20天开始尝试续期

查看续期日志

tail  -f  ~/.acme.sh/acme.sh.log

2.  Cron自动续期配置

bash

查看acme.sh的cron任务

crontab  -l  |  grep  acme.sh

手动安装cron任务(如果未自动安装)

acme.sh  --install-cronjob

自定义cron计划(每天凌晨3点检查续期)

(crontab  -l  2>/dev/null;  echo  "0  3  *  *  *  \"$HOME/.acme.sh/acme.sh\"  --cron  --home  \"$HOME/.acme.sh\"  >  /dev/null")  |  crontab  -

或者使用systemd  timer(更可靠)

cat  >  /etc/systemd/system/acme-renew.timer  <<  EOF

[Unit]

Description=ACME  Certificate  Renewal  Timer

[Timer]

OnCalendar=daily

Persistent=true

RandomizedDelaySec=1h

[Install]

WantedBy=timers.target

EOF

cat  >  /etc/systemd/system/acme-renew.service  <<  EOF

[Unit]

Description=Renew  ACME  certificates

After=network.target

[Service]

Type=oneshot

User=root

Environment="HOME=/root"

ExecStart=/root/.acme.sh/acme.sh  --cron  --home  /root/.acme.sh

StandardOutput=journal

StandardError=journal

EOF

systemctl  enable  --now  acme-renew.timer

3.  续期通知配置

bash

配置邮件通知

acme.sh  --set-notify  --notify-hook  mail  \

    --notify-level  2  \    #  1=错误,  2=续期成功,  3=所有事件

    --notify-script  "/path/to/notify.sh"

自定义通知脚本

cat  >  /usr/local/bin/acme-notify.sh  <<  'EOF'

!/bin/bash

acme.sh通知脚本

DOMAIN="$1"

STATUS="$2"    #  valid,  invalid,  renew_success,  renew_failed

ACTION="$3"    #  issue,  renew,  delete

case  $STATUS  in

        "renew_success")

                发送邮件通知

                echo  "证书  $DOMAIN  续期成功"  |  mail  -s  "证书续期成功:  $DOMAIN"  admin@example.com

                发送Slack通知

                curl  -X  POST  -H  'Content-type:  application/json'  \

                    --data  "{\"text\":\"证书  $DOMAIN  续期成功\"}"  \

                    https://hooks.slack.com/services/XXXX/XXXX/XXXX

                ;;

        "renew_failed")

                echo  "证书  $DOMAIN  续期失败"  |  mail  -s  "紧急:  证书续期失败:  $DOMAIN"  admin@example.com

                发送Telegram通知

                curl  -s  -X  POST  "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage"  \

                    -d  chat_id="$TELEGRAM_CHAT_ID"  \

                    -d  text="证书  $DOMAIN  续期失败,请立即检查!"

                ;;

esac

EOF

chmod  +x  /usr/local/bin/acme-notify.sh

应用通知配置

acme.sh  --set-notify  --notify-hook  /usr/local/bin/acme-notify.sh

五、高级自动化场景

1.  多域名批量管理

bash

!/bin/bash

multi-domain-manager.sh

域名列表文件格式:每行一个域名,支持通配符

DOMAIN_LIST="/etc/acme/domains.list"

读取域名列表

while  IFS=  read  -r  domain  ||  [[  -n  "$domain"  ]];  do

        跳过空行和注释

        [[  -z  "$domain"  ||  "$domain"  =~  ^#  ]]  &&  continue

        echo  "处理域名:  $domain"

        检查证书是否已存在

        if  acme.sh  --list  |  grep  -q  "$domain";  then

                echo  "证书已存在,检查续期"

                acme.sh  --renew  -d  "$domain"  --force

        else

                echo  "申请新证书"

                判断是否通配符证书

                if  [[  "$domain"  ==  *"*"*  ]];  then

                        acme.sh  --issue  -d  "$domain"  --dns  dns_cf

                else

                        acme.sh  --issue  -d  "$domain"  --webroot  /var/www/html

                fi

                安装证书

                acme.sh  --install-cert  -d  "$domain"  \

                    --key-file  "/etc/ssl/acme/$domain/key.pem"  \

                    --fullchain-file  "/etc/ssl/acme/$domain/fullchain.pem"  \

                    --reloadcmd  "/usr/local/bin/reload-services.sh  $domain"

        fi

done  <  "$DOMAIN_LIST"

2.  Docker容器内使用

dockerfile

Dockerfile示例

FROM  alpine:latest

安装acme.sh

RUN  apk  add  --no-cache  curl  socat  \

        &&  curl  https://get.acme.sh  |  sh  \

        &&  apk  del  curl

创建证书目录

RUN  mkdir  -p  /acme/certs

复制配置脚本

COPY  acme-config.sh  /usr/local/bin/

RUN  chmod  +x  /usr/local/bin/acme-config.sh

设置入口点

ENTRYPOINT  ["/usr/local/bin/acme-config.sh"]

bash

acme-config.sh  -  Docker容器内运行

!/bin/sh

设置DNS提供商凭证

export  CF_Token="$CF_TOKEN"

申请证书

~/.acme.sh/acme.sh  --issue  -d  "$DOMAIN"  --dns  dns_cf

导出证书

~/.acme.sh/acme.sh  --install-cert  -d  "$DOMAIN"  \

    --key-file  /acme/certs/key.pem  \

    --fullchain-file  /acme/certs/fullchain.pem  \

    --reloadcmd  "echo  '证书已更新'"

保持容器运行

tail  -f  /dev/null

3.  Kubernetes集成

yaml

acme-k8s-cronjob.yaml

apiVersion:  batch/v1

kind:  CronJob

metadata:

    name:  acme-cert-renewal

spec:

    schedule:  "0  2  *  *  *"    #  每天凌晨2点

    concurrencyPolicy:  Forbid

    jobTemplate:

        spec:

            template:

                spec:

                    containers:

                    -  name:  acme

                        image:  neilpang/acme.sh

                        env:

                        -  name:  CF_Token

                            valueFrom:

                                secretKeyRef:

                                    name:  cloudflare-secret

                                    key:  api-token

                        -  name:  DOMAIN

                            value:  "example.com"

                        command:

                        -  /bin/sh

                        -  -c

                        -  |

                            申请或续期证书

                            acme.sh  --issue  -d  $DOMAIN  --dns  dns_cf

                            导出证书到共享卷

                            acme.sh  --install-cert  -d  $DOMAIN  \

                                --key-file  /certs/key.pem  \

                                --fullchain-file  /certs/fullchain.pem

                                更新Kubernetes  Secret

                            kubectl  create  secret  tls  $DOMAIN-tls  \

                                --cert=/certs/fullchain.pem  \

                                --key=/certs/key.pem  \

                                --dry-run=client  -o  yaml  |  \

                                kubectl  apply  -f  -

                        volumeMounts:

                        -  name:  certs

                            mountPath:  /certs

                        -  name:  kubeconfig

                            mountPath:  /root/.kube

                    volumes:

                    -  name:  certs

                        emptyDir:  {}

                    -  name:  kubeconfig

                        secret:

                            secretName:  kubeconfig-secret

                    restartPolicy:  OnFailure

六、故障排除与监控

1.  调试模式

bash

启用详细日志

acme.sh  --issue  -d  example.com  \

    --debug  \

    --log-level  2  \

    --log  "/var/log/acme.log"

调试DNS验证

acme.sh  --issue  -d  example.com  \

    --dns  dns_cf  \

    --debug  \

    --dnssleep  300  \

    --test    #使用测试环境

检查证书详情

openssl  x509  -in  /etc/ssl/acme/example.com/fullchain.pem  -text  -noout

验证证书链

openssl  verify  -CAfile  /etc/ssl/acme/example.com/chain.pem  \

    /etc/ssl/acme/example.com/fullchain.pem

2.  监控脚本

bash

!/bin/bash

cert-monitor.sh

CERT_DIR="/etc/ssl/acme"

ALERT_DAYS=7

NOW=$(date  +%s)

check_cert_expiry()  {

        local  cert_file="$1"

        local  domain=$(basename  $(dirname  "$cert_file"))

        if  [[  !  -f  "$cert_file"  ]];  then

                echo  "错误:  证书文件不存在:  $cert_file"

                return  1

        fi

        获取证书过期时间

        local  expiry_date=$(openssl  x509  -in  "$cert_file"  -enddate  -noout  |  cut  -d=  -f2)

        local  expiry_timestamp=$(date  -d  "$expiry_date"  +%s)

        local  days_left=$((  (expiry_timestamp  -  NOW)  /  86400  ))

        

        if  [[  $days_left  -le  $ALERT_DAYS  ]];  then

                echo  "  警报:  证书  $domain  将在  $days_left  天后过期!"

                  发送警报

                send_alert  "$domain"  "$days_left"

                尝试自动续期

                if  [[  $days_left  -lt  3  ]];  then

                        echo  "尝试自动续期..."

                        acme.sh  --renew  -d  "$domain"  --force

                fi

        else

                echo  "正常:  证书  $domain  还有  $days_left  天过期"

        fi

}

遍历所有证书

for  cert_dir  in  $CERT_DIR/*/;  do

        cert_file="${cert_dir}fullchain.pem"

        if  [[  -f  "$cert_file"  ]];  then

                check_cert_expiry  "$cert_file"

        fi

done

3.  常见问题解决

bash

问题1:证书续期失败,提示账户未注册

acme.sh  --register-account  -m  my@example.com

问题2:DNS验证超时

增加等待时间

acme.sh  --issue  -d  example.com  --dns  dns_cf  --dnssleep  600

问题3:端口80被占用

使用备用端口或停止占用服务

acme.sh  --issue  -d  example.com  --standalone  --httpport  88

问题4:证书链不完整

更新acme.sh并强制更新证书

acme.sh  --upgrade

acme.sh  --renew  -d  example.com  --force

问题5:私钥权限问题

chmod  600  /etc/ssl/acme/example.com/key.pem

chown  root:root  /etc/ssl/acme/example.com/key.pem

七、安全最佳实践

1.  密钥安全

bash

使用ECC密钥(更安全,性能更好)

acme.sh  --issue  -d  example.com  \

    --keylength  ec-256  \

    --ecc    #  强制使用ECC


或使用更长的RSA密钥

acme.sh  --issue  -d  example.com  \

    --keylength  4096


保护私钥文件

mkdir  -p  /etc/ssl/acme

chmod  700  /etc/ssl/acme

find  /etc/ssl/acme  -name  "*.pem"  -exec  chmod  600  {}  \;

2.  证书透明度日志

bash

启用证书透明度日志

acme.sh  --issue  -d  example.com  \

    --preferred-chain  "ISRG  Root  X1"  \

    --cert-file  /etc/ssl/acme/example.com/cert.pem  \

    --ca-file  /etc/ssl/acme/example.com/ca.pem  \

    --fullchain-file  /etc/ssl/acme/example.com/fullchain.pem

验证证书是否在CT日志中

openssl  x509  -in  cert.pem  -text  |  grep  -A5  "CT  Precertificate  SCTs"

4.  OCSP装订优化

bash

自动更新OCSP响应

cat  >  /usr/local/bin/update-ocsp.sh  <<  'EOF'

#!/bin/bash

DOMAIN="example.com"

CERT_DIR="/etc/ssl/acme/$DOMAIN"

获取OCSP响应

openssl  ocsp  -noverify  -no_nonce  \

    -issuer  "$CERT_DIR/chain.pem"  \

    -cert  "$CERT_DIR/cert.pem"  \

    -url  http://ocsp.int-x3.letsencrypt.org  \

    -respout  "$CERT_DIR/ocsp.der"

转换为PEM格式(如果需要)

openssl  ocsp  -resp_in  "$CERT_DIR/ocsp.der"  \

    -respout  "$CERT_DIR/ocsp.pem"  -noverify

EOF

添加到cron

echo  "0  4  *  *  *  /usr/local/bin/update-ocsp.sh"  |  crontab  -

八、备份与恢复

1.  证书备份

bash

!/bin/bash

cert-backup.sh

BACKUP_DIR="/backup/acme/$(date  +%Y%m%d)"

mkdir  -p  $BACKUP_DIR

备份acme.sh配置

cp  -r  ~/.acme.sh  $BACKUP_DIR/acme-home

备份证书文件

cp  -r  /etc/ssl/acme  $BACKUP_DIR/certs

备份Nginx配置

cp  -r  /etc/nginx/ssl  $BACKUP_DIR/nginx-ssl

创建压缩包

tar  -czf  /backup/acme-backup-$(date  +%Y%m%d).tar.gz  -C  $BACKUP_DIR  .

上传到云存储(可选)

rclone  copy  /backup/acme-backup-$(date  +%Y%m%d).tar.gz  backup:bucket/

echo  "备份完成:  $BACKUP_DIR"

2.  灾难恢复

bash

!/bin/bash

cert-recovery.sh

BACKUP_FILE="/backup/acme-backup-20240101.tar.gz"

RESTORE_DIR="/tmp/acme-restore"

解压备份

mkdir  -p  $RESTORE_DIR

tar  -xzf  $BACKUP_FILE  -C  $RESTORE_DIR

恢复acme.sh配置

cp  -r  $RESTORE_DIR/.acme.sh  ~/

恢复证书文件

cp  -r  $RESTORE_DIR/certs/*  /etc/ssl/acme/

恢复服务配置

cp  -r  $RESTORE_DIR/nginx-ssl/*  /etc/nginx/ssl/

重新加载服务

systemctl  reload  nginx

systemctl  reload  postfix

验证恢复

for  cert  in  /etc/ssl/acme/*/fullchain.pem;  do

        echo  "验证证书:  $(basename  $(dirname  $cert))"

        openssl  x509  -in  $cert  -noout  -subject  -dates

done

用户通过以上配置步骤,acme.sh可以实现完全自动化的Let's  Encrypt证书管理,包括申请、部署、续期和监控,确保用户服务网站始终使用有效SSL证书