linux 云主机 pip 安装配置 letsencrypt certbot 为多个域名生成免费 https 证书实录(华为云 EulerOS)

摘要: linux 华为云 EulerOS 云主机使用 pip 方式安装配置 letsencrypt certbot, 并为多个域名生成免费 https 证书实录, 包括 python 环境配置, 下载 certbot 及 certbot-nginx, 生成证书及配置 certbot 周期性自动更新证书, 包含使用的全部命令, 每一步的操作截图及命令行输出

目录

本文记录了我在华为云 EulerOS linux 云主机使用 python pip 方式安装配置 Let's Encrypt certbot, 并为我的网站的多个域名生成免费 https 证书的整个过程, 包括 python 环境配置, 下载 certbot 及 certbot-nginx, 一次性生成多个域名的证书及注意事项, 以及最后配置 certbot 周期性自动更新证书, 包含使用的全部命令, 每一步的操作截图及命令行输出, 希望对想配置多个域名的免费 https 证书的同学起参考作用.

你的环境可能跟我的有所区别, 但大体的流程是相似的, 如果你也要申请证书, 建议先把整个过程先读一遍, 再去真正执行, 这样操作起来更加心中有数.

前言

先啰嗦几句背景知识, 关于 https 证书的, Let's Encrypt 组织和 Certbot 证书机器人相关的, 如果你已经了解这些, 可以跳过.

关于 https 证书

其实一般的网站是不一定需要搞这个 https 证书的, 主要是现在浏览器等方面对非 https 的网站歧视越来越严重, 另外很多 api 的调用也必须是 https 的, 逼着你不得不采用这些证书.

其实卖证书是一个很大的生意, 很多云服务厂商会免费给你一年的证书, 但后面就要钱, 而且你会发现这个价格还真不美丽, 特别是你想要那种支持多个域名的泛域名证书的时候, 所以也才有了找免费证书的动力. 毕竟除了风力发电机外, 谁的钱也不是大风刮来的, 能省点就省点.

关于 Let's Encrypt 和 Certbot 证书机器人

再简单说说 Let's Encrypt 和 Certbot, 如果你知道了这些或只想了解怎么操作, 也大可以不看.

不过我觉得多一点了解, 也会让你对接下来每一步操作的目的有更清晰的认知.

Let's Encrypt 是一家免费、开放、自动化的证书颁发机构, 由非营利组织互联网安全研究组(ISRG)运作.

简单讲呢, 前面不是说证书要钱还不便宜吗, 这组织就是给大家送福利的女菩萨, 它不但让大家白嫖证书, 还担心你不会搞, 所以整出了一个软件来帮助你, 也就是 certbot 证书机器人, 这是怎样的国际主义精神... 病?

当然了, 这也跟证书相关的一些安全背景知识我们掌握得比较薄弱有关. 大多数人作为一个应用程序员, 能把产品经理搞的需求实现完就不错啦, 有那闲工夫, 程序员可能去钓鱼, 程序媛不知道, 钓凯子还是钓金龟? 请留言.

应该说现在时代真好呀, 特别是在 AI 的加持下, 只要你愿意折腾, 哪怕只有半桶水, 不懂也照样可以搞很多东西出来, 有时让我不禁想起了那句啄木鸟的名言...

如果建筑工人像程序员写软件那样盖房子, 那第一只飞来的啄木鸟就能毁掉人类文明. (Gerald Weinberg)

If builders built houses the way programmers built programs, the first woodpecker to come along would destroy civilization.(Gerald Weinberg)

所以如果你有空闲, 其实我是建议可以多深入了解下相关安全背景知识的. 我也尽量在这个介绍过程中将涉及的一些东西做些科普, 这里很多细节我也是自行了解或询问 AI 得知的, 如果有什么不对的地方, 也欢迎指正.

正式操作

好了, 闲话不多说, 让我们进入正题.

官网, 选择 web server 和操作系统

首先进入 certbot 首页, 网址是 https://certbot.eff.org/, 为方便大家申请, 它还做得比较流程化, 它首先给你两个选项, 然后根据你的选择给出个性化的操作指南, 非常贴心了属于是.

  1. 首先是选 web server. 也你现在云服务器上的网站用的是哪个 web server 软件, 是 nginx 还是 apache 或是其它之类的, 你就在这里先选好, 因为我用的是 nginx, 所以我选的就是它, 后面的操作也是以它为例来说的. 如果你用的是其它, 流程细节上会有些许不同, 但大的流程是一样的.

    certbot-select-web-server

  2. 其次是选操作系统. 提供了包括 windows, mac, linux 等在内的多个选择, 其中 linux 下还有两个选项, linux(snap) 和 linux(pip). 在此我选的是 linux(pip), 下面会详述下这两个要怎么选.

    certbot-select-os-system

这套都选完了之后, 就会出来一套针对性的操作指南, 当然都是全英文的, 步骤也挺多的, 总共 10 大步骤, 下面都会说到.

另外, 如果以上两项你都不知道该怎么选, 它左下角还有个 "Help, I'm not sure!", 你可以点击进去看看, 不过我也没有详细看它的说明这时要怎么搞. 如果你搞过, 不妨留言说说.

关于 linux 下是用 snapd 还是 pip 的抉择

上个流程中提到, 对于 linux 系统, 有两个选项, 一个是 linux(snap), 还有一个是我选的 linux(pip), 如果你也纠结这两个要怎么选, 下面说下我当时的选择.

对于 linux, 机构推荐你使用 linux(snapd) 的方式. 这个 snapd 简单讲就是一个软件包的安装管理升级之类的工具, 有点像是应用商店或应用商城.

有种为装个软件, 得先装个 xxx 软件管家或 xxx 应用商店的感觉...

既然是它推荐的方式, 装就装吧, 我一开始也是选的用 snapd 方式, 结果搞了半天, 这个 snapd 在 eulerOS 上装不起来. 主要是下载不到这个玩意.

如果你是 centos 系统, 通过一些仓库的配置, 应该是可以成功下载到

所以最后放弃了 snapd 的方式, 决定采用 pip 的方式.

虽然我不是 python 大佬, 但 pip 这个我还是知道一点的, 所以当 snapd 方式失败后, 我就瞥见了这个显眼包安装工具...

选择 pip 安装 certbot

关于是选择 default 还是 wildcard 模式

在以上两个选项都选好之后, 它就会生成一系列有针对性的操作指南了, 我选的是 nginx + pip 方式, 所以下面的叙述也是这两种方式. 如果你是其它方式, 其实大的流程还是差不多的.

