在服务器间同步 TLS 证书

在一堆服务器上面都部署同一个(泛)域名、自动续期的 TLS 证书的最简单方法,自然是在每台服务器上都装一个 ACME 客户端。不过,这样也会收获一堆证书透明度记录。免费的证书短命,每隔几十天手工更新每一台服务器也太累人。

为什么不用反向代理?那当然是需要部署……(笑)

注意事项

本文中可能包含一些不好的 Linux 实践。欢迎您在评论区理性讨论并帮助作者改进,非常感谢!

准备

  • 1 台「管理服务器」(Management Server)
    • 安装 ACME 客户端、rsync
  • 若干台「目标服务器」(Target Server)
    • 安装 rsync 和需要使用证书的服务

管理服务器:签发证书

使用你喜欢的 ACME 客户端自行解决。

证书存储路径上,我选择了和某软件接近的 /usr/local/etc/acmesh

由于一些蛋疼的权限问题,我选择授予 crt 和 key 文件 644 权限。否则待会儿启动服务的时候,会抱怨读取不了 600 权限的 key。

管理服务器:生成 SSH Key

1
2
# 生成 ed25519 SSH Key
ssh-keygen -t ed25519

目标服务器:配置用户

我创建了一个新的用户 cert-updater 用于执行这项任务。根据实际情况和权限的精细管理,你可能需要进行创建用户组等额外操作。

以下命令可能需要以 root 权限执行,请注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 创建用户
adduser cert-updater

# 证书目录
mkdir -p /usr/local/etc/acmesh
chown cert-updater:cert-updater /usr/local/etc/acmesh
chmod 755 /usr/local/etc/acmesh

# 重载脚本目录
mkdir -p /usr/local/etc/acmesh_scripts

# 使用你喜欢的方式编写 reload.sh
chown root:cert-updater /usr/local/etc/acmesh_scripts/reload.sh
chmod +x /usr/local/etc/acmesh_scripts/reload.sh
# 里面的内容通常是 systemctl reload nginx.service 等等

# 编辑 sudo 配置,使 cert-updater 可以 sudo 执行服务重载脚本
visudo -f /etc/sudoers.d/95-cert-updater
# cert-updater ALL=(root) NOPASSWD: /usr/local/etc/acmesh_scripts/reload.sh

# 设置 authorized_keys
# 可以在管理服务器上直接运行 ssh-copy-id cert-updater@target-server-address,如果目标服务器没有禁止所有用户以密码登录
# 也可以手动填写 authorized_keys
# no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAA...

# 禁止此用户使用密码登录
nano /etc/ssh/sshd_config.d/95-cert-updater
# Match User cert-updater
# PasswordAuthentication no

配置好用户之后,将此用户的登录信息加入管理服务器的 .ssh/config

1
2
3
4
Host your-target-server-alpha
HostName your-target-server-alpha.com
User cert-updater
IdentityFile ~/.ssh/cert-updater_id_ed25519

我也考虑过是否可以直接使用 Tailscale SSH,不过想了想还是直连吧。(

管理服务器:同步

1
2
3
4
5
6
7
8
9
#!/bin/bash

# rsync
rsync -avz "/usr/local/etc/acmesh/" "cert-updater@your-target-server-alpha:/usr/local/etc/acmesh/"
# 如果有更多服务器就在这里重复

# reload
ssh cert-updater@your-target-server-alpha "sudo /usr/local/etc/acmesh_scripts/reload.sh"
# 如果有更多服务器就在这里重复

将这段脚本保存在你喜欢的地方。

鉴于我用的是 acme.sh,我将脚本设置为了 --reloadcmd,这样在证书更新时就会自动执行,更新远端的证书并重启 / 重载相关的服务。

测试

  • 检查 rsync 是否有权限问题、证书文件是否正常同步
    • 示例命令使用了 archive mode,如果这不符合你的需求,你可能需要调整
  • 检查重载脚本是否能正常执行
  • 检查重启后的服务是否能正常运行
    • 比如,是否会因为神秘的权限问题导致失败

(权限管控方面)不太好的实践,主要也是在这一块了。

作者

星野 みなと

发布于

2024-09-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×