summaryrefslogtreecommitdiff
path: root/internal/suites/example/compose/haproxy/auth-request.lua
diff options
context:
space:
mode:
Diffstat (limited to 'internal/suites/example/compose/haproxy/auth-request.lua')
-rw-r--r--internal/suites/example/compose/haproxy/auth-request.lua108
1 files changed, 100 insertions, 8 deletions
diff --git a/internal/suites/example/compose/haproxy/auth-request.lua b/internal/suites/example/compose/haproxy/auth-request.lua
index 37b75f160..1504948d4 100644
--- a/internal/suites/example/compose/haproxy/auth-request.lua
+++ b/internal/suites/example/compose/haproxy/auth-request.lua
@@ -19,9 +19,42 @@
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
+--
+-- SPDX-License-Identifier: MIT
local http = require("haproxy-lua-http")
+core.register_action("auth-request", { "http-req" }, function(txn, be, path)
+ auth_request(txn, be, path, "HEAD", ".*", "-", "-")
+end, 2)
+
+core.register_action("auth-intercept", { "http-req" }, function(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)
+ hdr_req = globToLuaPattern(hdr_req)
+ hdr_succeed = globToLuaPattern(hdr_succeed)
+ hdr_fail = globToLuaPattern(hdr_fail)
+ auth_request(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)
+end, 6)
+
+function globToLuaPattern(glob)
+ if glob == "-" then
+ return "-"
+ end
+ -- magic chars: '^', '$', '(', ')', '%', '.', '[', ']', '*', '+', '-', '?'
+ -- https://www.lua.org/manual/5.4/manual.html#6.4.1
+ --
+ -- this chain is:
+ -- 1. escaping all the magic chars, adding a `%` in front of all of them,
+ -- except the chars being processed later in the chain;
+ -- 1.1. all the chars inside the [set] are magic chars and have special
+ -- meaning inside a set, so we're also escaping all of them to avoid
+ -- misbehavior;
+ -- 2. converting "match all" `*` and "match one" `?` to their Lua pattern
+ -- counterparts;
+ -- 3. adding start and finish boundaries outside the whole string and,
+ -- being a comma-separated list, between every single item as well.
+ return "^" .. glob:gsub("[%^%$%(%)%%%.%[%]%+%-]", "%%%1"):gsub("*", ".*"):gsub("?", "."):gsub(",", "$,^") .. "$"
+end
+
function set_var_pre_2_2(txn, var, value)
return txn:set_var(var, value)
end
@@ -44,8 +77,49 @@ function sanitize_header_for_variable(header)
return header:gsub("[^a-zA-Z0-9]", "_")
end
+-- header_match checks whether the provided header matches the pattern.
+-- pattern is a comma-separated list of Lua Patterns.
+function header_match(header, pattern)
+ if header == "content-length" or header == "host" or pattern == "-" then
+ return false
+ end
+ for p in pattern:gmatch("[^,]*") do
+ if header:match(p) then
+ return true
+ end
+ end
+ return false
+end
-core.register_action("auth-request", { "http-req" }, function(txn, be, path)
+-- Terminates the transaction and sends the provided response to the client.
+-- hdr_fail filters header names that should be provided using Lua Patterns.
+function send_response(txn, response, hdr_fail)
+ local reply = txn:reply()
+ if response then
+ reply:set_status(response.status_code)
+ for header, value in response:get_headers(false) do
+ if header_match(header, hdr_fail) then
+ reply:add_header(header, value)
+ end
+ end
+ if response.content then
+ reply:set_body(response.content)
+ end
+ else
+ reply:set_status(500)
+ end
+ txn:done(reply)
+end
+
+-- auth_request makes the request to the external authentication service
+-- and waits for the response. hdr_* params receive a comma-separated
+-- list of Lua Patterns used to identify the headers that should be
+-- copied between the requests and responses. A dash `-` in these params
+-- mean that the headers shouldn't be copied at all.
+-- Special values and behavior:
+-- * method == "*": call the auth service using the same method used by the client.
+-- * hdr_fail == "-": make the Lua script to not terminate the request.
+function auth_request(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)
set_var(txn, "txn.auth_response_successful", false)
-- Check whether the given backend exists.
@@ -75,7 +149,7 @@ core.register_action("auth-request", { "http-req" }, function(txn, be, path)
-- socket.http's format.
local headers = {}
for header, values in pairs(txn.http:req_get_headers()) do
- if header ~= 'content-length' then
+ if header_match(header, hdr_req) then
for i, v in pairs(values) do
if headers[header] == nil then
headers[header] = v
@@ -87,28 +161,46 @@ core.register_action("auth-request", { "http-req" }, function(txn, be, path)
end
-- Make request to backend.
- local response, err = http.head {
+ if method == "*" then
+ method = txn.sf:method()
+ end
+ local response, err = http.send(method:upper(), {
url = "http://" .. addr .. path,
headers = headers,
- }
+ })
+
+ -- `terminate_on_failure == true` means that the Lua script should send the response
+ -- and terminate the transaction in the case of a failure. This will happen when
+ -- hdr_fail content isn't a dash `-`.
+ local terminate_on_failure = hdr_fail ~= "-"
-- Check whether we received a valid HTTP response.
if response == nil then
txn:Warning("Failure in auth-request backend '" .. be .. "': " .. err)
set_var(txn, "txn.auth_response_code", 500)
+ if terminate_on_failure then
+ send_response(txn)
+ end
return
end
set_var(txn, "txn.auth_response_code", response.status_code)
+ local response_ok = 200 <= response.status_code and response.status_code < 300
for header, value in response:get_headers(true) do
set_var(txn, "req.auth_response_header." .. sanitize_header_for_variable(header), value)
+ if response_ok and hdr_succeed ~= "-" and header_match(header, hdr_succeed) then
+ txn.http:req_set_header(header, value)
+ end
end
- -- 2xx: Allow request.
- if 200 <= response.status_code and response.status_code < 300 then
+ -- response_ok means 2xx: allow request.
+ if response_ok then
set_var(txn, "txn.auth_response_successful", true)
- -- Don't allow other codes.
+ -- Don't allow codes < 200 or >= 300.
+ -- Forward the response to the client if required.
+ elseif terminate_on_failure then
+ send_response(txn, response, hdr_fail)
-- Codes with Location: Passthrough location at redirect.
elseif response.status_code == 301 or response.status_code == 302 or response.status_code == 303 or response.status_code == 307 or response.status_code == 308 then
set_var(txn, "txn.auth_response_location", response:get_header("location", "last"))
@@ -116,4 +208,4 @@ core.register_action("auth-request", { "http-req" }, function(txn, be, path)
elseif response.status_code ~= 401 and response.status_code ~= 403 then
txn:Warning("Invalid status code in auth-request backend '" .. be .. "': " .. response.status_code)
end
-end, 2)
+end