Skip to content

[infra] - Select the bwrap AppArmor CI proof from Ubuntu VERSION_ID - #1644

Merged
cgfixit merged 2 commits into
mainfrom
agent/bwrap-apparmor-version-aware-1517
Oct 13, 2026
Merged

cgfixit merged 2 commits into
mainfrom
agent/bwrap-apparmor-version-aware-1517

Conversation

@cgfixit

@cgfixit cgfixit commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Branch naming (required for agent-opened PRs)

Head branch: agent/bwrap-apparmor-version-aware-1517.

The driver for this session is a Cursor cloud agent. The tracked hooks allow agent/ and refuse cursor/. The task asked for an agent/ branch cut from main, not #1641's branch agent/ubuntu-26-04-bwrap-apparmor-b991.

Title

[infra] - Select the bwrap AppArmor CI proof from Ubuntu VERSION_ID

Proposed changes

Makes the Linux bubblewrap AppArmor proof in .github/workflows/ci.yml follow VERSION_ID from /etc/os-release, and runs that same script on a non-required Ubuntu 26.04 job. Linked issue: #1626. Measured on #1641, run https://github.com/cgfixit/CyClaw/actions/runs/38352458013, job ubuntu-26-04-bwrap-diagnose.

scripts/ci/bwrap-apparmor-check.sh is the only copy of the proof. The required test matrix Linux legs and the new job both run bash scripts/ci/bwrap-apparmor-check.sh.

  • Ubuntu 22.04 keeps today's jammy pin bubblewrap=0.6.1-1ubuntu0.3 and the stock confinement command. It does not load cyclaw-bwrap.
  • Ubuntu 24.04 keeps today's behavior: noble pin bubblewrap=0.9.0-1ubuntu0.3, kernel.apparmor_restrict_unprivileged_userns must read 1, stock bwrap must be refused with a userns/permission denial, then deploy/apparmor/cyclaw-bwrap is loaded, then the confinement command must pass.
  • Ubuntu 26.04 and newer do not load cyclaw-bwrap and do not install 0.9.0-1ubuntu0.3. The script installs the candidate listed in apt-cache policy bubblewrap's version table (apt-cache show of the noble pin exited 0 on 26.04 even though that version was not in the table). It then requires the sysctl to read 1, Ubuntu's bwrap profile to be loaded in enforce mode, a copy of bwrap outside /usr/bin to fail with bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted, and the same confinement command to pass for /usr/bin/bwrap. The child label must be bwrap//&unpriv_bwrap (enforce).
  • Any other VERSION_ID fails the job.
  • The script only reads the sysctl. It never writes it.

The new job is ubuntu-26.04 bwrap (runs-on: ubuntu-26.04, continue-on-error: true, timeout-minutes: 15). That name is not ubuntu-latest, windows-latest, macos-latest, invariant-guard, or dotenv-guard. The test matrix entries are unchanged, so those required check names stay the same. When GitHub moves ubuntu-latest to 26.04, that required job follows VERSION_ID instead of loading cyclaw-bwrap on a host that already has Ubuntu's profile.

.github/actionlint.yaml allowlists the label ubuntu-26.04 for actionlint 1.7.12, which does not know that label. The label is a GitHub-hosted runner. The file does not provision a machine.

Operator docs that said "24.04 and newer: load cyclaw-bwrap" now say not to load it on 26.04, because that is the measured failure. The profile rules in deploy/apparmor/cyclaw-bwrap are unchanged.

Invariant / Governance Impact

  • None of I1, I2, I3, I4, I5, or I6. The diff does not touch gate.py, gate_ops.py, gate_auth.py, gate_memory.py, graph.py, mcp_hybrid_server.py, soul handling, the sanitizer, auth, or retrieval.
  • Evidence: the changed paths are the workflow, the shared shell script, the actionlint label allowlist, the bubblewrap operator docs, and a docstring on the static profile-scope test. No graph edge, gate route, or invariant-guard input changed.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Security (auth, privacy, injection, egress, redaction, or secrets)
  • Invariant / Governance refinement (use this for changes that strengthen or evolve the 6 invariants, I6 isolation, or harness phases)
  • Dependencies (added, removed, or bumped)
  • CI / infrastructure (workflows, hooks, templates, or checkers)
  • Refactor or tests (behavior-preserving, no new capability)
  • Documentation update
  • Other (none of the boxes above fit)

Optional free-text scope note (recommended):
Infrastructure / CI only. Operator docs for the bubblewrap profile were corrected so Ubuntu 26.04 is not told to load cyclaw-bwrap.

