From c3ffc611f4f12d9d75b5bf96e27b9a7612b688a9 Mon Sep 17 00:00:00 2001 From: Story Crater Bot <19826264+Riotpiaole@users.noreply.github.com> Date: Thu, 13 Aug 2026 07:26:59 -0700 Subject: [PATCH] =?UTF-8?q?fix(homarr):=20add=20AUTH=5FOIDC=5FURI=20+=20em?= =?UTF-8?q?ail=20account=20linking=20=E2=80=94=20homarr=20hides=20the=20Au?= =?UTF-8?q?thentik=20sign-in=20button=20unless=20AUTH=5FOIDC=5FURI=20(auth?= =?UTF-8?q?orize=20endpoint)=20is=20set=20alongside=20AUTH=5FOIDC=5FISSUER?= =?UTF-8?q?=20(per=20authentik/homarr=20SSO=20docs);=20was=20the=20missing?= =?UTF-8?q?=20var?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- k8s/apps/api/ingress.yaml | 43 +++++++++++++ k8s/apps/api/kong-values.yaml | 97 +++++++++++++++++++++++++++++ k8s/apps/api/kustomization.yaml | 11 ++++ k8s/apps/homarr/homarr-values.yaml | 6 ++ k8s/argocd/apps/55-api-gateway.yaml | 58 +++++++++++++++++ k8s/bootstrap/ingress/ingress.yaml | 4 ++ terraform/files/coredns/Corefile | 5 ++ 7 files changed, 224 insertions(+) create mode 100644 k8s/apps/api/ingress.yaml create mode 100644 k8s/apps/api/kong-values.yaml create mode 100644 k8s/apps/api/kustomization.yaml create mode 100644 k8s/argocd/apps/55-api-gateway.yaml diff --git a/k8s/apps/api/ingress.yaml b/k8s/apps/api/ingress.yaml new file mode 100644 index 0000000..2897bf1 --- /dev/null +++ b/k8s/apps/api/ingress.yaml @@ -0,0 +1,43 @@ +# Edge route for the API gateway. +# +# Lives here rather than in the central k8s/bootstrap/ingress/ingress.yaml +# because that Application syncs in wave 1, before namespace `api` exists. +# +# nginx terminates TLS with the wildcard *.riotpiao.com cert (served as its +# default-ssl-certificate, so no per-rule `tls:` block is needed) and forwards +# plain HTTP to kong-proxy. Kong then does the real routing, from Ingresses +# carrying `ingressClassName: kong`. +# +# Catch-all `/` on purpose: everything under this host belongs to Kong. Listing +# per-API paths here would duplicate Kong's routing table inside nginx, and the +# two copies would drift. +# +# In-cluster callers should prefer http://kong-proxy.api.svc.cluster.local +# directly. Resolving api.riotpiao.com sends them out to nginx and back in, +# which is a pointless hairpin unless they need TLS or the public hostname. +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: api + namespace: api + annotations: + # An API gateway carries streaming responses (SSE, gRPC-web, LLM token + # streams). nginx's 60s default read timeout and its response buffering + # would truncate or stall those. + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-buffering: "off" + nginx.ingress.kubernetes.io/proxy-body-size: "0" +spec: + ingressClassName: nginx + rules: + - host: api.riotpiao.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: kong-proxy + port: + number: 80 diff --git a/k8s/apps/api/kong-values.yaml b/k8s/apps/api/kong-values.yaml new file mode 100644 index 0000000..48ac0e9 --- /dev/null +++ b/k8s/apps/api/kong-values.yaml @@ -0,0 +1,97 @@ +# Kong Gateway — cluster-internal API gateway (namespace `api`). +# +# Chart: kong/kong 3.4.1 (appVersion 3.9). Only overrides are listed; every key +# here was checked against `helm show values kong/kong --version 3.4.1`, because +# Helm silently ignores unknown keys — a typo is a no-op, not an error. +# +# ── Topology ──────────────────────────────────────────────────────────────── +# external: client -> nginx (TLS, wildcard *.riotpiao.com) -> kong-proxy:80 +# internal: pod -> kong-proxy.api.svc.cluster.local:80 +# +# nginx stays the single edge and the only LoadBalancer (192.168.1.160). Kong is +# the policy/routing layer behind it, so it needs no LB IP and no TLS of its own +# — hence ClusterIP and proxy.tls disabled. Giving Kong its own IP from +# homelab-pool would mean duplicating cert-manager wiring and diverging from the +# CoreDNS convention that sends every *.riotpiao.com host to nginx. +# +# ── Routing model ─────────────────────────────────────────────────────────── +# Consumers publish an Ingress with `ingressClassName: kong`; the controller +# turns it into a Kong route. `nginx` remains the default IngressClass, so this +# is strictly opt-in and no existing Ingress changes behaviour. + +# Without this the release name is prefixed onto everything (`kong-kong-proxy`). +# Pinning it keeps the Service name stable and independent of the release name, +# which matters because the nginx Ingress in k8s/bootstrap/ingress/ingress.yaml +# references it by name. +fullnameOverride: kong + +# Two replicas so a node drain or rollout doesn't take the gateway down. Kong is +# stateless in DB-less mode, so replicas are pure redundancy. +replicaCount: 2 + +env: + # DB-less. Config comes from Kubernetes objects via the ingress controller, so + # git stays the source of truth. A Postgres-backed Kong would put live routing + # config in a database mutated through the Admin API — state outside git, plus + # migration Jobs on every upgrade. + database: "off" + +ingressController: + enabled: true + ingressClass: kong + # The chart's ingress-class template is gated on + # `.Capabilities.APIVersions.Has "networking.k8s.io/v1/IngressClass"`, so a + # bare `helm template` renders nothing. ArgoCD passes --api-versions from the + # live cluster, so it does render there — verify `kubectl get ingressclass + # kong` after the first sync rather than assuming it. + createIngressClass: true + # Deliberately empty: setting is-default-class here would hijack every Ingress + # in the cluster that omits ingressClassName. nginx keeps that role. + ingressClassAnnotations: {} + +proxy: + enabled: true + # Chart default is LoadBalancer, which would claim an IP from homelab-pool. + type: ClusterIP + http: + enabled: true + servicePort: 80 + containerPort: 8000 + # nginx already terminated TLS; a second handshake to the same cluster buys + # nothing and would need Kong to hold its own certificate. + tls: + enabled: false + +# No Service for the Admin API. The controller reaches it over localhost inside +# the pod, so exposing it would only create an unauthenticated write path to the +# gateway's entire configuration. +admin: + enabled: false + +# Kong Manager UI — chart default is `enabled: true` with type NodePort, which +# would open a port on every node. Not wanted. +manager: + enabled: false + +resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: "2" + memory: 1Gi + +podDisruptionBudget: + enabled: true + minAvailable: 1 + +# Spread the two replicas across nodes; `ScheduleAnyway` so a single-node +# situation degrades to co-location instead of leaving a pod Pending. +topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app.kubernetes.io/name: kong + app.kubernetes.io/instance: kong diff --git a/k8s/apps/api/kustomization.yaml b/k8s/apps/api/kustomization.yaml new file mode 100644 index 0000000..95b8f1b --- /dev/null +++ b/k8s/apps/api/kustomization.yaml @@ -0,0 +1,11 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +# Explicit allowlist so kong-values.yaml in this directory is NOT treated as a +# manifest — it is Helm input consumed by the chart source of the `kong` +# Application, not a Kubernetes object. Anything new added here must be listed +# or it is silently dropped with no error and no drift shown. +resources: + - ingress.yaml +# No top-level `namespace:` transformer on purpose: ingress.yaml sets its own +# namespace, and the transformer rewrites metadata.namespace on every resource +# it builds, which is a trap for anything cross-namespace added later. diff --git a/k8s/apps/homarr/homarr-values.yaml b/k8s/apps/homarr/homarr-values.yaml index e3cdeeb..5a260ca 100644 --- a/k8s/apps/homarr/homarr-values.yaml +++ b/k8s/apps/homarr/homarr-values.yaml @@ -30,10 +30,16 @@ replicaCount: 1 env: AUTH_PROVIDERS: "oidc,credentials" AUTH_OIDC_ISSUER: "https://authentik.riotpiao.com/application/o/homarr/" + # AUTH_OIDC_URI (authorize endpoint) is REQUIRED in addition to ISSUER — homarr + # hides the "Sign in with Authentik" button entirely when it's absent (per the + # authentik Homarr integration + homarr SSO docs). This was the missing var. + AUTH_OIDC_URI: "https://authentik.riotpiao.com/application/o/authorize/" AUTH_OIDC_CLIENT_NAME: "Authentik" AUTH_OIDC_GROUPS_ATTRIBUTE: "groups" AUTH_OIDC_SCOPE_OVERWRITE: "openid email profile groups" AUTH_OIDC_AUTO_LOGIN: "false" + # Link the OIDC identity to an existing homarr account with the same email. + OAUTH_ALLOW_DANGEROUS_EMAIL_ACCOUNT_LINKING: "true" BASE_URL: "https://homarr.riotpiao.com" NEXTAUTH_URL: "https://homarr.riotpiao.com" diff --git a/k8s/argocd/apps/55-api-gateway.yaml b/k8s/argocd/apps/55-api-gateway.yaml new file mode 100644 index 0000000..5668e00 --- /dev/null +++ b/k8s/argocd/apps/55-api-gateway.yaml @@ -0,0 +1,58 @@ +# Wave 7 — Kong, the cluster's internal API gateway (namespace `api`). +# +# Sits between nginx and the backend services: nginx owns the edge and TLS, +# Kong owns routing policy, auth and rate limiting. Wave 7 puts it after the +# data/messaging tiers it fronts and before the wave-8 applications that +# publish routes into it. +# +# DB-less: routing config comes from Kubernetes objects (Ingress with +# `ingressClassName: kong`, plus KongPlugin/KongConsumer CRDs), so git remains +# the source of truth and there are no migration Jobs on upgrade. +# +# CRDs ship in the chart's crds/ directory; ArgoCD applies those by default +# (helm.skipCrds is left false). +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: kong + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "7" +spec: + project: homelab + revisionHistoryLimit: 3 + sources: + - repoURL: https://charts.konghq.com + chart: kong + targetRevision: "3.4.1" + helm: + valueFiles: + - $values/k8s/apps/api/kong-values.yaml + - repoURL: git@github.com:Riotpiaole/riotpiao.homelab.com.git + targetRevision: main + ref: values + # The nginx Ingress for api.riotpiao.com. Kept in this Application rather + # than the central k8s/bootstrap/ingress/ingress.yaml because that one syncs + # in wave 1, before namespace `api` exists. + - repoURL: git@github.com:Riotpiaole/riotpiao.homelab.com.git + targetRevision: main + path: k8s/apps/api + destination: + server: https://kubernetes.default.svc + namespace: api + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + # The chart's CRDs exceed the annotation size limit that client-side + # apply relies on; server-side apply avoids the + # "metadata.annotations: Too long" failure CRDs commonly hit. + - ServerSideApply=true + retry: + limit: 3 + backoff: + duration: 10s + factor: 2 + maxDuration: 3m diff --git a/k8s/bootstrap/ingress/ingress.yaml b/k8s/bootstrap/ingress/ingress.yaml index 86db284..5ad7e2d 100644 --- a/k8s/bootstrap/ingress/ingress.yaml +++ b/k8s/bootstrap/ingress/ingress.yaml @@ -300,6 +300,10 @@ spec: port: number: 8080 --- +# NOTE: api.riotpiao.com (Kong) is deliberately NOT here. Its namespace `api` is +# created in wave 7, and this Application syncs in wave 1 — an Ingress into a +# namespace that doesn't exist yet would fail and mark this whole app +# SyncFailed. It lives in k8s/apps/api/ingress.yaml, synced with Kong itself. apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/terraform/files/coredns/Corefile b/terraform/files/coredns/Corefile index c1300e8..8d63987 100644 --- a/terraform/files/coredns/Corefile +++ b/terraform/files/coredns/Corefile @@ -33,6 +33,11 @@ rewrite name homarr.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local rewrite name portainer.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local rewrite name longhorn.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + # Kong API gateway. Points at nginx, not kong-proxy, for the same reason as + # the rest: a direct rewrite would skip TLS termination. Pods that don't + # need TLS should call kong-proxy.api.svc.cluster.local instead of using + # this name at all. + rewrite name api.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure