Stop Copying Empty Forward Auth Header

1. Output of caddy version:

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

2. How I run Caddy:

a. System environment:

Rocky 8 and systemd

b. Command:

sudo systemctl start caddy

c. Service/unit/compose file:

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

d. My complete Caddy config:

*.icantprovidethis.com, icantprovidethis.com {
	tls {
		dns route53 {
			access_key_id "ABC"
			secret_access_key "XYZ"
		}
		resolvers 8.8.8.8
	}
	log {
		format console
		output file /var/log/caddy/access.log {
			roll_size 10mb
			roll_keep 5
			roll_keep_for 720h
		}
	}

	@nexus host repo.icantprovidethis.com
	handle @nexus {
		reverse_proxy /outpost.goauthentik.io/* http://10.0.3.7:9000
		forward_auth http://10.0.3.7:9000 {
			uri /outpost.goauthentik.io/auth/caddy
			copy_headers {
				X-Authentik-Username>Remote-User
				}
		}

		reverse_proxy http://10.0.3.200:8081 {
			# This is awful but if the header wasn't set by forward_auth above then this is the default value
			# so we can use it to automatically replace everything
			#header_up X-Authentik-Username  "^{http\.reverse_proxy\.header\.X-Authentik-Username}$" "nexus_public_user"
			#header_up -X-Authentik-Username
		}
	}
}

3. The problem I’m having:

I’m trying to use Caddy forward auth and Authentik to authenticate against Sonatype Nexus 3 via header auth and still support anonymous auth as well. Currently if Authentik doesn’t set the X-Authentik-Username then Caddy is setting Remoute-User to {http.reverse_proxy.header.X-Authentik-Username} which I verified by looking in the request logs over on Nexus. I need to be able to not set the Remote-User header if X-Authentik-Username isn’t set by forward auth.

4. Error messages and/or full log output:

# This is the log from Caddy

2022/10/19 23:35:19.967	ERROR	http.log.access.log0	handled request	{"request": {"remote_ip": "10.0.3.71", "remote_port": "42570", "proto": "HTTP/2.0", "method": "GET", "host": "repo.icantprovidethis.com", "uri": "/repository/gitlab-focal-proxy/gitlab/gitlab-ce/ubuntu/pool/focal/main/g/gitlab-ce/gitlab-ce_15.4.2-ce.0_amd64.deb", "headers": {"User-Agent": ["curl/7.68.0"], "Accept": ["*/*"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4867, "proto": "h2", "server_name": "repo.icantprovidethis.com"}}, "user_id": "", "duration": 0.015877982, "size": 0, "status": 401, "resp_headers": {"X-Content-Type-Options": ["nosniff"], "Www-Authenticate": ["BASIC realm=\"Sonatype Nexus Repository Manager\""], "Content-Length": ["0"], "Date": ["Wed, 19 Oct 2022 23:35:19 GMT"], "Server": ["Caddy", "Nexus/3.40.1-01 (OSS)"], "Alt-Svc": ["h3=\":443\"; ma=2592000"]}}

#This is the log from Nexus showing the header that arrives from Caddy

10.0.3.71 - - [19/Oct/2022:23:02:23 +0000] "GET /repository/gitlab-focal-proxy/gitlab/gitlab-ce/ubuntu/pool/focal/main/g/gitlab-ce/gitlab-ce_15.4.2-ce.0_amd64.deb HTTP/1.1" 401 - 0 120 "curl/7.68.0" "{http.reverse_proxy.header.X-Authentik-Username}" [qtp2145215122-404]
10.0.3.71 - - [19/Oct/2022:23:02:25 +0000] "GET /repository/gitlab-focal-proxy/gitlab/gitlab-ce/ubuntu/pool/focal/main/g/gitlab-ce/gitlab-ce_15.4.2-ce.0_amd64.deb HTTP/1.1" 401 - 0 10 "curl/7.68.0" "{http.reverse_proxy.header.X-Authentik-Username}" [qtp2145215122-97]

5. What I already tried:

I have searched these forum, stackoverflow, and the caddy documentation for hours and have not found any suggestions on how to not copy a header if it’s not set by forward auth

6. Links to relevant resources:

This talks about the issue but I’m pretty sure this is caddy v1 and not for forward auth

I’m not sure I understand why Authentik wouldn’t be sending the header. How can you be “authenticated anonymously”? That’s an oxymoron. Authentication implies you’ve verified an identity.