Benefits / why

  • On the Ubuntu 26.04 runner, stock /usr/bin/bwrap already runs under bwrap (enforce) and the confinement command exits 0. Loading deploy/apparmor/cyclaw-bwrap beside that profile makes the same command fail with bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. The required ubuntu-latest steps used to load that profile whenever the runner label was ubuntu-latest. After GitHub's move to Ubuntu 26, those steps would fail the confinement proof. This change keeps the 24.04 proof and switches 26.04+ to the stock profile instead of weakening the 24.04 refusal.
  • The 26.04 job runs the identical script, so the canary cannot drift from the required leg.
  • The global userns sysctl is still read, not written. On the measured runner it was 1.
  • apt-cache policy's version table is what decides the 26.04 package. The noble pin is refused if it is the candidate.

Risks to monitor

  • The 26.04 job uses Actions time on every pull request and on pushes to main. continue-on-error: true means a later failure of that job does not fail the workflow. The signal is the job log. The required ubuntu-latest leg is still blocking, and it will take the 26.04 branch once that image is what ubuntu-latest is.
  • A future Ubuntu version that is not 22.04, 24.04, or 26.04+ fails loudly. That includes 24.10 and 25.10. That is intentional.
  • The 26.04 package is whatever the version table's candidate is, not a frozen pin. A later archive update changes the installed version. The noble pin 0.9.0-1ubuntu0.3 is still refused.
  • The unprofiled-copy assertion requires the measured stderr bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. A different refusal string fails the job. That is not a loosened check.
  • .github/actionlint.yaml allowlists ubuntu-26.04 for actionlint only.
  • [infra] - Prove bwrap AppArmor policy on Ubuntu 26.04 without weakening the checks #1641 also edits .github/workflows/ci.yml. Trial-merging this branch after [infra] - Prove bwrap AppArmor policy on Ubuntu 26.04 without weakening the checks #1641 conflicts in that file. Do not merge both.
  • No new shortcut around audit convergence, RAG-first, soul writes, or subprocess isolation. The product runtime is unchanged. The 24.04 load commands are still the ones setup-guide.md gives stock 24.04 users.
  • Drift signal: on 26.04, kernel.apparmor_restrict_unprivileged_userns=1, a loaded line bwrap (enforce), the copy's RTM_NEWADDR line, and /usr/bin/bwrap exit 0 with child label bwrap//&unpriv_bwrap (enforce). On 24.04, the stock refusal line and a passing confinement command after apparmor_parser -r.

Checklist

  • I have read docs/THREAT_MODEL.md, INVARIANTS.md and SECURITY.md (and any relevant Phase docs)
  • This change preserves all 6 security invariants and I6 module isolation (explicit evidence or invariant matrix included for core changes)
  • Full sandbox validation has been run (GROK_API_KEY=dummy pytest tests/ -q --tb=short, and bash .claude/skills/CyClaw-Sandbox/verify.sh for core RAG/agentic paths) and passes with no regressions
  • No new external network dependencies or mandatory online LLM assumptions were introduced without explicit justification + offline fallback path
  • For any agentic/fsconnect change: two-phase audit, quota enforcement, governed delete/trash, and write guards have been verified
  • Relevant architecture docs or threat model notes have been updated if core behavior or topology changed
  • Commit messages follow the title prefix convention above
  • For large or complex changes: before/after invariant matrix + sandbox evidence is included in "Further comments" or linked

Further comments

Human review note: the diff touches .github/workflows/ci.yml and adds .github/actionlint.yaml. The checkout action pin is the existing SHA actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 (v7.0.1) with persist-credentials: false. No new third-party action. This PR is a draft. Do not merge it and do not enable auto-merge.

What #1641 measured, and what this PR does with it:

  • Stock policy loads /etc/apparmor.d/bwrap-userns-restrict (profile bwrap /usr/bin/bwrap flags=(attach_disconnected,mediate_deleted)). Loaded profiles include bwrap (enforce). The confinement command exits 0. The child label was bwrap//&unpriv_bwrap (enforce).
  • A copy at another path exits 1 with bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted.
  • Loading cyclaw-bwrap makes that same command exit 1 with the same line. Removing cyclaw-bwrap makes it exit 0 again.
  • Distro bubblewrap there is 0.11.1-1ubuntu0.3 (bwrap --version printed bubblewrap 0.11.1). apt-cache show 'bubblewrap=0.9.0-1ubuntu0.3' printed noble_pin=present while the version table listed only 0.11.1-1ubuntu0.3 and 0.11.1-1.

