Skip to content

Instantly share code, notes, and snippets.

@samael500
Last active August 25, 2023 15:11
Show Gist options
  • Save samael500/5dbdf6d55838f841a08eb7847ad1c926 to your computer and use it in GitHub Desktop.
Save samael500/5dbdf6d55838f841a08eb7847ad1c926 to your computer and use it in GitHub Desktop.
Validating payloads from GitHub webhooks with Nginx + Lua
-- 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
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