That said, you can work around it by removing copy_headers and using a config like this:

	@hasUsername `{http.reverse_proxy.header.X-Authentik-Username} != null`
	request_header @hasUsername Remote-User {http.reverse_proxy.header.X-Authentik-Username}

This uses a CEL expression matcher to check if the proxy header is non-null, and only set the header on the request in that case.

Authentik allows you to exclude specific paths from requiring authentication and in my case I excluded /repository/.* so that if you aren’t already authenticated it lets Nexus handle it instead. For some reason Caddy doesn’t like the expression above so I probably have a typo or I’m missing something.

*.icantprovidethis.com, icantprovidethis.com {
	tls {
		dns route53 {
			access_key_id "XYZ"
			secret_access_key "ABC"
		}
		resolvers 8.8.8.8
	}
	log {
		format console
		output file /var/log/caddy/access.log {
			roll_size 10mb
			roll_keep 5
			roll_keep_for 720h
		}
	}

	@nexus host repo.icantprovidethis.com
	handle @nexus {
		reverse_proxy /outpost.goauthentik.io/* http://10.0.3.7:9000
		forward_auth http://10.0.3.7:9000 {
			uri /outpost.goauthentik.io/auth/caddy
			@hasUsername `{http.reverse_proxy.header.X-Authentik-Username} != null`
			request_header @hasUsername Remote-User {http.reverse_proxy.header.X-Authentik-Username}
		}
		reverse_proxy http://10.0.3.200:8081
	}
}
Error: adapting config using caddyfile: parsing caddyfile tokens for 'handle': /etc/caddy/Caddyfile:27 - Error during parsing: parsing caddyfile tokens for 'forward_auth': /etc/caddy/Caddyfile:23 - Error during parsing: unrecognized response matcher {http.reverse_proxy.header.X-Authentik-Username} != null

I had the expression in the wrong location, it needed to go outside of forward auth like this. The configuration below works.

*.icantprovidethis.com, icantprovidethis.com {
	tls {
		dns route53 {
			access_key_id "XYZ"
			secret_access_key "ABC"
		}
		resolvers 8.8.8.8
	}
	log {
		format console
		output file /var/log/caddy/access.log {
			roll_size 10mb
			roll_keep 5
			roll_keep_for 720h
		}
	}

	@nexus host repo.icantprovidethis.com
	handle @nexus {
		reverse_proxy /outpost.goauthentik.io/* http://10.0.3.7:9000
		forward_auth http://10.0.3.7:9000 {
			uri /outpost.goauthentik.io/auth/caddy
		}
		@hasUsername `{http.reverse_proxy.header.X-Authentik-Username} != null`
		request_header @hasUsername Remote-User {http.reverse_proxy.header.X-Authentik-Username}
		reverse_proxy http://10.0.3.200:8081
	}
}
2 Likes

As a final response @francislavoie I appreciate the help, now that I understand how these expressions can be used I was able to expand this out a little more. The idea here is that if you’ve logged in via SSO then set the appropriate headers and if you did not login via SSO don’t set the headers and don’t allow external access. This allows Nexus’s anonymous access to work for things like APT and YUM repos while still doing SSO. Here is a snippet of my final configuration that works great.

@nexus host repo.example.com
handle @nexus {
        reverse_proxy /outpost.goauthentik.io/* http://10.0.3.7:9000
        forward_auth http://10.0.3.7:9000 {
                uri /outpost.goauthentik.io/auth/caddy
        }

        # Only allow internal users to connect to Nexus without SSO
        @denied `{http.reverse_proxy.header.X-Authentik-Username} == null && !remote_ip('10.0.0.0/8')`
        respond @denied 401

        # Only set the Remote-User header if forward auth set's X-Authentik-Username
        @hasUsername `{http.reverse_proxy.header.X-Authentik-Username} != null`
        request_header @hasUsername Remote-User {http.reverse_proxy.header.X-Authentik-Username}

        reverse_proxy http://10.0.3.200:8081
}
2 Likes

FYI you can use the error directive instead of respond to trigger an error in Caddy, that you can later catch and handle with handle_errors, which gives you a way to render the error via file_server or something (HTML error page, up to you) which might give a better user experience.

This topic was automatically closed after 30 days. New replies are no longer allowed.