fix: judge POST /volumes/create against allow-bind-sources - #442
Merged
Merged
Conversation
Closes #419. docs/policy.md said, for a long time: "Named volumes are not binds -- `-v myvol:/data` has no host path to restrict and is never denied by this rule." True of the common case, false in general. A local-driver volume can name a host path: docker volume create -d local -o type=none -o o=bind \ -o device=/mnt/c/secrets esc docker run -v esc:/out ubuntu cat /out/... /volumes/create was judged by nothing, and the container create that follows carries only the volume's NAME -- which bindSources deliberately skips, because a name is not a path. So with allow-bind-sources: [C:\work] in force, those two commands read C:\secrets. The rule was not weak here; it was absent. device=/ is the same trick against the whole guest filesystem. The device is a GUEST path -- dockerd is what opens it -- so it is mapped back from /mnt/<drive>/... to Windows form before comparison, the inverse of the translation the bridge already applies to binds. Two consequences, both documented: - a device under no Windows drive (/, /etc, /var/lib/docker) is refused. It is under no allowed root, and that is the point. - a third-party volume driver is refused while this rule is in force, because its options are its own vocabulary and cannot be checked. Reporting "checked" would be a lie. The local driver -- the default -- is unaffected. Wired on BOTH backends in the same change ----------------------------------------- combinedGate gets DenyVolumeCreate and a compile-time VolumeGate guard. This is deliberate and it is not speculative: deny-unattributable-builds shipped in this same release consulting both layers for create/pull/push and only the WSL layer for build, so the rule was inert on the wslc backend while `policy show` reported it active. A new gate method is exactly where that recurs. Verification ------------ Tested at both levels, because one of them is not enough and this release is the reason. deny-unattributable-builds passed every Rules test while being a no-op in the product, since nothing drove the path the bridge takes. So: Rules tests for the rule, Watcher tests for what the bridge installs, and an end-to-end test through RewriteBindsGuarded with the real policy.Watcher. Negative control -- with the route removed: status = 201, want 403 the request reached the engine; a denial must stop at the bridge The permitted cases are asserted too, so the rule cannot have bought safety by refusing ordinary named volumes: a plain volume and a device inside the allowed root both reach the engine and return 201.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #419.
docs/policy.mdsaid, for a long time: "Named volumes are not binds —-v myvol:/datahas no host path to restrict and is never denied by this rule." True of the common case, false in general.POST /volumes/createwas judged by nothing, and the container create that follows carries only the volume's name — whichbindSourcesdeliberately skips, because a name is not a path. So withallow-bind-sources: [C:\work]in force, those two commands readC:\secrets. The rule was not weak here; it was absent.device=/is the same trick against the whole guest filesystem.How it is judged
The
deviceis a guest path — dockerd is what opens it — so it is mapped back from/mnt/<drive>/...to Windows form before comparison. That is the inverse of the translation the bridge already applies to binds.Two consequences, both documented:
/,/etc,/var/lib/docker). Under no allowed root, and refusing is the point.localdriver — the default — is unaffected.Wired on both backends, in the same change
combinedGategetsDenyVolumeCreateand a compile-timeVolumeGateguard.Deliberate, and not speculative:
deny-unattributable-buildsshipped in this same release consulting both layers for create/pull/push and only the WSL layer for build, so the rule was inert on the wslc backend whilepolicy showreported it active. A new gate method is exactly where that recurs.Verification
Tested at both levels, because one is not enough and this release is the reason —
deny-unattributable-buildspassed everyRulestest while being a no-op in the product, since nothing drove the path the bridge takes. So:Rulestests for the rule,Watchertests for what the bridge actually installs, and an end-to-end test throughRewriteBindsGuardedwith the realpolicy.Watcher.Negative control — route removed:
The permitted cases are asserted too, so this cannot have bought safety by refusing ordinary named volumes: a plain volume and a device inside the allowed root both reach the engine and return 201.