Skip to content

[infra] - Prove bwrap AppArmor policy on Ubuntu 26.04 without weakening the checks - #1641

Merged
cgfixit merged 3 commits into
mainfrom
agent/ubuntu-26-04-bwrap-apparmor-b991
Oct 13, 2026
Merged

cgfixit merged 3 commits into
mainfrom
agent/ubuntu-26-04-bwrap-apparmor-b991

Conversation

@cgfixit

@cgfixit cgfixit commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Proposed changes

Adds a non-required Ubuntu 26.04 diagnosis job for the bubblewrap AppArmor checks, and does not change the required checks. Linked issue: #1626.

The diff against main is two files: .github/workflows/ci.yml and .github/actionlint.yaml. The new job is ubuntu-26-04-bwrap-diagnose. It uses runs-on: ubuntu-26.04, continue-on-error: true, and timeout-minutes: 15. The job name is not a required check name. It installs the distro bubblewrap package, prints the AppArmor profiles and userns sysctls, and runs the same confinement command the 24.04 leg uses: under stock policy, after apparmor_parser -r loads deploy/apparmor/cyclaw-bwrap, and after that profile is removed. It also runs a copy of bwrap from the runner temp directory, so the attachment is not /usr/bin/bwrap. It does not write kernel.apparmor_restrict_unprivileged_userns.

.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.

The test matrix and the ubuntu-latest / ubuntu-22.04 bwrap steps are unchanged. This head does not make those steps pass on 26.04. It records why the canary failed.

On head 2a4b369c, run https://github.com/cgfixit/CyClaw/actions/runs/38352458013 job https://github.com/cgfixit/CyClaw/actions/runs/38352458013/job/115147715257 (ubuntu-26-04-bwrap-diagnose) completed with success. The log says PRETTY_NAME Ubuntu 26.04.1 LTS, VERSION_ID 26.04, VERSION_CODENAME=resolute. Apt policy says Installed: 0.11.1-1ubuntu0.3 and Candidate: 0.11.1-1ubuntu0.3. The version table lists 0.11.1-1ubuntu0.3 and 0.11.1-1 only. The script still printed noble_pin=present, because apt-cache show 'bubblewrap=0.9.0-1ubuntu0.3' exited 0 even though that version is not in the table.

