Skip to content

add deny flags for privileged, cap-add, and host namespaces#139

Closed
jalevin wants to merge 1 commit into
wollomatic:mainfrom
jalevin:feat/deny-hostconfig-fields
Closed

add deny flags for privileged, cap-add, and host namespaces#139
jalevin wants to merge 1 commit into
wollomatic:mainfrom
jalevin:feat/deny-hostconfig-fields

Conversation

@jalevin
Copy link
Copy Markdown

@jalevin jalevin commented May 15, 2026

Motivation

-allowbindmountfrom already body-inspects POST /containers/create and POST /containers/{id}/update for unsafe bind mount sources. But the same code path silently accepts other HostConfig fields that punch through container isolation:

  • HostConfig.Privileged=true — grants near-root inside the container, capability to access host devices, etc.
  • HostConfig.CapAdd — arbitrary kernel capabilities (SYS_ADMIN, NET_ADMIN, etc.)
  • HostConfig.NetworkMode=host / PidMode=host / IpcMode=host / UTSMode=host / UsernsMode=host — shares host namespaces

When socket-proxy is used to constrain what an untrusted client can ask the Docker daemon to do, these fields can defeat the bind-mount allowlist (e.g. --privileged plus a workdir bind, or --network=host to reach loopback services). This PR adds opt-in deny flags for each.

What changes

Three new flags, three matching env vars, three matching per-container labels. All default to off, so upgraders see no behavior change.

Flag Env var Container label Rejects when
-denyprivileged SP_DENYPRIVILEGED socket-proxy.deny.privileged HostConfig.Privileged=true
-denycapadd SP_DENYCAPADD socket-proxy.deny.capadd HostConfig.CapAdd non-empty
-denyhostnamespaces SP_DENYHOSTNAMESPACES socket-proxy.deny.hostnamespaces any of NetworkMode/PidMode/IpcMode/UTSMode/UsernsMode equals host (or host:* for modes that accept it)

Internally:

  • containerHostConfig is extended with the new fields, matching their JSON tags from github.com/docker/docker/api/types/container.HostConfig.
  • checkBindMountRestrictions(allowedBindMounts, r) is renamed and broadened to checkHostConfigRestrictions(allowedBindMounts, policy, r). Bind mount validation continues to work identically; the new hostConfigPolicy is purely additive.
  • A new checkHostConfigSecurity runs in the same body-inspection pass — no extra JSON parse, no extra socket roundtrip.
  • AllowList gains DenyPrivileged, DenyCapAdd, DenyHostNamespaces so per-container labels work alongside the default-allowlist flags.

Endpoint coverage

Applied to the same endpoints that already get bind-mount inspection:

  • POST /vX.YY/containers/create
  • POST /vX.YY/containers/{id}/update

Swarm service create/update only surface a Mounts list (not the host namespace / privileged fields), so the new checks intentionally do not fire there. The bind mount filter still applies to services as before.

Tests

  • TestCheckHostConfigSecurity — 14 cases covering each field individually plus combinations and the zero-policy no-op.
  • TestCheckHostConfigRestrictionsWithPolicy — 8 cases covering the HTTP dispatch path through container create/update and Swarm services, including the no-policy-and-no-bind-mounts early-return.
  • TestHostConfigPolicyIsZero — sanity check on the policy helper.
  • TestInitConfig_DenyHostConfigFlags, TestInitConfig_DenyHostConfigEnvVars, TestInitConfig_DenyHostConfigDefaultsFalse — config wiring for flags, env vars, and defaults.
  • Test_extractLabelData_DenyLabels — per-container label parsing including invalid value and unknown-key handling.

Existing tests are updated for the renamed checkBindMountRestrictionscheckHostConfigRestrictions signature and pass unchanged otherwise.

go test ./..., go vet ./..., and gofmt -l all clean.

Backwards compatibility

  • New flags default to off.
  • The renamed function is package-internal (lowercase), so nothing external depends on the old name.
  • Existing config (flags, env, labels) parses identically.
  • No new dependencies.

Out of scope / follow-up ideas

  • Symlink resolution on bind sources. The current validateBindMountSource uses filepath.Clean. A symlink planted inside an allowed dir pointing outside it is followed by the daemon at mount time, which is a known class of escape (cf. OpenClaw OC-13). EvalSymlinks would close this but requires the proxy container to have read access to the bind sources, which is a meaningful deployment change. Worth a separate discussion / issue.
  • docker exec / --mount on running containers. Not currently surfaced in this PR.

