登录

📑 我做了一套云服务器间的 TLS 证书同步方案

9921
  • 楼主博主 编辑于

    用了一下午时间做,之前的证书同步方案还行,但是后来 cloudflare dcv 记录占据了 _acme-challenge 位置,导致签发失败,Google Trust Service 支持 _validation-persist 虽然 lego 客户端已经支持了这个验证方式,但是 nixos 自带的 acme 模块并不支持直接配置这个选项并且考虑到以后可能加更多证书 / 域名,还是图形化管理更好用,所以把主签发机 us.or.dls.2 上的签发器从 acme 模块换成了 certwarden 这个 docker 里的 webui 证书管理器

    换完后发现直接把证书同步模块叫作 acme 不合适,于是又对证书同步模块大改了下,换了单元名字和证书存放路径,并且把它从机器配置里抽出来,成为了独立模块,还加了证书更新时 reload 相关单元的功能

    然后又发现 cert-sync 每天拉一次遇到紧急情况需要立刻同步证书时需要逐个 ssh 上去重启单元,非常麻烦所以又给 us.or.dls.2 部署了 ntfy 服务器,在上传后 certwarden 会向 ntfy 服务器发送消息,其他机器则用 cert-notify 单元和 ntfy 服务器保持 wss 长连接,在收到更新消息 cert-notify 会自动拉起 cert-sync 从存储桶拉取证书

    后续会详细写篇博客说是如何实现的(先让我咕咕咕一会)

    具体流程如下

    ---
    config:
      theme: redux-color
      themeVariables:
        edgeLabelBackground: "#ffffff"
    ---
    
    flowchart BT
        %% 公网下游 · 6 台 VPS
        subgraph EU["eu.de.0"]
            direction TB
            EU_N["cert-notify"]
            EU_S["cert-sync<br/>cert-sync.timer"]
        end
    
        subgraph HK2["hk.2"]
            direction TB
            HK2_N["cert-notify"]
            HK2_S["cert-sync<br/>cert-sync.timer"]
        end
    
        subgraph HK3["hk.3.cnix"]
            direction TB
            HK3_N["cert-notify"]
            HK3_S["cert-sync<br/>cert-sync.timer"]
        end
    
        subgraph SG["sg.0"]
            direction TB
            SG_N["cert-notify"]
            SG_S["cert-sync<br/>cert-sync.timer"]
        end
    
        subgraph PHX["us.az.phx.0"]
            direction TB
            PHX_N["cert-notify"]
            PHX_S["cert-sync<br/>cert-sync.timer"]
        end
    
        subgraph LAX["us.ca.lax.0"]
            direction TB
            LAX_N["cert-notify"]
            LAX_S["cert-sync<br/>cert-sync.timer"]
        end
    
        %% 证书与通知汇聚节点
        NB(("更新消息推送"))
        DB(("证书下载"))
    
        subgraph CF["Cloudflare Global Network"]
            direction LR
            EDGE["https://ntfy.nahida.im"]
            R2[("Cloudflare R2<br/>r2:tls")]
        end
    
        subgraph GCP["GCP 集群 · 同一 VPC"]
            direction LR
            subgraph G0["us.or.dls.0"]
                direction TB
                T0["cloudflared"]
                N0["cert-notify"]
                S0["cert-sync<br/>cert-sync.timer"]
            end
            subgraph G1["us.or.dls.1"]
                direction TB
                T1["cloudflared"]
                N1["cert-notify"]
                S1["cert-sync<br/>cert-sync.timer"]
            end
            subgraph G2["us.or.dls.2 · 签发主节点"]
                direction TB
                CW["Cert Warden<br/><br/>Google Trust Service + DNS-PERSIST-01"]
                NTFY["ntfy-sh Server<br/>10.138.0.5:8000"]
                T2["cloudflared"]
            end
        end
    
        %% 紫色:systemd trigger (0-7)
        EU_N -->|"trigger"| EU_S
        HK2_N -->|"trigger"| HK2_S
        HK3_N -->|"trigger"| HK3_S
        SG_N -->|"trigger"| SG_S
        PHX_N -->|"trigger"| PHX_S
        LAX_N -->|"trigger"| LAX_S
        N0 -->|"trigger"| S0
        N1 -->|"trigger"| S1
    
        %% 绿色:ntfy publish (8)
        CW -->|"R2 上传完成后 publish: sync"| NTFY
    
        %% 绿色:ntfy Tunnel / Origin (9-14)
        NTFY ---|"VPC Origin"| T0
        NTFY ---|"VPC Origin"| T1
        NTFY ---|"本机 Origin"| T2
        T0 ---|"Tunnel · 路径 0"| EDGE
        T1 ---|"Tunnel · 路径 1"| EDGE
        T2 ---|"Tunnel · 路径 2"| EDGE
    
        %% 绿色:ntfy JSON Streaming (15-23)
        NTFY -.->|"HTTP · VPC"| N0
        NTFY -.->|"HTTP · VPC"| N1
        EDGE -.- NB
        NB -.-> EU_N
        NB -.-> HK2_N
        NB -.-> HK3_N
        NB -.-> SG_N
        NB -.-> PHX_N
        NB -.-> LAX_N
    
        %% 蓝色:R2 证书上传、下载 (24-33)
        CW ==>|"上传 PEM"| R2
        R2 === DB
        DB ==> EU_S
        DB ==> HK2_S
        DB ==> HK3_S
        DB ==> SG_S
        DB ==> PHX_S
        DB ==> LAX_S
        DB ==> S0
        DB ==> S1
    
        %% 连线颜色与线宽
        linkStyle 0,1,2,3,4,5,6,7 stroke:#9333ea,color:#9333ea,stroke-width:1.2px;
        linkStyle 8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 stroke:#059669,color:#059669,stroke-width:1.3px;
        linkStyle 24,25,26,27,28,29,30,31,32,33 stroke:#2563eb,color:#2563eb,stroke-width:1.6px;
    
        %% 应用节点配色
        classDef issuer fill:#dbeafe,stroke:#2563eb,color:#1e40af;
        classDef cloudflare fill:#ffedd5,stroke:#ea580c,color:#9a3412;
        classDef notify fill:#dcfce7,stroke:#16a34a,color:#166534;
    
        class CW issuer
        class EDGE,R2,T0,T1,T2 cloudflare
        class NTFY,EU_N,HK2_N,HK3_N,SG_N,PHX_N,LAX_N,N0,N1 notify
    
        %% 汇聚节点:白色背景、彩色边框与文字
        classDef notifyBus fill:#ffffff,stroke:#16a34a,color:#16a34a;
        classDef dataBus fill:#ffffff,stroke:#2563eb,color:#2563eb;
    
        class NB notifyBus
        class DB dataBus
    
        %% Sky Blue:所有机器的独立容器
        classDef machine fill:#f0f9ff,stroke:#38bdf8,color:#0369a1,stroke-width:1px;
        class EU,HK2,HK3,SG,PHX,LAX,G0,G1,G2 machine
    
        %% Cloudflare:橙色
        style CF fill:#fff7ed,stroke:#f97316,color:#9a3412,stroke-width:1.5px
    
        %% GCP 集群:黄色
        style GCP fill:#fefce8,stroke:#eab308,color:#854d0e,stroke-width:1.5px
    
    收藏 1支持反对打赏
    个性签名

    我的网站:nahida.im | 相关服务监控:status.nahida.im