kernel.apparmor_restrict_unprivileged_userns=1 and kernel.apparmor_restrict_unprivileged_unconfined=1. Stock policy loads /etc/apparmor.d/bwrap-userns-restrict, whose header is profile bwrap /usr/bin/bwrap flags=(attach_disconnected,mediate_deleted) {. Loaded profiles before any CyClaw profile: bwrap (enforce), unpriv_bwrap (enforce), unprivileged_userns (enforce). The stock confinement command exited 0. The child label was child_apparmor=bwrap//&unpriv_bwrap (enforce).

A copy at another path exited 1 with bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. The kernel log says apparmor="AUDIT" operation="userns_create" ... profile="unconfined" ... comm="bwrap-unprofile" requested="userns_create" target="unprivileged_userns" execpath="/home/runner/work/_temp/bwrap-unprofiled" and apparmor="DENIED" operation="capable" class="cap" profile="unprivileged_userns" ... comm="bwrap-unprofile" capability=12 capname="net_admin".

Loading deploy/apparmor/cyclaw-bwrap exited 0 and left both cyclaw-bwrap (unconfined) and bwrap (enforce) loaded. The same confinement command then exited 1 with bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. Removing cyclaw-bwrap made that command exit 0 again.

ubuntu-latest on this head was still queued when this description was written. invariant-guard on this head completed with success. pytest tests/ was not run.

Invariant / Governance Impact

  • None of I1, I2, I3, I4, I5, or I6. The diff does not touch gate.py, graph.py, soul handling, the sanitizer, auth, or retrieval.
  • Evidence: the changed paths are .github/workflows/ci.yml and .github/actionlint.yaml only. The invariant-guard check on head 2a4b369c completed with success.

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

Benefits / why

  • The required ubuntu-latest steps still assume stock AppArmor refuses bwrap until deploy/apparmor/cyclaw-bwrap loads. This run shows 26.04 already ships an enforce-mode bwrap profile for /usr/bin/bwrap, and that loading cyclaw-bwrap beside it makes the confinement command fail on RTM_NEWADDR. That is the evidence for a later strict check. This PR does not weaken the 24.04 refusal or confinement scripts.
  • The job is continue-on-error, so this diagnosis does not become a required check name.
  • The global userns sysctl is still read, not written. It was 1 on the runner.

Risks to monitor

  • The job uses Actions time on every pull request and on pushes to main. It is not a release gate. continue-on-error: true means a later failure of this job does not fail the workflow. The signal is the job log, not the workflow conclusion.
  • Loading cyclaw-bwrap on the 26.04 runner is diagnostic. The log shows that load is what turns a passing stock confinement command into bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted. The runner is discarded. This is not a reason to stack that profile on a host that already has /etc/apparmor.d/bwrap-userns-restrict.
  • apt-cache show 'bubblewrap=0.9.0-1ubuntu0.3' printed noble_pin=present while the version table did not list 0.9.0-1ubuntu0.3. Do not treat that line as proof the noble pin exists on 26.04.
  • .github/actionlint.yaml allowlists ubuntu-26.04 for actionlint only.
  • No new shortcut around audit convergence, RAG-first, soul writes, or subprocess isolation. The product runtime is unchanged.
  • Drift signal: probe stock-full exit 0, probe unprofiled-copy with capname="net_admin", and probe after-cyclaw-full with RTM_NEWADDR.
  • ubuntu-latest was still queued here, so this description does not claim the 24.04 leg passed on this head.

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.

Head described here: 2a4b369c. That commit is the diagnosis job plus GitHub's merge of main (663a8bbd, #1639). The required bwrap steps in the test matrix are the ones already on main.

What the 26.04 job actually showed, and what it did not change:

  • Stock /usr/bin/bwrap is confined by Ubuntu's bwrap profile (bwrap (enforce) stacked with unpriv_bwrap), and the confinement command exits 0.
  • An unprofiled path is transitioned to unprivileged_userns, which denies net_admin, and loopback setup fails with RTM_NEWADDR.
  • cyclaw-bwrap attaches to the same /usr/bin/bwrap path. After it loads, both profiles are present and the confinement command fails the same way. Unloading it restores exit 0.
  • kernel.apparmor_restrict_unprivileged_userns stayed 1. No sysctl write.

Local checks before this description, not a full suite: actionlint 1.7.12 with ShellCheck 0.9.0 on .github/workflows/ci.yml exited 0, and zizmor 1.28.0 --min-severity=high on .github/workflows/ reported no high findings. Those were run on the diagnosis commit before the GitHub merge of main. The hosted invariant-guard check on 2a4b369c succeeded. workflow-lint (actionlint + zizmor) was still queued. ubuntu-latest was still queued. pytest tests/ was not run.

Suggested merge order of open PRs

Trial merges verified (git merge --no-commit --no-ff + abort) against main @ 663a8bb on 2026-10-13.

#1637 · Blocked ⛔
Impact: Telemetry egress notes for the Keychain CLI and uv. Trial merge clean. No dependency on #1641.
Action: Do not merge while Check PR body against template is failing. Other checks were still running.

#1638 · Blocked ⛔
Impact: Ponytail audit notes. Trial merge clean. No dependency on #1641.
Action: Do not merge while Check PR body against template is failing. Other checks were still running.

#1640 · Blocked ⛔
Impact: Dependency currency notes. Trial merge clean. No dependency on #1641.
Action: Do not merge while Check PR body against template is failing. Other checks were still running.

#1641 · Blocked ⛔
Impact: Non-required Ubuntu 26.04 bwrap diagnosis. Trial merge clean. Draft; required bwrap steps unchanged.
Action: Do not merge and do not auto-merge. ubuntu-latest was still queued.

ELI5

The required Ubuntu test still expects stock AppArmor to block bubblewrap until CyClaw loads deploy/apparmor/cyclaw-bwrap. On the Ubuntu 26.04 runner that already happened in job ubuntu-26-04-bwrap-diagnose (run 38352458013), stock /usr/bin/bwrap is allowed because Ubuntu's own bwrap profile is loaded in enforce mode, and the confinement command exits 0. A copy of bubblewrap that is not at /usr/bin/bwrap is refused: the kernel denies net_admin under unprivileged_userns, and bubblewrap prints 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; removing CyClaw's profile makes the command exit 0 again. This pull request only adds that practice job and an actionlint allowlist for the ubuntu-26.04 label. It does not change the required 24.04 steps, it does not turn the kernel switch off, and it does not fix those steps for 26.04 yet. The practice job is continue-on-error. ubuntu-latest was still queued, so this does not claim that leg passed. Do not merge this draft.

Last updated: 2026-10-13 01:47 ET

cursoragent and others added 3 commits October 13, 2026 05:35
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

PR is a complete fill of .github/PULL_REQUEST_TEMPLATE.md (every section, ticked boxes, merge order, ELI5 last, last-updated stamp, title, branch).

@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-13T10:34:55.732619Z 2a4b369 Draft marked ready
ℹ️ 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 chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a4b369c4e

ℹ️ 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".

Comment thread .github/workflows/ci.yml
Comment on lines +546 to +547
if apt-cache show 'bubblewrap=0.9.0-1ubuntu0.3' >/dev/null 2>&1; then
echo "noble_pin=present"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Detect an absent Noble version from command output

When the pinned Noble version is absent—the expected Ubuntu 26.04 case—apt-cache show 'bubblewrap=0.9.0-1ubuntu0.3' still exits successfully with no records; this occurs with apt 2.8.3, and the commit's hosted 26.04 evidence reports the same false positive. The Ubuntu apt-cache manual defines show as displaying matching package records, so its exit status is not an existence predicate. This branch therefore emits noble_pin=present despite the version being unavailable; test for non-empty exact-version output or inspect apt-cache policy/madison instead.

AGENTS.md reference: AGENTS.md:L49-L49

Useful? React with 👍 / 👎.

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