Skip to content

Instantly share code, notes, and snippets.

@akshaal
Last active December 9, 2022 03:39
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akshaal/d7ee07e217de5d7632722a601dfb8827 to your computer and use it in GitHub Desktop.
Save akshaal/d7ee07e217de5d7632722a601dfb8827 to your computer and use it in GitHub Desktop.
-- Based upon https://github.com/TimWolla/haproxy-auth-request/
-- http://w3.impa.br/~diego/software/luasocket/
local http = require("socket.http")
-- Doucmentation (if still works):
-- https://www.arpalert.org/src/haproxy-lua-api/1.9/index.html
-- Split 'str'-ing by 'pat'-tern
function splitString(str, pat)
local t = {}
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find (fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
----------------------------------------------------------------------------------------------------
core.register_action(
"auth-request", -- name of the converter (action)
{"http-req"}, -- list of haproxy-actions we react upon (possible are 'tcp-req', 'tcp-res', 'http-req' or 'http-res')
function(txn, backend, path)
-- txn: TXN class: https://www.arpalert.org/src/haproxy-lua-api/1.9/index.html#txn-class
-- 'backend' means backend,
-- 'path' on backend
-- Set as not successful in case of failure somewhere later
txn:set_var("txn.ak_auth_response_successful", false)
-- Check whether the given backend exists.
if core.backends[backend] == nil then
txn:Alert("Unknown auth-request backend '" .. backend .. "'")
return
end
-- Check whether the given backend has servers that are not `DOWN`.
local addr = nil
local proxy = core.backends[backend]
for name, server in pairs(proxy.servers) do
local status = server:get_stats()["status"]
if status == "no check" or status:find("UP") == 1 then
addr = server:get_addr()
break
end
end
if addr == nil then
txn:Warning("No servers available for auth-request backend: '" .. backend .. "'")
return
end
-- Find out asecretcookie cookie.
-- Prepare filtered out set of cookies to be used for the request to the endpoint server (not the auth-server).
local new_cookies = {}
local asecretcookie = ""
local req_headers = txn.http:req_get_headers()
local req_cookie_header = req_headers['cookie']
if req_cookie_header then
for i, cookie_header in pairs(req_cookie_header) do
for j, cookie in pairs(splitString(cookie_header, "%s*;%s*")) do
if string.match(cookie, "^%s*asecretcookie=") then
_, _, asecretcookie = string.find(cookie, "^%s*asecretcookie=%s*(.*)%s*$")
else
table.insert(new_cookies, cookie)
end
end
end
end
local code = -1
local ak_auth_cache_key = ""
-- Make request to backend or take from auth-cache if our cookie is present.
if asecretcookie ~= "" then
-- Use both cookie and IP for the sake of fingerprinting.
ak_auth_cache_key = txn.f:src() .. ";" .. asecretcookie
-- Lookup the key in stick table.
local found_in_auth_cache = txn.c:in_table(ak_auth_cache_key, "auth_request") ~= 0
if found_in_auth_cache then
code = 200
else
-- Prpare headers for the request to the auth-server.
-- Transform table of request headers from haproxy's to socket.http's format.
local headers = {}
for header, values in pairs(req_headers) do
if header ~= "content-length" then
for i, v in pairs(values) do
if headers[header] == nil then
headers[header] = v
else
headers[header] = headers[header] .. ", " .. v
end
end
end
end
-- Make request
local body = nil
body, code =
http.request {
url = "http://" .. addr .. path,
headers = headers,
create = core.tcp,
redirect = false
}
-- Check whether we received a valid HTTP response.
if body == nil then
txn:Warning("Failure in auth-request backend '" .. backend .. "': " .. code)
return
end
end
end
-- Decide upon the code
if code == 200 then
local new_cookie_header = table.concat(new_cookies, "; ")
if new_cookie_header == "" then
txn.http:req_del_header('cookie')
else
txn.http:req_set_header('cookie', new_cookie_header)
end
txn:set_var("txn.ak_auth_response_successful", true)
txn:set_var("txn.ak_auth_cache_key", ak_auth_cache_key)
elseif code ~= 401 and code ~= 403 and code ~= -1 then
txn:Warning("Invalid status code in auth-request backend '" .. backend .. "': " .. code)
end
end,
2 -- number of arguments we expect to be passed on invocation of the registered function
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment