Add workload identity authentication enhancement proposal - #45
Conversation
|
|
||
| ## 7. Compatibility and rollout | ||
|
|
||
| - Additive feature, gated by FEATURE_KUBERNETES_SA_BOOTSTRAP and off by default. |
There was a problem hiding this comment.
The JIRA (PROJQUAY-11090) requires the output to be "a standard OAuth Bearer token" with enforced scopes. This section says "reuse existing organization token CRUD" but doesn't specify the actual mechanism:
- Does the SA present its JWT to the existing org token endpoint and receive a scoped OAuth token back? If so, what request parameters does it pass (org, scope)?
- Or does SA authentication itself produce an identity (e.g., a robot account) that then uses the existing token CRUD APIs separately?
The initial implementation chose option 2 — SA JWT maps to a robot account directly, with no scoped OAuth token issuance. This means the SA gets full robot permissions rather than a scoped token, which doesn't match the JIRA's acceptance criteria or the spec's own §1 ("obtain scoped Quay Management API OAuth tokens").
Recommendation: specify the request/response contract. At minimum: what endpoint does the workload call, what does it send (JWT + requested scope), and what does it receive (scoped OAuth token with expiry)?
There was a problem hiding this comment.
Agreed. The intended flow is ServiceAccount JWT + target organization + requested scopes into the existing organization token issuance path, returning a standard scoped Quay OAuth bearer token with expiration. The direct robot-identity approach was rejected because it gives the workload the robot’s standing permissions rather than a separately scoped token.
|
|
||
| - Which authorization mapping model should be adopted? | ||
| - Is SubjectAccessReview required in addition to TokenReview, and what exact decision does it provide? | ||
| - Where should trust and mapping configuration live, and how is it managed by the Quay Operator? |
There was a problem hiding this comment.
This bullet identifies audience requirements as needed but doesn't define them. The initial implementation sets verify_aud: False, disabling audience validation entirely. This means any valid SA JWT from the cluster authenticates to Quay regardless of its intended audience — a token minted for Vault, ArgoCD, or any other service would also work against Quay.
Recommendation: define a required audience value (e.g., SERVER_HOSTNAME or a dedicated audience like quay-management-api). The workload would request a projected token with that audience:
volumes:
- name: quay-token
projected:
sources:
- serviceAccountToken:
audience: quay.example.comQuay rejects tokens without the expected aud claim. This is how IRSA, GCP Workload Identity, and Vault K8s auth all work — audience binding is the primary defense against token reuse across services.
There was a problem hiding this comment.
Agreed. TokenReview is the primary validation path, with Quay requesting validation for its expected audience; missing or mismatched audiences are rejected. Audience validation is part of establishing a Quay-intended identity, separate from the still-open authorization choice between an administrator-provided mapping and SAR.
| - Authenticate eligible Kubernetes ServiceAccounts to Quay. | ||
| - Issue standard Quay OAuth tokens with enforced scopes and normal expiration/revocation behavior. | ||
| - Preserve existing human, non-Kubernetes, and programmatic-bootstrap flows. | ||
| - Provide auditable success and failure outcomes. |
There was a problem hiding this comment.
What's the test plan going to be? how are we going to verify this feature is ready for production?
There was a problem hiding this comment.
Good question, I hadn't fully fleshed out that yet but I've added a section on testing so we have an idea of what the e2e behavior should be
| - Namespace and ServiceAccount identity (including audience/issuer constraints where required). | ||
| - The Quay subject to use, such as a robot account or service identity. | ||
| - The permitted Quay organizations, repositories, operations, or Management API scopes. | ||
| - Lifecycle, ownership, audit, and revocation behavior. |
There was a problem hiding this comment.
So to be clear, a Quay admin will define the mapping between a k8s SA to an existing robot account?
There was a problem hiding this comment.
seems to be the way it's heading, at least in my head right now, the responsibility for the mapping should fall on the quay admin and we just provide the tools for it to happen
| **Concerns:** Kubernetes verbs/resources do not map naturally to Quay repository, organization, and | ||
| **Management API** scopes; it adds another Kubernetes API dependency and call; and it makes Quay | ||
| permissions indirect and harder to audit. **SAR** is appropriate only if the product deliberately | ||
| defines Kubernetes RBAC as the source of truth for Quay access. |
There was a problem hiding this comment.
The permissions models not lining up properly is a fairly large issue. It's going to be challenging to match RBAC permissions to our existing auth model. It's going to be confusing as the user as well. It also adds yet another source of truth for authentication model instead of reusing what we have now.
Summary
References