21 条回复

  • 站长
    #1

    支持技术贴

    个性签名

    生于安乐,死于忧患。

  • 楼主博主 编辑于
    #2

    @Showfom 能考虑支持下 Mermaid 代码块渲染图表吗?(虽然确实没多少人用)

    个性签名

    我的网站:nahida.im | 相关服务监控:status.nahida.im

    :

    你发我 GitHub 链接

    #8
    共 1 条回复
  • #3

    这个已经有成熟方案了

    个性签名

    全球烧饼交流论坛

    楼主:

    啊?流泪(发出倒地的声音)

    什么成熟方案我去看看

    #4
    #6
    楼主 回复 Xsb:

    噢,这个我知道,它的逻辑是 ssh 上去复制,但是我机器有可能离线,所以没用这个(心情好点了)

    并且这个还不支持 _validation-persist,无法签我的证书

    #7
    楼主 回复 Xsb:

    不过 certimate 确实好看也好用,我也准备在 certimate 更新到 v0.5.X 并且支持 _validation-persist 之后把 certwarden 换掉

    #20
    共 4 条回复
  • #5

    https://github.com/certimate-go/certimate

    个性签名

    全球烧饼交流论坛

  • 管理博主
    #9

    Mermaid 代码块渲染图表测试中,等我把 GitHub 支持的都支持上再发布

    image.webp

    个性签名

    我思故我在,烧饼博客与你同行。

  • #10

    支持 收藏

  • #11

    支持一下

  • 管理博主
    亮了 1#12

    Mermaid 代码块渲染已按照 GItHub 的说明支持

    个性签名

    我思故我在,烧饼博客与你同行。

  • #13

    好奇为什么不直接用 caddy @grok

    个性签名

    楼上楼下都是大水比,没说我不是

    :

    不是单机自动上 HTTPS 的问题,Caddy 替换不了这套流程。

    楼主卡在签发方式:Cloudflare 的 DCV 占了 _acme-challenge,只能走 Google Trust Services 的 _validation-persist。lego 支持,NixOS 自带的 acme 模块不能直接配,所以签发端改成了 Cert Warden,并且要在一个 Web UI 里管以后更多证书。

    证书还要分到 eu、hk、sg、phx、lax 和 GCP 上那几台。这些机器会离线,服务也不都是 Caddy,更新后要 reload 各自的单元。所以是签发机上传 PEM 到 R2,其余机器用定时器兜底,紧急时靠 ntfy 叫醒 cert-sync 来拉。第 3 楼那种 SSH 上去复制,楼主也是因为离线才没用。

    每台自己跑 Caddy 去签,DNS 凭据得铺开,挑战记录还是会和 DCV 抢,离线的签不了,也覆盖不了非 Caddy 的服务。

    #14
    回复 grok:

    叽里咕噜说什么呢,懒狗就用 caddy

    #15
    回复 gy:

    不是懒得配。签发卡在 Cloudflare 的 DCV 占了 _acme-challenge,只能走 Google Trust Services 的 _validation-persist;NixOS 自带的 acme 配不了这个,所以签发端换成了 Cert Warden。

    下游那几台会离线,服务也不都是 Caddy,更新后还要 reload 各自的单元。每台自己跑 Caddy 去签,DNS 凭据得铺开,挑战记录照样和 DCV 抢,离线的也签不了。

    #16
    楼主:

    ai 差不多把我的想法解释完了,我不用 caddy 的原因主要是这几个:

    1. 我使用的是 Google Trust Service,需要注册 EAB 账号,如果用 caddy 的话需要每台机器一个账号,麻烦
    2. 我的 _acme-challenge 被 cloudflare dcv 占用了,必须用 DNS-PERSIST-01 或者 DNS-ACCOUNT-01 签
    3. 我的大多数服务都是 haproxy、nginx,不想迁 caddy
    4. 部分使用 tls 证书的服务并不是在 caddy 后面的,比如 singbox
    5. 我不希望续期时有多少 vps 就续多少张证书
    #17
    回复 纳西妲:

    还真是,之前查了一下 gts 的文档,扫了一下发现比较麻烦,懒到底用 caddy + dns01 自动搞 let's encrypted 证书蒜鸟

    #19
    共 5 条回复
  • #18

    bdbd

    个性签名

    无敌大水比

  • #21

    牛掰牛掰

发表回复