webhook 完整指南:5 分钟在服务器上搭好自动部署 Webhook
【免费下载链接】webhookwebhook is a lightweight incoming webhook server to run shell commands项目地址: https://gitcode.com/gh_mirrors/we/webhook
如果你还在每次 push 代码后 SSH 进服务器手动跑部署脚本,这个工具应该能让你省下不少重复劳动。webhook 是一个用 Go 编写的轻量级入站 Webhook 服务器:它在你的机器上暴露一个 HTTP 端点,请求到达且校验通过时,自动执行你预先配置的 shell 命令,还能把请求头、请求体、URL 参数里的值精确地交给脚本使用——没有中间队列,没有重型平台,一个二进制文件加一份 JSON 就能跑起来。
三步跑通第一个 hook(快速上手)
整个过程只需要三个文件、两条命令,建议跟着做一遍再往下看。
第 1 步:拿到可执行文件。如果你有 Go 1.21+ 环境,从源码构建最干净:
git clone https://gitcode.com/gh_mirrors/we/webhook cd webhook && go build .Debian/Ubuntu 也可以直接sudo apt-get install webhook,FreeBSD 用pkg install webhook。
第 2 步:写一份最小配置。新建hooks.json,顶层是一个 hook 数组,id和execute-command是仅有的两个必填项(完整字段清单见 docs/Hook-Definition.md):
[ { "id": "redeploy", "execute-command": "/var/scripts/redeploy.sh", "command-working-directory": "/var/www" } ]注意你的脚本首行要带#!/bin/sh。喜欢 YAML 的话同样支持,hooks.yaml即可。
第 3 步:启动并验证:
./webhook -hooks hooks.json -verbose默认监听 9000 端口,每个 hook 都会得到一个http://你的服务器:9000/hooks/<id>端点。发一个请求试试:
curl -X POST http://localhost:9000/hooks/redeploy看到日志里脚本被执行,恭喜,链路已经通了。端口、监听 IP、URL 前缀等都能通过启动参数覆盖,详见 docs/Webhook-Parameters.md。
⚠️ 此时这个 hook 是"裸奔"的:任何知道 URL 的人都能触发你的命令。下一步必须给它加门卫。
按场景选校验方式:GitHub、GitLab、Bitbucket
trigger-rule(触发规则)就是 hook 的门卫:请求必须满足规则,命令才会执行。规则支持and/or/not任意嵌套组合,原子匹配类型覆盖精确值、正则、HMAC 签名、IP 白名单,完整语法见 docs/Hook-Rules.md。不同平台的"身份证"放在不同位置,选型如下:
| 平台 | 凭证位置 | match 类型 | 说明 |
|---|---|---|---|
| GitHub / Gogs / Gitea | X-Hub-Signature/X-Gogs-Signature等请求头 | payload-hmac-sha1/256/512 | 用共享密钥对请求体做 HMAC 签名校验 |
| GitLab | X-Gitlab-Token请求头 | value | 与后台生成的 Token 精确比对 |
| Bitbucket | 不提供签名 | ip-whitelist | 只能按 CIDR 段放行官方 IP 范围 |
以 Gitea/Gogs 为例,"签名正确且推送到了 main 分支"才能部署:
"trigger-rule": { "and": [ { "match": { "type": "payload-hmac-sha256", "secret": "mysecret", "parameter": { "source": "header", "name": "X-Gogs-Signature" } } }, { "match": { "type": "value", "value": "refs/heads/main", "parameter": { "source": "payload", "name": "ref" } } } ] }把第二项换成or就能同时放行 master 和 development;分支名想模糊匹配(比如所有feature/*),用regex类型。多个分支、多个来源的复杂组合写法,docs/Hook-Rules.md 的 Multi-level 一节有现成模板。
Slack 这类聊天平台的场景则更简单:斜杠命令或 Outgoing Webhook 的 body 里自带token字段,用value匹配它,再配上response-message就能即时把"正在处理"的反馈回显到会话里。
把请求数据喂给脚本:参数、环境变量与文件
webhook 能引用的请求值共四类:header(请求头)、url(查询参数)、request(method、remote-addr)、payload(请求体),语法见 docs/Referencing-Request-Values.md。交给脚本有三种通道:
命令行参数(
pass-arguments-to-command):适合短小的标识值。JSON 嵌套值用点号路径访问,如head_commit.id:"pass-arguments-to-command": [ { "source": "payload", "name": "pusher.name" }, { "source": "payload", "name": "pusher.email" } ]环境变量(
pass-environment-to-command):值多或含空格时更稳妥。不写envname时变量名自动为HOOK_参数名,写了则自定义。临时文件(
pass-file-to-command):二进制或大文件的正解。数据会先 base64 编码传输,webhook 落盘成请求级临时文件,文件路径通过环境变量告知脚本:"pass-file-to-command": [ { "source": "payload", "name": "binary", "envname": "FILE_DATA", "base64decode": true } ]脚本里读
$FILE_DATA即可;不指定command-working-directory时文件放在系统临时目录,命令结束自动清理。发送端一条命令就能构造请求:jo binary=%app.zip | curl -H 'Content-Type: application/json' -X POST -d @- \ http://localhost:9000/hooks/file-handler
进阶玩法:四种"不标准"请求的处理
① JSON 套 JSON:用parse-parameters-as-json二次解析。Travis CI 把 JSON 塞进payload=<JSON字符串>这个表单字段里发过来;Plex 的 multipart 表单也是把 JSON 放在名为payload的 part 中。只需声明哪个参数是 JSON,之后就能像普通对象一样用payload.state引用:
"parse-parameters-as-json": [ { "source": "payload", "name": "payload" } ]② XML 请求体:属性名加-前缀,元素文本用#text,同类元素按数组索引。例如<user id="1" name="Jeff"/>的 name 属性写app.users.user.0.-name,<message>的内文写app.messages.message.#text。
③ JSON 数组 body:数组 payload 会被包进root对象,引用一律从root.开始,如root.0.event、root.1.email。
④ 配置模板化:-template参数。启动时加-template,hooks.json会按 Go 模板解析,内置getenv、cat(读本地文件)、credential(systemd credentials)三个函数,密钥可以彻底移出配置文件:
"secret": "{{ getenv "GITHUB_SECRET" | js }}"细节见 docs/Templates.md。此外,-secure -cert -key可让服务直接提供 HTTPS;systemd 平台还支持 socket 激活(普通用户也能占用 80/443 端口),见 docs/Systemd-Activation.md。
排错指南:现象 → 原因 → 解法
| 现象 | 可能原因 | 解法 |
|---|---|---|
| curl 返回 404 | 端点拼错、id与 URL 不一致,或-urlprefix改过默认值 | 核对启动参数;默认端点形如/hooks/<id> |
| 请求有响应但脚本没执行 | trigger-rule未通过(签名/分支/token 不符) | 加-verbose看拒绝原因;用or放宽分支条件做对照测试 |
| 脚本拿不到参数值 | 点号路径写错;数组 payload 没加root.前缀;match 规则里的数字/布尔值未加引号 | 先用下文"探针 hook"打印原始 payload 核对结构 |
反代之后ip-whitelist永远不匹配 | webhook 看到的是代理 IP 而非客户端 IP | IP 限制改在反代层做,webhook 不再重复校验 |
| 想确认命令输出 | 默认响应不会回传 stdout | 设置"include-command-output-in-response": true,成功回 200 附带输出,失败回 500 |
| 改了 hooks.json 不生效 | 进程未重载 | 启动加-hotreload自动热加载,或kill -HUP/-USR1 <pid>手动重载 |
调试时还有一个省时技巧:仓库自带的 test/hookecho.go 是个小工具,编译后它会原样打印收到的所有参数和HOOK_前缀环境变量。临时把它配成一个 hook,就能立刻看清"webhook 到底给了我什么"。
上线前的安全检查清单
逐项过一遍再交付生产:
- 每个 hook 都有
trigger-rule,且至少包含一层签名(HMAC)、token 或 IP 校验 - 密钥没有明文写在 hooks 文件里,改用
-template+getenv/cat/credential注入 - 用
http-methods收窄允许的 HTTP 方法(多数场景只需POST) - 进程以最小权限运行:
-setuid/-setgid降权,command-working-directory指向尽可能小的目录 - 生产流量走 HTTPS(
-secure或反代终结 TLS),必要时用-tls-min-version抬高底线 - 开启
-logfile与-pidfile,请求来路与进程状态可追溯
下一步行动建议
- 先用仓库里的
test/hookecho.go建一个"探针" hook,确认平台真实发来的 payload 结构; - 给探针 hook 加上对应平台的
trigger-rule,用 curl 模拟一次合法与非法请求,验证门卫生效; - 换成真实部署脚本,临时开启
include-command-output-in-response自查输出,稳定后关掉; - 开启
-hotreload,把整套配置交给 docs/Hook-Examples.md 里的更多平台样例去扩充你的自动化版图。
【免费下载链接】webhookwebhook is a lightweight incoming webhook server to run shell commands项目地址: https://gitcode.com/gh_mirrors/we/webhook
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考