Happy to split into smaller commits, rename flags (e.g. -deny-privileged with dash), or invert the semantics (-allowprivileged defaulting to allow) if it fits the project's preferences better. The current style mirrors -allowGET/-allowbindmountfrom (one word, no dash) and the opt-in deny direction.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added security flags to restrict dangerous container configurations. New deny options allow rejecting containers with privileged mode, added capabilities, or host namespace access. Configure globally via CLI flags and environment variables, or apply per-container using Docker labels for granular control.
  • Documentation

    • Updated README documentation with security flag reference, configuration tables, and practical examples.

Review Change Stack

Body-inspecting bind mount validation already rejects unsafe `HostConfig.Binds`
sources, but the same code path silently accepts other `HostConfig` fields
that punch through container isolation: `Privileged`, `CapAdd`, and the
host-namespace modes (`NetworkMode`/`PidMode`/`IpcMode`/`UTSMode`/`UsernsMode`).

This adds three opt-in deny flags that extend the existing body inspection
in `POST /containers/create` and `POST /containers/{id}/update`. Each
defaults to off, so upgraders see no behavior change.

- `-denyprivileged` / `SP_DENYPRIVILEGED` — rejects `Privileged=true`
- `-denycapadd`     / `SP_DENYCAPADD`     — rejects non-empty `CapAdd`
- `-denyhostnamespaces` / `SP_DENYHOSTNAMESPACES` — rejects `host` (or
  `host:*`) values in any of the namespace mode fields

The deny flags are also exposed as per-container labels with the new
`socket-proxy.deny.` prefix (e.g. `socket-proxy.deny.privileged=true`)
to match the existing per-container allowlist pattern.

Swarm services do not surface these `HostConfig` fields in their API,
so the new flags have no effect on service create/update; only the bind
mount filter applies there as before.
Copilot AI review requested due to automatic review settings May 15, 2026 22:20
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 72c1c6b7-81d9-4bf5-843d-00f62f08ffdc

📥 Commits

Reviewing files that changed from the base of the PR and between cfc5255 and f624951.

📒 Files selected for processing (6)
  • README.md
  • cmd/socket-proxy/bindmount.go
  • cmd/socket-proxy/bindmount_test.go
  • cmd/socket-proxy/handlehttprequest.go
  • internal/config/config.go
  • internal/config/config_test.go

Walkthrough

This pull request introduces opt-in deny flags that allow socket-proxy to reject container creation and update requests containing unsafe HostConfig settings: privileged mode, capability additions, and host namespace modes. The feature integrates configuration parsing from CLI flags, environment variables, and Docker labels, with runtime validation enforced during request handling.

Changes

Host config deny policy enforcement

Layer / File(s) Summary
Deny policy data model and configuration parsing
internal/config/config.go, internal/config/config_test.go
New denyLabels structure, AllowList struct fields (DenyPrivileged, DenyCapAdd, DenyHostNamespaces), and parsing logic for CLI flags (-denyprivileged, -denycapadd, -denyhostnamespaces), environment variables (SP_DENYPRIVILEGED, SP_DENYCAPADD, SP_DENYHOSTNAMESPACES), and Docker label prefixes (socket-proxy.deny.*). Default deny values are false unless explicitly configured.
Host config validation and enforcement
cmd/socket-proxy/bindmount.go, cmd/socket-proxy/bindmount_test.go
Extends containerHostConfig to include Privileged, CapAdd, and namespace modes (NetworkMode, PidMode, IpcMode, UTSMode, UsernsMode). New hostConfigPolicy dispatcher and checkHostConfigSecurity enforcer detect container/service create/update endpoints and apply policy-driven rejections for privileged containers, non-empty CapAdd, and host namespace values (recognising "host" and "host:..." patterns). Comprehensive tests cover policy enforcement, request handling, and edge cases.
Request handler integration and documentation
cmd/socket-proxy/handlehttprequest.go, README.md
handleHTTPRequest builds hostConfigPolicy from the allowlist deny fields and calls checkHostConfigRestrictions. README documents the new opt-in security flags with endpoint applicability, composite configuration examples, and extended parameter/environment-variable reference table. Per-container allowlist examples updated with new deny label keys.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • wollomatic/socket-proxy#126: Modifies AllowList struct and label extraction parsing in internal/config/config.go and allowlist consumption in handlehttprequest.go, overlapping with this PR on allowlist configuration plumbing.

Poem

🐰 Hop, skip, and bound—this rabbit rejoices!
Safe containers now, through wise config choices.
No privileged shells, no capabilities to breach,
Just namespaces tamed, and security in reach! ✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jalevin
Copy link
Copy Markdown
Author

