全站 HTTPS 【完】:acme.sh 泛域名证书生成和配置

全站 HTTPS 【完】:acme.sh 泛域名证书生成和配置

September 05, 2022
分享 ,

目前已经全部搞妥了,有了泛域名证书之后我把之前所有的域名证书全部替换了一下,现在更方便管理了。


既然要开启全站 HTTPS 肯定离不开 SSL 证书这个话题。

我以前一直使用的腾讯的 TrustAsia,也就是诚信亚洲的证书。但是腾讯的这个证书有个毛病就是不支持泛域名(不能说不支持,是要花钱,而且死贵),而且只能一年一年申请。上次我笔记服务 Trilium 的证书过期了就要重新申请并再次上传到服务器上替换掉旧的证书,那个时候觉的超级麻烦,这也是我一直没弄 HTTPS 的主要原因。

这次在给又拍云弄 SSL 证书的时候忽然就想着要不要查查看有没有免费的泛域名证书用,这一搜果然有 —— acme.sh ,他可以支持自动化的从 Let’s Encrypt 获取免费的泛域名证书,而且还支持自动更新。我之前有一次想给 Ghost 博客搞全站 HTTPS 时还用过这个服务,当时 Ghost 支持一键调用 acme.sh 生成证书并启用 HTTPS,不过当时没研究这个东西所以不太懂,后来出了些配置问题弄不懂我也就弃用了。

acme.sh 介绍

acme.sh 是个开源的 shell 证书生成脚本,他可以自动生成 Let’s Encrypt 的证书,而 Let’s Encrypt 是一家免费、开放、自动化的证书颁发机构(CA),为公众的利益而运行。它是一项由 Internet Security Research Group(ISRG)提供的服务。我们以尽可能对用户友好的方式免费提供为网站启用 HTTPS(SSL/TLS)所需的数字证书。这是因为我们想要创建一个更安全,更尊重隐私的 Web 环境。

💡
It's probably the easiest & smartest shell script to automatically issue & renew the free certificates.
这可能是最简单和最智能的 shell 脚本自动发布和更新免费证书。
GitHub - acmesh-official/acme.sh: A pure Unix shell script implementing ACME client protocol
A pure Unix shell script implementing ACME client protocol - GitHub - acmesh-official/acme.sh: A pure Unix shell script implementing ACME client protocol

安装

我根据 acme.sh 的 github 上的中文说明安装好了这个证书,不过最开始我看的是网络上的其他教程踩了不少坑,很多教程要不就是太老,要不就是各种稀奇古怪的原因无法正常安装。

首先需要安装 acme.sh。我们只需要执行很简单的一行命令即可安装该脚本。不过使用国内的腾讯轻量云服务器安装时会在安装过程中经常卡住,我看了一下 sh 脚本好像要请求 Github 的服务器,但是国内不挂梯子访问 Github 会很慢。所以我每次卡住就直接结束命令重新再执行一次,直到顺畅执行成功为止(反正就是碰运气。)

curl  https://get.acme.sh | sh -s email=my@example.com
ubuntu 执行以上命令安装

这个命令会把 acme.sh 安装到你的用户目录下,并设为隐藏,你可以在安装完成之后通过 ls -a 来检查是否有名为 .acme.sh 目录。

安装成功,并自动设置了 cron job

在安装完成之后你可能需要执行 alias acme.sh=~/.acme.sh/acme.sh 创建 一个 bash 的 alias,我安装的时候看的另外一个教程没有说这一部,导致一直提示 acme.sh: command not found ,无法执行 acme.sh 命令,卡了我好几个小时。

认证

官方提供的证书认证方式有三种

  • http 方式需要在你的网站根目录下放置一个文件,来验证你的域名所有权,完成验证。然后就可以生成证书了
  • 手动 dns 方式,手动在域名上添加一条 txt 解析记录,验证域名所有权.
  • 可以使用域名解析商提供的 api 自动添加 txt 记录完成验证.

前面两种都比较麻烦,我就直接使用了第三种:利用域名解析商提供的 API 让 acme.sh 自动进行相关的验证操作。acme.sh 目前支持 cloudflare, dnspod, cloudxns, godaddy 以及 ovh 等数十种解析商的自动集成。

获取 DNS 服务商 token

