From e65de2938b59e679ed6e7197286f665dc5312ac0 Mon Sep 17 00:00:00 2001 From: Andrew Stoltz Date: Fri, 24 Apr 2026 01:07:12 -0500 Subject: [PATCH] feat(ingress): single-host Guacamole via guacamole-namespace IngressRoute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cluster Traefik disallows cross-namespace service refs from IngressRoutes, so the PathPrefix(/guacamole) rule I added to fc-desktop IngressRoute in 292528e failed with: "service guacamole/guacamole not in the parent resource namespace fc-desktop" Move the /guacamole path match into the guacamole namespace where the Service actually lives: - apps/guacamole/guacamole.yaml adds a new `guacamole-desktop-path` IngressRoute matching `Host(desktop.iamworkin.lan) && PathPrefix(/guacamole)` → guacamole:8080 (no add-prefix middleware; the browser already sends the /guacamole/* path that Guacamole's servlet serves at). - New Certificate `desktop-guacamole-path-tls` for desktop.iamworkin.lan in the guacamole namespace, issued by step-ca-acme. Separate cert from fc-desktop's remotedesktop-web-tls because Secret refs are also scoped per-namespace; duplicating the cert is cheaper than enabling cross-namespace secret refs cluster-wide. - Revert the cross-namespace attempt in apps/fc-desktop/fc-desktop.yaml back to a Host-only route. Traefik's router matching precedence (longer/more-specific rule wins) handles the /guacamole vs catch-all priority without explicit priority: fields. Closes the single-host Guacamole URL regression Codex's branch introduced — GuacamolePublicUrl=https://desktop.iamworkin.lan/guacamole now resolves to the Guacamole webapp end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/fc-desktop/fc-desktop.yaml | 23 +++++++------------ apps/guacamole/guacamole.yaml | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/apps/fc-desktop/fc-desktop.yaml b/apps/fc-desktop/fc-desktop.yaml index 786a087..92846e2 100644 --- a/apps/fc-desktop/fc-desktop.yaml +++ b/apps/fc-desktop/fc-desktop.yaml @@ -23,23 +23,16 @@ spec: entryPoints: - websecure routes: - # Single-host Guacamole routing: Traefik forwards the /guacamole - # path-prefix directly to the guacamole Service in the guacamole - # namespace. Must precede the catch-all Host() rule so priority - # resolves the more-specific match first. RemoteDesktop.Web then - # emits launch URLs with host=desktop.iamworkin.lan + /guacamole - # prefix, keeping Guacamole reachable through the same public - # surface (GuacamolePublicUrl=https://desktop.iamworkin.lan/guacamole). - - match: Host(`desktop.iamworkin.lan`) && PathPrefix(`/guacamole`) - kind: Rule - priority: 20 - services: - - name: guacamole - namespace: guacamole - port: 8080 + # Host-level catch-all for desktop.iamworkin.lan. The /guacamole + # path-prefix match lives in apps/guacamole/guacamole.yaml as a + # separate IngressRoute in the guacamole namespace — the cluster + # Traefik disallows cross-namespace service refs, so the PathPrefix + # rule can't sit here. Traefik's router matching precedence gives + # longer/more-specific rules priority automatically, so as long as + # the guacamole IngressRoute exists it takes /guacamole traffic + # before this catch-all sees it. - match: Host(`desktop.iamworkin.lan`) kind: Rule - priority: 10 services: - name: remotedesktop-web port: 8080 diff --git a/apps/guacamole/guacamole.yaml b/apps/guacamole/guacamole.yaml index c06e3d7..da309ec 100644 --- a/apps/guacamole/guacamole.yaml +++ b/apps/guacamole/guacamole.yaml @@ -444,6 +444,46 @@ spec: tls: secretName: guacamole-tls --- +# Single-host Guacamole routing — matches RemoteDesktop.Web launch URLs +# that embed Guacamole as a path-prefixed iframe on the primary desktop +# host (https://desktop.iamworkin.lan/guacamole/#/client/...). The +# Traefik IngressRoute lives in the guacamole namespace because the +# cluster disallows cross-namespace service refs from IngressRoutes. +# No add-prefix middleware: the browser already sends /guacamole/* +# which is the servlet path Guacamole's webapp serves at. +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: desktop-guacamole-path-tls + namespace: guacamole +spec: + secretName: desktop-guacamole-path-tls + issuerRef: + name: step-ca-acme + kind: ClusterIssuer + dnsNames: + - desktop.iamworkin.lan +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: guacamole-desktop-path + namespace: guacamole + labels: + app.kubernetes.io/part-of: flowercore + app.kubernetes.io/component: guacamole-ingress +spec: + entryPoints: + - websecure + routes: + - match: Host(`desktop.iamworkin.lan`) && PathPrefix(`/guacamole`) + kind: Rule + services: + - name: guacamole + port: 8080 + tls: + secretName: desktop-guacamole-path-tls +--- # 1Password secret sync — creates guacamole-credentials K8s Secret # Fields: username, password, DB-User, DB-Password, DB-Root-Password, DB-Name, URL apiVersion: onepassword.com/v1