Skip to content

Harden Dev Container capabilities and PID bound to match installers - #174

Merged
BrettKinny merged 3 commits into
mainfrom
hardening/devcontainer-parity
Oct 1, 2026
Merged

BrettKinny merged 3 commits into
mainfrom
hardening/devcontainer-parity

Conversation

@BrettKinny

Copy link
Copy Markdown
Collaborator

Summary

install.sh, install.ps1 and docker-compose.yml create the Box with --cap-drop=ALL plus a small re-added capability set and --pids-limit=4096. .devcontainer/devcontainer.json had neither. This PR adds the same hardening through runArgs:

--cap-drop=ALL --cap-add=CHOWN --cap-add=DAC_OVERRIDE --cap-add=FOWNER
--cap-add=SETUID --cap-add=SETGID --cap-add=KILL --cap-add=SYS_CHROOT
--pids-limit=4096

runArgs is used because the Dev Containers spec has no PID-limit property and no way to drop capabilities (capAdd can only add).

Why the extra SYS_CHROOT

The digest-locked sshd:1.1.0 Feature runs OpenSSH 10.2, whose privilege separation chroots the pre-auth child into /run/sshd. Without SYS_CHROOT, sshd -d logs chroot("/run/sshd"): Operation not permitted [preauth] and every connection is reset during key exchange. No other capability was needed: the Feature listens on port 2222, so NET_BIND_SERVICE isn't required, and logins succeeded without AUDIT_WRITE. The reason is recorded in SECURITY.md.

Verification

Ran devcontainer up (CLI 0.87.0) with --docker-path podman (rootless Podman 6.1.1) against a copy of the tree:

  • Build plus postCreate (scripts/devcontainer-postcreate.sh, which installed claude and node) finished with outcome: success
  • Effective caps were CHOWN DAC_OVERRIDE FOWNER KILL SETGID SETUID SYS_CHROOT, and /sys/fs/cgroup/pids.max was 4096
  • sudo -n apt-get update as dev succeeded
  • The Feature's sshd started at boot. A key-authenticated ssh -p 2222 dev@127.0.0.1 worked, both without and with a pty (-tt)
  • With the installer set only (no SYS_CHROOT), the SSH connection failed as described above

Not verified: rootful Docker (the Docker socket wasn't reachable) and Codespaces.

Tests

tests/test-repository-consistency.sh now checks that the Dev Container:

  • keeps --cap-drop=ALL and --pids-limit=4096
  • has no widening flags (--privileged, --cap-add=ALL, --security-opt, and no capAdd, privileged or securityOpt properties)
  • has a cap-add set equal to the install.sh set (also cross-checked against install.ps1) plus SYS_CHROOT

I confirmed the check fails when SYS_CHROOT or the PID limit is removed. All 19 host tests pass.

🤖 Generated with Claude Code

BrettKinny and others added 3 commits October 1, 2026 12:15
Apply the installers' --cap-drop=ALL capability set and --pids-limit=4096
to .devcontainer/devcontainer.json via runArgs. Add SYS_CHROOT, which the
SSH server Feature's OpenSSH privilege separation needs (chroot to
/run/sshd); without it every SSH connection is reset during key exchange.

A static consistency check now requires the Dev Container to drop all
capabilities, keep the 4096 PID bound, avoid widening flags, and use
exactly the installers' capability set plus the documented SYS_CHROOT.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@BrettKinny
BrettKinny merged commit 70d8d51 into main Oct 1, 2026
4 checks passed
@BrettKinny
BrettKinny deleted the hardening/devcontainer-parity branch October 1, 2026 04:46
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.

1 participant