-
Notifications
You must be signed in to change notification settings - Fork 68
OSAC-4109: wire real fulfillment VolumeClient into OSAC CSI driver #405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,13 +140,16 @@ is_tenant_idp_manager if { | |
| role in tenant_idp_manager_roles | ||
| } | ||
|
|
||
| # CSI driver realm roles. The OSAC CSI driver authenticates as a per-tenant Keycloak client | ||
| # ("osac-csi-<tenant>") whose service account is granted the "osac-csi" realm role. | ||
| # CSI driver realm roles. The OSAC CSI driver's Keycloak service account is granted the | ||
| # "osac-csi-driver" realm role. The role name is distinct from VAST's array-side VMS role | ||
| # "osac-csi-<tenant>" to avoid collision. Short term the driver uses a single shared client; | ||
| # the end state is a per-tenant client ("osac-csi-driver-<tenant>") carrying that tenant's | ||
| # organization claim. | ||
| csi_client_roles := { | ||
| "osac-csi", | ||
| "osac-csi-driver", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] role-rename / backward-compatibility Role renamed from osac-csi to osac-csi-driver with no transition period. If Keycloak still has clients assigned the old role during rollout, they will be denied access. Requires coordinated Keycloak realm role rename at deployment time. Suggested fix: Verify Keycloak realm role rename is deployed before or atomically with this policy change. |
||
| } | ||
|
|
||
| # CSI driver identity. Authorization keys on the "osac-csi" realm role - assigned only by a | ||
| # CSI driver identity. Authorization keys on the "osac-csi-driver" realm role - assigned only by a | ||
| # realm administrator - rather than on the username. Keying on the username would be unsafe: | ||
| # users and service accounts share the same username field, so an ordinary user could obtain | ||
| # CSI permissions by choosing a matching username. The CSI driver is a restricted identity - | ||
|
|
@@ -454,11 +457,24 @@ subject_user = split(input.auth.identity.user.username, ":")[3] if { | |
| subject_tenant_result = ["*"] if { | ||
| is_admin | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [medium] scope-creep The PR renames the CSI realm role and grants universal tenant scope, extending beyond 'wire real fulfillment VolumeClient.' Both are justified prerequisites for the shared-client model, well-documented with tracking tickets (OSAC-4197, OSAC-3279). |
||
| # TEMPORARY (shared CSI model, OSAC-4109): the CSI driver currently authenticates with a | ||
| # single shared "osac-csi-driver" client that has no per-tenant organization claim, so it is | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [high] privilege-escalation The new subject_tenant_result = ["*"] if { is_csi } rule grants the CSI driver universal tenant scope. Combined with method-restricted access to private Volumes Create/Get/Delete/List, a compromised osac-csi-driver credential can list, create, and delete volumes across all tenants. The CSI ListVolumes client code filters only by metadata.name (not tenant), so a List call can return volumes across tenants. Acknowledged as temporary (OSAC-4109/OSAC-4197) but requires explicit human reviewer approval. Suggested fix: Add tenant to ListVolumesParams and include in the filter expression to limit cross-tenant exposure. Ensure OSAC-4197 (per-tenant CSI clients) is prioritized to restore identity-based tenant isolation. |
||
| # granted universal tenant scope here. The tenant a volume belongs to is carried on the | ||
| # request (metadata.tenant, from the StorageClass "tenant" parameter) rather than enforced by | ||
| # identity. This is a deliberate short-term shortcut: it means fulfillment does NOT enforce | ||
| # tenant isolation by identity for CSI calls. Remove this rule (and the "not is_csi" guards | ||
| # below) once per-tenant "osac-csi-driver-<tenant>" clients land - tracked in OSAC-4197. | ||
| subject_tenant_result = ["*"] if { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [high] privilege-escalation / tenant-isolation-bypass The CSI driver identity is granted universal tenant scope ([""]) via the new subject_tenant_result = [""] if { is_csi } rule. Any bearer of the osac-csi-driver realm role can operate on volumes belonging to any tenant. Tenant isolation for CSI calls is now entirely dependent on the application layer honoring the metadata.tenant field set by the CSI driver. The PR acknowledges this is temporary (OSAC-4197) and code comments document the shortcut. Suggested fix: 1. Ensure OSAC-4197 is prioritized with a firm deadline to revert to per-tenant osac-csi-driver- clients. 2. Add server-side validation in fulfillment-service Volume handlers to verify metadata.tenant matches a legitimate tenant. 3. Add audit logging for CSI-scoped volume operations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [medium] scope-creep The universal tenant scope grant is a significant relaxation of the multi-tenancy model. While the PR body declares this in scope and OSAC-4197 tracks the revert, the authorization policy change is distinct from the stated OSAC-4109 scope. Noted as a design decision — the universal scope is a prerequisite for the real VolumeClient to function until per-tenant clients exist. |
||
| is_csi | ||
| } | ||
|
Comment on lines
+461
to
+470
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift Do not grant the shared CSI identity universal tenant scope.
Use per-tenant CSI credentials before enabling this client. If the shared client must remain, enforce a trusted identity-to-tenant mapping before authorization. 🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Intentional and time-boxed for the shared-client model: tenant is carried on the request, not enforced by identity, in this window. Tracked for removal in OSAC-4197 (per-tenant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The security risk remains until OSAC-4197 provides per-tenant credentials and removes ✏️ Learnings added
🧠 Learnings usedYou are interacting with an AI system. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] authorization-fragility The not is_csi guards on JWT and serviceaccount subject_tenant_result rules are correct but no default subject_tenant_result is defined (pre-existing omission). Adding a default would make fail-closed behavior explicit. Suggested fix: Add default subject_tenant_result = [] to make fail-closed behavior explicit. |
||
| subject_tenant_result = subject_tenants if { | ||
| not is_admin | ||
| not is_csi | ||
| input.auth.identity.authnMethod == "jwt" | ||
| } | ||
| subject_tenant_result = [split(input.auth.identity.user.username, ":")[2]] if { | ||
| not is_admin | ||
| not is_csi | ||
| input.auth.identity.authnMethod == "serviceaccount" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # | ||
| # Copyright (c) 2025 Red Hat Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with | ||
| # the License. You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an | ||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| # specific language governing permissions and limitations under the License. | ||
| # | ||
|
|
||
| version: v2 | ||
|
|
||
| managed: | ||
| enabled: true | ||
| override: | ||
| - file_option: go_package_prefix | ||
| value: github.com/osac-project/osac/osac-csi-driver/internal/api | ||
| disable: | ||
| - module: buf.build/googleapis/googleapis | ||
| - module: buf.build/grpc-ecosystem/grpc-gateway | ||
| - module: buf.build/bufbuild/protovalidate | ||
|
|
||
| # The CSI driver only consumes the private Volumes service, so generation is | ||
| # scoped to that service and its import closure (volume + metadata + storage | ||
| # common types) rather than the whole private API. Keep this list in sync with | ||
| # the imports of volumes_service.proto / volume_type.proto if they change. | ||
| inputs: | ||
|
|
||
| - directory: ../fulfillment-service/proto/private | ||
| paths: | ||
| - ../fulfillment-service/proto/private/osac/private/v1/volumes_service.proto | ||
| - ../fulfillment-service/proto/private/osac/private/v1/volume_type.proto | ||
| - ../fulfillment-service/proto/private/osac/private/v1/metadata_type.proto | ||
| - ../fulfillment-service/proto/private/osac/private/v1/storage_common_type.proto | ||
|
|
||
| plugins: | ||
|
|
||
| - remote: buf.build/protocolbuffers/go:v1.36.5 | ||
| out: internal/api | ||
| opt: | ||
| - paths=source_relative | ||
| - default_api_level=API_HYBRID | ||
|
|
||
| - remote: buf.build/grpc/go:v1.5.1 | ||
| out: internal/api | ||
| opt: | ||
| - paths=source_relative |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[medium] permission-expansion
The role rename from per-tenant osac-csi- (with organization claim) to a single shared osac-csi-driver role (without organization claim) regresses the identity model's tenant granularity. A single credential compromise now affects all tenants. Deliberate trade-off tracked in OSAC-4197.
Suggested fix: Consider short-lived token rotation for the shared CSI credential and audit logging for cross-tenant CSI volume operations.