Skip to content

docs: record DataAction registry invariant for CheckAccess mapping changes - #26

Merged
Artem Kolomeetc (arxhive) merged 3 commits into
masterfrom
docs/dataaction-registry-invariant
Aug 21, 2026
Merged

docs: record DataAction registry invariant for CheckAccess mapping changes#26
Artem Kolomeetc (arxhive) merged 3 commits into
masterfrom
docs/dataaction-registry-invariant

Conversation

@arxhive

@arxhive Artem Kolomeetc (arxhive) commented Aug 14, 2026

Copy link
Copy Markdown

Summary

  • Guard builds the Azure RBAC DataAction strings in getResourceAndAction and getDataActions. Guard does not own that action namespace.
  • An action string works only if the resource provider already publishes the same operation. If the operation does not exist, Azure denies the request and the customer cannot grant the permission.
  • This failure mode is easy to miss. Unit tests pass, and standard AKS clusters do not show the problem.
  • This change adds the rule, the verification command, and the current list of registered and unregistered actions to CLAUDE.md.

Documentation only. No code changes.

Changes

CLAUDE.md:

  • Two new items in Known Gotchas: the DataAction namespace rule, and the system: user bypass in authz/providers/azure/azure.go.
  • A new section, Changing the DataAction Mapping, that gives:
    • The az provider operation show command to verify each new action.
    • What happens when the operation is absent: ARM refuses the action in a custom role with InvalidDataActionOrNotDataAction. Reader is leaf-only, so it never matches. Writer is mixed, so it does match an unregistered action whose parent resource one of its wildcards covers. Only a covering wildcard matches at all.
    • The registered and unregistered action inventory, measured on 2026-08-13.
    • Two review traps: a unit test asserts the string but not the grantability, and system: identities hide the change on standard clusters.

How the facts were verified

Each statement was measured, not assumed:

Statement Method
pods/exec/action is registered az provider operation show --namespace Microsoft.ContainerService
certificatesigningrequests/nodeclient/action is absent same command
pods/{attach,portforward,proxy}/action are absent same command
services/proxy/action and nodes/proxy/action are absent same command
ARM refuses an unregistered DataAction az role definition create returned InvalidDataActionOrNotDataAction
Reader is leaf-only; Writer is 24 wildcards / 11 leaf az role definition list for all four built-in roles

Architecture Diagrams

How Guard resolves a DataAction today, and where an unregistered action fails:

flowchart TD
    A[SubjectAccessReview] --> B{User starts with 'system:'?}
    B -->|Yes| C[NoOpinion. Azure RBAC is skipped.<br/>Standard AKS nodes stop here.]
    B -->|No| D[getResourceAndAction]
    D --> E[DataAction string]
    E --> F{Operation published by the RP?}
    F -->|Yes| G[Role can grant it.<br/>Allow or deny by assignment.]
    F -->|No| H["Only a covering wildcard matches.<br/>Writer where it has resource/*,<br/>otherwise Admin and Cluster Admin."]
    H --> I[Denied for Reader and<br/>for every custom role]
    I --> J[Customer cannot fix it.<br/>ARM refuses the action<br/>in a custom role.]

    style C fill:#fff4ce,stroke:#8a6d00,color:#000
    style H fill:#ffd6cc,stroke:#a33,color:#000
    style I fill:#ffd6cc,stroke:#a33,color:#000
    style J fill:#ffb3a7,stroke:#a33,color:#000
Loading

Review process before and after this change:

flowchart LR
    subgraph BEFORE["Before"]
        A1[Change the mapping] --> A2[Add a unit test<br/>for the string]
        A2 --> A3[Tests pass]
        A3 --> A4[Merge]
        A4 --> A5[Denials found<br/>in production]
    end

    subgraph AFTER["After"]
        B1[Change the mapping] --> B2[Add a unit test<br/>for the string]
        B2 --> B3[NEW: check the RP<br/>operations registry]
        B3 --> B4{Operation exists?}
        B4 -->|Yes| B5[Merge]
        B4 -->|No| B6[Hold. The RP manifest<br/>must ship first.]
    end

    style A5 fill:#ffd6cc,stroke:#a33,color:#000
    style B3 fill:#d4f5d4,stroke:#2a7,color:#000
    style B6 fill:#fff4ce,stroke:#8a6d00,color:#000
