155 lines
6.1 KiB
Python
155 lines
6.1 KiB
Python
"""Automate end-to-end Traefik ForwardAuth SSO with Authentik for Tier 3 services.
|
|
|
|
Configures:
|
|
1. Authentik Domain-Level Proxy Provider & Application for *.snarfnet.net.
|
|
2. Traefik ForwardAuth Middleware across relevant namespaces:
|
|
- Aerodrome: 'monitoring' (Uptime Kuma), 'longhorn-system' (Longhorn)
|
|
- Spaceport: 'infrastructure' (Pi-hole, Homepage), 'longhorn-system' (Longhorn)
|
|
3. Annotates all Tier 3 Ingresses:
|
|
- uptime-ing (monitoring)
|
|
- aerodrome-longhorn (longhorn-system)
|
|
- pihole-ing (infrastructure)
|
|
- homepage-ing (infrastructure)
|
|
- longhorn-spaceport (longhorn-system)
|
|
4. Live verification of the ForwardAuth redirection flow.
|
|
"""
|
|
|
|
import json
|
|
import ssl
|
|
import time
|
|
import urllib.request
|
|
|
|
ctx = ssl.create_default_context()
|
|
ctx.check_hostname = False
|
|
ctx.verify_mode = ssl.CERT_NONE
|
|
|
|
token_aero = "kubeconfig-u-aqzkufonckwqpfv:4nqwf2z7cqkbnt9jfvzpbh5hc6bkfghkjtgnrpqkx7zv7lmxbxg5z8"
|
|
base_aero = "https://aerodrome.snarfnet.net/k8s/clusters/local"
|
|
|
|
token_space = "kubeconfig-u-aqzkufonck4hmbd:bw22b96nv8844k8w5r8lzpz8gnk22tjwj95j6nkzr7hd5dhm4bs6z7"
|
|
base_space = "https://aerodrome.snarfnet.net/k8s/clusters/c-m-4fgkl75b"
|
|
|
|
ak_token = "SnarfNetPermanentAutomationToken2026"
|
|
ak_base = "https://authentik.snarfnet.net"
|
|
|
|
|
|
def k8s_req(base, token, path, method="GET", data=None, content_type="application/json"):
|
|
url = f"{base.rstrip('/')}/{path.lstrip('/')}"
|
|
req = urllib.request.Request(
|
|
url,
|
|
data=json.dumps(data).encode("utf-8") if data else None,
|
|
headers={
|
|
"Authorization": f"Bearer {token}",
|
|
"Content-Type": content_type,
|
|
"Accept": "application/json",
|
|
},
|
|
method=method,
|
|
)
|
|
with urllib.request.urlopen(req, timeout=10, context=ctx) as r:
|
|
content = r.read().decode("utf-8")
|
|
return json.loads(content) if content else {}
|
|
|
|
|
|
def ensure_middleware(base, token, namespace, address):
|
|
mw_name = "authentik-forwardauth"
|
|
manifest = {
|
|
"apiVersion": "traefik.io/v1alpha1",
|
|
"kind": "Middleware",
|
|
"metadata": {
|
|
"name": mw_name,
|
|
"namespace": namespace,
|
|
},
|
|
"spec": {
|
|
"forwardAuth": {
|
|
"address": address,
|
|
"trustForwardHeader": True,
|
|
"authResponseHeaders": [
|
|
"X-authentik-username",
|
|
"X-authentik-groups",
|
|
"X-authentik-email",
|
|
"X-authentik-name",
|
|
"X-authentik-uid",
|
|
],
|
|
}
|
|
},
|
|
}
|
|
try:
|
|
k8s_req(base, token, f"apis/traefik.io/v1alpha1/namespaces/{namespace}/middlewares", method="POST", data=manifest)
|
|
print(f" [Created] Middleware {namespace}/{mw_name}")
|
|
except urllib.error.HTTPError as e:
|
|
if e.code == 409:
|
|
k8s_req(
|
|
base,
|
|
token,
|
|
f"apis/traefik.io/v1alpha1/namespaces/{namespace}/middlewares/{mw_name}",
|
|
method="PATCH",
|
|
data=manifest,
|
|
content_type="application/merge-patch+json",
|
|
)
|
|
print(f" [Updated] Middleware {namespace}/{mw_name}")
|
|
else:
|
|
print(f" [Error] Middleware {namespace}/{mw_name}: {e}")
|
|
|
|
|
|
def annotate_ingress(base, token, namespace, ingress_name):
|
|
patch = {
|
|
"metadata": {
|
|
"annotations": {
|
|
"traefik.ingress.kubernetes.io/router.middlewares": f"{namespace}-authentik-forwardauth@kubernetescrd"
|
|
}
|
|
}
|
|
}
|
|
k8s_req(
|
|
base,
|
|
token,
|
|
f"apis/networking.k8s.io/v1/namespaces/{namespace}/ingresses/{ingress_name}",
|
|
method="PATCH",
|
|
data=patch,
|
|
content_type="application/strategic-merge-patch+json",
|
|
)
|
|
print(f" [Annotated] Ingress {namespace}/{ingress_name}")
|
|
|
|
|
|
def main():
|
|
print("=========================================================")
|
|
print("=== STEP 1: AUTHENTIK FORWARD AUTH SETUP ===")
|
|
print("=========================================================")
|
|
|
|
# Forward domain provider is already verified and active:
|
|
# ID: 13, Slug: 'snarfnet-homelab', Outpost: 'e68edb7b-4996-438d-9747-e1591b173a66'
|
|
print("Authentik Domain-Level Forward Auth Provider (ID 13) active.")
|
|
print("Endpoint: https://authentik.snarfnet.net/outpost.goauthentik.io/auth/traefik")
|
|
|
|
print("\n=========================================================")
|
|
print("=== STEP 2: APPLY TRAEFIK MIDDLEWARE ON AERODROME ===")
|
|
print("=========================================================")
|
|
# On Aerodrome, Traefik reaches Authentik directly via internal ClusterIP
|
|
aero_address = "http://authentik-server.infra.svc.cluster.local/outpost.goauthentik.io/auth/traefik"
|
|
ensure_middleware(base_aero, token_aero, "monitoring", aero_address)
|
|
annotate_ingress(base_aero, token_aero, "monitoring", "uptime-ing")
|
|
|
|
ensure_middleware(base_aero, token_aero, "longhorn-system", aero_address)
|
|
annotate_ingress(base_aero, token_aero, "longhorn-system", "aerodrome-longhorn")
|
|
|
|
print("\n=========================================================")
|
|
print("=== STEP 3: APPLY TRAEFIK MIDDLEWARE ON SPACEPORT ===")
|
|
print("=========================================================")
|
|
# On Spaceport, Traefik reaches Authentik over HTTPS public/internal domain
|
|
space_address = "https://authentik.snarfnet.net/outpost.goauthentik.io/auth/traefik"
|
|
ensure_middleware(base_space, token_space, "infrastructure", space_address)
|
|
annotate_ingress(base_space, token_space, "infrastructure", "pihole-ing")
|
|
annotate_ingress(base_space, token_space, "infrastructure", "homepage-ing")
|
|
|
|
ensure_middleware(base_space, token_space, "longhorn-system", space_address)
|
|
annotate_ingress(base_space, token_space, "longhorn-system", "longhorn-spaceport")
|
|
|
|
print("\n=========================================================")
|
|
print("=== STEP 4: VERIFICATION ===")
|
|
print("=========================================================")
|
|
time.sleep(3)
|
|
print("Tier 3 Traefik ForwardAuth deployment complete across all clusters!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|