Building Providers
RBAC & Security
What a provider may touch, bind grants, claim acceptance, and the isolation contracts.
kedge’s provider security model is small enough to hold in your head. Four principals, four boundaries:
| Principal | May touch |
|---|---|
Provider SA (provider in your workspace) | Everything inside your own workspace; tenant data only through the virtual workspace, bounded by bindings + accepted claims |
| Tenant user | Their own workspaces (they’re cluster-admin there); your API once bound |
| The hub | Everything — it provisions workspaces and mediates Enable/Disable with its admin client |
| Edge agents | Only their per-edge SA in the provider’s plane |
Your service account
Provisioning (from the Provider object) creates a ServiceAccount provider in your workspace’s default namespace with a ClusterRoleBinding to cluster-admin — scoped to that one logical cluster. kcp SA tokens are pinned to their cluster (the cluster name is in the token claims), so the credential physically can’t be replayed against another workspace.
The contract every shipped provider follows, and yours should too:
- No admin or root kcp client, ever. If you find yourself wanting one, the design is wrong.
- Controllers and background work: provider SA through the virtual workspace (pattern A).
- Request handling: the caller’s token, scoped to the caller’s cluster (pattern B) — so kcp RBAC, not your code, is the authorization boundary.
Bind grants
Tenants need permission to bind your export. init creates it: a ClusterRole/ClusterRoleBinding pair kedge:providers:bind:<export-name> in your workspace granting the bind verb on your APIExport to system:authenticated. Any authenticated kedge user may bind — the platform admin gates which providers exist at onboarding time, and each tenant gates whether to enable per workspace.
Permission claims and acceptance
Claims are declared by you, enforced by kcp, and accepted by tenants:
- The Enable dialog lists every claim; on accept, the hub writes them onto the
APIBindingwith the verbs taken from your declaration — the API is server-side, so a tenant can’t grant you more than you asked for, and you can’t get more than they accepted. tenantScoped: trueclaims are bounded to the tenant’s own workspace and auto-acceptable. Anything else requires the platform-admin override annotation (kedge.faros.sh/accept-untrusted-claims) — treat that as a red flag in review.- Claims on
*.faros.shgroups need the claimed export’sidentityHash(admin-supplied at deploy time); built-in Kubernetes types don’t.
Enable-time RBAC
Enable/Disable runs through the hub’s REST API (POST /api/orgs/{org}/workspaces/{ws}/providers/{name}/enable) with membership checks, and the hub creates the APIBinding using its own admin client. Two extras happen at Enable when declared:
- Dependencies — if your CatalogEntry lists
dependencies, the hub refuses (409) until they’re enabled in that workspace. edgeProxyAccess— the hub grants your provider SA (as a cluster-qualified subject) theproxyverb onedges.kedge.faros.shresources in the tenant workspace. Disable always removes the grant.
Anti-spoofing
The backend proxy strips inbound X-Kedge-* headers before injecting verified values. Corollaries for your code:
- Trust
X-Kedge-User/X-Kedge-Tenant/X-Kedge-Clusteronly on traffic that arrived through the hub proxy. - Don’t expose your backend on any other network path (keep the Service ClusterIP; no Ingress of your own for the proxied surface).
- Never log or persist the forwarded bearer token.
Provider isolation
Rules that keep the ecosystem composable — violating them couples releases and breaks tenancy:
- No credentials into another provider’s backend, database, or runtime cluster. Reach other providers only through their published APIs, as the caller.
- No hardcoded provider URLs. Discover endpoints from resource status (like the edges
status.URL) or go through the hub. - Your data plane is yours alone. If others need it, publish it as CR subresources so access is authorized by binding + caller identity.
Deletion is destructive
Deleting a Provider object tears down the whole workspace — export, schemas, CatalogEntry, SA — and strands tenant bindings. There is no soft-delete. Removing a provider from a deployment should start with tenants disabling it, then the admin deleting the Provider last.