watchstate: expose externally at watchstate.unkin.net
ci/woodpecker/pr/vector-test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/kubeconform Pipeline was successful

Add a public front door for the WatchState admin UI on watchstate.unkin.net
via the external (DMZ) Traefik, alongside the existing internal
watchstate.k8s.syd1.au.unkin.net gateway. oauth2-proxy fronts both hostnames.

- DNSRecord watchstate-dns-internal (bind-internal/authoritative, unkin.net
  zone) -> A 198.18.199.0, the traefik-external VIP (arrstack precedent).
- watchstate-external Gateway (traefik-external) + HTTPRoutes: http->https
  redirect and https -> watchstate-oauth2:80. TLS terminated with the
  Let's Encrypt *.unkin.net wildcard (wildcard-unkin-net-tls), so no
  cert-manager/external-dns annotations.
- oauth2-proxy: relative redirect-url (/oauth2/callback) so reverse-proxy mode
  derives scheme+host per request, making the callback work on BOTH hosts;
  cookie + whitelist domains cover both hostnames.
- Drop the no-op sync-wave: "0" annotation on the vaultauth default VaultAuth.

Dependencies:
- wildcard-unkin-net-tls reflection into the watchstate namespace (reflector
  allow-list, argocd-apps PR #418).
- Both callback URIs registered on the Authentik watchstate provider
  (terraform-authentik, separate PR).
This commit is contained in:
2026-08-25 21:46:35 +10:00
parent 48632eb9f9
commit 931bfcf923
6 changed files with 118 additions and 5 deletions
@@ -132,3 +132,18 @@ spec:
# traefik-internal gateway VIP; the logviewer Gateway serves
# logviewer.unkin.net there.
- 198.18.200.4
---
apiVersion: bind.unkin.net/v1alpha1
kind: DNSRecord
metadata:
name: watchstate-dns-internal
namespace: bind-internal
spec:
zoneRef: unkin-net
name: watchstate
type: A
ttl: 600
values:
# traefik-EXTERNAL (DMZ) gateway VIP; the watchstate-external Gateway serves
# the watchstate.unkin.net front door (oauth2-proxy) there.
- 198.18.199.0
@@ -0,0 +1,40 @@
---
# External (DMZ) front for the WatchState admin UI, served on watchstate.unkin.net
# via the external Traefik (LB VIP 198.18.199.0). TLS is terminated with the real
# Let's Encrypt *.unkin.net wildcard (Certificate wildcard-unkin-net in the
# cert-manager namespace, reflected into this namespace as wildcard-unkin-net-tls
# by the emberstack reflector), so there is no cert-manager annotation here. The
# apex watchstate.unkin.net A record lives in the bind-operator unkin.net zone
# (bind-internal/authoritative), NOT external-dns, so no external-dns annotation
# either. The internal watchstate Gateway (watchstate.k8s.syd1.au.unkin.net) is
# untouched; oauth2-proxy fronts both hostnames.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
labels:
traefik.io/instance: external
name: watchstate-external
namespace: watchstate
spec:
gatewayClassName: traefik-external
listeners:
- name: http
port: 80
protocol: HTTP
hostname: watchstate.unkin.net
allowedRoutes:
namespaces:
from: Same
- name: https
port: 443
protocol: HTTPS
hostname: watchstate.unkin.net
allowedRoutes:
namespaces:
from: Same
tls:
mode: Terminate
certificateRefs:
- group: ""
kind: Secret
name: wildcard-unkin-net-tls
@@ -0,0 +1,49 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: watchstate-external-http-redirect
namespace: watchstate
spec:
hostnames:
- watchstate.unkin.net
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: watchstate-external
sectionName: http
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
matches:
- path:
type: PathPrefix
value: /
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: watchstate-external
namespace: watchstate
spec:
hostnames:
- watchstate.unkin.net
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: watchstate-external
sectionName: https
rules:
- backendRefs:
- group: ""
kind: Service
name: watchstate-oauth2
port: 80
weight: 1
matches:
- path:
type: PathPrefix
value: /
+2
View File
@@ -13,3 +13,5 @@ resources:
- service.yaml
- gateway.yaml
- httproute.yaml
- gateway-external.yaml
- httproute-external.yaml
@@ -13,7 +13,12 @@ data:
OAUTH2_PROXY_HTTP_ADDRESS: "0.0.0.0:4180"
OAUTH2_PROXY_PROVIDER: "oidc"
OAUTH2_PROXY_OIDC_ISSUER_URL: "https://identity.k8s.syd1.au.unkin.net/application/o/watchstate/"
OAUTH2_PROXY_REDIRECT_URL: "https://watchstate.k8s.syd1.au.unkin.net/oauth2/callback"
# Relative (host-less) redirect URL: with reverse-proxy mode on, oauth2-proxy
# derives scheme+host per request from X-Forwarded-Proto/Host, so the same
# deployment serves BOTH the external watchstate.unkin.net and internal
# watchstate.k8s.syd1.au.unkin.net callbacks. Both absolute callback URIs are
# registered on the Authentik provider (terraform-authentik, separate PR).
OAUTH2_PROXY_REDIRECT_URL: "/oauth2/callback"
OAUTH2_PROXY_UPSTREAMS: "http://watchstate.watchstate.svc.cluster.local:8080/"
OAUTH2_PROXY_SCOPE: "openid email profile ak_groups"
OAUTH2_PROXY_OIDC_GROUPS_CLAIM: "ak_groups"
@@ -23,8 +28,12 @@ data:
# enforced Authentik-side, so accepting the unverified email is safe.
OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL: "true"
OAUTH2_PROXY_COOKIE_SECURE: "true"
OAUTH2_PROXY_COOKIE_DOMAINS: "watchstate.k8s.syd1.au.unkin.net"
OAUTH2_PROXY_WHITELIST_DOMAINS: "watchstate.k8s.syd1.au.unkin.net"
# One cookie domain per host (a single parent-domain cookie can't span
# unkin.net and k8s.syd1.au.unkin.net cleanly); oauth2-proxy picks the domain
# matching the request host. Whitelist both so post-auth `rd` redirects to
# either front door are honoured.
OAUTH2_PROXY_COOKIE_DOMAINS: "watchstate.unkin.net,watchstate.k8s.syd1.au.unkin.net"
OAUTH2_PROXY_WHITELIST_DOMAINS: "watchstate.unkin.net,watchstate.k8s.syd1.au.unkin.net"
OAUTH2_PROXY_REVERSE_PROXY: "true"
OAUTH2_PROXY_PROVIDER_CA_FILES: "/etc/ssl/combined/ca-certificates.crt"
OAUTH2_PROXY_CODE_CHALLENGE_METHOD: "S256"
-2
View File
@@ -4,8 +4,6 @@ kind: VaultAuth
metadata:
name: default
namespace: watchstate
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
allowedNamespaces:
- watchstate