main cdb891cf5299 cached
39 files
48.8 KB
14.1k tokens
1 requests
Download .txt
Repository: TimWolla/haproxy-auth-request
Branch: main
Commit: cdb891cf5299
Files: 39
Total size: 48.8 KB

Directory structure:
gitextract_3ykm4_1e/

├── .github/
│   ├── dependabot.yml
│   ├── vtest.json
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .gitmodules
├── .mailmap
├── LICENSE
├── Makefile
├── README.md
├── auth-request.lua
├── debian/
│   ├── changelog
│   ├── compat
│   ├── control
│   ├── copyright
│   ├── haproxy-auth-request.docs
│   ├── rules
│   └── source/
│       └── format
└── test/
    ├── allow.vtc
    ├── allow_mt.vtc
    ├── deny.vtc
    ├── deny_close.vtc
    ├── deny_garbage.vtc
    ├── dynamic_method.vtc
    ├── expose_arbitrary_headers.vtc
    ├── expose_location.vtc
    ├── expose_location_last.vtc
    ├── expose_status_code.vtc
    ├── headers_complete.vtc
    ├── headers_fail.vtc
    ├── headers_fail_multiple.vtc
    ├── headers_filter.vtc
    ├── headers_request.vtc
    ├── headers_succeed.vtc
    ├── multiple_requests.vtc
    ├── multiple_requests_mt.vtc
    ├── no_variable_leak.lua
    ├── no_variable_leak.vtc
    ├── pass_headers_to_backend.vtc
    └── starts.vtc

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"


================================================
FILE: .github/vtest.json
================================================
{
	"problemMatcher": [
		{
			"owner": "vtest",
			"pattern": [
				{
					"regexp": "^#(\\s+top\\s+TEST\\s+(.*)\\s+FAILED.*)",
					"file": 2,
					"message": 1
				}
			]
		}
	]
}


================================================
FILE: .github/workflows/test.yml
================================================
# The MIT License (MIT)
#
# Copyright (c) 2020 Tim Düsterhus
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# 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

name: Test

on:
  push:
  pull_request:
  schedule:
  - cron: '0 0 * * 0'

