Webhook服务端需用Flask/Express等支持POST的服务,配置Nginx透传签名头,部署目录属主与SSH密钥需匹配运行用户,用flock防并发冲突,通过HMAC或token校验来源确保安全。

Webhook 服务端收不到 POST 请求
Git 服务商(GitHub/GitLab)发来的 Webhook 默认是 POST 请求,但很多轻量服务(比如用 python -m http.server 或简单 netcat 测试)只处理 GET,直接丢弃 POST,导致“点了推送没反应”。
- 必须用能解析
POSTbody 的服务,推荐flask(Python)或express(Node.js),不建议用静态文件服务器凑合 - GitHub 发送时带
X-Hub-Signature-256头,GitLab 是X-Gitlab-Token,验证前先确认请求头是否到达——用curl -v模拟或在服务端加日志打印request.headers - Nginx 反代时默认会 strip 掉部分 header,需显式配置:
proxy_pass_request_headers on;和underscores_in_headers on;(否则X-Gitlab-Token可能被忽略)
收到请求后执行部署脚本权限失败
Webhook 服务通常以非登录用户(如 www-data、node)运行,但部署操作常需写入项目目录、切换分支、拉取代码——直接 git pull 会报 Permission denied (publickey) 或 fatal: not a git repository。
- 部署目录属主必须设为 Webhook 进程用户,例如:
sudo chown -R www-data:www-data /var/www/myapp - SSH key 必须由该用户生成并添加到 Git 托管平台,且不能有密码(
ssh-agent在无终端环境下不生效) - 不要在脚本里用
sudo提权——改用setgid目录或sudoers白名单授权特定命令,例如:www-data ALL=(ALL) NOPASSWD: /usr/bin/git pull
并发提交导致部署冲突或覆盖
连续两次 push 触发两个部署流程,第二个可能在第一个还没完成时就进入 git pull,出现 error: Your local changes would be overwritten 或检出混乱。
- 用文件锁最简单:
flock -n /tmp/deploy.lock -c "cd /var/www/myapp && git pull && systemctl reload nginx",-n表示不等待,直接跳过 - 避免在 Webhook 处理中做耗时操作(如
npm install、composer install),应拆成异步任务(systemd-run --scope或队列) - GitLab 支持
CI/CD环境变量CI_PIPELINE_ID,GitHub 有GITHUB_RUN_ID,可用于日志隔离,但不解决并发问题
如何安全校验 Webhook 来源
只靠 URL 暴露在公网等于开门揖盗,攻击者可伪造请求反复触发部署,删库跑路。
- GitHub 用
HMAC-SHA256签名:从X-Hub-Signature-256头取值,用仓库设置的secret对原始 payload 做哈希比对,flask可用hmac.compare_digest()防时序攻击 - GitLab 用
X-Gitlab-Token,是明文 token,必须和 Webhook 配置页填的一致,且不能硬编码在脚本里——通过环境变量或/etc/webhook/secrets文件读取 - 别依赖 IP 白名单:GitHub 的 IP 段常变,GitLab 共享 IP,且 CDN 后真实源 IP 可能被
X-Forwarded-For伪造
真正的难点不在写几行脚本,而在于让每次 git pull 都干净、每次权限都刚好够、每次签名都校验到位——少一个环节,上线就变下线。


















