diff --git a/internal/server/public_html.gen.go b/internal/server/public_html.gen.go
index d3c354df..50999109 100644
--- a/internal/server/public_html.gen.go
+++ b/internal/server/public_html.gen.go
@@ -2,5 +2,6 @@ package server
import "aletheia.icu/broccoli/fs"
-// Mock the embedded filesystem for unit tests.
-var br = fs.New(false, []byte(""))
+// Mock the embedded filesystem for unit tests. The bundle is built from an empty file and
+// allows to run the dev workflow without failure.
+var br = fs.New(false, []byte("\x1b~\x00\x80\x8d\x94n\xc2|\x84J\xf7\xbfn\xfd\xf7w;.\x8d m\xb2&\xd1Z\xec\xb2\x05\xb9\xc00\x8a\xf7(\x80^78\t(\f\f\xc3p\xc2\xc1\x06[a\xa2\xb3\xa4P\xe5\xa14\xfb\x19\xb2cp\xf6\x90-Z\xb2\x11\xe0l\xa1\x80\\\x95Vh\t\xc5\x06\x16\xfa\x8c\xc0\"!\xa5\xcf\xf7$\x9a\xb2\a`\xc6\x18\xc8~\xce8\r\x16Z\x9d\xc3\xe3\xff\x00"))
diff --git a/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml b/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml
index 02a473f5..a9781dd7 100644
--- a/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml
+++ b/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml
@@ -16,6 +16,7 @@ services:
labels:
# Traefik 1.x
- 'traefik.frontend.rule=Host:login.example.com;PathPrefix:/api'
+ - 'traefik.protocol=https'
# Traefik 2.x
- 'traefik.http.routers.authelia_backend.rule=Host(`login.example.com`) && PathPrefix(`/api`)'
- 'traefik.http.routers.authelia_backend.entrypoints=https'
diff --git a/internal/suites/example/compose/haproxy/Dockerfile b/internal/suites/example/compose/haproxy/Dockerfile
index b5f52ebd..a85535a1 100644
--- a/internal/suites/example/compose/haproxy/Dockerfile
+++ b/internal/suites/example/compose/haproxy/Dockerfile
@@ -5,6 +5,5 @@ apk add --no-cache \
curl \
lua5.3-socket \
openssl && \
-curl -Lfs -o /usr/local/etc/haproxy/auth-request.lua "https://raw.githubusercontent.com/TimWolla/haproxy-auth-request/master/auth-request.lua" && \
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=AU/ST=Victoria/L=Melbourne/O=Authelia/CN=*.example.com" -keyout haproxy.key -out haproxy.crt && \
cat haproxy.key haproxy.crt > /usr/local/etc/haproxy/haproxy.pem
\ No newline at end of file
diff --git a/internal/suites/example/compose/haproxy/auth-request.lua b/internal/suites/example/compose/haproxy/auth-request.lua
new file mode 100644
index 00000000..debb8195
--- /dev/null
+++ b/internal/suites/example/compose/haproxy/auth-request.lua
@@ -0,0 +1,103 @@
+-- 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.
+
+local http = require("socket.http")
+
+core.register_action("auth-request", { "http-req" }, function(txn, be, path)
+ txn:set_var("txn.auth_response_successful", false)
+
+ -- Check whether the given backend exists.
+ if core.backends[be] == nil then
+ txn:Alert("Unknown auth-request backend '" .. be .. "'")
+ txn:set_var("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 .. "'")
+ txn:set_var("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 ~= '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 to backend.
+ local b, c, h = http.request {
+ url = "http://" .. addr .. path,
+ headers = headers,
+ create = core.tcp,
+ -- Disable redirects, because DNS does not work here.
+ redirect = false,
+ -- We do not check body, so HEAD
+ method = "HEAD",
+ }
+
+ -- Check whether we received a valid HTTP response.
+ if b == nil then
+ txn:Warning("Failure in auth-request backend '" .. be .. "': " .. c)
+ txn:set_var("txn.auth_response_code", 500)
+ return
+ end
+
+ txn:set_var("txn.auth_response_code", c)
+
+ -- 2xx: Allow request.
+ if 200 <= c and c < 300 then
+ if h["remote-user"] then
+ txn:set_var("txn.auth_user", h["remote-user"])
+ end
+ if h["remote-groups"] then
+ txn:set_var("txn.auth_groups", h["remote-groups"])
+ end
+ txn:set_var("txn.auth_response_successful", true)
+ -- Don't allow other codes.
+ -- Codes with Location: Passthrough location at redirect.
+ elseif c == 301 or c == 302 or c == 303 or c == 307 or c == 308 then
+ txn:set_var("txn.auth_response_location", h["location"])
+ -- 401 / 403: Do nothing, everything else: log.
+ elseif c ~= 401 and c ~= 403 then
+ txn:Warning("Invalid status code in auth-request backend '" .. be .. "': " .. c)
+ end
+end, 2)
\ No newline at end of file
diff --git a/internal/suites/example/compose/haproxy/docker-compose.yml b/internal/suites/example/compose/haproxy/docker-compose.yml
index 90ad45b5..452bd094 100644
--- a/internal/suites/example/compose/haproxy/docker-compose.yml
+++ b/internal/suites/example/compose/haproxy/docker-compose.yml
@@ -4,6 +4,7 @@ services:
build: ./example/compose/haproxy/
volumes:
- ./example/compose/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
+ - ./example/compose/haproxy/auth-request.lua:/usr/local/etc/haproxy/auth-request.lua
networks:
authelianet:
# Set the IP to be able to query on port 8080
diff --git a/internal/suites/example/compose/haproxy/haproxy.cfg b/internal/suites/example/compose/haproxy/haproxy.cfg
index b5b1ea9b..ef18ac11 100644
--- a/internal/suites/example/compose/haproxy/haproxy.cfg
+++ b/internal/suites/example/compose/haproxy/haproxy.cfg
@@ -22,6 +22,7 @@ frontend fe_http
acl host-authelia-portal hdr(host) -i login.example.com:8080
acl api-path path_beg -i /api
acl protected-frontends hdr(host) -m reg -i ^(admin|home|public|secure|singlefactor)\.example\.com
+ acl is_headers path -i -m end /headers
http-request set-var(req.scheme) str(https) if { ssl_fc }
http-request set-var(req.scheme) str(http) if !{ ssl_fc }
@@ -37,9 +38,11 @@ frontend fe_http
# does not know how to handle it (see https://github.com/TimWolla/haproxy-auth-request/issues/12).
http-request lua.auth-request be_auth_request /api/verify if protected-frontends
+ http-request redirect location https://login.example.com:8080 if protected-frontends !{ var(txn.auth_response_successful) -m bool }
+
use_backend be_authelia if host-authelia-portal api-path
use_backend fe_authelia if host-authelia-portal !api-path
- use_backend be_authelia if protected-frontends !{ var(txn.auth_response_successful) -m bool }
+ use_backend be_httpbin if protected-frontends is_headers
use_backend be_protected if protected-frontends
use_backend be_mail if { hdr(host) -i mail.example.com:8080 }
@@ -63,3 +66,11 @@ backend be_mail
backend be_protected
server nginx-backend nginx-backend:80
+
+backend be_httpbin
+ acl remote_user_exist var(txn.auth_user) -m found
+ acl remote_groups_exist var(txn.auth_groups) -m found
+
+ http-request set-header Remote-User %[var(txn.auth_user)] if remote_user_exist
+ http-request set-header Remote-Groups %[var(txn.auth_groups)] if remote_groups_exist
+ server httpbin-backend httpbin:8000
\ No newline at end of file
diff --git a/internal/suites/example/compose/httpbin/docker-compose.yml b/internal/suites/example/compose/httpbin/docker-compose.yml
index 48b3e34b..1f64ca3b 100644
--- a/internal/suites/example/compose/httpbin/docker-compose.yml
+++ b/internal/suites/example/compose/httpbin/docker-compose.yml
@@ -4,3 +4,20 @@ services:
image: citizenstig/httpbin
networks:
- authelianet
+ labels:
+ # Traefik 1.x
+ - 'traefik.frontend.rule=Host:public.example.com;Path:/headers'
+ - 'traefik.frontend.priority=120'
+ - 'traefik.frontend.auth.forward.address=https://authelia-backend:9091/api/verify?rd=https://login.example.com:8080/'
+ - 'traefik.frontend.auth.forward.tls.insecureSkipVerify=true'
+ - 'traefik.frontend.auth.forward.trustForwardHeader=true'
+ - 'traefik.frontend.auth.forward.authResponseHeaders=Remote-User,Remote-Groups'
+ # Traefik 2.x
+ - 'traefik.http.routers.httpbin.rule=Host(`public.example.com`) && Path(`/headers`)'
+ - 'traefik.http.routers.httpbin.priority=150'
+ - 'traefik.http.routers.httpbin.tls=true'
+ - 'traefik.http.routers.httpbin.middlewares=authelia'
+ - 'traefik.http.middlewares.authelia.forwardauth.address=https://authelia-backend:9091/api/verify?rd=https://login.example.com:8080/'
+ - 'traefik.http.middlewares.authelia.forwardauth.tls.insecureSkipVerify=true'
+ - 'traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true'
+ - 'traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User, Remote-Groups'
diff --git a/internal/suites/example/compose/nginx/portal/nginx.conf b/internal/suites/example/compose/nginx/portal/nginx.conf
index 563d2452..c4d10720 100644
--- a/internal/suites/example/compose/nginx/portal/nginx.conf
+++ b/internal/suites/example/compose/nginx/portal/nginx.conf
@@ -167,10 +167,10 @@ http {
auth_request /auth_verify;
auth_request_set $user $upstream_http_remote_user;
- proxy_set_header Custom-Forwarded-User $user;
+ proxy_set_header Remote-User $user;
auth_request_set $groups $upstream_http_remote_groups;
- proxy_set_header Custom-Forwarded-Groups $groups;
+ proxy_set_header Remote-Groups $groups;
set $target_url $scheme://$http_host$request_uri;
error_page 401 =302 https://login.example.com:8080/?rd=$target_url;
diff --git a/internal/suites/scenario_custom_headers_test.go b/internal/suites/scenario_custom_headers_test.go
index 0981e27e..57a6306e 100644
--- a/internal/suites/scenario_custom_headers_test.go
+++ b/internal/suites/scenario_custom_headers_test.go
@@ -60,12 +60,17 @@ func (s *CustomHeadersScenario) TestShouldNotForwardCustomHeaderForUnauthenticat
body, err := s.WebDriver().FindElement(selenium.ByTagName, "body")
s.Assert().NoError(err)
- s.WaitElementTextContains(ctx, s.T(), body, "httpbin:8000")
+ s.WaitElementTextContains(ctx, s.T(), body, "\"Host\"")
+
+ b, err := body.Text()
+ s.Assert().NoError(err)
+ s.Assert().NotContains(b, "john")
+ s.Assert().NotContains(b, "admins")
}
type Headers struct {
- ForwardedGroups string `json:"Custom-Forwarded-Groups"`
- ForwardedUser string `json:"Custom-Forwarded-User"`
+ ForwardedGroups string `json:"Remote-Groups"`
+ ForwardedUser string `json:"Remote-User"`
}
type HeadersPayload struct {
diff --git a/internal/suites/suite_haproxy.go b/internal/suites/suite_haproxy.go
index 4fe94f01..94a4460a 100644
--- a/internal/suites/suite_haproxy.go
+++ b/internal/suites/suite_haproxy.go
@@ -16,6 +16,7 @@ func init() {
"internal/suites/example/compose/nginx/backend/docker-compose.yml",
"internal/suites/example/compose/haproxy/docker-compose.yml",
"internal/suites/example/compose/smtp/docker-compose.yml",
+ "internal/suites/example/compose/httpbin/docker-compose.yml",
})
setup := func(suitePath string) error {
diff --git a/internal/suites/suite_haproxy_test.go b/internal/suites/suite_haproxy_test.go
index 800dd639..cdc602f7 100644
--- a/internal/suites/suite_haproxy_test.go
+++ b/internal/suites/suite_haproxy_test.go
@@ -22,6 +22,10 @@ func (s *HAProxySuite) TestTwoFactorScenario() {
suite.Run(s.T(), NewTwoFactorScenario())
}
+func (s *HAProxySuite) TestCustomHeaders() {
+ suite.Run(s.T(), NewCustomHeadersScenario())
+}
+
func TestHAProxySuite(t *testing.T) {
suite.Run(t, NewHAProxySuite())
}
diff --git a/internal/suites/suite_traefik.go b/internal/suites/suite_traefik.go
index 99cb4606..a12e8846 100644
--- a/internal/suites/suite_traefik.go
+++ b/internal/suites/suite_traefik.go
@@ -16,6 +16,7 @@ func init() {
"internal/suites/example/compose/nginx/backend/docker-compose.yml",
"internal/suites/example/compose/traefik/docker-compose.yml",
"internal/suites/example/compose/smtp/docker-compose.yml",
+ "internal/suites/example/compose/httpbin/docker-compose.yml",
})
setup := func(suitePath string) error {
diff --git a/internal/suites/suite_traefik2.go b/internal/suites/suite_traefik2.go
index 85e5ce0d..596a6379 100644
--- a/internal/suites/suite_traefik2.go
+++ b/internal/suites/suite_traefik2.go
@@ -16,6 +16,7 @@ func init() {
"internal/suites/example/compose/nginx/backend/docker-compose.yml",
"internal/suites/example/compose/traefik2/docker-compose.yml",
"internal/suites/example/compose/smtp/docker-compose.yml",
+ "internal/suites/example/compose/httpbin/docker-compose.yml",
})
setup := func(suitePath string) error {
diff --git a/internal/suites/suite_traefik2_test.go b/internal/suites/suite_traefik2_test.go
index a27bd3f6..674f48a3 100644
--- a/internal/suites/suite_traefik2_test.go
+++ b/internal/suites/suite_traefik2_test.go
@@ -22,6 +22,10 @@ func (s *Traefik2Suite) TestTwoFactorScenario() {
suite.Run(s.T(), NewTwoFactorScenario())
}
+func (s *Traefik2Suite) TestCustomHeaders() {
+ suite.Run(s.T(), NewCustomHeadersScenario())
+}
+
func TestTraefik2Suite(t *testing.T) {
suite.Run(t, NewTraefik2Suite())
}
diff --git a/internal/suites/suite_traefik_test.go b/internal/suites/suite_traefik_test.go
index d23b713b..83c6625d 100644
--- a/internal/suites/suite_traefik_test.go
+++ b/internal/suites/suite_traefik_test.go
@@ -26,6 +26,10 @@ func (s *TraefikSuite) TestRedirectionURLScenario() {
suite.Run(s.T(), NewRedirectionURLScenario())
}
+func (s *TraefikSuite) TestCustomHeaders() {
+ suite.Run(s.T(), NewCustomHeadersScenario())
+}
+
func TestTraefikSuite(t *testing.T) {
suite.Run(t, NewTraefikSuite())
}