fix: shrink workspace privilege and egress surface - #180
Conversation
Three independent problems, all of which weaken or complicate network
enforcement on workspace pods.
realworld ran its workspace container with privileged = true and
allow_privilege_escalation = true. Neither is needed: its image
(coder-aienv) ships no container runtime, and nothing in the template or
any of its modules uses sudo. memorycard runs the same image with both
set to false. A privileged container holds host capabilities and could in
principle tamper with the eBPF maps backing pod network policy, so this
also removed the main caveat on any egress-lockdown claim for the
template. devbox, datasci and universal-sandbox keep privileged: their
image installs podman and symlinks docker to it.
memorycard and realworld passed use_cached = true to vscode-web with no
install_prefix. The module resolves ${INSTALL_PREFIX}/bin/code-server and
falls back to /tmp/vscode-web, which is empty on a fresh pod, so both
templates re-downloaded the VS Code server from Microsoft on every start.
The image already bakes it into /usr/local/lib/vscode-web and symlinks
/usr/local/bin/code-server, so install_prefix = "/usr/local" makes the
cache hit. realworld's launch script hardcoded the old /tmp path, which
would have broken once the prefix moved, so it now calls code-server off
PATH as memorycard already did.
devbox's Boundary post-install script executed the config path as a
command and chmod'd the file before the heredoc created it. Under
/bin/sh with no set -e both failed silently, leaving the config at mode
644. Reordered so the file is written first and then restricted.
782e41e to
6a66832
Compare
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/a47a5bf0-a2c1-4323-98b6-ef5f53bf90a8 |
|
✅ Agent Validation PASSED for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/24e350f6-85f0-44b3-bce2-ecc3eeef39ad |
|
🔍 Build Failure Analysis for template
|
|
✅ Agent Validation PASSED for template
|
|
🔍 Build Failure Analysis for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/f780450b-7589-48a3-a5a9-6cc732a885d7 |
|
🔍 Build Failure Analysis for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/25f6b452-dbb6-4f5e-a5b4-d6d5dcea7c48 |
|
✅ Agent Validation PASSED for template
|
|
✅ Agent Validation PASSED for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/8a7eb680-b721-433c-87a4-bfb836949f7c |
|
✅ Agent Validation PASSED for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/b615cf21-072c-4c58-88ac-aa99903669ff |
|
✅ Agent Validation PASSED for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/ee682dbd-91c7-424b-97ea-33e0e4cf468c |
|
✅ Agent Validation PASSED for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/479974c2-feae-474c-b074-7ed02cb5a242 |
|
✅ Agent Validation PASSED for template
|
|
I independently checked the privilege-hardening portion of this PR using IaC-Guard-V 0.1.0a1 against the current base (7995315…) and head (4cf0bd6…). For kubernetes_deployment_v1.this, Checkov CKV_K8S_16 and CKV_K8S_20 both transition from failing at the base revision to passing at the PR head. Scanner-integrity, Terraform-parse, and target-scoped regression gates pass, resulting in VERIFIED. This is target-scoped verification of those two findings, not a whole-PR claim. Reproduction steps and the sanitized immutable report are here: |
Both templates set install_prefix = "/usr/local" on vscode-web but their extension-install loops still invoked /tmp/vscode-web/bin/code-server. coder-aidev is built FROM coder-aienv, which bakes VS Code Web into /usr/local/lib/vscode-web and symlinks /usr/local/bin/code-server, so nothing exists at the /tmp path and every extension install was failing. It failed silently: the loop only echoes on a non-zero exit and continues, and CI validates that the workspace and agent come up rather than that extensions landed. Call code-server off PATH, matching the realworld fix in this branch and what memorycard already did. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/7cc4758e-933e-4bf2-9efe-2871096411ed |
|
✅ Agent Validation PASSED for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/e945ac5f-df07-4806-83f3-5be4e9f73757 |
|
✅ Agent Validation PASSED for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/10fd39c3-dece-4d32-9f8c-b94084162497 |
|
✅ Agent Validation PASSED for template
|
|
Coder Agents Chat: agent finished or is awaiting input Chat: https://ai.coder.com/agents/7beecc59-c137-42a2-8837-0ca05b9b39cb |
|
✅ Agent Validation PASSED for template
|
Stacked on #179. Prerequisites for adding Cilium egress policy: each of these
either weakens the enforcement claim or enlarges the allowlist Cilium has to permit.
1. Drop unnecessary
privilegedfromrealworldrealworldran its workspace container withprivileged = trueandallow_privilege_escalation = true. Verified unnecessary:public.ecr.aws/f7a1d7a4/coder-aienv, installs no container runtime(
grep podman\|docker images/aws/coder-aienv/Dockerfile.noble→ nothing)sudoanywhere in the template, and none in any of its six registry modulesmemorycardruns the same image with both set tofalse, in productionA privileged container holds host capabilities and can in principle manipulate the
eBPF maps that back pod network policy, so this was the main caveat on any
egress-lockdown claim for the template.
memorycardwas previously the onlydefensible one; now it is two.
Not changed:
devbox,datascianduniversal-sandboxkeepprivileged.Their image (
coder-aidev) doesapt install podman podman-composeand symlinksdocker→podman, anduniversal-sandbox's MongoDB preset runssudo apt-get install, whichallow_privilege_escalation = falsewould break by settingno_new_privs. Also unchanged:envbox(needs it) and selenium'sexectracesidecar.2.
install_prefixon vscode-web: stop re-downloading VS Code every startmemorycardandrealworldsetuse_cached = truewith noinstall_prefix.The module resolves
VSCODE_WEB="${INSTALL_PREFIX}/bin/code-server"and defaultsINSTALL_PREFIXto/tmp/vscode-web, which is empty on a fresh pod, so the cachecheck at
run.sh:83misses and it downloads the server fromvscode.download.prss.microsoft.comon every workspace start.The image already bakes it in and symlinks
/usr/local/bin/code-server, soinstall_prefix = "/usr/local"makes the cache hit, matchingdevboxanddatasci.Stale
code-serverpath in the launch scriptsrealworld's launch script hardcoded/tmp/vscode-web/bin/code-server --install-extensionand would have broken as soonas the prefix moved. It now calls
code-serveroff PATH, asmemorycardalready did.This matters because the cached path
exit 0s before the module's own extensioninstall, so that loop is the real extension installer.
devboxanddatascihad the same bug already live onmain: both setinstall_prefix = "/usr/local"but still invoked/tmp/vscode-web/bin/code-server.coder-aidevis builtFROM coder-aienv, which bakes VS Code Web into/usr/local/lib/vscode-weband symlinks/usr/local/bin/code-server, so nothingexists at the
/tmppath and every extension install in those two templates wasfailing. Silently: the loop only echoes on a non-zero exit and continues, and CI
checks that the workspace and agent come up rather than that extensions landed.
Fixed the same way.
Not touched:
universal-sandbox, whose matching line is commented out and whoseprefix is
$HOME/.vscode-serverrather than/usr/local.Removes two Microsoft CDN domains from the eventual allowlist, and cuts startup time.
3.
devboxBoundary config write bugUnder
#!/bin/shwith noset -eboth failed silently, so the Boundary allowlistended up mode 644 instead of 600. Reordered: write, then restrict. This is the L7
half of the enforcement design, so its config needs to actually be protected.
Verification
terraform fmt -check -recursiveclean;terraform validateSuccess on all fourtouched templates (
memorycard,realworld,devbox,datasci).CI builds a real workspace per template, so an agent that fails to start after
losing
privilegedor after thecode-serverpath change will fail the check.Rollback is re-activating the previous template version, per
--activate=false.🤖 Generated with Claude Code