Created
March 4, 2022 20:27
-
-
Save borfig/b1da30f06e2754a3c1475dd5a4979fbe to your computer and use it in GitHub Desktop.
Reverse Proxy helper for HTTP Redirect Binding
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
-- Installation instructions | |
-- 1. Install OpenResty (https://openresty.org/) | |
-- 2. Use opm to install dependencies: | |
-- opm get hamishforbes/lua-ffi-zlib && opm get liyo/neturl | |
-- 3. Change the configuration below to suit your needs | |
-- 4. Reference this file using header_filter_by_lua_file directive, | |
-- in the same block as the relevant proxy_pass directive. | |
-- Configuration | |
-- The following 2 locals are editable | |
local upstream_hostname = "my.internal.app" | |
local reverse_proxy_hostname = "my.app" | |
-- End of configuration | |
-- fetch Location HTTP header | |
local location = ngx.header["location"] | |
if not location -- no header found? do nothing | |
then | |
return | |
end | |
-- get the SAMLRequest query parameter | |
-- it contains the SAML AuthnRequest XML | |
local url = require('net.url') | |
local parsedLocation = url.parse(location) | |
local request = parsedLocation.query.SAMLRequest | |
if not request -- no SAML request? do nothing | |
then | |
return | |
end | |
-- zlib inflate and deflate helpers | |
local zlib = require('ffi-zlib') | |
local function inflate(compressed) | |
local i = 1 | |
local input = function(bufsize) | |
if i > #compressed then | |
return nil | |
end | |
local ret = string.sub(compressed, i, i + bufsize - 1) | |
i = i + bufsize | |
return ret | |
end | |
local output_table = {} | |
local output = function(data) | |
table.insert(output_table, data) | |
end | |
local ok, err = zlib.inflateGzip(input, output, nil, -15) | |
if not ok then | |
return nil, err | |
end | |
return table.concat(output_table, '') | |
end | |
local function deflate(uncompressed) | |
local i = 1 | |
local input = function(bufsize) | |
if i > #uncompressed then | |
return nil | |
end | |
local ret = string.sub(uncompressed, i, i + bufsize - 1) | |
i = i + bufsize | |
return ret | |
end | |
local output_table = {} | |
local output = function(data) | |
table.insert(output_table, data) | |
end | |
local options = {windowBits = 15} | |
local ok, err = zlib.deflateGzip(input, output, nil, options) | |
if not ok then | |
return nil, err | |
end | |
local compressed = string.sub(table.concat(output_table, ''), 2 + 1, -(4 + 1)) | |
return compressed, nil | |
end | |
-- helper for patching AuthnRequest | |
local function patchSamlRequest(request) | |
-- base64-decode the request | |
request, err = ngx.decode_base64(string.gsub(request, "\n", "")) | |
if not request | |
then | |
return nil, err | |
end | |
-- decompress (inflate) the request | |
request, err = inflate(request) | |
if not request | |
then | |
return nil, err | |
end | |
-- replace the upstream hostname with the reverse proxy's one | |
request = string.gsub(request, upstream_hostname, reverse_proxy_hostname) | |
-- compress (deflate) the request | |
request, err = deflate(request) | |
if not request | |
then | |
return nil, err | |
end | |
-- base64-encode the request | |
return ngx.encode_base64(request) | |
end | |
-- patch the request | |
request, err = patchSamlRequest(request) | |
if not request -- handle any error | |
then | |
ngx.log(ngx.ERR, err) -- by logging it | |
return | |
end | |
-- replace the query parameter with the new request | |
parsedLocation.query.SAMLRequest = request | |
-- replace the Location HTTP header with the new one | |
ngx.header["location"] = parsedLocation:build() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment