Skip to content

leaf/357-creds-net-enforcement: implement creds axis in prep-spawn-gh.sh and net tier allowlists in proxy.py #925

Open

Description

@ruslan-shaydullin

Charter: #357
Depends-on: #920

Purpose

Implement the creds and net enforcement axes in crewboss-prep-spawn-gh.sh and proxy.py. This leaf modifies the spawn pipeline so that CB_CREDS and CB_NET extracted from the role manifest are enforced at runtime. The test gate (capabilities-static.test.sh, classified ALLOW in leaf #920) runs when this PR merges and must pass.

You own: reference/runtime/crewboss-prep-spawn-gh.sh, reference/runtime/proxy.py.
Do NOT touch: files under reference/tests/, crewboss-spawn.sh (except the CB_PROFILE mapping in sibling leaf #924), manifest.sh, role .md files, .kafel files, or runtime-manifest.tsv.

Charter context (self-contained)

Charter #357 extends the PoC fs-ro physical-barrier (charter #356) with three enforcement axes. This leaf implements creds and net. Key code facts:

  • crewboss-prep-spawn-gh.sh line 10: sets GH_TOKEN unconditionally. Lines 12-19: export block for GH_TOKEN/GIT_CONFIG_*. Manifest library is sourced starting around line 32-61 (gated on CB_MANIFEST). Lines ~454-460: CB_FS_WORK/CB_FS_CBNET are extracted via manifest_role_field in this block. Line ~462: exec into spawn.sh.
  • crewboss-spawn.sh line 93: runs nsjail with -e (inherit-all-env). Any env var set at exec time flows into the jail, regardless of --env flags. This means merely skipping the export of GH_TOKEN is NOT sufficient — if GH_TOKEN is already in the calling environment (from launcher, CI, or shell), it still enters the jail via -e.
  • proxy.py: currently reads no CB_NET; allowlist is static. The Anthropic API endpoints (api.anthropic.com, console.anthropic.com) are already in ALLOW_EXACT and must remain always-on regardless of tier. platform.claude.com is explicitly NOT in the always-on baseline because crewboss-spawn.sh line 95 sets CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 in every jail (programmatic agents communicate exclusively via api.anthropic.com).

Task A — creds enforcement in crewboss-prep-spawn-gh.sh

All capability-var extraction happens in the 454-460 block (after manifest load), before the exec at line 462. NOT at lines 12-19, which precede manifest load.

In reference/runtime/crewboss-prep-spawn-gh.sh, within the same block as CB_FS_WORK/CB_FS_CBNET (lines ~454-460):

  1. Extract CB_CREDS via manifest_role_field "$CB_MANIFEST" "$ROLE" creds (same pattern as CB_FS_WORK/CB_FS_CBNET). Normalise to lowercase.

  2. Extract CB_NET via manifest_role_field "$CB_MANIFEST" "$ROLE" net. Normalise to lowercase. export CB_NET — this is the value proxy.py reads from the process environment before exec. Without this step, os.environ.get("CB_NET") in proxy.py returns None on every spawn and the default-deny branch fires unconditionally (net:board/git/open would be silently unreachable).

  3. If CB_CREDS=none: add unset GH_TOKEN GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0 HERE, in this block, BEFORE exec. Do NOT try to gate lines 12-19 instead — manifest is not loaded at line 12 so manifest_role_field is unavailable there. The unset clears the token that was set at line 10/12, so it is absent when spawn.sh is exec'd. The -e flag carries all set vars into the jail, so unset must happen before exec, not merely skip the line-12 export block.

  4. If CB_CREDS=board: existing export block at lines 12-19 is unchanged.

  5. If CB_CREDS=git-write: existing export block at lines 12-19 unchanged; additionally register an inline git credential helper that serves the token for git push authentication (e.g. git config credential.helper "...").

  6. Add an inline comment in the CB_CREDS block documenting:

    • Why unset is in the 454-460 block rather than at lines 12-19 (manifest not loaded until line 32+)
    • Why unset is required rather than conditional export skip (GH_TOKEN may be pre-set in calling env from launcher/CI; -e carries all set vars into the jail)
    • Whether -e in spawn.sh will be narrowed or retained, and why the unset-before-exec approach is sufficient either way

Task B — net enforcement in proxy.py

In reference/runtime/proxy.py:

  1. Declare {api.anthropic.com, console.anthropic.com} as a hardcoded always-on baseline set, NOT gated by CB_NET. These must be reachable at every tier because every Claude Code agent communicates via the Anthropic API. Add an inline comment: "platform.claude.com is intentionally excluded from the always-on baseline: crewboss-spawn.sh line 95 sets CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 in every jail; programmatic agents use api.anthropic.com exclusively."

  2. Read CB_NET from os.environ (set by prep-spawn-gh.sh Task A above) and build the effective allowlist:

    • none (or unset/unrecognized): Anthropic baseline only — deny all other CONNECT
    • board: Anthropic baseline + {api.github.com, github.com}
    • git: board + {codeload.github.com, objects.githubusercontent.com} + npm/pypi registries
    • open: current full ALLOW_EXACT + ALLOW_SUFFIX set (unchanged from today)
  3. Default-deny on unrecognized or unset CB_NET (fall back to none = Anthropic only). Log the effective net level at startup.

  4. Pass CB_NET to the proxy process via env — ensure the extraction in prep-spawn-gh.sh (Task A step 2) is how proxy.py receives the value.

Acceptance (machine)

  • check: grep -q "unset GH_TOKEN GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0" reference/runtime/crewboss-prep-spawn-gh.sh
  • check: grep -q "CB_NET" reference/runtime/crewboss-prep-spawn-gh.sh
  • check: grep -q "export CB_NET" reference/runtime/crewboss-prep-spawn-gh.sh
  • check: grep -q "api.anthropic.com" reference/runtime/proxy.py
  • check: grep -q "CB_NET" reference/runtime/proxy.py
  • check: bash reference/tests/capabilities-static.test.sh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions