使用 nginx 的 lua 验证 GitHub webhooks 并删除 cron-lock-file

ben*_*org 2 lua json github nginx webhooks

我拥有的:

  • GNU/Linux 主机
  • nginx 已启动并运行
  • 有一个 cron-job 计划在删除特定文件后立即运行(类似于 run-crons)
  • 当有人推送到存储库时,GitHub 会发送一个 Webhook

我想要的是:

我现在确实想运行 lua 或任何类似的东西来解析 GitHub 的请求并验证它,然后删除一个文件(当然,如果请求有效)。

最好所有这一切都应该在没有维护额外 PHP 安装的麻烦的情况下发生,因为目前没有,或者需要使用 fcgiwrap 或类似的。

模板:

在 nginx 方面,我有相当于

location /deploy {
    # execute lua (or equivalent) here
}
Run Code Online (Sandbox Code Playgroud)

Mak*_*hod 5

要读取 GH webhook 的 json 正文,您需要使用JSON4Lua lib,并使用luacrypto验证 HMAC 签名。

预配置

安装所需模块

$ sudo luarocks install JSON4Lua
$ sudo luarocks install luacrypto
Run Code Online (Sandbox Code Playgroud)

在 Nginx 中定义部署位置

  location /deploy {
    client_body_buffer_size 3M;
    client_max_body_size  3M;

    content_by_lua_file /path/to/handler.lua;
  }
Run Code Online (Sandbox Code Playgroud)

和应该等于以防止max_body_size错误body_buffer_size

不支持临时文件中的请求正文

https://github.com/openresty/lua-nginx-module/issues/521


处理网络钩子

获取请求负载数据并检查是否正确

ngx.req.read_body()
local data = ngx.req.get_body_data()

if not data then
    ngx.log(ngx.ERR, "failed to get request body")
    return ngx.exit (ngx.HTTP_BAD_REQUEST)
end
Run Code Online (Sandbox Code Playgroud)

使用 luacrypto 验证 GH 签名

local function verify_signature (hub_sign, data)
    local sign = 'sha1=' .. crypto.hmac.digest('sha1', data, secret)
    -- this is simple comparison, but it's better to use a constant time comparison
    return hub_sign == sign
end

-- validate GH signature
if not verify_signature(headers['X-Hub-Signature'], data) then
    ngx.log(ngx.ERR, "wrong webhook signature")
    return ngx.exit (ngx.HTTP_FORBIDDEN)
end
Run Code Online (Sandbox Code Playgroud)

将数据解析为 json 并检查是否为 master 分支,用于部署

data = json.decode(data)
-- on master branch
if data['ref'] ~= branch then
    ngx.say("Skip branch ", data['ref'])
    return ngx.exit (ngx.HTTP_OK)
end
Run Code Online (Sandbox Code Playgroud)

如果一切正确,调用部署函数

local function deploy ()
    -- run command for deploy
    local handle = io.popen("cd /path/to/repo && sudo -u username git pull")
    local result = handle:read("*a")
    handle:close()

    ngx.say (result)
    return ngx.exit (ngx.HTTP_OK)
end
Run Code Online (Sandbox Code Playgroud)

例子

恒定时间字符串比较示例

local function const_eq (a, b)
    -- Check is string equals, constant time exec
    getmetatable('').__index = function (str, i)
        return string.sub(str, i, i)
    end

    local diff = string.len(a) == string.len(b)
    for i = 1, math.min(string.len(a), string.len(b)) do
        diff = (a[i] == b[i]) and diff
    end
    return diff
end
Run Code Online (Sandbox Code Playgroud)

我如何在 github gist 中使用它的完整示例 https://gist.github.com/Samael500/5dbdf6d55838f841a08eb7847ad1c926