jalevin commented May 15, 2026

Closing — my claude session got a little overzealous. I'm testing locally.

@jalevin jalevin closed this May 15, 2026
@jalevin jalevin deleted the feat/deny-hostconfig-fields branch May 15, 2026 22:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds opt-in Docker HostConfig deny controls to socket-proxy so operators can block privileged containers, capability additions, and selected host namespace modes alongside existing bind-mount filtering.

Changes:

  • Adds CLI/env/config/label wiring for new deny flags.
  • Expands HostConfig request inspection to enforce bind-mount and security policies.
  • Documents the new controls and adds unit coverage for config parsing and policy checks.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
README.md Documents new deny flags, env vars, labels, and examples.
internal/config/config.go Adds deny policy fields, flag/env parsing, and label extraction.
internal/config/config_test.go Adds tests for deny flag/env/default and label parsing behavior.
cmd/socket-proxy/handlehttprequest.go Passes deny policy into HostConfig restriction checks.
cmd/socket-proxy/bindmount.go Implements HostConfig security checks and broadens bind-mount validation path.
cmd/socket-proxy/bindmount_test.go Updates existing tests and adds HostConfig policy coverage.
Comments suppressed due to low confidence (1)

internal/config/config.go:557

  • The event-driven path has the same policy bypass as the initial population path: a container with per-container allow labels gets an AllowList whose deny flags come only from labels, so globally enabled deny flags stop applying after this allowlist is added. Merge the default deny flags into this AllowList so start/restart updates cannot weaken the global policy.
			ID:                 cntr.ID,
			AllowedRequests:    allowedRequests,
			AllowedBindMounts:  allowedBindMounts,
			DenyPrivileged:     deny.Privileged,
			DenyCapAdd:         deny.CapAdd,
			DenyHostNamespaces: deny.HostNamespaces,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/config/config.go
Comment on lines +482 to +484
DenyPrivileged: deny.Privileged,
DenyCapAdd: deny.CapAdd,
DenyHostNamespaces: deny.HostNamespaces,
Comment thread internal/config/config.go
Comment on lines +769 to +778
parsedVal, err := strconv.ParseBool(labelValue)
if err != nil {
return nil, nil, denyLabels{}, fmt.Errorf("invalid boolean value %q for label %s", labelValue, labelName)
}
switch denySpec {
case "privileged":
deny.Privileged = parsedVal
case "capadd":
deny.CapAdd = parsedVal
case "hostnamespaces":
Comment on lines +55 to +63
Binds []string // List of volume bindings for this container.
Mounts []mountMount `json:",omitempty"` // Mounts specs used by the container.
Privileged bool `json:",omitempty"` // Is the container in privileged mode.
CapAdd []string `json:",omitempty"` // List of kernel capabilities to add to the container.
NetworkMode string `json:",omitempty"` // Network namespace ("host" gives host networking).
PidMode string `json:",omitempty"` // PID namespace ("host" gives host PID).
IpcMode string `json:",omitempty"` // IPC namespace ("host" gives host IPC).
UTSMode string `json:",omitempty"` // UTS namespace ("host" gives host UTS).
UsernsMode string `json:",omitempty"` // User namespace mode ("host" disables user namespace remapping).
Comment on lines +150 to +153
// checkService checks bind mounts and host config in service creation/update requests.
// Swarm services only allow specifying Mounts (not Binds) and do not expose the
// host-namespace/privileged fields, so only the Mounts list is forwarded for validation.
func checkService(allowedBindMounts []string, policy hostConfigPolicy, r *http.Request) error {
Comment on lines +150 to +153
// checkService checks bind mounts and host config in service creation/update requests.
// Swarm services only allow specifying Mounts (not Binds) and do not expose the
// host-namespace/privileged fields, so only the Mounts list is forwarded for validation.
func checkService(allowedBindMounts []string, policy hostConfigPolicy, r *http.Request) error {
Comment thread README.md
| --- | --- | --- |
| `-denyprivileged` | `SP_DENYPRIVILEGED` | `HostConfig.Privileged == true` |
| `-denycapadd` | `SP_DENYCAPADD` | `HostConfig.CapAdd` is non-empty (any added kernel capability) |
| `-denyhostnamespaces` | `SP_DENYHOSTNAMESPACES` | `HostConfig.NetworkMode` / `PidMode` / `IpcMode` / `UTSMode` / `UsernsMode` equals `host` (or a `host:*` form for modes that accept it) |
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.

2 participants