操作指南出来后, 其实还有一个选择, 你可以看到有一个选择卡, 有两个页签:

  • default, 也就是普通的证书, 是缺省的选择.
  • wildcard, 是通配符模式, 也就是泛域名证书, 这个就比较高级了. certbot-default-wilcard

普通模式比较好理解, 就以我的域名 xiaogd.net 为例, 如果我要一个 www 的三级域名, 那我就要申请一张 www.xiaoge.net 的证书.

如果我还想要一个 cc 的域名, 那我又要申请另外一张 cc.xiaoge.net 的证书.

而 wildcard 的泛域名证书呢, 大概就类似于你搞到了一张 *.xiaogd.net 类似的证书, 三级域名随便你定, 它都能通配.

当你想要很多个不同子域名时, 这样就很方便了, 你也不用反复去申请.

说到这里, 你可能会想, 那我们这次是不是就是去申请这个 wildcard 的泛域名证书呢? 很遗憾, 不是的.

我大概看了下申请泛域名证书的流程, 比普通的方式要更为复杂, 而且涉及到了一个插件, 它只针对国外一些云服务厂商提供了相应的支持, 但我没有找到国内云的官方插件, 华为云的自然也没有, 所以我就放弃了申请这个泛域名证书.

不过呢, 普通模式也是可以一次性申请多个域名的, 所以我最终还是选择了普通模式, 然后一口气申请了 8 个子域名, 目前来说足够用了. 而且不够了后面也还是可以再去追加申请的. 所以虽然它相对泛域名证书麻烦了些, 但也还是可以接受的.

如果你知道泛域名的怎么申请, 欢迎留言.

安装 python 环境

下面说的就是以普通模式申请多个证书的过程了.

既然要用 python pip 的方式, 那么首先自然要有 python 环境及 pip 工具. 如果你的系统中已经有了, 那这步可以跳过.

我发现 EulerOS 上的基础镜像就自带了 python3 及相应的 pip 工具, 所以这个不用安装了.

具体为 Huawei Cloud EulerOS 2.0 标准版 64位(公共镜像)

如果你的主机上没有安装, 而且你也是选择 pip 而不是 snapd 方式, 那么你需要先安装好 python3 环境.

如果你是 APT 系列的 linux 发行版, 如 Debian, Ubuntu, 那么用下述命令:

sudo apt update
sudo apt install python3 python3-venv

如果你是 RPM 系列的 linux 发行版, 如 Fedora, CentOS, RHEL, 那么用下述命令:

sudo dnf install python3

如果你是 RHEL 红帽系列的, 你应该用 python3X 这样的形式代替 python3, 比如 3.6 版:

sudo dnf install python36

如果你的发行版比较旧, 不支持较新的 dnf 命令, 那么就用 rpm:

sudo rpm install python3

对于华为云的 EulerOS, 其实我也没弄懂它属于哪个发行版系列, 如果你知道的话, 欢迎留言.

但我用 apt 命令它反馈说找不到, 而 rpm 则是可以的, 所以它大概算 rpm 系列或支持 rpm 及 dnf 的.

另: 关于 dnf 及 rpm, dnf 可以说是 rpm 的改进版, 是准备在未来替代 rpm 的, 也拥有比 rpm 更好的性能.

所以如果你的系统支持 dnf, 可以优先考虑使用 dnf.

安装 augeas-libs 配置文件读写工具

augeas-libs 是个配置文件读写工具, certbot 用来读取和修改 nginx 或 apache 等 server 的配置文件用的.

同上, 如果你是 RPM 系列的 linux 发行版, 如 Fedora, CentOS, RHEL, 那么用下述命令:

sudo dnf install augeas-libs

dnf install augeas-libs

同样的, dnf 不支持的就改用 rpm, 后同, 不再赘述

如果你是 APT 系列的 linux 发行版, 如 Debian, Ubuntu, augeas 的包名有所不同, 使用如下命令:

sudo apt install libaugeas0

注: 在不同发行版下, 包名可能略有区别, 你可以用 "augeas on" 关键字先搜索一下包名:

sudo apt search "augeas on"

删除先前的 certbot-auto 和 所有的 Certbot OS 包

这步是可选的. 如果你是新装, 可以不用管, 直接跳过此步骤.

但如果你之前曾使用 apt, rpm 或 dnf 装过 certbot, 或者安装失败, 那你应该先尝试卸载删除它们, 以保持环境的干净, 减少冲突.

