feat: gate platform-admin on a configurable role for mTLS-authenticated identities
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 8.7k
- Forks
- 1.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 253
Description
User Story
As an operator running the gateway behind a trusted fronting service that creates workspaces and sandboxes on behalf of users, I want a platform-admin identity authenticated by an mTLS client certificate and gated by a configurable admin role, without running an OIDC provider, so that only my fronting service holds platform admin and presenting any valid client certificate does not make a caller an admin.
Problem Statement
The gateway only enforces platform-admin RBAC when OIDC is configured. Without an OIDC issuer there is no way to distinguish an admin from an ordinary authenticated caller, so every mTLS-authenticated caller is treated as a platform admin. There is no supported managed platform identity that avoids standing up OIDC.
Impact / Why This Matters
To give a fronting service admin while end users have none, an operator must deploy a full OIDC issuer purely for role separation — disproportionate for a deployment whose only authenticated caller is a certificate-holding backend, and it adds an external dependency with its own availability and rotation concerns.
On main today:
AuthzPolicyis built only from OIDC config and isNonewithout an issuer (crates/openshell-server/src/multiplex.rs:282).- The role check runs only when that policy exists (
crates/openshell-server/src/multiplex.rs:1153); with no policy, anyPrincipal::Userreaches platform-admin methods unchecked. MtlsAuthConfigexposes onlyenabled(crates/openshell-core/src/config.rs:972-978), so an operator cannot declare which certificates are admin.
Proposed Design
mTLS authentication supports role-based authorization on its own, resolving admin/user roles from config when no OIDC issuer is present.
- Configurable roles on the mTLS block.
[openshell.gateway.mtls_auth]gainsadmin_roleanduser_role. A certificate carryingOU=<admin_role>is authorized for platform-admin methods;OU=<user_role>is a standard user subject to workspace-membership checks; neither is denied. - Fail closed. An empty
admin_rolenever makes a CA-signed certificate an admin. When mTLS user auth is enabled with no resolvable admin role, the gateway refuses to start. - Reject the unsafe combination. mTLS user auth together with
allow_unauthenticated_usersis rejected (the local-dev principal carries platform-admin). - Deployment. Helm renders the new fields into
gateway.tomland fails rendering on the same misconfigurations the binary rejects.
Observable outcome: an operator configures mTLS with an admin role, issues admin-OU certificates only to the fronting service, and that service can create workspaces/sandboxes while no other caller reaches platform-admin methods — with no OIDC anywhere.
Acceptance Criteria
- With mTLS enabled, an
admin_roleset, and no OIDC, a cert carryingOU=<admin_role>is authorized for platform-admin methods; a CA-signed cert without it is denied. - With no OIDC and no admin role configured, an mTLS identity is not treated as platform-admin.
- mTLS user auth enabled with no resolvable admin role fails at startup with a clear error.
- mTLS user auth plus
allow_unauthenticated_usersfails at startup with a clear error. - Configuring both OIDC and mTLS user auth resolves per the Open Question (proposed: fail at startup).
- Helm renders the mTLS role fields and fails rendering on the misconfigurations the binary rejects.
- Sandbox supervisor calls continue to authenticate via gateway-minted sandbox JWTs, unchanged.
-
docs/reference/gateway-config.mdxandarchitecture/gateway.mddocument the path and its guardrails.
Open Question
What happens when both OIDC and mTLS user auth are configured? Proposed: fail at startup. Silently honoring one and ignoring the other hides a misconfiguration on the auth boundary, and an mTLS caller carries cert OUs, not OIDC claims — so "OIDC wins" would leave it authenticated but effectively role-less. Whether both can even be active on the same listener needs confirmation.
Alternatives Considered
- Require OIDC for role separation (status quo). Forces an external identity provider onto a certificate-only deployment for no functional benefit.
- Treat all mTLS callers as admin (implicit today). This is the gap — every authenticated caller becomes admin.
- A dedicated admin-only client CA. Heavier operationally than a role OU and resolves to the same authorization question; possible follow-up, not the primary design.
Agent Investigation
The identity layer is already provider-agnostic (an Identity carries roles whether from OIDC or a cert), mTLS already extracts CN→subject and OU→roles (crates/openshell-server/src/multiplex.rs:1398-1409), and the role check is provider-neutral. The missing pieces are narrow: role fields on the mTLS config, a single resolver for the effective role names when there is no OIDC issuer, and having the authorization middleware consult it instead of only an OIDC-derived policy. The fail-closed and startup guards sit at existing config-validation and authorization sites. The sandbox JWT credential path is orthogonal and unchanged.
Checklist
- I've reviewed existing issues and the architecture docs
- This is a design proposal, not a "please build this" request
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with MtlsAuthConfig in crates/openshell-core/src/config.rs and the authorization and mTLS identity paths in crates/openshell-server/src/multiplex.rs, especially the cited sections. Review the Helm gateway configuration and the documentation files named in the acceptance criteria, then resolve the OIDC/mTLS behavior before implementation. Done means the startup guards, authorization outcomes, Helm rendering, and docs match the acceptance criteria without changing sandbox JWT authentication.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- helm, rust
- Domain
- authentication, authorization, backend-api-design, devops, documentation
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100