Local checks, not a full suite: bash -n on the script; a fixture of that version table (the noble string in a pin URL is not treated as a version row; 0.9.0-1ubuntu0.3 as a real candidate is recognized so the script can refuse it); is_2604_or_newer for 22.04, 24.04, 24.10, 25.10, 26.03, 26.04, 26.10, 28.04, and 08.04 (decimal, not octal); ShellCheck 0.9.0; actionlint 1.7.12; zizmor 1.28.0 --min-severity=high both offline and with a GitHub token; the five functions in tests/security/test_bwrap_apparmor_profile_scope.py; ruff F/B/S on that file. pytest tests/ was not run. Hosted proof is run https://github.com/cgfixit/CyClaw/actions/runs/38355973489, which completed with success on head 70610083: ubuntu-26.04 bwrap passed (job 115158837026) and required ubuntu-latest on Ubuntu 24.04.5 passed (job 115158836925), with all checks green.

Suggested merge order of open PRs

Trial merges verified (git merge --no-commit --no-ff + abort) against main @ 663a8bb on 2026-10-13. This branch after a local merge of #1641 conflicts in .github/workflows/ci.yml.

#1637 · Safe ✅
Impact: Telemetry egress notes for the Keychain CLI and uv. Trial merge clean. No file overlap with this PR.
Action: Merge when the in-progress checks finish green. No current failing check; an older template check was cancelled.

#1638 · Safe ✅
Impact: Ponytail audit notes. Trial merge clean. No dependency on this PR.
Action: Merge when the in-progress checks finish green. No current failing check; an older template check was cancelled.

#1640 · Safe ✅
Impact: Dependency currency notes. Trial merge clean. No dependency on this PR.
Action: Merge when the still-running checks finish green. No failing check at this trial merge.

#1641 · Blocked ⛔
Impact: Non-required Ubuntu 26.04 diagnosis. Trial merge against main is clean. Merging it before this PR conflicts in .github/workflows/ci.yml.
Action: Do not merge and do not auto-merge. This PR is the fix. Close #1641 if this one lands.

#1642 · Blocked ⛔
Impact: Skill-doc alignment. Trial merge clean. Check PR body against template is failing.
Action: Do not merge while that check is red and the PR is still a draft.

#1643 · Blocked ⛔
Impact: Checker-skill resync. Trial merge clean. Still a draft; checks were still queued.
Action: Do not merge until it is ready and its checks are green.

#1644 · Blocked ⛔
Impact: Version-aware bwrap proof. Draft. Replaces #1641's diagnosis job. Trial merge against main is clean.
Action: Do not merge and do not auto-merge. Required check names are unchanged. CI run 38355973489 passed on head 70610083. Still a draft; needs the Advisor's pass and the owner's go.

ELI5

The required Ubuntu test used to assume stock AppArmor blocks bubblewrap until CyClaw loads deploy/apparmor/cyclaw-bwrap. On Ubuntu 26.04 that assumption is backwards. Job ubuntu-26-04-bwrap-diagnose in run 38352458013 showed stock /usr/bin/bwrap already works, because Ubuntu's own bwrap profile is loaded in enforce mode, and the confinement command exits 0. A copy that is not at /usr/bin/bwrap is refused with bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. Loading CyClaw's profile next to Ubuntu's profile makes /usr/bin/bwrap fail the same way.

scripts/ci/bwrap-apparmor-check.sh reads VERSION_ID from /etc/os-release. Ubuntu 24.04 still does the old proof: refuse, load cyclaw-bwrap, then confine, and it still installs the noble package pin. Ubuntu 26.04 and newer do not load CyClaw's profile and do not install that pin. They check that the kernel switch still reads 1, that Ubuntu's bwrap profile is in enforce mode, that a copy outside /usr/bin is refused, and that /usr/bin/bwrap passes the same confinement command. Any other version fails on purpose. The script never turns the kernel switch off.

The required check is still named ubuntu-latest. A separate practice job named ubuntu-26.04 bwrap runs the same script on ubuntu-26.04 and is continue-on-error, so it is not a required check. pytest tests/ was not run here. Do not merge this draft, and do not turn on auto-merge.

Last updated: 2026-10-13 03:15 ET

Ubuntu 26.04 already enforces profile bwrap on /usr/bin/bwrap. Loading
cyclaw-bwrap beside it denies net_admin and the confinement command fails.
24.04 keeps the noble pin, the stock refusal, and the profile load.

Co-authored-by: Chris Grady <cgfixit@users.noreply.github.com>
@cgfixit

cgfixit commented Oct 13, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-13T12:47:18.087944Z 87b6b32 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 70610083c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…version-aware-1517

# Conflicts:
#	.github/workflows/ci.yml
@cgfixit
cgfixit enabled auto-merge October 13, 2026 13:02
@cgfixit
cgfixit merged commit 6396152 into main Oct 13, 2026
65 checks passed
@cgfixit
cgfixit deleted the agent/bwrap-apparmor-version-aware-1517 branch October 13, 2026 13:06
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