你可以用诸如 sudo apt-get remove certbot, 或是 sudo dnf remove certbot, 或是 sudo yum remove certbot 之类的命令先删除它们. `

设置 Python 虚拟环境(virtual environment) 并更新 pip

你需要为 certbot 配置一个专门的 python 虚拟环境, 这可以提高 certbot 运行的稳定性, 避免受到系统缺省 python 环境变化的影响.

使用以下命令设置虚拟环境并更新 pip

sudo python3 -m venv /opt/certbot/
sudo /opt/certbot/bin/pip install --upgrade pip

设置 certbot 的 python 虚拟环境及升级 pip

更新 pip 这个应该只是一个可选, 就是升级 pip 到最新版本, 为保险起见, 还是按照它的要求先升级一波 pip, 从 pip21 升到了 pip24, 只要 pip 不是太旧的版本, 我觉得一般来说不升级可能也没啥问题.

另外注意这里用的是刚才专门为 certbot 配置的虚拟环境的 pip, 而不是系统本身那个 pip, 为区别起见, 需要带上完整的路径名, 否则用的是系统的 pip.

[root@ecs-230d ~]# /opt/certbot/bin/pip install --upgrade pip
WARNING: When the --extra-index-url option is used, the installation package is downloaded from an unverified URL. Exercise caution when using this option.
Requirement already satisfied: pip in /opt/certbot/lib/python3.9/site-packages (21.3.1)
Collecting pip
  Downloading pip-24.2-py3-none-any.whl (1.8 MB)
     |████████████████████████████████| 1.8 MB 12 kB/s              
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 21.3.1
    Uninstalling pip-21.3.1:
      Successfully uninstalled pip-21.3.1
Successfully installed pip-24.2

install certbot 和 certbot-nginx

在漫长的前戏完成之后, 终于可以进入主题开干了, 就是安装 certbotcertbot-nginx.

使用以下命令:

sudo /opt/certbot/bin/pip install certbot certbot-nginx

这个过程比较长, 这里只截图了开始的一小部分, 完整内容见后面的文字版输出内容: pip 安装 certbot 及 certbot nginx

另外, 如果你用的不是 nginx, 自然这里的 certbot-nginx 要改成相应的, 比如 certbot-apache 之类的.

这个请参考它为你生成的指南.

安装成功后如下: pip 安装 certbot 及 certbot nginx 成功

请保持耐心, 并祈祷你的网络能保持顺畅, 因为这两玩意又依赖一大堆的其它包, 并递归依赖更多的包, 所以需要下载很多东西, 有点 nodejs 的 node_modules 那味了... 不过即便失败了也没啥关系, 大不了从头再来嘛...

当你看到最后出现 "Successfully installed blah, blah..." 字样后, 也没有其它异常或错误的信息, 说明就安装成功了, 整个成功安装过程的输出如下:

[root@ecs-230d ~]# /opt/certbot/bin/pip install certbot certbot-nginx
Collecting certbot
  Downloading certbot-2.11.0-py3-none-any.whl.metadata (8.2 kB)
Collecting certbot-nginx
  Downloading certbot_nginx-2.11.0-py3-none-any.whl.metadata (1.4 kB)
Collecting acme>=2.11.0 (from certbot)
  Downloading acme-2.11.0-py3-none-any.whl.metadata (1.4 kB)
Collecting ConfigArgParse>=1.5.3 (from certbot)
  Downloading ConfigArgParse-1.7-py3-none-any.whl.metadata (23 kB)
Collecting configobj>=5.0.6 (from certbot)
  Downloading configobj-5.0.8-py2.py3-none-any.whl.metadata (3.4 kB)
Collecting cryptography>=3.2.1 (from certbot)
  Downloading cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl.metadata (5.4 kB)
Collecting distro>=1.0.1 (from certbot)
  Downloading distro-1.9.0-py3-none-any.whl.metadata (6.8 kB)
Collecting josepy>=1.13.0 (from certbot)
  Downloading josepy-1.14.0-py3-none-any.whl.metadata (1.8 kB)
Collecting parsedatetime>=2.4 (from certbot)
  Downloading parsedatetime-2.6-py3-none-any.whl.metadata (4.7 kB)
Collecting pyrfc3339 (from certbot)
  Downloading pyRFC3339-1.1-py2.py3-none-any.whl.metadata (2.0 kB)
Collecting pytz>=2019.3 (from certbot)
  Downloading pytz-2024.1-py2.py3-none-any.whl.metadata (22 kB)
Requirement already satisfied: setuptools>=41.6.0 in /opt/certbot/lib/python3.9/site-packages (from certbot) (59.4.0)
Collecting importlib-metadata>=4.6 (from certbot)
  Downloading importlib_metadata-8.2.0-py3-none-any.whl.metadata (4.7 kB)
Collecting PyOpenSSL!=23.1.0,>=17.5.0 (from certbot-nginx)
  Downloading pyOpenSSL-24.2.1-py3-none-any.whl.metadata (13 kB)
Collecting pyparsing>=2.2.1 (from certbot-nginx)
  Downloading pyparsing-3.1.2-py3-none-any.whl.metadata (5.1 kB)
Collecting requests>=2.20.0 (from acme>=2.11.0->certbot)
  Downloading requests-2.32.3-py3-none-any.whl.metadata (4.6 kB)
Collecting six (from configobj>=5.0.6->certbot)
  Downloading six-1.16.0-py2.py3-none-any.whl.metadata (1.8 kB)
Collecting cffi>=1.12 (from cryptography>=3.2.1->certbot)
  Downloading cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB)
Collecting zipp>=0.5 (from importlib-metadata>=4.6->certbot)
  Downloading zipp-3.19.2-py3-none-any.whl.metadata (3.6 kB)
Collecting pycparser (from cffi>=1.12->cryptography>=3.2.1->certbot)
  Downloading pycparser-2.22-py3-none-any.whl.metadata (943 bytes)
Collecting charset-normalizer<4,>=2 (from requests>=2.20.0->acme>=2.11.0->certbot)
  Downloading charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (33 kB)
Collecting idna<4,>=2.5 (from requests>=2.20.0->acme>=2.11.0->certbot)
  Downloading idna-3.7-py3-none-any.whl.metadata (9.9 kB)
Collecting urllib3<3,>=1.21.1 (from requests>=2.20.0->acme>=2.11.0->certbot)
  Downloading urllib3-2.2.2-py3-none-any.whl.metadata (6.4 kB)
Collecting certifi>=2017.4.17 (from requests>=2.20.0->acme>=2.11.0->certbot)
  Downloading certifi-2024.7.4-py3-none-any.whl.metadata (2.2 kB)
Downloading certbot-2.11.0-py3-none-any.whl (407 kB)
Downloading certbot_nginx-2.11.0-py3-none-any.whl (98 kB)
Downloading acme-2.11.0-py3-none-any.whl (95 kB)
Downloading ConfigArgParse-1.7-py3-none-any.whl (25 kB)
Downloading configobj-5.0.8-py2.py3-none-any.whl (36 kB)
Downloading cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl (4.0 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.0/4.0 MB 1.3 MB/s eta 0:00:00
Downloading distro-1.9.0-py3-none-any.whl (20 kB)
Downloading importlib_metadata-8.2.0-py3-none-any.whl (25 kB)
Downloading josepy-1.14.0-py3-none-any.whl (32 kB)
Downloading parsedatetime-2.6-py3-none-any.whl (42 kB)
Downloading pyOpenSSL-24.2.1-py3-none-any.whl (58 kB)
Downloading pyparsing-3.1.2-py3-none-any.whl (103 kB)
Downloading pytz-2024.1-py2.py3-none-any.whl (505 kB)
Downloading pyRFC3339-1.1-py2.py3-none-any.whl (5.7 kB)
Downloading cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (443 kB)
Downloading requests-2.32.3-py3-none-any.whl (64 kB)
Downloading zipp-3.19.2-py3-none-any.whl (9.0 kB)
Downloading six-1.16.0-py2.py3-none-any.whl (11 kB)
Downloading certifi-2024.7.4-py3-none-any.whl (162 kB)
Downloading charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (142 kB)
Downloading idna-3.7-py3-none-any.whl (66 kB)
Downloading urllib3-2.2.2-py3-none-any.whl (121 kB)
Downloading pycparser-2.22-py3-none-any.whl (117 kB)
Installing collected packages: pytz, parsedatetime, zipp, urllib3, six, pyrfc3339, pyparsing, pycparser, idna, distro, ConfigArgParse, charset-normalizer, certifi, requests, importlib-metadata, configobj, cffi, cryptography, PyOpenSSL, josepy, acme, certbot, certbot-nginx
Successfully installed ConfigArgParse-1.7 PyOpenSSL-24.2.1 acme-2.11.0 certbot-2.11.0 certbot-nginx-2.11.0 certifi-2024.7.4 cffi-1.16.0 charset-normalizer-3.3.2 configobj-5.0.8 cryptography-43.0.0 distro-1.9.0 idna-3.7 importlib-metadata-8.2.0 josepy-1.14.0 parsedatetime-2.6 pycparser-2.22 pyparsing-3.1.2 pyrfc3339-1.1 pytz-2024.1 requests-2.32.3 six-1.16.0 urllib3-2.2.2 zipp-3.19.2

最后会依赖装上一大堆的, 不用担心, 这些都在它专有的虚拟环境中, 也不会影响你的 python 全局环境.

ln 链接 certbot 到全局可执行环境中

然后是给 certbot 程序做个软链接(符号链接), 大概就是生成一个类似于 windows 下的快捷方式并放到 /usr/bin 下, 因为这个路径默认已经被 PATH 环境变量包含, 这样 certbot 就能在全局的各个地方执行了, 而不需要带上完整的路径名.

ln -s /opt/certbot/bin/certbot /usr/bin/certbot

-s 指 symbolic, 即符号链接, 相对于硬链接而言也称为软链接.

可以执行 ls -l /usr/bin | grep certbot 检查下, 如果发现一条指向 /opt/certbot/bin/certbot 的快捷方式记录, 说明链接成功了 view-certbot-link

测试 certbot 命令

在 certbot 安装好并设置为全局可访问后, 就可以测试一下了, 使用以下命令:

certbot --version

测试 certbot 命令

如果正确输出了版本号, 说明 certbot 已被正确安装并可以访问了.

[root@ecs-230d ~]# certbot --version
certbot 2.11.0

让 certbot 获取并安装证书(方式一, 半手工)

一切准备就绪后, 接下来就到我们的重头戏, 让 certbot 获取并安装证书并自动配置 nginx.

此时也有两种选择:

  1. 只获取证书 : 只是单纯让 certbot 获取并安装证书到你的云服务器上, 但并不帮你配置 nginx.
  2. 获取并在 nginx 上配置证书: 一条龙服务, certbot 获取并安装证书到你的云服务器上, 并自动帮你调整 nginx 配置以启用 https.

先说第一种, 如果你只想单纯获取证书, 那么执行以下命令就好了:

sudo certbot certonly --nginx

这里的 certonly 选项表示它只帮你生成证书, 然后你要自己去编辑 nginx.conf 文件, 启用 https 协议所使用的 443 端口, 配置证书的路径, 甚至包括让 80 端口自动跳转 443 端口的配置等等.

另外, 执行这个命令之前, 你要确保你的 nginx.conf 已经正确配置了想要生成证书的域名, 以我的网站为例, 假如我想为 www.xiaogd.net 和 cc.xiaogd.net 这两个域名配置证书, 那么我要先确保这两个域名可以用普通的 http 协议在 80 端口上访问, 像这样:

  • http://www.xiaogd.net:80/
  • http://cc.xiaogd.net:80/

当然, http 协议默认对应的端口就是 80, 因此 url 可以省略端口, 通常这样访问就可以了:

  • http://www.xiaogd.net/
  • http://cc.xiaogd.net/

这也是我们通常见到的形式.

如果以上都 OK, 那么执行上述命令成功后, 证书就会获得并下载保存到本地, 缺省情况下, 以我的网站域名 xiaogd.net 为例, 保存的路径是 /etc/letsencrypt/live/xiaogd.net, 你可以用 ls 命令查看:

certbot-cert-location

可以看到有 4 个文件, cert.pem, chain.pem, fullchain.pem, privkey.pem.

  • cert.pem: 狭义上的证书, 主体内容就是一个公钥, 公开给浏览器端去加密用的, 以及关于域名的一些基础信息, 另外则是授予机构的签名.
  • chain.pem: 证书链文件. 也就是记录了谁给你签发的证书, 就是为你的证书的安全性背书的那些机构, 及更上层的机构, 一层层形成一个所谓的链.

    理论上你自己都可以生成公钥/私钥, 自己搞个证书. 但没人给你的证书的安全性背书的话, 浏览器仍然会认为你的网站是不值得信任的. 浏览器不但会验证你的证书, 还会溯源上去到直到根证书, 有种查到祖宗十八代看看是不是都是清白的感觉...

  • fullchain.pem: 这个是把 cert.pem 和 chain.pem 合并在一起了成了一个文件.

    现在 web server 端所谓配置其实主要就是配置这个, 除非你是早期或一些特殊版本的 server 软件, 则你可能需要单独分开配置那两个文件. 所以这个可以认为是简化配置用的, 只要配置并发送这一个文件给浏览器就行了.

  • privkey.pem: 这个是就是私钥文件了, 解密用的, 要保管好不要泄露了.

    浏览器用公钥加密的内容, 发到你的服务器上, 那你要怎么解密呢? 当然不是用公钥了, 因为公钥是公开的, 人人都能拿到. 如果用公钥解密就毫无秘密可言了, 当然公钥其实也解不了公钥加的密, 这个解密只能用私钥. 所以私钥你是要严格保管好的, 不能随意泄露给别人. 自然这个东西是要配置给 web server 的, 这样 nginx 或 apache 等才能解密.

关于非对称加密

这里啰嗦几句关于证书及它的所谓非对称加密(解密)方式, 如果你了解了相关背景或对这些背后原理没啥兴趣, 只想知道怎么操作就好, 那也可以放心跳过这一部分.

公钥/私钥这种加密解密方式与平常的用同一个密码(密钥)去加密和解密的方式是不太一样的.

用同一个密钥去加密和解密, 这个称为 对称加密(解密)

打个比喻, 其实我们日常用钥匙锁上(加密)和开锁(解密)就是对称的, 因为用的都是同一把钥匙. 都是同一把钥匙, 那可对称了.

而公钥/私钥这种加密解密方式, 则称为 非对称加密(解密).

同样打比喻的话, 大概就是有人发明了一种特殊的锁, 有两把钥匙, 一把公钥, 另一把呢, 不叫母钥, 叫私钥, 母的那是锁孔...

一把用来把锁给锁上(通常是公钥), 而另一把则用来开锁(私钥), 两把钥匙是不一样的, 自然也就不对称了, 也即所谓的 非对称 了.

至于这个锁到底是两个锁孔给两把不同的钥匙去插, 还是同一个锁孔但能同时接受两把钥匙, 这个就关系不大了, 反正重要的是有两把不同的钥匙

当然, 你可能会想, 谁这么无聊发明这么繁琐的方式. 其实是有它的应用场景的. 举个现实中的例子, 比如你是一个负责管理举报的, 那你完全可以设计这样的一个非对称的举报箱.

你自己保管好私钥, 举报箱旁边挂着公钥, 谁都可以拿去用或拿去配一把都无所谓. 有人要举报, 就把举报材料用你给的公钥锁在举报箱里, 这样就只有你才能用私钥打开, 并获得材料.

当然, 你可能会说, 像现在普遍见到的举报箱那样, 上面挖个孔让人把材料投进去不就行了.

但严格说起来, 那样是有漏洞的, 既然能投进去, 别人用一根细铁丝之类的是不是就可以勾出来?

而对于互联网的场景来说, 一个网站要面临一大堆的陌生访问者, 密钥首先无法在线下进行大规模的分发, 而线上如果分发对称密钥的话, 就会面临一个困境:

一开始时, 信道是不能加密的, 因为对方都还没收到密钥;

但如果公开的不加密的发送密钥, 则密钥在这个分发过程中就有可能被别有用心的人截获, 那后面的加密可能就无效了, 因为截获了密钥的人一样可以解密.

而采用非对称加密的话, 那一开始的信道是不加密的就不成问题了, 因为公钥本身就是公开的, 不需要加密去传输的.

当然, 这个过程中仍存在一种中间人攻击的风险. 即中间人截获你的公钥, 并掉包成它自己的另一套公钥, 发给你的访问者, 那这种情况就要通过证书链去防止了.

其实证书里不但包含了公钥, 还有证书的签发者对相关信息的签名, 而所谓签名, 其实就是用私钥对相关摘要信息进行加密.

是的, 私钥其实也可以用来加密的, 而私钥加密的, 则需要用公钥来解密; 正如公钥加密的, 要用私钥解密那样.

就是两个钥都可以加密, 但也都同时要依赖对方来解密.

以刚才举的举报箱为例, 你可以用私钥锁上, 别人可以用公钥打开.

但私钥锁上的, 你自己是无法用私钥打开的, 也要用公钥打开.

而公钥锁上的, 自然更无法用公钥打开了, 只能是用私钥打开.

所以一对公私钥的区别来自于你选择公开那个以及私藏哪个.

中间人虽然可以掉包你的公钥, 但它没有签发者的私钥, 因此就无法伪造证书链上的各级签名信息.

所谓签名, 大意就是授予者用自己的私钥对证书相关信息摘要的哈希值做一个加密并附在上面.

而浏览器安装好了本身就是自带集成了那些根证书的公钥, 因此可以验证整个证书链的有效性, 如果存在中间人的篡改, 浏览器会检测出来, 就会发出警告, 甚至拒绝访问.

另一方面, 私钥加密, 公钥解密同样可以用在个人签名上, 甚至在某些情况下这些签名还是具有法律效力的, 比如在某些电子商务的转账方面, 你用自己的私钥对某笔转账进行了签名, 那就表明你认可了这次的交易, 与传统手写签名认可一样.

举个例子, 你通过线上方式向某机构贷款了 10000 元, 那机构就可以要求你的公钥, 并要求你用私钥对类似 "我, 名字是xxx, 向xxx机构借款一万元" 这样的一句话进行签名, 简单讲就是用你的私钥对这句话进行加密, 并把加密信息附在上面, 与明文一起交给机构.

加密后的这句话就成了一堆乱七八糟的东西, 但如果用你的公钥一解密, 就可以得到那句借款的有意义的话, 可以与明文印证.

所以如果你有一天想抵赖, 那机构就可以让第三方来公证: 看, 这是一堆乱七八糟的东西, 可用你的公钥一解密, 居然成了一句有意义的借款的话, 世上哪有这么巧的事情? 所以肯定是你当初认可并用你的私钥加密的.

这种情况下, 第三方还是会认可你的确借款了, 你不能否认. 当然你可以辩解说我的私钥泄露了, 但妥善保管私钥也是你的责任呀, 不管怎样你都要还钱.

那以上就是整个公私钥非对称加解密的一个大概运作原理了. 当然这些东西都比较专业, 正如我前面所说, 作为一个应用层开发的程序员, 我们可能没那么多精力了解底层太多的细节, 但有个大概的了解, 还是有一定好处的, 我也是根据自己的了解, 尽量与大家科普一下.

理解了这些, 我们也就清楚大概要配置什么东西了. 如果以上说的有什么不妥之处, 也欢迎指正.

至于怎么手工配置 nginx, 我们看了后面全自动方式配置的结果就可以参考了.

让 certbot 获取并安装证书并自动配置 nginx(方式二, 全自动)

以上说了只获取证书的方式, 并啰嗦了一下关于非对称加密的一些背景知识. 当然我们还是用全自动一条龙的服务. 毕竟既然有这么好的东西, 为啥还不用呢?

真是帮人帮到底, 送佛送到西, 这么好的组织真的不多, 且珍惜吧.

但为一切顺利, 我们最好还是先做些检查, 确保我们的 nginx 的初始配置是正确的, 相关域名也是可以通过 80 端口正常访问的.

就以我的配置为例, nginx.conf 文件里的初始配置大概是这样的, 这里省略了一些无关的配置, 以及只保留了三个域名作为示例, 更多也是一样的:

# ... 其它配置略(下同)

http {
    # ...(略)
    
    server {
        server_name www.xiaogd.net;
        listen 80;
        location / {
            # ...(略)
        }
    }
    
    server {
        server_name cc.xiaogd.net;
        listen 80;
        location / {
            # ...(略)
        }
    }
    
    server {
        server_name spcp.xiaogd.net;
        listen 80;
        location / {
            # ...(略)
        }
    }
    
    # ...(其它域名的 server, 略)
}

在以上简化的配置中, 有三个域名, 那对于我来说, 我首先要确保它们都是正常可在 80 端口以普通不加密的 http 方式访问的, 也即是这样三个链接都能访问:

  • http://www.xiaogd.net:80/
  • http://cc.xiaogd.net:80/
  • http://spcp.xiaogd.net:80/

当然, 正如前面所说, http 协议默认对应的端口就是 80, 因此 url 可以省略端口, 通常这样就可以了:

  • http://www.xiaogd.net/
  • http://cc.xiaogd.net/
  • http://spcp.xiaogd.net/

有了以上前提后, 就可以开始执行命令了:

certbot --nginx

如果你是第一次配置, 它在前面阶段还会有些额外环节, 它会收集一些联系信息, 然后要求你同意一些服务条款之类的.

certbot 配置 nginx

首先它会提示你输入一个联系邮箱, 以便在 certbot 需要升级更新或有安全问题时通知你.

[root@ecs-230d ~]# certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): xxx@xxx.xx(你的邮箱)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

输入自己的邮箱, 回车继续.

当然这个它也提示可以输入一个 c, 表示取消(cancel), 我也清楚它这只是取消邮箱还是取消整个流程.

为了不出什么幺蛾子, 我还是老实按照它的要求输入了.

如果你有什么担心, 你可以选用一个不常用的邮箱也行.

不幸应了那句: 免费, 是的, 可代价是什么? 大概就是你至少会收到一些垃圾邮件吧...

接着它问你是否同意服务条款, 这个必须同意, 输入 y, 回车继续.

Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in
order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

接着它又问你是否同意把你的邮箱共享提供给一个什么组织, 似乎就是 certbot 它爹, 一大堆英文, 我也没耐心看, 大概你同意了就可能收一些对你来说差不多就垃圾邮件的东西了, 我怕出什么幺蛾子, 加上当时也懒得细看, 就同意了, 还是输入 y, 继续.

但是我感觉这里你拒绝应该也问题不大.

还挺鸡贼的, 夹在这些流程中, 不过对于我们中国人来说, 邮件一般也不怎么看的, 随便发吧, 反正邮箱里常年几百封未读, 再多一个, 也不过一斗芝麻掉一颗, 有你不多, 没你不少.

还是跟之前一样, 如果你在意, 可以在前面就提供一个不那么重要的邮箱, 或者这里拒绝也行.

不过我没有试过拒绝会是什么效果.

Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
Account registered.

再之后就到了要你仔细睁大眼睛看清楚的时候了, 这时问的就是跟你的域名相关的了.

它会读取你的 nginx.conf 配置文件, 然后列出里面配置的所有域名:

Which names would you like to activate HTTPS for?
We recommend selecting either all domains, or all domains in a VirtualHost/server block.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: xiaogd.net
2: bc.xiaogd.net
3: blog.xiaogd.net
4: book.xiaogd.net
5: cc.xiaogd.net
6: exp.xiaogd.net
7: spcp.xiaogd.net
8: www.xiaogd.net
9: zyxq.xiaogd.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 
Requesting a certificate for xiaogd.net and 8 more domains

像我这里的就是这样, 整整有 9 条, 包括那个裸域名在内, 每条前面有一个序号, 从 1 到 9.

其实有些也还没用到, 就是我之前说的, 先屯下来再说, 一次搞定, 免得后面又去折腾.

xiaogd.net: 这个会跳转到 www.xiaogd.net, 只是为方便别人输入裸域名访问的场景

bc.xiaogd.net: 这个给小朋友编程(bc)整理的一些资料

blog.xiaogd.net: 初步规划, 暂时没用到

book.xiaogd.net: 这是 gitbook 相关的, 目前里面放了三本书, 《circuitjs 电路模拟器的使用》,《编码: 隐匿在计算机软硬件背后的语言 在线交互电路操作说明》《穿越计算机的迷雾 在线交互电路操作说明》

cc.xiaogd.net: 这个是 circuitjs 的在线电路模拟器的一个应用.

exp.xiaogd.net: 这个是实验性测试一些东西的, 目前也没啥太多内容.

spcp.xiaogd.net: 这个也是一个 gitbook, 只是单独给了个域名, 是关于如何从零开始制作一个简易计算机的.

www.xiaogd.net: 这个就是主站的域名了.

zyxq.xiaogd.net: 这个是自己写的一个简单的组合易位象棋游戏.

看上去很多, 其实没多少, 基本都是些静态内容, 我的服务器也是最低端的那种配置, 动态的多了也跑不动.

我是觉得多用域名来区分不同内容, 比在同一个域名下, 用不同路径前缀去区分要好一些, nginx 配置起来也方便, 另外还能保持 cookie 的独立.

当然也会有一些麻烦的地方, 比如相互间的一些访问可能会涉及跨域等问题.

如果全都都要申请证书, 那直接按回车即可.

如果你只想配置部分的域名, 那你可以输入那些要的域名前面的序号, 多个数字间以空格或逗号隔开, 然后回车, 这样只会为你挑选的这些域名生成证书.

我是全部选了, 所以不用输什么, 直接回车了.

不过这里我犯了个错误, 就是之前提到的, 有些域名都还没配好 DNS 映射, 或者 nginx.conf 里没有配置 server 主页, 还不能访问.

我当时想趁这个申请就顺手多薅几个羊吧, 免得后面又再去申请, 麻烦, 就一口气先在 nignx.conf 里配置了一堆, 忘记了有些都还不能访问, 最后 certbot 提示部分证书申请失败了, 因为访问不了相关域名.

第一次最后阶段失败的整个输出记录如下:

[root@ecs-230d ~]# certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): xxx@xxx.xx(你的邮箱)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in
order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
Account registered.

Which names would you like to activate HTTPS for?
We recommend selecting either all domains, or all domains in a VirtualHost/server block.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: xiaogd.net
2: bc.xiaogd.net
3: blog.xiaogd.net
4: book.xiaogd.net
5: cc.xiaogd.net
6: exp.xiaogd.net
7: spcp.xiaogd.net
8: www.xiaogd.net
9: zyxq.xiaogd.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 
Requesting a certificate for xiaogd.net and 8 more domains

Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems:
  Domain: blog.xiaogd.net
  Type:   dns
  Detail: DNS problem: NXDOMAIN looking up A for blog.xiaogd.net - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for blog.xiaogd.net - check that a DNS record exists for this domain

  Domain: book.xiaogd.net
  Type:   dns
  Detail: DNS problem: NXDOMAIN looking up A for book.xiaogd.net - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for book.xiaogd.net - check that a DNS record exists for this domain

  Domain: spcp.xiaogd.net
  Type:   dns
  Detail: DNS problem: NXDOMAIN looking up A for spcp.xiaogd.net - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for spcp.xiaogd.net - check that a DNS record exists for this domain

Hint: The Certificate Authority failed to verify the temporary nginx configuration changes made by Certbot. Ensure the listed domains point to this nginx server and that it is accessible from the internet.

Some challenges have failed.
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.

我只好又回去把域名 DNS 映射好, 然后给各个域名下至少有个简单的主页文件 index.html, 能访问到就可以了, 内容是啥都无所谓, 写个 hello world 甚至空着都无关紧要, 只要不出 503, 404 之类的错误就好.

弄好了之后, 我又重新执行了 certbot --nginx

certbot 证书域名选择

这次, 前面那些问邮箱之类的流程就没有了, 因为已经配置过了, 直接就到了挑选域名的阶段, 直接回车, 然后证书就申请到了, 并保存到了云服务器上.

对于我的域名来说, 就是这样一个路径 /etc/letsencrypt/live/xiaogd.net/:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/xiaogd.net/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/xiaogd.net/privkey.pem
This certificate expires on 2024-11-04.
These files will be updated when the certificate renews.

虽然这里申请了很多个域名的证书, 但因为是一起申请的, 所以它们一起打包到了同一个文件里.

其中 fullchain.pem 是你的证书(含有公钥)及证书链合并一块的文件; 而 privkey.pem 则是私钥, 前面都介绍过了, 这里就不再赘述了.

因为我选的是一条龙服务, 所以证书申请到后, certbot 又自动开始配置 nginx, 一个一个域名去调整, 末了它还会自动帮你测试一把 https 的访问是否 OK 了.

如果你最后看到了 Congratulations!, 那就真的要 Congratulate 你了, 说明都搞掂了.

Deploying certificate
Successfully deployed certificate for xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for bc.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for blog.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for book.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for cc.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for exp.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for spcp.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for www.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for zyxq.xiaogd.net to /etc/nginx/nginx.conf
Congratulations! You have successfully enabled HTTPS on https://xiaogd.net, https://bc.xiaogd.net, https://blog.xiaogd.net, https://book.xiaogd.net, https://cc.xiaogd.net, https://exp.xiaogd.net, https://spcp.xiaogd.net, https://www.xiaogd.net, and https://zyxq.xiaogd.net

那对我的 nginx 而言, 自动配置之后, nginx.conf 大概就变成了这个样子, 这里也给出来给大家做个参考, 手动配置的也可以参考:

# ... 其它配置略(下同)

http {
    # ...(略)

    server {
        server_name  www.xiaogd.net;
        location / {
            # ...(略)
        }
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/xiaogd.net/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/xiaogd.net/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    }

    server {
        server_name cc.xiaogd.net;
        location / {
        	# ...(略)
        }
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/xiaogd.net/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/xiaogd.net/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    }

    server {
        server_name spcp.xiaogd.net;
        location / {
        	# ...(略)
        }
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/xiaogd.net/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/xiaogd.net/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    }

    # 80 端口到 https 的 301 跳转
    server {
        if ($host = cc.xiaogd.net) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
        server_name cc.xiaogd.net;
        listen 80;
        return 404; # managed by Certbot
    }
    
    server {
        if ($host = spcp.xiaogd.net) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
        server_name spcp.xiaogd.net;
        listen 80;
        return 404; # managed by Certbot
    }
    
    server {
        if ($host = www.xiaogd.net) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
        server_name  www.xiaogd.net;
        listen 80;
        return 404; # managed by Certbot
    }

    # ...(其它域名的 server, 略)
}

其实在每个域名下增加的配置都是一样的, 因为全部域名都打包到了同一个文件里, 就是如下 5 条, 每个后面都还加了个注释 # managed by Certbot 表明它是 certbot 自动配置, 也是提醒你不要随意去改动它, 除非你很清楚你在做什么:

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/xiaogd.net/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xiaogd.net/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

各个指令的一个具体解释:

  • listen 443 ssl: 这个是开启 443 端口的侦听, 因为 https 协议的默认端口就是 443.
  • ssl_certificate: 这个是证书及证书链
  • ssl_certificate_key: 私钥, 用于解密公钥加密的内容.
  • include: 包含 options-ssl-nginx.conf 配置文件. 该文件由 certbot 在生成或更新 ssl 证书时自动管理, 包含了优化和安全的 SSL/TLS 配置选项, 比如协议版本, 密码套件的选择等. 可以确保 nginx 采用了推荐的安全设置, 而不需要手动维护这些细节. 这个如果你手动配置, 不清楚的话可以不去配置.
  • ssl_dhparam: 指定了 Diffie-Hellman(DH) 参数文件的位置, Diffie-Hellman 密钥交换是一种加密协议, 用于在不安全的网络环境中安全地协商密钥. 使用强大的 DH 参数能够增强 SSL 连接的安全性. 这个如果你手动配置, 不清楚的话同样可以不去配置.

另外, 在最后面, nginx 还为每个域名配置了 80 端口到 https 的 301 永久性跳转, 加了这个之后, 普通 http 访问都会被跳转到 https, 也即是无法再通过普通方式访问了:

# ...(略)
server {
    if ($host = www.xiaogd.net) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    server_name  www.xiaogd.net;
    listen 80;
    return 404; # managed by Certbot
}
# ...(略)

当然, 如果你希望还是保留 80 端口, 允许访问者以不安全的 http 协议访问, 那这些配置你可以删除, 并重新加上 listen 80, 它可以与 listen 443 并存.

因为我的这些域名主要都是阅读相关的, 不涉及金钱, 甚至都没有涉及登录, 所以我还是删掉了那些跳转的, 并加了 listen 80; 允许了 80 端口的普通访问, 像这样:

# ...(略)
server {
    server_name spcp.xiaogd.net;
    location / {
        # ...(略)
    }
    listen 80;
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/xiaogd.net/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/xiaogd.net/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
# ...(略)

最后, 你也可以自行在浏览器里以 https 的方式去访问测试下, 像这样:

  • https://www.xiaogd.net:443/
  • https://cc.xiaogd.net:443/
  • https://spcp.xiaogd.net:443/

同样的, https 协议默认对应的端口就是 443, 因此 url 可以省略端口, 通常这样就可以了:

  • https://www.xiaogd.net/
  • https://cc.xiaogd.net/
  • https://spcp.xiaogd.net/

如果一切正常, 那么证书申请及配置到这一步就结束了.

certbot 证书安装成功

整个过程输出如下:

[root@ecs-230d ~]# certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log

Which names would you like to activate HTTPS for?
We recommend selecting either all domains, or all domains in a VirtualHost/server block.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: xiaogd.net
2: bc.xiaogd.net
3: blog.xiaogd.net
4: book.xiaogd.net
5: cc.xiaogd.net
6: exp.xiaogd.net
7: spcp.xiaogd.net
8: www.xiaogd.net
9: zyxq.xiaogd.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 
Requesting a certificate for xiaogd.net and 8 more domains

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/xiaogd.net/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/xiaogd.net/privkey.pem
This certificate expires on 2024-11-04.
These files will be updated when the certificate renews.

Deploying certificate
Successfully deployed certificate for xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for bc.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for blog.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for book.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for cc.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for exp.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for spcp.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for www.xiaogd.net to /etc/nginx/nginx.conf
Successfully deployed certificate for zyxq.xiaogd.net to /etc/nginx/nginx.conf
Congratulations! You have successfully enabled HTTPS on https://xiaogd.net, https://bc.xiaogd.net, https://blog.xiaogd.net, https://book.xiaogd.net, https://cc.xiaogd.net, https://exp.xiaogd.net, https://spcp.xiaogd.net, https://www.xiaogd.net, and https://zyxq.xiaogd.net

NEXT STEPS:
- The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

当然, 如果你眼尖的话, 会看到里面的输出还有这么一句: "This certificate expires on 2024-11-04." 大意就是这些证书会在 2024-11-04 这天过期.

我当时是 2024年8月初申请的, 所以大概就是三个月后就过期.

这就要说到 Let's Encrypt 这个组织所颁发的证书的不方便之处了, 就是一次申请的证书只有三个月的有效期, 而不是更长或者永久的.

其实所有云厂商提供的证书都不可能是永久的, 即便是付费的, 但付费的通常周期会长些, 一般至少有一年, 当然如果你比较豪, 一下子就掏钱买个三五年也不是不行.

那现在你可能要问, 三个月之后咋办? 简单的回答就是, 三个月之后又去申请一次延期, 如此反复, 你就相当于得到了长期的免费证书了.

当然, 你会说, 这也太麻烦了, 没错, 所以我们的流程还有最后一步, 那就是配置 certbot 让它自动为我们的证书续期. 是的, 它的证书是支持脚本化的程序去自动续期的.

如此一来, 人为反复更新的麻烦就不存在了.

配置 certbot 使用 crontab 调度器周期性自动续期更新证书

这里就要用到一个叫 crontab 之类的定时调度工具了.

如果你搞过开发, 开发过定时调度之类的功能, 那你可能听过这玩意.

执行以下命令:

echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null

配置 certbot 的定时更新

稍微解释一下这条指令, 当然你不想了解具体细节也没有关系, 只要明白它创建了一条定时调度的任务(cron job), 在每天中午的 12 点 ~ 1 点以及凌晨的 0 点 ~ 1 点之间的一个随机时间点, 触发执行 sudo certbot renew -q 这条指令.

详细的解释如下:

echo: echo命令用于输出文本内容到标准输出(通常是屏幕). 在这里, 它输出的是一条 cron 定时任务的规则.

cron 表达式 0 0,12 * * *: 这是一个 cron 时间格式表达式, 用于定义任务周期性执行的时间规则.

0 分钟

0,12 每天的 0 点和 12 点

* 代表每个月的每一天

* 代表每年的每一月

* 代表一周中的每一天

综上, 它表示任务将在每天的 00:00:00 和 12:00:00 两个时间点, 也即中午和凌晨触发执行.

root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)':

用位于 /opt/certbot/bin/python 的解释器执行一段 Python 代码, 这段代码首先导入了 random 和 time 模块, 然后让程序睡眠一个 0 到 3600 秒之间的随机时长, 以防止多个定时任务同时运行导致的资源竞争. 如果很多机子上都固定配置了相同的 cron 任务, 那当它们同时运行时, 对于证书服务器来说可能不亚于一次 DDoS 攻击, 所以这里利用 python 代码错开大家的访问.

续接(&&)命令 && sudo certbot renew -q: 当前面的 Python 脚本执行完毕后, 如果成功(没有错误退出), 则继续执行 sudo certbot renew -q 命令. 这个命令是以静默模式 -q 运行 certbot 工具来自动续订 SSL 证书. 这是脚本中最重要的一个命令, 也是真正用来续命的. 手动的话也就是执行它.

管道符号 | : 将前面 echo 命令的输出作为输入传递给下一个命令.

sudo tee -a /etc/crontab:

tee 命令用于读取标准输入的数据, 并将其写入到指定的文件.

-a 参数告诉 tee 命令追加(append)而不是覆盖文件内容.

/etc/crontab 是 cron 任务的系统配置文件, 这意味着上面构造的 cron 任务规则会被追加到这个文件中.

> /dev/null: 这个重定向操作符 > 将 echo 命令原本直接输出到屏幕的信息丢弃, 直接送入黑洞设备 /dev/null, 因此不会在屏幕上显示任何输出信息, 保持命令执行的干净.

而这个 sudo certbot renew -q 指令的作用就是去检查证书是否快过期了, 如果是, certbot 就会自己去更新证书; 如果离过期日期还早得很, 那 certbot 不会去更新.

配置了这个任务后呢, 有它一天查两趟, 我们就不需要去操心什么过期问题了. 有了自动更新, 三个月的短期证书与 "长期饭票" 也没啥区别了.

你可以用 cat /etc/crontab 观察下配置好的任务, 像我的服务器里的输出如下: crontab 示例

注: 你可能还配有更多的定时任务, 那么你的输出里可能与我的会有所不同. 新配置的任务位于最底端.

另外这里它还有个简单的定时周期性任务时间配置的英文说明, 如果还想定义其它任务, 可以参考:

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

当然, 如果你不想配自动任务, 当然也可以手动更新, 那就你自己算着日期, 差不多快过期了的时候, 不要忘记去登录你的服务器, 并执行这个 sudo certbot renew -q 去更新你的证书.

后记, 每月更新 certbot

到这一步, 基本的工作就算都完成了, 指南里最后还给了个每月尝试去更新升级一下 certbot 这个工具软件的建议:

指令如下:

sudo /opt/certbot/bin/pip install --upgrade certbot certbot-nginx

这样可以保持你的 certbot 应用始终是最新的, 避免遭遇后端系统更新后可能的失效问题. 当然, 我觉得这个就不是非常有必要了.

就像很多人的操作系统或是手机上的 APP 常年不更新也不见得会有什么问题那样, 有时甚至更新了反而出问题, 所以是否要更新就仁者见仁了, 这一套东西总的来说还是相对比较稳定的, 所以我的建议是, 如果 certbot 的自动证书续期没有出现什么问题, 那大可不必去更新.

最后, 如果你更新了, 却不幸失败了, 你可以先执行下述命令删掉整个 certbot:

sudo rm -rf /opt/certbot

然后重新执行前面的安装 certbot 的指令重新安装它即可.

关于利用 certbot 去获取多个免费证书并自动续期的介绍就到这里.