Skip to content

feat: gate platform-admin on a configurable role for mTLS-authenticated identities #3071

Description

@dimityrmirchev

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:

  • AuthzPolicy is built only from OIDC config and is None without 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, any Principal::User reaches platform-admin methods unchecked.
  • MtlsAuthConfig exposes only enabled (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] gains admin_role and user_role. A certificate carrying OU=<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_role never 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_users is rejected (the local-dev principal carries platform-admin).
  • Deployment. Helm renders the new fields into gateway.toml and 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_role set, and no OIDC, a cert carrying OU=<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_users fails 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.mdx and architecture/gateway.md document 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions