Skip to content

Repository files navigation

krm-foyer

The browser's way in to Kubernetes: login, API access and live krm-stream resources.

krm-foyer is a backend for frontend (BFF) for browser applications built on Kubernetes APIs. It owns OIDC login and sessions, proxies Kubernetes API requests with the user's own credential, and hosts krm-stream resource streams on the same origin as your frontend. The browser holds the session in a cookie sealed with keys only krm-foyer has: page scripts never see a token, and sessions survive a restart.

KRM is the Kubernetes Resource Model: the idea that everything is a declarative resource with a spec and a status. krm-foyer is for applications whose domain is modelled that way.

Status: a working prototype, not yet for a cluster that matters. Sign-in through OIDC, sessions and the API proxy work, and e2e specs against a real API server and Dex try to get past each security boundary. task demo runs an example application against them in your browser. Request and response bounds, live streams and opt-in shared watches are implemented and tested, and a Helm chart installs it. Sessions survive a restart, but last no longer than their ID token, and logout does not revoke a copied cookie. Run one replica. Certificate reload, token refresh and more replicas remain to be built. The roadmap gives the next priorities and their required evidence.

Try it

In the devcontainer:

task demo

This starts a disposable k3d cluster with Dex, krm-foyer and the hello example in it, and port-forwards the example and Dex into the devcontainer, where VS Code forwards them to your machine, also when Docker runs elsewhere. Open https://foyer.localhost:8443 and sign in as alice@example.com, who may edit the notes, or bob@example.com, who may only read them. The password is password. The certificates come from the fixture's own CA, so import .e2e/ca.crt into your browser or accept the warnings. task e2e-down removes it all. A fixture made by an older version of the scripts is refused with a message saying so; run task e2e-down once, then task demo again.

The example is one HTML file and one script with no backend of its own: it follows the notes live through /stream, one shared watch for every user that RBAC allows, and changes them through /k8s as the signed-in user; the 403 and 409 it shows are Kubernetes' own answers. Open it in two tabs to see a change in one appear in the other, and a conflict when both edit the same note.

Principles

  • The issuer decides who you are; Kubernetes decides what you may do. Proxied API requests and per-user watches use the user's own OIDC token; RBAC and admission answer them. No impersonation, and no fallback to krm-foyer's service account. A shared watch, where configured, is opened once with an identity of its own, and the API server is asked about every user who reads it.
  • Frontend code never receives a token. The browser holds the session sealed (AES-GCM) in a Secure, HttpOnly cookie that only krm-foyer's keys open. The cookie is itself a bearer credential until it expires, logout or not, and is guarded like one.
  • One domain is one trust boundary. The application, krm-foyer and any domain backend share an origin, routed by path. Everything on that domain can act as the signed-in user, so host only what you would trust with that access. More
  • No access rules of our own. krm-foyer has no allowlist: what RBAC allows the user is reachable, and what it refuses is refused by the API server. So a session carries the user's full Kubernetes access; give users grants that match the application. Why, and what a scope would add
  • Kubernetes semantics, exactly. Status codes, errors, patch types and conflicts pass through unchanged. A mutation that reached Kubernetes is never automatically replayed. Why

Documentation

Document What it answers
docs/vision.md Why krm-foyer exists, who it helps, what it expects from your domain, and what it will not become
docs/design.md The contract: routes, access, sessions, upstream responses, streams and release criteria
docs/application-scope.md Why a browser application might be limited beyond RBAC, what that would block, and how to give the browser its own narrow identity in the cluster
docs/roadmap.md What exists, what is next, and in what order
docs/bounds.md Limits, defaults, metrics and the scope of the capacity measurements
docs/watches.md Choosing per-user streams, shared streams or native watches, and configuring sharing
docs/bff-choice.md Whether your application should use a universal BFF like this one, a domain backend, or both
docs/ingress.md Terminating TLS itself or behind an ingress, sharing one domain with other services, /auth/check for a login gate and a domain backend, and recipes for Gateway API, Traefik, nginx and Vite
docs/room-pass.md Signing an audience in with Room Pass's QR join through krm-foyer
docs/frontend.md Which pages krm-foyer serves itself, and why it ships no single-page application
docs/testing.md How the tests prove krm-foyer invents neither authentication nor authorization, and how to run the e2e fixture
docs/alternatives.md Nearby tools and the goals each meets: other ways to reach the Kubernetes API, and JavaScript clients in place of the browser helper
docs/investigations/choosing-an-issuer.md Which OIDC issuers fit krm-foyer (Dex, Pinniped, Keycloak, authentik, Authelia, OpenUnison), and why the tests use Dex
docs/heritage.md Where it comes from: the Voter demo, what broke on stage, and its sibling projects
docs/name.md Why it is called krm-foyer

Run it

task run          # http://localhost:8080
task verify       # everything CI checks, including e2e against k3d and Dex

Without flags, krm-foyer serves its start page and probes only. Sign-in and the API proxy and streams come together, and need:

Flag
-public-url krm-foyer's origin as browsers reach it, such as https://app.example.com
-oidc-issuer, -oidc-client-id, -oidc-client-secret-file The issuer the API server trusts, and krm-foyer's client there
-session-keys-file The keys session cookies are sealed with: one to four lines, each head -c 32 /dev/urandom | base64, the first sealing. Keep them across restarts (sessions)
-kubernetes-server The API server, such as https://kubernetes.default.svc

Optional: -oidc-ca-file and -kubernetes-ca-file (CAs to trust), -oidc-scopes, -login-config-file (login parameters and session claims), -session-absolute-timeout (8h), -tls-cert-file and -tls-key-file to serve TLS, -listen (:8080) and -metrics-listen (:9090). The bounds each have a flag and a documented default: the request rate and concurrent requests per session, concurrent requests per replica, how long a response may stay open, its size, and how often an open response checks its session. Run one replica: logins in progress and logout's reach are each process's own. Shared watches are opt-in; see their configuration and identity requirements. The OIDC redirect URI to register is <public-url>/auth/callback. The public URL, issuer and Kubernetes server must use HTTPS. Mounted TLS certificates, CA bundles, the session keys and the OIDC client secret are read at startup; restart after changing them. The explicitly configured shared-watch token file is reread by client-go for rotation.

Or as a container:

task image
docker run --rm -p 8080:8080 ghcr.io/configbutler/krm-foyer:$(git describe --tags --always)

Or in a cluster, with the Helm chart, which the e2e suite installs too. Each release publishes it beside the image (from a checkout, charts/krm-foyer works in its place). The client secret and the TLS certificate are Secrets you create first:

kubectl create namespace krm-foyer
kubectl -n krm-foyer create secret generic krm-foyer-oidc --from-literal=client-secret=...
kubectl -n krm-foyer create secret tls krm-foyer-tls --cert tls.crt --key tls.key
helm install krm-foyer oci://ghcr.io/configbutler/charts/krm-foyer --version <release> \
  --namespace krm-foyer \
  --set publicURL=https://app.example.com \
  --set oidc.issuer=https://issuer.example.com,oidc.clientID=krm-foyer \
  --set oidc.clientSecret.secretName=krm-foyer-oidc,tls.secretName=krm-foyer-tls

krm-foyer: Browser login, Kubernetes API access and live resources on one origin, with every access decision left to the API server

— Open in Artifact Hub
<script async src="https://artifacthub.io/artifacthub-widget.js"></script>

The repository comes with a devcontainer that has Go, Task, the linters CI runs, and k3d/kubectl for testing against a real API server. See CONTRIBUTING.md.

License

Apache 2.0

About

The browser's way in to Kubernetes: login, API access and live krm-stream resources.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages