Skip to content

0020: The published console, and port-forwards without an Access login

Date: 2026-10-13 · Status: accepted

Context

The design puts the console behind Cloudflare Tunnel and Access, so that people without cluster access can use it. The chart accepted expose.cloudflared.consoleHostname, but nothing published it: an administrator had to create the Access application, its DNS record and the tunnel route by hand, then copy the application's audience tag and the team domain into the chart. Meanwhile people with cluster access used tenax console, a port-forward to tenax-api. Once tenax-api verifies Access tokens, a port-forward carries none and is refused.

Decision

  • Tenax publishes the console. With expose.provider: cloudflare and a consoleHostname, the controller creates and keeps (re-checked every 15 minutes) an Access application for the hostname with the sandbox routes' rules (allowEmails, allowGroups; a changed list updates it), a proxied CNAME to the tunnel and the tunnel route to the tenax-api Service. The only credential is still the one API token.
  • tenax-api learns how to verify tokens from the controller. The controller writes the application's audience tag and the team domain to the Secret <release>-console-access; tenax-api mounts it (optional) and re-reads it as it changes. Until it exists every request is refused with "console login not ready". Keys, cached tokens and issuers are tied to the team domain, so a changed team never accepts the old team's tokens. The team domain comes from auth.cloudflareAccess.teamDomain when set, else from the account's Access organization (Access: Organizations Read), else from the *.cloudflareaccess.com login redirect Access sends for an anonymous request to the console.
  • Port-forwards get their own port. With auth.provider: cloudflare-access, tenax-api also listens on 127.0.0.1:8444 (container port local, not in the Service). Only kubectl port-forward reaches a pod's loopback address, so Kubernetes RBAC (pods/portforward in Tenax's namespace) has already decided who may connect; requests there get auth.portForwardRole (default restorer-admin, empty turns it off) and are audited as kubectl-port-forward. The address must be a loopback one or tenax-api refuses to start. tenax console forwards to local when the pod has it.

Consequences

  • An administrator sets a hostname and the email rules; people open https://<hostname> or run tenax login --server https://<hostname>, with no kubeconfig and no values to copy from Cloudflare.
  • People with cluster access keep using tenax console. Anyone who may port-forward into Tenax's namespace gets portForwardRole, so that permission must be granted as carefully as the role itself.
  • auth.provider: cloudflare-access without a team domain and audience now fails at render time unless Tenax publishes the console; before, tenax-api failed at start.
  • A console route an administrator made by hand on the same hostname is taken over (its application's rules are replaced by Tenax's).