tasks: public ingress carve-out for PWA icons; adopt orphaned stack state
All checks were successful
ci/woodpecker/push/default Pipeline was successful

macOS Safari's Add to Dock (and iOS/Android home-screen installs) fetch
the app icon and web manifest without any session cookies, so the
Authentik forward-auth 302 on tasks.viktorbarzin.me made Safari fall
back to a letter monogram instead of the real icon. Viktor asked for an
ingress carve-out so exactly these five static PWA assets are publicly
fetchable: /apple-touch-icon.png, /favicon.png, /pwa-192x192.png,
/pwa-512x512.png, /manifest.webmanifest.

A second ingress_factory instance (auth=none, dns_type=none, same host)
routes only those paths straight to the tasks service; the SPA shell and
/api stay behind Authentik exactly as before. The new carve-out is also
registered in the Authentik walling-off probe so a future regression
(anything 302-ing these paths to Authentik again) alarms, and the
service catalog entry records the exception.

stacks/tasks/imports.tf adopts the live tasks resources into Terraform
state first: the stack's first-ever apply (pipeline 477, 2026-07-03)
died mid-apply after creating the resources but before the pg state
write, leaving tasks.states empty — without the import blocks this (and
every future) tasks apply would create-fail with 'already exists'. Same
pattern as the monitoring alert-digest adoption.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-07-04 10:01:53 +00:00
parent c91fa881e6
commit c311a6a3c9
4 changed files with 92 additions and 1 deletions

53
stacks/tasks/imports.tf Normal file
View file

@ -0,0 +1,53 @@
# One-shot adoption of the live tasks-stack resources that exist in-cluster but
# were never persisted to Terraform state: pipeline 477 (2026-07-03, the stack's
# first apply) died mid-`[tasks] apply` after creating the resources, before
# the pg backend write so `tasks.states` stayed empty and every later apply
# would create-fail with `namespaces "tasks" already exists` (same class as the
# monitoring alert-digest adoption in stacks/monitoring/imports.tf). Importing
# reconciles them into state so `terraform apply` UPDATES instead of failing to
# create. These blocks are idempotent (a no-op once the resources are in state)
# and may be removed after the next green apply. Defs: main.tf.
# (module.ingress_icons is deliberately NOT here it does not exist live yet;
# the same apply creates it.)
import {
to = kubernetes_namespace.tasks
id = "tasks"
}
import {
to = kubernetes_manifest.external_secret
id = "apiVersion=external-secrets.io/v1,kind=ExternalSecret,namespace=tasks,name=tasks-secrets"
}
import {
to = kubernetes_manifest.db_external_secret
id = "apiVersion=external-secrets.io/v1,kind=ExternalSecret,namespace=tasks,name=tasks-db-creds"
}
import {
to = kubernetes_deployment.tasks
id = "tasks/tasks"
}
import {
to = kubernetes_service.tasks
id = "tasks/tasks"
}
import {
to = kubernetes_network_policy_v1.tasks_ingress
id = "tasks/tasks-ingress"
}
import {
to = module.ingress.kubernetes_ingress_v1.proxied-ingress
id = "tasks/tasks"
}
# Cloudflare record ID looked up via the API (zone fd2c5dd4 / record for
# tasks.viktorbarzin.me, CNAME the cfargotunnel target, proxied).
import {
to = module.ingress.cloudflare_record.proxied[0]
id = "fd2c5dd4efe8fe38958944e74d0ced6d/a8e6901a074c5255d09700d93eaaf705"
}

View file

@ -293,6 +293,40 @@ module "ingress" {
tls_secret_name = var.tls_secret_name
}
# Carve-out for the PWA icon assets + web manifest. macOS Safari's
# "Add to Dock" (and every other OS icon fetcher: iOS Add-to-Home-Screen,
# Android install prompt) fetches these in a cookie-less context behind
# forward-auth it got the Authentik 302 and fell back to a letter monogram.
# Traefik prioritises these longer path prefixes over the main "/" router,
# so ONLY these five static files bypass Authentik; the SPA shell and /api
# stay gated by the main ingress above (and the app itself 401s /api
# without the identity header). Guarded against regression by the
# tasks-icons entry in the Authentik walling-off probe
# (stacks/monitoring/modules/monitoring/authentik_walloff_probe.tf).
module "ingress_icons" {
source = "../../modules/kubernetes/ingress_factory"
# auth = "none": public static icons + manifest, no user data; required for
# OS icon fetchers (Safari Add-to-Dock etc.) that carry no session and
# cannot complete the Authentik redirect dance.
auth = "none"
namespace = kubernetes_namespace.tasks.metadata[0].name
name = "tasks-icons"
service_name = kubernetes_service.tasks.metadata[0].name
port = 8000
ingress_path = [
"/apple-touch-icon.png",
"/favicon.png",
"/pwa-192x192.png",
"/pwa-512x512.png",
"/manifest.webmanifest",
]
full_host = "tasks.viktorbarzin.me" # MUST match the main ingress host; otherwise the factory derives tasks-icons.viktorbarzin.me and the carve-out never matches.
dns_type = "none" # host record already owned by the main tasks ingress
tls_secret_name = var.tls_secret_name
anti_ai_scraping = false # Five static icons + a manifest; nothing for scrapers to mine.
homepage_enabled = false # path carve-out, not its own dashboard tile
}
# --- NetworkPolicy: scoped pod ingress (security-review finding SEC-1). ---
# The app trusts X-authentik-username unconditionally, so its ENTIRE auth
# model depends on requests only ever arriving through Traefik (where the