Last active
December 9, 2022 03:39
-
-
Save akshaal/d7ee07e217de5d7632722a601dfb8827 to your computer and use it in GitHub Desktop.
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
-- 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