Skip to content

Instantly share code, notes, and snippets.

@borfig
Created March 4, 2022 20:27
Show Gist options
  • Save borfig/b1da30f06e2754a3c1475dd5a4979fbe to your computer and use it in GitHub Desktop.
Save borfig/b1da30f06e2754a3c1475dd5a4979fbe to your computer and use it in GitHub Desktop.
Reverse Proxy helper for HTTP Redirect Binding
-- 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