跳转到内容
use-cases / cert-renewal-no-ssh / hero
CRON · EXEC · 无 SSH

不开 SSH 会话也能轮换 TLS 证书

Hoody Cron 负责调度。Hoody Exec 负责续期逻辑。周日 04:00 一条 curl 触发、certbot 跑起来、新证书包用 PATCH 推到代理上、reload 被确认。无 shell 会话、~/.ssh 里无密钥、你和证书之间无跳板机。

阅读 cron 文档
use-cases / cert-renewal-no-ssh / lifecycle

三个 URL,无 shell

整条续期流水线就是两个端点和一段 5 字段调度。Cron 触发 Exec。Exec 调用 ACME 并 PATCH 代理。确认以 200 返回。

续期生命周期POST 一次 · 每周运行
01 · SCHEDULE

POST 一个 cron 条目

POST /users/me/entries,schedule 是 "0 4 * * 0",command 是 "curl /scripts/certs/renew"。cron 服务把它写进用户 crontab,开始计时。这件事你只在任何能跑 HTTPS 的地方做了一次。

02 · RUN

Exec 跑脚本

周日 04:00,cron curl 那个 exec URL。Exec 拉起脚本,跟 Let's Encrypt 的 ACME 端点对话,验证 http-01 challenge,把续期后的证书包写进 /files。没有节点登录、不需要会话。

03 · RELOAD

PATCH 代理证书包

脚本把新证书和密钥 PATCH 到 Hoody 代理的证书包端点。代理热加载——不重启——下一次 TLS 握手就用新证书。整个循环不到一分钟。

每一步都是你能从终端重放的 HTTP 调用——想 debug 就 `curl --dry-run`,想看 header 就 `curl -i`。流水线在任何一刻都不依赖一次已登录会话。

use-cases / cert-renewal-no-ssh / mechanism

两个端点长什么样

左边是调度任务的 cron 条目。右边是干活的 exec 脚本。两者都通过 HTTP 可达——都可审计、都可以从任意笔记本或手机重放。

cron · 注册调度
POST · 一次
# register the weekly renewal
curl -X POST \
  https://cron.containers.hoody.com/users/me/entries \
  -H "Content-Type: application/json" \
  -d '[ "schedule":"0 4 * * 0", "command":"curl -fsS https://exec.containers.hoody.com/scripts/certs/renew", "comment":"weekly TLS renewal", "enabled":true ]'

# response
HTTP/1.1 201 Created
{ "id":"7a92", "schedule":"0 4 * * 0", "enabled":true }
exec · scripts/certs/renew.ts
GET · 每周运行
// scripts/certs/renew.ts
// @mode serverless
// @timeout 120000

const domains = ["your-app.com", "api.your-app.com", ...];

for (const d of domains) {
  const cert = await acme.order(d);
  await fetch("/api/v1/proxy/cert/bundle", {
    method: "PATCH",
    body: JSON.stringify({ d, cert })
  });
}

return { "renewed": domains.length };

两个 URL、一个周日早晨、零 SSH。cron 条目是数据——POST 一次,它就在那里直到你 DELETE。exec 脚本是文件——通过 API 改它,下一次运行就拾取新逻辑。这个回路里没有任何环节需要人在机器上。

use-cases / cert-renewal-no-ssh / powers

为什么这胜过 SSH crontab

结果一样——每周一张续期的证书——但有三个老方案从来没有的属性。

AUTH · URL TOKEN

~/.ssh 里没有私钥

认证是 URL token,不是 SSH 密钥。通过 API 轮换它,旧 token 处处同时失效。无 agent forwarding、无 bastion、无你三年前忘记拷到 CI 机的密钥文件。

AUDIT · HTTP TRACE

每次续期都是请求日志

每次运行是 cron 日志里的一行,也是 exec 日志里的一条请求。grep 谁触发的、何时、哪张证书、什么响应码。代理的 200 就是 reload 已落地的回执。

DEBUG · DRY-RUN A CURL

你不通过 SSH 调试

在笔记本上用 `curl /scripts/certs/renew?dry_run=true` 重放续期。看响应、用 exec 写 API 改脚本、再试。整个回路走 HTTPS——和生产调度走的是同一根管子。

use-cases / cert-renewal-no-ssh / capacity

你跳过的东西

数字来自这次部署本身,不是基准。重要的是形状:活动部件极少,而且全都讲 HTTP。

  1. SSH 会话0

    建立靠一次 POST。续期是按计划的一次 GET。调试是一次 curl。流水线里没有任何 shell 会话——你的私钥从不必出现在那台机器上。

  2. 用到的端点2

    一个 cron 条目调度运行。一个 exec 路由跑脚本。第三个 URL——代理证书包的 PATCH——藏在脚本里,负责重载证书。

  3. 字段 · CRON 调度5

    标准 5 字段表达式 "0 4 * * 0"——周日 04:00。或 `@weekly`,如果你不在乎是哪一天。cron 服务两者都接受,外加用于一次性续期的自动过期。

Cron 服务:5 字段表达式和宏(`@hourly`、`@daily`、`@weekly`、`@monthly`、`@yearly`)、按用户隔离、可选 `expires_at`。Exec:V8 isolate、Bun runtime、用于 mode/timeout/CORS 的魔法注释。ACME 流程在你的脚本里——Hoody 不替你跑 certbot,它按调度跑你的代码。

use-cases / cert-renewal-no-ssh / punchline

续期就是按计划的一次 curl——无 shell 会话、无密钥、无跳板机。

old · 证书过期前的周六new · cron 已经跑了九个月
旧栈长这样ssh prod && certbot renew && systemctl reload nginx~/.ssh 里有密钥 · 前面立着 bastion · 祈祷 cron 行熬过了上一次重启
现在它长这样
阅读 exec 文档
use-cases / cert-renewal-no-ssh / replaces

它替代了什么

在生产里维持 TLS 有效的标准方式。每一种要么需要 shell 会话、要么需要控制平面服务,或两者都要。cron + exec 这对组合两样都不需要。

  • SSH + 手动 certbot一个 shell 会话、一把密钥、一个你休假时会忘的续期步骤
  • ssh-bastion + 脚本你只为跑一行脚本而维护一个跳板机
  • AWS Certificate Manager(闭环)免费,但只在 AWS——证书也从不离开他们的代理
  • HashiCorp Vault PKI用一整个 secrets 守护进程来发你每周续一次的证书
  • Kubernetes 上的 cert-manager一个控制器、一个 CRD、一个 webhook——为了原本只是两次 HTTP 调用的事
  • 自定义续期 cron + 跳板机流水线bastion 上的胶水脚本、每个 CI runner 一把密钥、没有审计轨迹
use-cases / cert-renewal-no-ssh / cta

别再为续期登录服务器。POST 一次调度。每个周日早晨看代理自己 reload。

阅读 kit 文档
use-cases / cert-renewal-no-ssh / related

阅读其他内容