Loading

Infrastructure Changes

None. No new environment variable, secret, or permission.

To run the verification command, an engineer needs an Azure CLI login. The command is read-only.

Trade-offs

  • The inventory has a date and will age. The registered and unregistered lists are correct on 2026-08-13. The az command next to them is the source of truth. The list helps a reader see the current shape. The command gives the current answer.
  • The rule does not run in CI. A CI check would need an Azure login with the correct subscription, so this is a written rule in CLAUDE.md, not a gate. A future automated check could parse the mapping and compare it against the operations registry.
  • The system: note describes present behaviour, not a recommendation. The bypass in authz/providers/azure/azure.go is recorded because it hides this class of change during testing. This PR does not propose to change it.
  • Documentation only. This PR does not change any action mapping. It gives reviewers the check to apply to the next mapping change.

…anges

Guard composes DataAction strings in getResourceAndAction/getDataActions, but
it does not own that namespace. An action string is only authorizable if the
resource provider already publishes the matching operation. When it does not,
ARM rejects the action in custom role definitions and the built-in Reader and
Writer roles enumerate leaf actions, so only wildcard-bearing roles can match.
The request is then denied for every principal on a least-privilege role with
no customer-side remediation.

Document the invariant, the az verification command, the current registered
and unregistered action inventory, and the two reasons this is easy to miss in
review: unit tests assert the composed string rather than its grantability,
and system:-prefixed identities keep standard clusters from reproducing it.
@ryanzhang-oss

Copy link
Copy Markdown

are the graphs in the description anywhere in the repo?

Document the DataAction Id composition path, which the Azure provider README
did not previously cover. The README described only the subresource attribute
allowlist, so the two subresource mechanisms were easy to conflate.

Add a DataAction Mapping section with three mermaid diagrams: how an action is
resolved, the review gate for a mapping change, and the split between the
DataAction Id and the subresource attribute. Record the registry invariant, the
az verification command, and the current registered and unregistered actions.

Clarify the existing Subresource Support section so it states that it covers
the attribute mechanism only, and cross-link the two. Point CLAUDE.md at the
README so the short operational form and the full explanation stay together.
Comment thread CLAUDE.md Outdated
Reader and Writer were both described as leaf-enumerated. That holds for
Reader, whose 31 DataActions all end in /read, but not for Writer: 24 of its
35 DataActions are resource wildcards, including
Microsoft.ContainerService/managedClusters/pods/*.

An Azure RBAC * spans /, so pods/* matches pods/attach/action. An unregistered
action is therefore still reachable whenever Writer carries a wildcard over the
parent resource. nodes/proxy/action and
certificatesigningrequests/nodeclient/action fall under no Reader or Writer
entry, so those stay Admin and Cluster Admin only.

Record the per-role shape and the per-action reachability, and correct the
resolution diagram node that named only Admin and Cluster Admin. Refresh the
inventory date after re-running both checks.

Measured with az role definition list for the four built-in roles and
az provider operation show --namespace Microsoft.ContainerService.
@arxhive

Copy link
Copy Markdown
Author

are the graphs in the description anywhere in the repo?

Yes, as of 42e6f03. They are in authz/providers/azure/README.md under the new "DataAction Mapping" section:

  • How an action is resolved is the first diagram from the description.
  • Review gate for a mapping change is the AFTER half of the second one. I dropped the BEFORE half, since it describes the state prior to this PR and dates immediately once the PR merges.
  • Two different subresource mechanisms is a third diagram that is not in the description. The README previously documented only the subresource attribute allowlist, so the DataAction Id path and the attribute path were easy to conflate.

CLAUDE.md keeps the short operational form and links to that README section, so the rule and the diagrams stay in one place.

The first diagram has since been amended in 8119167: its "wildcard roles match" node named only Admin and Cluster Admin, which chrischangcode's review showed to be wrong because Writer carries managedClusters/pods/*.

@arxhive
Artem Kolomeetc (arxhive) merged commit f9296e4 into master Aug 21, 2026
2 checks passed
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.

3 participants