Skip to content

List safe subresources instead of sensitive ones - #25

Open
Artem Kolomeetc (arxhive) wants to merge 1 commit into
masterfrom
fix/subresource-safelist-inversion
Open

List safe subresources instead of sensitive ones#25
Artem Kolomeetc (arxhive) wants to merge 1 commit into
masterfrom
fix/subresource-safelist-inversion

Conversation

@arxhive

Copy link
Copy Markdown

Follow-up to #22, addressing Mo Khan (@enj)'s review comment: #22 (comment)

This is not the right approach. It should be inverted to define what is safe (i.e. the status sub-resource), not what is unsafe.

Why

getResourceAndAction listed the subresources that must keep their own DataAction and collapsed everything else into the parent resource action. That default is the wrong way round - a subresource nobody has classified yet is silently covered by the parent resource's permission, so the list has to grow every time someone notices a new one. That list has in fact grown three times (exec, then nodeclient, then proxy, then attach/portforward), each time after the gap was found rather than before.

What changed

Collapse only the subresources upstream treats as part of the parent resource's permission, and give every other subresource its own <resource>/<subresource>/action.

The safe set is taken from the upstream view ClusterRole (bootstrappolicy.viewRules), which grants exactly these subresource kinds alongside their parent: status, scale, and pods/log. So safeSubresources = {status, scale, log, logs} (logs because guard has historically seen the plural spelling).

Unchanged by design:

  • Wildcard requests (* resource/subresource/verb) - expanded from the operations map elsewhere.
  • Special verbs (bind, escalate, use, impersonate, inference) - there the verb, not the subresource, identifies the operation, so userextras/scopes + impersonate stays userextras/impersonate/action.

Behavioral impact - please review

Anything not on the safe list now resolves to <resource>/<subresource>/action. The Microsoft.ContainerService operations registry currently defines only managedClusters/pods/{read,write,delete} and managedClusters/pods/exec/action, so these actions match only roles carrying a wildcard (managedClusters/*, pods/*) and are denied for roles that enumerate leaf actions. That is the intended fail-closed direction, but it is a behavioral change.

Two points that need an explicit decision:

  1. CSR subresources are now gated. certificatesigningrequests/approval and /approvals no longer collapse into the parent write/delete, and the same rule would apply to /nodeclient. Revert "fix: CSR subresource action collapse allowing unauthorized kubelet certificate issuance"聽#23 reverted fix: CSR subresource action collapse allowing unauthorized kubelet certificate issuance聽#12, which had gated nodeclient, so this rule partially re-applies what that revert removed. If CSR should stay collapsed until the corresponding DataActions exist in the registry, say so and I will add the CSR subresources to the safe set explicitly.
  2. Safe-set completeness. If any other subresource is expected to be covered by the parent permission today, it should be added to safeSubresources rather than discovered through a denial.

Testing

  • go build ./..., gofmt -l, golangci-lint run in ghcr.io/appscode/golang-dev:1.25 - 0 issues
  • go test ./authz/providers/azure/rbac/... ./auth/providers/azure/graph/... - all pass
  • Added coverage for: status/log/scale collapsing, an unclassified subresource getting its own action (the fail-closed default), and the existing exec/attach/portforward/proxy and special-verb cases.

The subresource handling in getResourceAndAction listed the subresources
that must keep their own DataAction and collapsed everything else into the
parent resource action. That default is wrong way round: any subresource
nobody has classified yet - including ones added by later Kubernetes
versions, by CRDs or by aggregated API servers - is silently covered by the
parent resource permission, and the list has to be extended every time a new
one is noticed.

Invert it. Collapse only the subresources that upstream Kubernetes treats as
part of the parent resource's permission - the read-only view ClusterRole
grants pods/log, pods/status and the /status and /scale subresources
alongside their parent - and give every other subresource its own
"<resource>/<subresource>/action" DataAction.

Wildcard requests and the special verbs (bind, escalate, use, impersonate)
keep their existing mapping: for those the verb, not the subresource,
identifies the operation being authorized.

Signed-off-by: Artem Kolomeetc <akolomeetc@microsoft.com>
@arxhive
Artem Kolomeetc (arxhive) requested a lite review from Copilot and removed request for Copilot August 12, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant