Last active
August 25, 2023 15:11
-
-
Save samael500/5dbdf6d55838f841a08eb7847ad1c926 to your computer and use it in GitHub Desktop.
Validating payloads from GitHub webhooks with Nginx + Lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- luarocks install JSON4Lua | |
-- luarocks install luacrypto | |
local json = require "json" | |
local crypto = require "crypto" | |
local secret = '<MY SUPER SECRET>' | |
local event = 'push' | |
local branch = 'refs/heads/master' | |
ngx.header.content_type = "text/plain; charset=utf-8" | |
getmetatable('').__index = function (str, i) | |
return string.sub(str, i, i) | |
end | |
local function const_eq (a, b) | |
-- Check is string equals, constant time exec | |
local equal = string.len(a) == string.len(b) | |
for i = 1, math.max(string.len(a), string.len(b)) do | |
equal = (a[i] == b[i]) and equal | |
end | |
return equal | |
end | |
local function verify_signature (hub_sign, data) | |
local sign = 'sha1=' .. crypto.hmac.digest('sha1', data, secret) | |
return const_eq(hub_sign, sign) | |
end | |
local function validate_hook () | |
-- should be POST method | |
if ngx.req.get_method() ~= "POST" then | |
ngx.log(ngx.ERR, "wrong event request method: ", ngx.req.get_method()) | |
return ngx.exit (ngx.HTTP_NOT_ALLOWED) | |
end | |
local headers = ngx.req.get_headers() | |
-- with correct header | |
if headers['X-GitHub-Event'] ~= event then | |
ngx.log(ngx.ERR, "wrong event type: ", headers['X-GitHub-Event']) | |
return ngx.exit (ngx.HTTP_NOT_ACCEPTABLE) | |
end | |
-- should be json encoded request | |
if headers['Content-Type'] ~= 'application/json' then | |
ngx.log(ngx.ERR, "wrong content type header: ", headers['Content-Type']) | |
return ngx.exit (ngx.HTTP_NOT_ACCEPTABLE) | |
end | |
-- read request body | |
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 | |
-- 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 | |
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 | |
return true | |
end | |
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 | |
if validate_hook() then | |
deploy() | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
server { | |
# .... | |
location /deploy { | |
client_body_buffer_size 3M; | |
client_max_body_size 3M; | |
content_by_lua_file /path/to/handler.lua; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment