Summary
config.default_policy: true (#105) composes a policy into every managed role,
with no way to exempt one. A deliberately narrow role cannot stay narrow.
Detail
The composition pass rewrites every entry in role_updates unconditionally
(quiltx/acl.py:3561 onward). The only role that escapes is a
config.unmanaged: true one, and that is a side effect of unmanaged roles having no
role_updates entry rather than an opt-out: declaring a role unmanaged changes far
more than its floor — quiltx stops creating, updating and deleting it, and its
permissions move to IAM where the downgrade analysis reports them as undetermined.
Impact
Service accounts, bot roles and canary-style roles are exactly the roles a stack
wants scoped to one bucket, and they are managed roles like any other. Today,
declaring an org-wide floor silently widens them too. The workaround — moving the
role to config.unmanaged: true — trades a small problem for a bigger one.
Proposed fix
A per-role opt-out, e.g. config.default_policies: false on a role entry, excluding
that role from the composition pass while leaving it fully managed.
Naming needs care: config.default_policy is the flag on a policy, so a role-side
key spelled almost identically will be misread. Alternatives worth weighing:
config.no_default_policies: true, or an explicit config.policies_only: true
meaning "this role composes exactly what it names".
Also worth deciding: whether an opted-out role should be reported in the diff, since
a floor that does not reach one role is the kind of exception a reviewer should see
rather than have to grep the file for.
Notes
Depends on #105 (merged in PR #107). Independent of the other follow-ups.
Summary
config.default_policy: true(#105) composes a policy into every managed role,with no way to exempt one. A deliberately narrow role cannot stay narrow.
Detail
The composition pass rewrites every entry in
role_updatesunconditionally(
quiltx/acl.py:3561onward). The only role that escapes is aconfig.unmanaged: trueone, and that is a side effect of unmanaged roles having norole_updatesentry rather than an opt-out: declaring a role unmanaged changes farmore than its floor — quiltx stops creating, updating and deleting it, and its
permissions move to IAM where the downgrade analysis reports them as undetermined.
Impact
Service accounts, bot roles and canary-style roles are exactly the roles a stack
wants scoped to one bucket, and they are managed roles like any other. Today,
declaring an org-wide floor silently widens them too. The workaround — moving the
role to
config.unmanaged: true— trades a small problem for a bigger one.Proposed fix
A per-role opt-out, e.g.
config.default_policies: falseon a role entry, excludingthat role from the composition pass while leaving it fully managed.
Naming needs care:
config.default_policyis the flag on a policy, so a role-sidekey spelled almost identically will be misread. Alternatives worth weighing:
config.no_default_policies: true, or an explicitconfig.policies_only: truemeaning "this role composes exactly what it names".
Also worth deciding: whether an opted-out role should be reported in the diff, since
a floor that does not reach one role is the kind of exception a reviewer should see
rather than have to grep the file for.
Notes
Depends on #105 (merged in PR #107). Independent of the other follow-ups.