Thursday, July 02, 2026
Containers Kubernetes

A web-based portal for exposing Kubernetes PVC data, gated by SSO

Every platform team eventually hits the same small, annoying problem: some application on Kubernetes keeps its data on a shared volume — label templates for a print server, custom report definitions, a report database — and a human operator needs to get files in or out of it. Not a developer with kubectl, not a CI pipeline. An operator, from a browser.

The usual answers are all bad. kubectl cp means giving out cluster credentials. A shell into the pod is worse. An SFTP sidecar is a second auth system to run and audit. So I built file-portal — and I’ve just open-sourced it under MIT.

What it is

file-portal is a Helm chart plus a small FastAPI app. You point it at PVCs that already exist in a namespace, and it serves them as a web portal behind SSO:

  • ReadOnlyMany (ROX) volumes are export-only — browse and download.
  • ReadWriteMany (RWX) volumes get export + import — upload and delete too.

That’s the whole product surface. No database, no accounts, no background jobs.

The one design rule: be boring

The app has almost no logic, on purpose. There’s no dependency injection, no plugin system, no clever abstractions. The only two pieces of real behaviour are a read-only gate and an audit logger. Everything else is FastAPI serving files off a mounted filesystem.

Read-only is the part I care most about, because “the UI hides the upload button” is not a security control. In file-portal, a ROX volume is read-only at three layers: the Kubernetes mount is readOnly: true, the UI never renders an import control, and the handler returns a 403 if you POST to it anyway. The mount is the one that matters — even if the app had a bug, the kernel won’t let it write.

Every action, and every denied action, is written to the audit log by the operator’s email. If someone downloads a report or is refused an upload, there’s a line for it on stdout, ready for your log pipeline.

Authentication is not the app’s job

file-portal does no authentication itself. An oauth2-proxy sidecar sits in front, authenticates against your IDP over OIDC, and forwards identity as headers (X-Forwarded-EmailX-Forwarded-Groups). In production the app fails closed: no X-Forwarded-Email, no request served.

Authorization is two group tiers, enforced at the proxy and the app:

  • readers — browse + download.
  • writers — also import + delete (a superset of readers).

Who may open the portal at all is readGroups ∪ writeGroups; non-members never reach the app. It works with any OIDC provider — Keycloak, Okta, and Entra ID (Azure AD) each get copy-paste values in the docs, including the fiddly bits like Entra’s App Roles and Okta’s classic sign-out.

The Kubernetes-specific sharp edges

A few things that took real debugging and are baked into the chart and its docs:

  • It references PVCs by name; it never creates them. A pod can only mount PVCs in its own namespace, so the portal installs into the namespace the volumes live in.
  • ReadWriteOnce is rejected — at helm install time and again at app startup. An RWO PVC binds to a single pod, so a separate portal pod can’t mount a volume another workload already holds. If you need shared write, the volume has to be RWX.
  • NFS write identity. These shares are NFS-backed and owned root:1001 with a group-writable setgid directory, so write permission comes from group 1001, not the user. The chart defaults to fsGroup: 1001 so uploads just work — until you hit OpenShift, which assigns a random UID with gid 0 and needs group 1001 added as a supplemental group (and sometimes a custom SCC). All of that is documented.
  • Oversized OIDC cookies. Keycloak tokens with a groups claim get chunked across cookies and blow past the default ~4–8 KB proxy header buffers, producing a cryptic “invalid response from upstream” after a successful login. The fix lives on the ingress/gateway, not the app — so the README explains it for Kong, ingress-nginx, and Envoy-based gateways.

Running it

You build the image from the included Dockerfile, push it to a registry your cluster can pull from, and point the chart at it. Then it’s one helm upgrade --install with a values file. It runs unchanged on vanilla Kubernetes and OpenShift.

Open source

file-portal is MIT licensed — chart, app source, and any image you build from it. It’s deliberately small; if it solves your “operators need files off a PVC” problem, take it, fork it, make it yours.

Repo: https://github.com/infrabytes-io/file-portal

If you’d like access to the code repository, please get in touch.

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top
error: Content is protected !!