✨ feat(filter): swarm ContainerSpec User/Privileges enforcement parity - #92
Merged
Conversation
Service create/update now mirrors four container-create hardening knobs onto the swarm ContainerSpec, closing a bypass where a service could request a workload posture /containers/create would have denied: - 🔒 require_non_root_user → ContainerSpec.User (reuses the numeric-UID parse, so zero-padded "00"/"0:0" forms are rejected, not just "0") - 🔒 require_no_new_privileges → ContainerSpec.Privileges.NoNewPrivileges (a direct bool, unlike the container-create SecurityOpt string) - 🔒 require_readonly_rootfs → ContainerSpec.ReadOnly - 🔒 require_drop_all_capabilities → ContainerSpec.CapabilityDrop ⊇ "ALL" All default off. Swarm has no privileged mode, per-service namespace sharing, or runtime/device selection, so those container-create rails have no service equivalent and are intentionally not mirrored. Seccomp/ AppArmor map only to a narrow enum (unconfined/disabled) rather than the named-profile allowlist, deferred to avoid divergent semantics. - ✨ feat(config): add the four require_* service knobs + Viper defaults - 🧪 test: rail-by-rail deny/allow, zero-padded UID, update path, env-var registration guard, and options-mapping coverage - 📝 docs(changelog/readme/config): document the new rails and env vars
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
biggest-littlest
approved these changes
Jun 11, 2026
biggest-littlest
left a comment
Member
There was a problem hiding this comment.
Approving: swarm ContainerSpec User/Privileges parity. CI fully green (36/36), scope cleanly mirrors the container-create rails with no phantom swarm fields, tests cover every rail + zero-padded UID + env-var registration.
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.
What
Service create/update now enforces the same identity/privilege hardening rails the container-create inspector already enforces, mirrored onto the swarm
ContainerSpec. This closes a bypass where a swarm service could request a workload posture thatPOST /containers/createwould have denied.Four new opt-in
request_body.serviceknobs (all default off):require_non_root_userUserrequire_non_root_userrequire_no_new_privilegesPrivileges.NoNewPrivileges(bool)require_no_new_privilegesrequire_readonly_rootfsReadOnlyrequire_readonly_rootfsrequire_drop_all_capabilitiesCapabilityDrop ⊇ "ALL"require_drop_all_capabilitiesThe root-user check reuses container-create's numeric-UID parse, so zero-padded
"00"/"0:0"forms are rejected, not just the literal"0".NoNewPrivilegesis a directPrivilegesboolean here (a nilPrivilegesblock = unset = denied), unlike the container-createHostConfig.SecurityOptstring encoding.Scope rationale
Swarm has no privileged mode, no per-service namespace sharing (
PidMode/IpcMode/UsernsMode), and no runtime/device selection inContainerSpec, so those container-create rails have no service equivalent and are intentionally not mirrored. Seccomp/AppArmor map only to a narrow enum (unconfined/disabled) rather than container-create's named-profile allowlist — deferred to a follow-up to avoid shipping divergent semantics under the same config names (tracked in the README roadmap).Tests
root/0:0UID rejectionNoNewPrivilegesnil-block vs explicit-false vs true/services/*/updatepathgofmt,go vet,golangci-lint, and fullgo test ./...all clean locally; pre-push suite passed.Docs
CHANGELOG
Added, README roadmap (item moved to done; seccomp/apparmor remainder noted), andconfiguration.mdx(prose, options table, example YAML, four env-var rows) updated atomically.