caddy: www holds the homepage; naked domain redirects at the TLS edge
The proxy provider matches external_host exactly, so Host=www matched zero providers and Authentik answered its error page while the naked domain served the homepage. Flip the topology: www.franksdank.cloud is the one guarded vhost carrying the homepage through Anubis -> Authentik -> :8017; franksdank.cloud (plus legacy names and the bare-IP catch-all) 308-redirects onto www before any challenge. Tests flipped to assert the new contract.
This commit is contained in:
+16
-7
@@ -55,24 +55,33 @@
|
||||
|
||||
# --- Bare-IP access (health checks, convenience) ---
|
||||
:80 {
|
||||
redir https://franksdank.cloud/ 302
|
||||
redir https://www.franksdank.cloud/ 308
|
||||
}
|
||||
|
||||
# --- Homepage (Anubis → Authentik SSO) ---
|
||||
franksdank.cloud www.franksdank.cloud {
|
||||
# www holds the homepage; the naked domain redirects onto it at the TLS edge.
|
||||
# A redirect needs no challenge, so unauthenticated naked traffic never enters
|
||||
# the auth chain. The proxy provider's external_host must match the host that
|
||||
# actually carries traffic (https://www.franksdank.cloud) or Authentik matches
|
||||
# zero providers and answers its error page instead.
|
||||
franksdank.cloud {
|
||||
redir https://www.franksdank.cloud/ 308
|
||||
}
|
||||
|
||||
www.franksdank.cloud {
|
||||
reverse_proxy anubis:8923 {
|
||||
header_up X-Real-Ip {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Legacy name: the old homepage name redirects to the naked domain ---
|
||||
# --- Legacy name: the old homepage name redirects straight onto www ---
|
||||
dankclaw.franksdank.cloud {
|
||||
redir https://franksdank.cloud/ 302
|
||||
redir https://www.franksdank.cloud/ 308
|
||||
}
|
||||
|
||||
# --- Legacy name: the media host has no Caddy of its own anymore ---
|
||||
dankserver.franksdank.cloud {
|
||||
redir https://franksdank.cloud/ 302
|
||||
redir https://www.franksdank.cloud/ 308
|
||||
}
|
||||
|
||||
# --- Authentik (SSO / OIDC provider) ---
|
||||
@@ -234,7 +243,7 @@ qbittorrent.franksdank.cloud {
|
||||
# in place, authenticated traffic is forwarded back to http://:8017 and
|
||||
# routed straight to the app. Exempt vhosts never enter this block.
|
||||
http://:8016 {
|
||||
@home host franksdank.cloud www.franksdank.cloud
|
||||
@home host www.franksdank.cloud
|
||||
handle @home {
|
||||
reverse_proxy authentik-server:9000 {
|
||||
header_up X-authentik-meta-outpost 7e39655b-58d3-4fe6-a80a-513f31051217
|
||||
@@ -295,7 +304,7 @@ http://:8016 {
|
||||
# straight to its app. This is the end of the chain — nothing dials back
|
||||
# into Authentik from here, so the chain cannot loop.
|
||||
http://:8017 {
|
||||
@home host franksdank.cloud www.franksdank.cloud
|
||||
@home host www.franksdank.cloud
|
||||
handle @home {
|
||||
root * /srv/homepage
|
||||
handle /admin* {
|
||||
|
||||
+19
-9
@@ -111,26 +111,36 @@ UNIT_DIR="$REPO_ROOT/dankclaw/authentik/quadlet"
|
||||
grep -qE '^http://:8017 \{' "$CADDYFILE"
|
||||
}
|
||||
|
||||
# --- Homepage: moved to naked domain + www ---
|
||||
# --- Homepage: www holds the homepage; naked redirects onto it ---
|
||||
|
||||
@test "the homepage vhost is the naked domain + www" {
|
||||
grep -qE '^franksdank\.cloud[[:space:]]+www\.franksdank\.cloud[[:space:]]*\{' "$CADDYFILE"
|
||||
@test "the homepage vhost is www, guarded by Anubis" {
|
||||
local block
|
||||
block=$(awk '/^www\.franksdank\.cloud[[:space:]]*\{/,/^}/' "$CADDYFILE")
|
||||
echo "$block" | grep -q 'reverse_proxy anubis:8923'
|
||||
}
|
||||
|
||||
@test "dankclaw.franksdank.cloud redirects to the naked domain" {
|
||||
@test "the naked domain redirects to www at the TLS edge" {
|
||||
local block
|
||||
block=$(awk '/^franksdank\.cloud[[:space:]]*\{/,/^}/' "$CADDYFILE")
|
||||
echo "$block" | grep -q 'redir https://www.franksdank.cloud/ 308'
|
||||
}
|
||||
|
||||
@test "dankclaw.franksdank.cloud redirects straight onto www" {
|
||||
local block
|
||||
block=$(awk '/^dankclaw\.franksdank\.cloud/,/^}/' "$CADDYFILE")
|
||||
echo "$block" | grep -q 'redir https://franksdank.cloud/'
|
||||
echo "$block" | grep -q 'redir https://www.franksdank.cloud/ 308'
|
||||
}
|
||||
|
||||
@test "dankserver.franksdank.cloud redirects to the naked domain" {
|
||||
@test "dankserver.franksdank.cloud redirects straight onto www" {
|
||||
local block
|
||||
block=$(awk '/^dankserver\.franksdank\.cloud/,/^}/' "$CADDYFILE")
|
||||
echo "$block" | grep -q 'redir https://franksdank.cloud/'
|
||||
echo "$block" | grep -q 'redir https://www.franksdank.cloud/ 308'
|
||||
}
|
||||
|
||||
@test "the bare-IP catch-all redirects to the naked domain" {
|
||||
grep -q 'redir https://franksdank.cloud/' "$CADDYFILE"
|
||||
@test "the bare-IP catch-all redirects onto www" {
|
||||
local block
|
||||
block=$(awk '/^:80 \{/,/^}/' "$CADDYFILE")
|
||||
echo "$block" | grep -q 'redir https://www.franksdank.cloud/ 308'
|
||||
}
|
||||
|
||||
# --- .env.example: authentik placeholders ---
|
||||
|
||||
@@ -139,8 +139,10 @@ CADDYFILE="$REPO_ROOT/dankclaw/Caddyfile"
|
||||
grep -q '{env.PORKBUN_API_SECRET_KEY}' "$CADDYFILE"
|
||||
}
|
||||
|
||||
@test "the bare-IP catch-all redirects to the HTTPS homepage" {
|
||||
grep -q 'redir https://franksdank.cloud/' "$CADDYFILE"
|
||||
@test "the bare-IP catch-all redirects onto the HTTPS homepage at www" {
|
||||
local block
|
||||
block=$(awk '/^:80 \{/,/^}/' "$CADDYFILE")
|
||||
echo "$block" | grep -q 'redir https://www.franksdank.cloud/ 308'
|
||||
}
|
||||
|
||||
@test "the caddy quadlet uses the locally built porkbun image without pulling" {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env bats
|
||||
# Seam: the homepage site block in dankclaw/Caddyfile. Since the Anubis
|
||||
# rollout the naked domain + www are guarded vhosts: the public listener
|
||||
# proxies everything through Anubis (bot challenge), and the Authentik
|
||||
# forward_auth for SSO runs on Caddy's internal listener (http://:8016)
|
||||
# after Anubis has verified the client. caddy is not on the dev box, so the
|
||||
# tests assert the structure statically.
|
||||
# rollout, www holds the homepage as a guarded vhost (the naked domain
|
||||
# redirects onto it at the TLS edge): the public listener proxies www
|
||||
# through Anubis (bot challenge), and the Authentik SSO flow runs on
|
||||
# Caddy's internal listener (http://:8016) after Anubis has verified the
|
||||
# client. caddy is not on the dev box, so the tests assert the structure
|
||||
# statically.
|
||||
|
||||
load helpers
|
||||
|
||||
@@ -33,11 +34,11 @@ setup() {
|
||||
}
|
||||
|
||||
@test "the homepage vhost chains Anubis (public) before Authentik SSO (internal listener)" {
|
||||
# Public listener: the naked domain proxies everything through Anubis.
|
||||
# Public listener: www proxies everything through Anubis; the naked
|
||||
# domain never enters the chain (it redirects at the TLS edge first).
|
||||
local block
|
||||
block=$(awk '/^franksdank\.cloud[[:space:]]/,/^}/' "$CADDY")
|
||||
block=$(awk '/^www\.franksdank\.cloud[[:space:]]*\{/,/^}/' "$CADDY")
|
||||
echo "$block" | grep -q 'reverse_proxy anubis:8923'
|
||||
# The SSO check itself runs on the internal listener that Anubis
|
||||
# forwards accepted traffic to; Host survives the hop, so per-vhost
|
||||
# routing resumes there — guarded vhosts proxy the whole request
|
||||
# through Authentik, which forwards authenticated traffic back on the
|
||||
|
||||
Reference in New Issue
Block a user