Repository navigation
fix: enforce --readonly in the router for every write (#75) - #77
Merged
Merged
Conversation
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.
Description
Closes #75.
--readonlyonly reached the middleware chain, and the chain runs only on container create. In read-only mode the proxy therefore still forwarded container lifecycle actions (start,stop,restart,kill,wait,pause,unpause),DELETE /containers/{name}and image pulls, although the README promised that read-only mode deniesPOST/PUT/DELETE/PATCH.What changes
NewRouter(m).WithReadonly(b), RustRouter::new(m).with_readonly(b), TSnew Router(m).withReadonly(b)), wired from--readonly. Its first check denies every method other thanGETandHEAD, on every path, with the existing messageread-only mode: <METHOD> requests are not allowed. Path shape (version prefix,%,//) cannot get around it.ReadonlyGateis removed in all three: it would be unreachable, as ExecGate was (refactor: remove the unreachable ExecGate; refresh docs and counts (#55) #74). The chain constructors lose theirreadonlyparameter. The docs now say "4 validation gates + 1 config mutator".spec/router.qnt): aREADONLYconstant, the propertyreadonlyAllowsOnlyReads, and arouter_readonlyinstance with rows for stop, delete and create (denied) and GET/HEAD (allowed). The four existing instances getREADONLY = false.make test-specrunsrouter_readonlywith--match='^readonly.*Test$', because the shared write rows are denied when the flag is on (documented inspec/README.md).TestRouteReadonly/test_route_readonly/ "denies every write in read-only mode"): 10 read-only rows plus a flag-off row; a handler test per language that a read-onlyDELETEgets 403 and never reaches the transport. Integration: a second compose serviceproxy-readonly(--readonly), with 5 checks:/_ping200, and stop, delete, create and pull each 403 with the exact read-only message.deploy/test.sh,spec/README.md; counts recounted.Found in review, not fixed here: #76 (P0)
Read-only mode still allows
GET, and a websocket attach is aGET. Through the Go proxy,GET /containers/{name}/attach/ws?stdin=1&stream=1withUpgrade: websocketis upgraded (101) and can write to the container's stdin, with or without--readonly. I reproduced it against Docker 29.4.0. Rust relays the101but the connection breaks; TS times out; neither delivered stdin. This predates this PR and affects every mode, so it is tracked in #76 and noted in the README.Review notes
bump=patch tag=v0.3.8.--readonlywill now get 403 for lifecycle actions,DELETEand pulls, as documented.Verification
d43c26f, tests only): the new tests do not compile (Goh.router.WithReadonly undefined; TSProperty 'withReadonly' does not exist on type 'Router').main's Go code with this branch'sdeploy/test.shand compose file:router_readonlywithREADONLY = falsefailsreadonlySoundTest,readonlyLifecycleStopDeniedTest,readonlyDeleteDeniedTestandreadonlyCreateDeniedTest; the two allow rows pass.make test-all: Go ok (108), Rust140 passed, TS157 tests, 156 pass, 1 skipped.make lint-all,make typecheck,make test-spec(7 runs),make verify([ok] No violation found, both modules) pass.ALL 50 TESTS PASSED; socket suiteALL 15 TESTS PASSED.Type of change
Implementation(s) changed
Testing
make test-all)make verify,make test-spec)Checklist