jobs:
  VTest:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        haproxy-versions:
        - "1.8"
        - "1.9"
        - "2.0"
        - "2.1"
        - "2.2"
        - "2.3"
        - "2.4"
        - "2.5"
        - "2.6"
        - "2.7"
        - "2.8"
        - "2.9"
        - "3.0"
        - "3.1"
        - "3.2"
        - "3.3"
        - "3.4"
      fail-fast: false
    steps:
    - name: Install HAProxy ${{ matrix.haproxy-versions }}.
      uses: timwolla/action-install-haproxy@main
      id: install-haproxy
      with:
        branch: ${{ matrix.haproxy-versions }}
        install_vtest: yes
        use_lua: yes
    - name: Install apt dependencies.
      run: sudo apt-get install -y lua-json
    - uses: actions/checkout@v6
      with:
        submodules: recursive
    - name: Install haproxy-lua-http globally.
      run: |
        sudo install -m755 -d /usr/local/share/lua/5.3/
        sudo install -m644 haproxy-lua-http/http.lua /usr/local/share/lua/5.3/haproxy-lua-http.lua
        sed -i 's/lua-prepend-path/#lua-prepend-path/g' test/*.vtc
    - name: Install problem matcher for VTest.
      run: echo "::add-matcher::.github/vtest.json"
    - name: Run tests using HAProxy ${{ steps.install-haproxy.outputs.version }}.
      run: |
        set -euo pipefail
        vtest -Dhaproxy_version=${{ steps.install-haproxy.outputs.version }} -k -t 10 test/*.vtc 2>&1 \
          |tee >(
            awk '
              $5=="passed"{icon=":heavy_check_mark:"}
              $5=="skipped"{icon=":grey_question:"}
              $5=="FAILED"{icon=":x:"}
              $1 == "#" && $3=="TEST"{
                print "- " icon " " $4
              }
              {line=$0}
              END{print "# " $0}
            ' \
            |tac >> $GITHUB_STEP_SUMMARY
          )


================================================
FILE: .gitignore
================================================
debian/debhelper-build-stamp
debian/files
debian/haproxy-auth-request.debhelper.log
debian/haproxy-auth-request.substvars
debian/haproxy-auth-request/


================================================
FILE: .gitmodules
================================================
[submodule "haproxy-lua-http"]
	path = haproxy-lua-http
	url = https://github.com/haproxytech/haproxy-lua-http.git
	branch = master


================================================
FILE: .mailmap
================================================
Tim Düsterhus <tim@bastelstu.be> <timwolla@googlemail.com>


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2018 Tim Düsterhus

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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.



================================================
FILE: Makefile
================================================
# SPDX-License-Identifier: MIT

default:

install: auth-request.lua haproxy-lua-http/http.lua
	install -d "$(DESTDIR)/usr/share/haproxy"
	install -m644 haproxy-lua-http/http.lua "$(DESTDIR)/usr/share/haproxy"
	install -m644 auth-request.lua "$(DESTDIR)/usr/share/haproxy"

.PHONY: install


================================================
FILE: README.md
================================================
# auth-request ![Test](https://github.com/TimWolla/haproxy-auth-request/workflows/Test/badge.svg)

auth-request allows you to add access control to your HTTP services based on a
subrequest to a configured HAProxy backend. The workings of this Lua script are
loosely based on the [ngx_http_auth_request_module] module for nginx.

## Requirements

- HAProxy 1.8.4+ (2.2.0+ recommended)
  - Only the latest version of each HAProxy branch is supported.
- `USE_LUA=1` must be set at compile time.
- [haproxy-lua-http] must be available within the Lua path.
  - A `json` library within the Lua path (dependency of haproxy-lua-http).
  - With HAProxy 2.1.3+ you can use the [`lua-prepend-path`] configuration
    option to specify the search path.

## Usage

1. Load this Lua script in the `global` section of your `haproxy.cfg`:
    ```haproxy
    global
        # *snip*
        lua-prepend-path /usr/share/haproxy/?/http.lua # If haproxy-lua-http is saved as /usr/share/haproxy/haproxy-lua-http/http.lua
        lua-load /usr/share/haproxy/auth-request.lua
    ```

2. Define a backend that is used for the subrequests:
    ```haproxy
    backend auth_request
        mode http
        server auth_request 127.0.0.1:8080 check
    ```

3. Execute the subrequest in your frontend (as early as possible):
    ```haproxy
    frontend http
        mode http
        bind :::80 v4v6

        # *snip*

        # auth-request syntax:
        #                             Backend name     Path to request
        http-request lua.auth-request auth_request     /is-allowed

        # auth-intercept syntax:                                           (Headers to copy)
        #                               Backend name  Path         Method  Request  Success  Failure
        http-request lua.auth-intercept auth_request  /is-allowed  HEAD    *        -        -
    ```

4. Act on the results:
    ```haproxy
    frontend http
        # *snip*

        http-request deny if ! { var(txn.auth_response_successful) -m bool }
    ```

### Parameters

The scripts receive a list of parameters used to build the authentication
request:

* **Backend name**: is the name of an HAProxy backend. See the
[Inner Workings](#inner-workings) section.
* **Path to request**: the request URL sent to the auth-request backend.

The following parameters are only available in the `auth-intercept` script:

* **Method**: the HTTP method that should be used. Use an asterisk `*` to ask
`auth-intercept` to copy the same method used by the client. `auth-request`
uses the `HEAD` method.
* **Headers to copy on Request**: a comma-separated list of a simplified glob
pattern that should match the HTTP header names to copy from the client to the
auth-intercept backend. Use a dash `-` to not copy any header.
* **Headers to copy on Success**: a comma-separated list of a simplified glob
pattern that should match the HTTP header names to copy from the auth-intercept
backend to the protected backend server, if the auth-intercept backend respond
with 2xx response code and the request succeed. All headers received from the
auth-intercept will override headers with the same name provided by the client.
Use `*` to copy all headers, or use a dash `-` to not copy any header. HAProxy
variables are always created, see the [Available Variables](#available-variables)
section.
* **Headers to copy on Failure**: a comma-separated list of a simplified glob
pattern that should match the HTTP header names to copy from the auth-intercept
backend to the client, if the request failed. `auth-intercept` will use the
same HTTP method and body sent by the auth-intercept backend to respond to the
client, closing the transaction. The protected backend server will not be used.
Use `*` to copy all headers. Use a dash `-` to not close the transaction and
leave to the HAProxy configuration the task to deny the request based on the
`txn.auth_response_successful` variable. HAProxy variables are always created,
see the [Available Variables](#available-variables) section.

Simplified glob pattern: use an asterisk `*` to match any sequence of
characters and `?` to match a single char. `*` will match any header name.
`x-*` will match all header names started with `x-`. `x-????` will match
`x-user` but will not match neither `x-token` nor `x-id`.

HAProxy 2.1 or older: the On Failure param (the last one) will close the
transaction and respond to the client if the value is not a dash `-`, however
this feature is only supported on HAProxy 2.2 or newer. The only supported
option on 2.1 and older is a dash `-`.

### Available Variables

auth-request uses HAProxy variables to communicate the results back to you. The
[`var()` sample fetch] can be used to retrieve the variable contents.

The following list of variables may be set.

<dl>
<dt><code>txn.auth_response_successful</code></dt>
<dd>
Set to <code>true</code> if the subrequest returns an HTTP status code in the
<code>2xx</code> range. <code>false</code> otherwise.
</dd>

<dt><code>txn.auth_response_code</code></dt>
<dd>
The HTTP status code of the subrequest. If the subrequest did not return a
valid HTTP response the value will be <code>500</code>.
</dd>

<dt><code>txn.auth_response_location</code></dt>
<dd>
The <code>location</code> response header of the subrequest.

This variable is only set if the HTTP status code of the subrequest indicates a
redirect (i.e. <code>301</code>, <code>302</code>, <code>303</code>,
<code>307</code>, or <code>308</code>).
</dd>

<dt><code>req.auth_response_header.*</code>
<dd>
These variables store the subrequest’s response headers. The values of
duplicate response headers will be merged with a comma.

HAProxy variables may only contain alphanumeric characters, the dot
(<code>.</code>), and an underscore <code>_</code>. Any non-alphanumeric
characters will be replaced with an underscore to be representable. If the
response contains duplicate response headers <em>after</em> normalizing the
header name the result for these headers will be undefined.

Normalization examples:
<dl>
<dt><code>X-Authenticated-User</code></dt>
<dd><code>req.auth_response_header.x_authenticated_user</code></dd>
<dt><code>Success</code></dt>
<dd><code>req.auth_response_header.success</code></dd>
</dl>

Please note: The scope of the response header variables is <code>req</code>
compared to <code>txn</code> for the other variables. The contents will no
longer be available during response processing to save memory. Copy the values
of interest into a <code>txn.</code> variable if you need access them during
response processing.
</dd>
</dl>

## Inner Workings

The Lua script will make a HTTP request to the *first* server in the given
backend that is either marked as `UP` or that does not have checks enabled.
This allows for basic health checking of the auth-request backend. If you need
more complex processing of the request forward the auth-request to a separate
HAProxy *frontend* that performs the required modifications to the request and
response.

The requested URL is the one given in the second parameter.

Any request headers will be forwarded as-is to the auth-request backend, with
the exception of the `content-length` header which will be stripped, because
the request body will not be forwarded.

## Known limitations

- The Lua script only supports basic health checking, without redispatching or
  load balancing of any kind.
- The backend must not be using TLS.

[ngx_http_auth_request_module]: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html
[haproxy-lua-http]: https://github.com/haproxytech/haproxy-lua-http
[`lua-prepend-path`]: http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#lua-prepend-path
[`var()` sample fetch]: http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#7.3.2-var


================================================
FILE: auth-request.lua
================================================
-- The MIT License (MIT)
--
-- Copyright (c) 2018 Tim Düsterhus
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- 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
function set_var_post_2_2(txn, var, value)
	return txn:set_var(var, value, true)
end

set_var = function(txn, var, value)
	local success = pcall(set_var_post_2_2, txn, var, value)
	if success then
		set_var = set_var_post_2_2
	else
		set_var = set_var_pre_2_2
	end

	return set_var(txn, var, value)
end

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

-- 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.
	if core.backends[be] == nil then
		txn:Alert("Unknown auth-request backend '" .. be .. "'")
		set_var(txn, "txn.auth_response_code", 500)
		return
	end

	-- Check whether the given backend has servers that
	-- are not `DOWN`.
	local addr = nil
	for name, server in pairs(core.backends[be].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: '" .. be .. "'")
		set_var(txn, "txn.auth_response_code", 500)
		return
	end

	-- Transform table of request headers from haproxy's to
	-- socket.http's format.
	local headers = {}
	for header, values in pairs(txn.http:req_get_headers()) do
		if header_match(header, hdr_req) 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 to backend.
	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

	-- response_ok means 2xx: allow request.
	if response_ok then
		set_var(txn, "txn.auth_response_successful", true)
	-- 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"))
	-- 401 / 403: Do nothing, everything else: log.
	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


================================================
FILE: debian/changelog
================================================
haproxy-auth-request (0) UNRELEASED; urgency=medium

  * Initial Release.

 -- Tim Düsterhus <tim@bastelstu.be>  Mon, 08 Jan 2018 22:56:57 +0000


================================================
FILE: debian/compat
================================================
9


================================================
FILE: debian/control
================================================
Source: haproxy-auth-request
Section: net
Priority: optional
Maintainer: Tim Düsterhus <tim@bastelstu.be>
Build-Depends: debhelper (>= 9)
Standards-Version: 3.9.8
Homepage: https://github.com/TimWolla/haproxy-auth-request
Vcs-Git: https://github.com/TimWolla/haproxy-auth-request.git
Vcs-Browser: https://github.com/TimWolla/haproxy-auth-request.git

Package: haproxy-auth-request
Architecture: all
Depends: ${misc:Depends},
         lua-json
Suggests: haproxy
Description: HTTP access control using subrequests
 auth-request allows you to add access control to your HTTP services based
 on a subrequest to a configured haproxy backend. The workings of this Lua
 script are loosely based on the ngx_http_auth_request_module module for
 nginx.


================================================
FILE: debian/copyright
================================================
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: haproxy-auth-request
Source: <url://example.com>

Files: *
Copyright: Tim Düsterhus <tim@bastelstu.be>
License: MIT

Files: debian/*
Copyright: 2018 Tim Düsterhus <tim@bastelstu.be>
License: MIT

License: MIT
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
 to deal in the Software without restriction, including without limitation
 the rights to use, copy, modify, merge, publish, distribute, sublicense,
 and/or sell copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following conditions:
 .
 The above copyright notice and this permission notice shall be included
 in all copies or substantial portions of the Software.
 .
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER 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.

# Please also look if there are files or directories which have a
# different copyright/license attached and list them here.
# Please avoid picking licenses with terms that are more restrictive than the
# packaged work, as it may make Debian's contributions unacceptable upstream.


================================================
FILE: debian/haproxy-auth-request.docs
================================================
README.md


================================================
FILE: debian/rules
================================================
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
# export DH_VERBOSE = 1

%:
	dh $@


================================================
FILE: debian/source/format
================================================
3.0 (native)


================================================
FILE: test/allow.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that a successful request allows access."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -start

server s_auth_backend {
    rxreq
    txresp \
        -status 204
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-request deny if ! { var(txn.auth_response_successful) -m bool }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 200
} -run


================================================
FILE: test/allow_mt.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that a successful request allows access for multi-threaded Lua."
feature ignore_unknown_macro
feature cmd "dpkg --compare-versions ${haproxy_version} ge 2.4"

server s1 {
    rxreq
    txresp
} -start

server s_auth_backend {
    rxreq
    txresp \
        -status 204
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load-per-thread ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-request deny if ! { var(txn.auth_response_successful) -m bool }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 200
} -run


================================================
FILE: test/deny.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that a failing request denies access."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -start

server s_auth_backend {
    rxreq
    txresp \
        -status 403
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-request deny if ! { var(txn.auth_response_successful) -m bool }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 403
} -run


================================================
FILE: test/deny_close.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that a backend close denies access."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -start

server s_auth_backend {
    rxreq
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-request deny if ! { var(txn.auth_response_successful) -m bool }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 403
} -run


================================================
FILE: test/deny_garbage.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that a backend sending garbage denies access."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -repeat 7 -start

server s_auth_backend {
    rxreq
    send "foo"
    accept
    rxreq
    send "foo\r\n"
    accept
    rxreq
    send "HTTP/1.0 200 Ok"
    accept
    rxreq
    send "HTTP\r\n"
    accept
    rxreq
    send "HTTP/1\r\n"
    accept
    rxreq
    send "HTTP/1.0 200 Ok\r\n"
    accept
    rxreq
    send "HTTP/1.0 XXX Fail\r\n\r\n"
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-request deny if ! { var(txn.auth_response_successful) -m bool }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 403
} -repeat 7 -run


================================================
FILE: test/dynamic_method.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that auth-request backend receives the client method."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -repeat 3 -start

server s_auth_backend {
    rxreq
    expect req.method == "POST"
    txresp

    accept
    rxreq
    expect req.method == "GET"
    txresp

    accept
    rxreq
    expect req.method == "HEAD"
    txresp
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"
        http-request lua.auth-intercept auth_backend / * * - -
        http-request deny if ! { var(txn.auth_response_successful) -m bool }
        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq -method "POST"
    rxresp
    txreq -method "GET"
    rxresp
    txreq -method "HEAD"
    rxresp
} -run


================================================
FILE: test/expose_arbitrary_headers.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that arbitrary response headers are exposed."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -repeat 4 -start

server s_auth_backend {
    rxreq
    txresp \
        -status 200 \
        -hdr "content-type: text/plain" \
        -hdr "x-authenticated-email: guest@example.com" \
        -hdr "x-authenticated-user: guest"

    accept
    rxreq
    txresp \
        -status 200 \
        -hdr "x-authenticated-user: root"

    accept
    rxreq
    txresp \
        -status 200 \
        -hdr "content-type: text/plain" \
        -hdr "x-authenticated-email: root@example.com" \
        -hdr "x-authenticated-user: root"

    accept
    rxreq
    txresp \
        -status 200 \
        -hdr "content-type: text/plain" \
        -hdr "x-authenticated-email: root@example.com" \
        -hdr "x-authenticated-email: root@example.net" \
        -hdr "x-authenticated-user: root"
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-request deny unless { var(req.auth_response_header.x_authenticated_user) -m str root }

        http-request set-header x-echo "%[var(req.auth_response_header.content_type)]|%[var(req.auth_response_header.x_authenticated_user)]|%[var(req.auth_response_header.x_authenticated_email)]"
        http-request set-var(txn.echo) req.fhdr(x-echo)

        http-response set-header x-echo %[var(txn.echo)]

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 403
} -run

client c2 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 200
    expect resp.http.x-echo == "|root|"
    txreq
    rxresp
    expect resp.status == 200
    expect resp.http.x-echo == "text/plain|root|root@example.com"
    txreq
    rxresp
    expect resp.status == 200
    expect resp.http.x-echo == "text/plain|root|root@example.com,root@example.net"
} -run


================================================
FILE: test/expose_location.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that location header is exposed for redirects."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -repeat 4 -start

server s_auth_backend {
    rxreq
    txresp \
        -status 301 \
        -hdr "location: https://example.com"

    accept
    rxreq
    txresp \
        -status 303 \
        -hdr "location: https://example.com"

    accept
    rxreq
    txresp \
        -status 307 \
        -hdr "location: https://example.com"

    accept
    rxreq
    txresp \
        -status 200 \
        -hdr "location: https://example.com"
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-response set-header x-status %[var(txn.auth_response_code)]
        http-response set-header x-location %[var(txn.auth_response_location)] if { var(txn.auth_response_location) -m found }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.http.x-status == 301
    expect resp.http.x-location == "https://example.com"
    txreq
    rxresp
    expect resp.http.x-status == 303
    expect resp.http.x-location == "https://example.com"
    txreq
    rxresp
    expect resp.http.x-status == 307
    expect resp.http.x-location == "https://example.com"
    txreq
    rxresp
    expect resp.http.x-status == 200
    expect resp.http.x-location == "<undef>"
} -run


================================================
FILE: test/expose_location_last.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that last location header is exposed for redirects."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -repeat 4 -start

server s_auth_backend {
    rxreq
    txresp \
        -status 301 \
        -hdr "location: https://example.net" \
        -hdr "location: https://example.com"

    accept
    rxreq
    txresp \
        -status 301 \
        -hdr "location: https://example.com" \
        -hdr "location: https://example.net"

} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-response set-header x-status %[var(txn.auth_response_code)]
        http-response set-header x-location %[var(txn.auth_response_location)] if { var(txn.auth_response_location) -m found }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.http.x-status == 301
    expect resp.http.x-location == "https://example.com"
    txreq
    rxresp
    expect resp.http.x-status == 301
    expect resp.http.x-location == "https://example.net"
} -run


================================================
FILE: test/expose_status_code.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that response status is exposed."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -repeat 4 -start

server s_auth_backend {
    rxreq
    txresp \
        -status 200

    accept
    rxreq
    txresp \
        -status 400

    accept
    rxreq
    txresp \
        -status 403

    accept
    rxreq
    txresp \
        -status 200
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-response set-header x-status %[var(txn.auth_response_code)]

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.http.x-status == 200
    txreq
    rxresp
    expect resp.http.x-status == 400
    txreq
    rxresp
    expect resp.http.x-status == 403
    txreq
    rxresp
    expect resp.http.x-status == 200
} -run


================================================
FILE: test/headers_complete.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that filtered request headers are passed to the auth-intercept backend."
feature ignore_unknown_macro
feature cmd "dpkg --compare-versions ${haproxy_version} ge 2.2"

server s1 {
    rxreq
    expect req.http.x-user == "logan"
    expect req.http.x-app-data == "secret"
    expect req.http.token == "bearer value"
    txresp \
        -status 201 \
        -hdr "x-field: value"
} -start

server s_auth_backend {
    rxreq
    expect req.http.x-app-data == "<undef>"
    expect req.http.token == "bearer value"
    txresp \
        -status 401 \
        -hdr "x-reason: invalid pwd"

    accept
    rxreq
    expect req.http.x-app-data == "<undef>"
    expect req.http.token == "bearer value"
    txresp \
        -status 200 \
        -hdr "x-user: logan"
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"
        http-request lua.auth-intercept auth_backend / HEAD token x-user x-reason
        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq \
        -hdr "x-user: try-to-override" \
        -hdr "x-app-data: secret" \
        -hdr "token: bearer value"
    rxresp
    expect resp.status == 401
    expect resp.http.x-reason == "invalid pwd"
    expect resp.http.x-field == "<undef>"

    txreq \
        -hdr "x-user: try-to-override" \
        -hdr "x-app-data: secret" \
        -hdr "token: bearer value"
    rxresp
    expect resp.status == 201
    expect resp.http.x-reason == "<undef>"
    expect resp.http.x-field == "value"
} -run


================================================
FILE: test/headers_fail.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that filtered auth backend response headers are passed to the client."
feature ignore_unknown_macro
feature cmd "dpkg --compare-versions ${haproxy_version} ge 2.2"

server s_auth_backend {
    rxreq
    txresp \
        -status 401 \
        -hdr "x-user: admin" \
        -hdr "x-passwd: 123" \
        -hdr "x-reason: invalid pwd" \
        -hdr "token: asd" \
        -hdr "other: value" \
        -body "{\"msg\":\"invalid pwd\"}"
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"
        http-request lua.auth-intercept auth_backend / * * - x-user,x-reason

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 401
    expect resp.http.x-user == "admin"
    expect resp.http.x-passwd == "<undef>"
    expect resp.http.x-reason == "invalid pwd"
    expect resp.http.token == "<undef>"
    expect resp.http.other == "<undef>"
    expect resp.body == "{\"msg\":\"invalid pwd\"}"
} -run


================================================
FILE: test/headers_fail_multiple.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that multi-valued filtered auth backend response headers are passed to the client."
feature ignore_unknown_macro
feature cmd "dpkg --compare-versions ${haproxy_version} ge 2.2"

server s_auth_backend {
    rxreq
    txresp \
        -status 401 \
        -hdr "x-user: admin" \
        -hdr "x-passwd: 123" \
        -hdr "x-reason: invalid pwd" \
        -hdr "x-reason: account expired" \
        -hdr "set-cookie: csrf=1234;" \
        -hdr "set-cookie: session=;" \
        -body "{\"msg\":\"invalid pwd\"}"
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        # VTest only sees the first header with a given name, thus
        # we split the expected headers into separate headers that
        # can be checked independently.
        http-response set-header set-cookie1 %[res.fhdr(set-cookie,1)]
        http-response set-header set-cookie2 %[res.fhdr(set-cookie,2)]
        http-response set-header x-reason1 %[res.fhdr(x-reason,1)]
        http-response set-header x-reason2 %[res.fhdr(x-reason,2)]

        server be ${h1_fe2_addr}:${h1_fe2_port}

    listen fe2
        mode http
        bind "fd@${fe2}"
        http-request lua.auth-intercept auth_backend / * * - x-user,x-reason,set-cookie

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 401
    expect resp.http.x-user == "admin"
    expect resp.http.x-passwd == "<undef>"

    expect resp.http.x-reason !~ ","
    expect resp.http.x-reason1 == "invalid pwd"
    expect resp.http.x-reason2 == "account expired"

    expect resp.http.set-cookie !~ ","
    expect resp.http.set-cookie1 == "csrf=1234;"
    expect resp.http.set-cookie2 == "session=;"

    expect resp.body == "{\"msg\":\"invalid pwd\"}"
} -run


================================================
FILE: test/headers_filter.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that headers are correctly filtered using a simplified glob pattern."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp -status 201
} -start

server s_auth_backend {
    rxreq
    expect req.http.x-user == "logan"
    expect req.http.x-passwd == "top$secret"
    expect req.http.y-token == "<undef>"
    expect req.http.y-uid == "dead-...-beef"
    expect req.http.z-myapp == "<undef>"
    expect req.http.z-app == "another-data"
    txresp
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"
        http-request lua.auth-intercept auth_backend / * x-*,y-???,*-app - -
        http-request deny if ! { var(txn.auth_response_successful) -m bool }
        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq \
        -hdr "x-user: logan" \
        -hdr "x-passwd: top$secret" \
        -hdr "y-token: bearer value" \
        -hdr "y-uid: dead-...-beef" \
        -hdr "z-myapp: some-data" \
        -hdr "z-app: another-data"
    rxresp
    expect resp.status == 201
} -run


================================================
FILE: test/headers_request.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that filtered request headers are passed to the auth-intercept backend."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp -status 201
} -start

server s_auth_backend {
    rxreq
    expect req.http.x-user == "admin"
    expect req.http.x-passwd == "123"
    expect req.http.token == "asd"
    expect req.http.other == "<undef>"
    txresp
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"
        http-request lua.auth-intercept auth_backend / * x-*,token - -
        http-request deny if ! { var(txn.auth_response_successful) -m bool }
        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq \
        -hdr "x-user: admin" \
        -hdr "x-passwd: 123" \
        -hdr "token: asd" \
        -hdr "other: value"
    rxresp
    expect resp.status == 201
} -run


================================================
FILE: test/headers_succeed.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that filtered auth backend response headers are passed to the protected backend server."
feature ignore_unknown_macro

server s1 {
    rxreq
    expect req.http.x-user == "admin"
    expect req.http.x-passwd == "<undef>"
    expect req.http.token == "asd"
    expect req.http.other == "<undef>"
    txresp -status 201
} -start

server s_auth_backend {
    rxreq
    txresp \
        -hdr "x-user: admin" \
        -hdr "x-passwd: 123" \
        -hdr "token: asd" \
        -hdr "other: value"
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"
        http-request lua.auth-intercept auth_backend / * * x-user,token -
        http-request deny if ! { var(txn.auth_response_successful) -m bool }
        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 201
} -run


================================================
FILE: test/multiple_requests.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that multiple clients work fine."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -repeat 150 -start

server s_auth_backend_allow {
    rxreq
    txresp \
        -status 204
} -repeat 100 -start

server s_auth_backend_deny {
    rxreq
    txresp \
        -status 403
} -repeat 50 -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend_allow /allow if { path /allow }
        http-request lua.auth-request auth_backend_deny  /deny  if { path /deny }
        http-request deny if ! { var(txn.auth_response_successful) -m bool }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend_allow
        mode http
        server auth_backend_allow ${s_auth_backend_allow_addr}:${s_auth_backend_allow_port}

    backend auth_backend
        mode http
        server auth_backend_deny ${s_auth_backend_deny_addr}:${s_auth_backend_deny_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq -url "/allow"
    rxresp
    expect resp.status == 200
} -repeat 75 -run

client c2 -connect ${h1_fe1_sock} {
    txreq -url "/deny"
    rxresp
    expect resp.status == 403
} -repeat 50 -run

client c3 -connect ${h1_fe1_sock} {
    txreq -url "/deny"
    rxresp
    expect resp.status == 403
} -repeat 50 -run

client c1 -connect ${h1_fe1_sock} {
    txreq -url "/allow"
    rxresp
    expect resp.status == 200
} -repeat 25 -run


================================================
FILE: test/multiple_requests_mt.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that multiple clients work fine for multi-threaded Lua."
feature ignore_unknown_macro
feature cmd "dpkg --compare-versions ${haproxy_version} ge 2.4"

server s1 {
    rxreq
    txresp
} -repeat 150 -start

server s_auth_backend_allow {
    rxreq
    txresp \
        -status 204
} -repeat 100 -start

server s_auth_backend_deny {
    rxreq
    txresp \
        -status 403
} -repeat 50 -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load-per-thread ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend_allow /allow if { path /allow }
        http-request lua.auth-request auth_backend_deny  /deny  if { path /deny }
        http-request deny if ! { var(txn.auth_response_successful) -m bool }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend_allow
        mode http
        server auth_backend_allow ${s_auth_backend_allow_addr}:${s_auth_backend_allow_port}

    backend auth_backend
        mode http
        server auth_backend_deny ${s_auth_backend_deny_addr}:${s_auth_backend_deny_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq -url "/allow"
    rxresp
    expect resp.status == 200
} -repeat 75 -run

client c2 -connect ${h1_fe1_sock} {
    txreq -url "/deny"
    rxresp
    expect resp.status == 403
} -repeat 50 -run

client c3 -connect ${h1_fe1_sock} {
    txreq -url "/deny"
    rxresp
    expect resp.status == 403
} -repeat 50 -run

client c1 -connect ${h1_fe1_sock} {
    txreq -url "/allow"
    rxresp
    expect resp.status == 200
} -repeat 25 -run


================================================
FILE: test/no_variable_leak.lua
================================================
-- SPDX-License-Identifier: MIT

core.register_fetches("leak_check", function(txn, var)
	local result = txn:get_var(var)
	
	if result == nil then
		return "<nil>"
	end
	
	return result
end)


================================================
FILE: test/no_variable_leak.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that we don't leak variables."
feature ignore_unknown_macro
feature cmd "dpkg --compare-versions ${haproxy_version} ge 2.2"
feature cmd "dpkg --compare-versions ${haproxy_version} lt 2.5-dev6"

server s1 {
    rxreq
    txresp
} -start

server s_auth_backend {
    rxreq
    txresp \
        -status 301 \
        -hdr "location: https://example.com"
} -repeat 2 -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua
        lua-load ${testdir}/no_variable_leak.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-response set-header leak-check %[lua.leak_check(txn.auth_response_location)]

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.http.leak-check == "<nil>"
} -run


================================================
FILE: test/pass_headers_to_backend.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that request headers are passed to the backend."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -start

server s_auth_backend {
    rxreq
    expect req.http.x-foo == "bar"
    txresp \
        -status 200
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"

        http-request lua.auth-request auth_backend /allow
        http-request deny if ! { var(txn.auth_response_successful) -m bool }

        server s1 ${s1_addr}:${s1_port}

    backend auth_backend
        mode http
        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq \
        -hdr "x-foo: bar"
    rxresp
    expect resp.status == 200
} -run


================================================
FILE: test/starts.vtc
================================================
# SPDX-License-Identifier: MIT

varnishtest "Verify that HAProxy starts with auth-request.lua loaded."
feature ignore_unknown_macro

server s1 {
    rxreq
    txresp
} -start

haproxy h1 -conf {
    global
        lua-prepend-path ${testdir}/../?/http.lua
        lua-load ${testdir}/../auth-request.lua

    listen fe1
        mode http
        bind "fd@${fe1}"
        server s1 ${s1_addr}:${s1_port}
} -start

client c1 -connect ${h1_fe1_sock} {
    txreq
    rxresp
    expect resp.status == 200
} -run
Download .txt
gitextract_3ykm4_1e/

├── .github/
│   ├── dependabot.yml
│   ├── vtest.json
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .gitmodules
├── .mailmap
├── LICENSE
├── Makefile
├── README.md
├── auth-request.lua
├── debian/
│   ├── changelog
│   ├── compat
│   ├── control
│   ├── copyright
│   ├── haproxy-auth-request.docs
│   ├── rules
│   └── source/
│       └── format
└── test/
    ├── allow.vtc
    ├── allow_mt.vtc
    ├── deny.vtc
    ├── deny_close.vtc
    ├── deny_garbage.vtc
    ├── dynamic_method.vtc
    ├── expose_arbitrary_headers.vtc
    ├── expose_location.vtc
    ├── expose_location_last.vtc
    ├── expose_status_code.vtc
    ├── headers_complete.vtc
    ├── headers_fail.vtc
    ├── headers_fail_multiple.vtc
    ├── headers_filter.vtc
    ├── headers_request.vtc
    ├── headers_succeed.vtc
    ├── multiple_requests.vtc
    ├── multiple_requests_mt.vtc
    ├── no_variable_leak.lua
    ├── no_variable_leak.vtc
    ├── pass_headers_to_backend.vtc
    └── starts.vtc
Condensed preview — 39 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (55K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 119,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/vtest.json",
    "chars": 182,
    "preview": "{\n\t\"problemMatcher\": [\n\t\t{\n\t\t\t\"owner\": \"vtest\",\n\t\t\t\"pattern\": [\n\t\t\t\t{\n\t\t\t\t\t\"regexp\": \"^#(\\\\s+top\\\\s+TEST\\\\s+(.*)\\\\s+FAIL"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 3034,
    "preview": "# The MIT License (MIT)\n#\n# Copyright (c) 2020 Tim Düsterhus\n#\n# Permission is hereby granted, free of charge, to any pe"
  },
  {
    "path": ".gitignore",
    "chars": 151,
    "preview": "debian/debhelper-build-stamp\ndebian/files\ndebian/haproxy-auth-request.debhelper.log\ndebian/haproxy-auth-request.substvar"
  },
  {
    "path": ".gitmodules",
    "chars": 132,
    "preview": "[submodule \"haproxy-lua-http\"]\n\tpath = haproxy-lua-http\n\turl = https://github.com/haproxytech/haproxy-lua-http.git\n\tbran"
  },
  {
    "path": ".mailmap",
    "chars": 59,
    "preview": "Tim Düsterhus <tim@bastelstu.be> <timwolla@googlemail.com>\n"
  },
  {
    "path": "LICENSE",
    "chars": 1081,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2018 Tim Düsterhus\n\nPermission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "Makefile",
    "chars": 289,
    "preview": "# SPDX-License-Identifier: MIT\n\ndefault:\n\ninstall: auth-request.lua haproxy-lua-http/http.lua\n\tinstall -d \"$(DESTDIR)/us"
  },
  {
    "path": "README.md",
    "chars": 7798,
    "preview": "# auth-request ![Test](https://github.com/TimWolla/haproxy-auth-request/workflows/Test/badge.svg)\n\nauth-request allows y"
  },
  {
    "path": "auth-request.lua",
    "chars": 7627,
    "preview": "-- The MIT License (MIT)\n--\n-- Copyright (c) 2018 Tim Düsterhus\n--\n-- Permission is hereby granted, free of charge, to a"
  },
  {
    "path": "debian/changelog",
    "chars": 145,
    "preview": "haproxy-auth-request (0) UNRELEASED; urgency=medium\n\n  * Initial Release.\n\n -- Tim Düsterhus <tim@bastelstu.be>  Mon, 08"
  },
  {
    "path": "debian/compat",
    "chars": 2,
    "preview": "9\n"
  },
  {
    "path": "debian/control",
    "chars": 743,
    "preview": "Source: haproxy-auth-request\nSection: net\nPriority: optional\nMaintainer: Tim Düsterhus <tim@bastelstu.be>\nBuild-Depends:"
  },
  {
    "path": "debian/copyright",
    "chars": 1625,
    "preview": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: haproxy-auth-request\nSource: <"
  },
  {
    "path": "debian/haproxy-auth-request.docs",
    "chars": 10,
    "preview": "README.md\n"
  },
  {
    "path": "debian/rules",
    "chars": 159,
    "preview": "#!/usr/bin/make -f\n# See debhelper(7) (uncomment to enable)\n# output every command that modifies files on the build syst"
  },
  {
    "path": "debian/source/format",
    "chars": 13,
    "preview": "3.0 (native)\n"
  },
  {
    "path": "test/allow.vtc",
    "chars": 830,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a successful request allows access.\"\nfeature ignore_unknown_mac"
  },
  {
    "path": "test/allow_mt.vtc",
    "chars": 928,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a successful request allows access for multi-threaded Lua.\"\nfea"
  },
  {
    "path": "test/deny.vtc",
    "chars": 827,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a failing request denies access.\"\nfeature ignore_unknown_macro\n"
  },
  {
    "path": "test/deny_close.vtc",
    "chars": 792,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a backend close denies access.\"\nfeature ignore_unknown_macro\n\ns"
  },
  {
    "path": "test/deny_garbage.vtc",
    "chars": 1119,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a backend sending garbage denies access.\"\nfeature ignore_unknow"
  },
  {
    "path": "test/dynamic_method.vtc",
    "chars": 1051,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that auth-request backend receives the client method.\"\nfeature ignor"
  },
  {
    "path": "test/expose_arbitrary_headers.vtc",
    "chars": 2261,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that arbitrary response headers are exposed.\"\nfeature ignore_unknown"
  },
  {
    "path": "test/expose_location.vtc",
    "chars": 1719,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that location header is exposed for redirects.\"\nfeature ignore_unkno"
  },
  {
    "path": "test/expose_location_last.vtc",
    "chars": 1397,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that last location header is exposed for redirects.\"\nfeature ignore_"
  },
  {
    "path": "test/expose_status_code.vtc",
    "chars": 1174,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that response status is exposed.\"\nfeature ignore_unknown_macro\n\nserv"
  },
  {
    "path": "test/headers_complete.vtc",
    "chars": 1802,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that filtered request headers are passed to the auth-intercept backe"
  },
  {
    "path": "test/headers_fail.vtc",
    "chars": 1244,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that filtered auth backend response headers are passed to the client"
  },
  {
    "path": "test/headers_fail_multiple.vtc",
    "chars": 2040,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that multi-valued filtered auth backend response headers are passed "
  },
  {
    "path": "test/headers_filter.vtc",
    "chars": 1338,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that headers are correctly filtered using a simplified glob pattern."
  },
  {
    "path": "test/headers_request.vtc",
    "chars": 1132,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that filtered request headers are passed to the auth-intercept backe"
  },
  {
    "path": "test/headers_succeed.vtc",
    "chars": 1155,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that filtered auth backend response headers are passed to the protec"
  },
  {
    "path": "test/multiple_requests.vtc",
    "chars": 1592,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that multiple clients work fine.\"\nfeature ignore_unknown_macro\n\nserv"
  },
  {
    "path": "test/multiple_requests_mt.vtc",
    "chars": 1690,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that multiple clients work fine for multi-threaded Lua.\"\nfeature ign"
  },
  {
    "path": "test/no_variable_leak.lua",
    "chars": 190,
    "preview": "-- SPDX-License-Identifier: MIT\n\ncore.register_fetches(\"leak_check\", function(txn, var)\n\tlocal result = txn:get_var(var)"
  },
  {
    "path": "test/no_variable_leak.vtc",
    "chars": 1084,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that we don't leak variables.\"\nfeature ignore_unknown_macro\nfeature "
  },
  {
    "path": "test/pass_headers_to_backend.vtc",
    "chars": 900,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that request headers are passed to the backend.\"\nfeature ignore_unkn"
  },
  {
    "path": "test/starts.vtc",
    "chars": 507,
    "preview": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that HAProxy starts with auth-request.lua loaded.\"\nfeature ignore_un"
  }
]

About this extraction

This page contains the full source code of the TimWolla/haproxy-auth-request GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 39 files (48.8 KB), approximately 14.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!