我用的是 DNSPod,所以这里就详细说下 DNSPod 怎么获取 token。

  • 登录 DNSPod 后台
  • 点击右上角自己的头像
  • 点击左侧选项卡伤的的 DNSPod Tokens
  • 点击 创建密钥 后起个名字,接着就能拿到 ID 和 token 值,另外注意,Token 值只显示一次,请记好备份。

设置 DNSPod token

通过在命令行里执行以下命令设置 token,要严格按格式来, DP_Id 后的 = 号前后不能有空格。 DP_Id 为 DNSPod 里生成的 idDP_Key 为生成的 token

export DP_Id=""
export DP_Key=""

生成

生成证书,这里一定要先添加主域名,再添加泛域名。我们可以通过 -d 来添加多个域名。域名生成后证书文件会自动存放在 .acme.sh 目录里,文件夹名称为你添加的主域名。

acme.sh   --issue   --dns dns_dp   -d aa.com  -d *.aa.com

安装证书到指定目录

因为我用的 Nginx,所以这里说一下怎么安装到 Nginx 里。

我之前看的教程是直接 cp 证书到 nginx 证书目录里,但是看了官方的说明和其他一些网友分享的经验才知道最好是通过命令来安装证书文件。

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

我们需要通过 --install-cert 来安装证书文件,使用官方的这种方式以后可以自动无感续期、自动更新等。官方安装说明 reloadcmd 使用的是:  service nginx force-reload ,我用的下面的,不知道会不会有什么问题。

acme.sh --install-cert -d example.com \
--key-file       /youdata/ssl/nginx/yourdomain.key  \
--fullchain-file /youdata/ssl/nginx/fullchain.cer \
--reloadcmd     "sudo nginx -s reload"
安装证书文件到指定目录,并设置重载命令

这样在指定目录会自动生成对应的证书文件,接下来我们在 Nginx 配置文件里进行引用就行了。

续期

我们前面在安装 acme.sh 的时候默认会自动生成一个 crontab 任务,我们可以通过 crontab -l 查看。默认的计划任务是每天检查一次是否需要续期,如果需要则会自动续期,当然你也可以自由设置任务,crontab 示例配置如下:

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

Nginx 配置文件

我以前没有用代码片段来管理 ssl 证书,也不懂要怎么弄,这次在 这篇文章里 解锁了代码段的使用(但是这篇文章的其他部分都是坑,可能是因为比较老的缘故,4 年前的文章了)

先在 /etc/nginx/snippets 目录下创建代码段文件 ssl-params.conf 专门拿来配置 SSL 相关的设置,内容如下,用的也是上面那位作者的设置,不知道 4 年前的东西现在是不是要改什么?如果有看到这里的大佬麻烦指点一下。

# /etc/nginx/snippets/ssl-params.conf

server_tokens   off;

ssl_session_cache        shared:SSL:10m;
ssl_session_timeout      60m;

ssl_session_tickets      on;

ssl_stapling             on;
ssl_stapling_verify      on;

resolver                 8.8.4.4 8.8.8.8  valid=300s;
resolver_timeout         10s;
ssl_prefer_server_ciphers on;

# 证书路径 绝对地址
ssl_certificate          /youdata/ssl/nginx/fullchain.cer;
ssl_certificate_key      /youdata/ssl/nginx/yourdomain.key;

ssl_protocols            TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload";
add_header  X-Frame-Options  deny;
add_header  X-Content-Type-Options  nosniff;
add_header x-xss-protection "1; mode=block";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: https:; connect-src 'self' https:; img-src 'self' data: https: blob:; style-src 'unsafe-inline' https:; font-src https:";

接下来再新建一个 nginx 配置文件,并配置 server 项,并在 server 项里通过 include 命令导入我们之前写的 ssl 配置代码段即可。

server {
    listen 443 ssl;
    server_name test.yourdomain.com;
    
    include snippets/ssl-params.conf;

  location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header Host $http_host;
      proxy_pass http://127.0.0.1:15215;

      # 如果您要使用本地存储策略,请将下一行注释符删除,并更改大小为理论最大文件尺寸
      # client_max_body_size 20000m;
  }


    location ~ /.well-known {
        allow all;
    }

    client_max_body_size 50m;
}


加入评论