Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions .cargo/config.toml.example
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
# Copy to .cargo/config.toml to build the ABI V9 / RESOLVE_UNIX path:
# cp .cargo/config.toml.example .cargo/config.toml
# NOT NEEDED FOR A NORMAL BUILD as of v0.2.
#
# The V9 landlock crate is wired in by a committed `[patch.crates-io]` section in
# Cargo.toml pointing at `third_party/rust-landlock`, whose sources are VENDORED
# (committed to this repo directly — not a git submodule), so a fresh clone
# builds with no init step and no override:
#
# cargo build --features resolve-unix
# `paths` must point at a landlock checkout that adds ABI V8/V9 +
# AccessFs::ResolveUnix — upstream rust-landlock PR #135
# (https://github.com/landlock-lsm/rust-landlock/pull/135). Its head branch lives
# in the author's fork, so fetch it by PR number rather than by branch name:
#
# (`--features resolve-unix` is mandatory since the V9 floor — a build without it
# stops at the compile_error! in src/landlock.rs. See D-4b.)
#
# NOTE: the Landlock ABI is a property of the RUNNING KERNEL —
# LANDLOCK_CREATE_RULESET_VERSION reports what the kernel implements. A V9-capable
# crate on a 6.15 kernel still detects V7, and oring refuses at the floor gate.
# Running needs an actual >= 7.1 kernel (or a VM booting one, as CI does), not
# just this crate.
#
# --- The only remaining use: point at a DIFFERENT landlock checkout ------------
# Useful when developing against the fork itself, or testing upstream
# rust-landlock#135 (https://github.com/landlock-lsm/rust-landlock/pull/135)
# before it is vendored into third_party/. Copy to .cargo/config.toml and adjust.
#
# A `paths` override SHADOWS the [patch.crates-io] in Cargo.toml and makes cargo
# emit a "recommended to use the [patch] feature" warning — that warning is
# expected here and means the override is in effect.
#
# git clone https://github.com/landlock-lsm/rust-landlock ../rust-landlock
# git -C ../rust-landlock fetch origin pull/135/head:pr135
# git -C ../rust-landlock checkout pr135
# (verified: oring builds and its full suite passes with --features resolve-unix
# against this checkout on a real V9 kernel.) Without this override the
# `resolve-unix` feature will not compile against the published crate, so
# `cargo install oring --features resolve-unix` needs a local #135 checkout until
# upstream ships V9 in a release.
paths = ["../rust-landlock"]
#
# paths = ["../rust-landlock"]
5 changes: 5 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
# SPDX-License-Identifier: BSD-3-Clause
# Installed by `make hooks`, never automatically — a repository that edits the
# developer's git configuration on build is a repository nobody should trust.
exec "$(git rev-parse --show-toplevel)/scripts/check-commit-msg.sh" "$@"
225 changes: 62 additions & 163 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,194 +1,93 @@
# SPDX-License-Identifier: BSD-3-Clause
# Makes the gates SECURITY.md/DESIGN describe REAL: fmt, clippy, cargo-deny,
# static-musl build + static-link assertion, and the test suite. Actions are
# pinned by SHA. GitHub's ubuntu-24.04 runner is below oring's V7/6.15 floor, so
# on it the confinement tests SKIP-and-count (require_or_skip gates on
# ABI>=FLOOR_ABI) and lint-build instead asserts the real fail-closed REFUSE.
# The confinement suite is WITNESSED on a real >=floor kernel by the
# `confinement-vng` job below: it boots a >=6.15 guest under virtme-ng/KVM (KVM
# is available on the free runner via a udev rule) and runs the suite with
# ORING_REQUIRE_KERNEL=1 so a skip is a HARD failure.
#
# CI is the make targets. Nothing here does anything a maintainer cannot run
# with one command, and nothing here is the authority on whether oring confines.
#
# WHAT THIS CANNOT DO, stated first because it is the whole shape of the file.
# oring's floor is Landlock ABI V10 / kernel 7.2. No hosted GitHub runner is at
# that floor — 7.2 is not released — so a hosted runner cannot witness a single
# containment cell. `make check` sets ORING_REQUIRE_KERNEL=1 precisely so a
# machine that cannot witness containment REFUSES to report that it did, which
# means running `make check` here would fail on every push, for a reason that has
# nothing to do with the commit.
#
# So this workflow runs the half a sub-floor runner can genuinely check: format,
# lints, dependency policy, the static build, and the seccomp SMT gate — which is
# pure computation over the emitted cBPF and needs no Landlock at all.
#
# CONTAINMENT IS WITNESSED BY `make check` ON A >=7.2 HOST, and nowhere else.
# On the maintainer's box that is 34 suites, 0 failed, plus a matrix of 34 checks
# with all 21 denial cells attributed against a bare control. There is no CI job
# that reproduces it and this file does not pretend otherwise.
#
# A previous version of this file defined three virtme-ng/KVM jobs that built a
# 7.2-rc4 kernel and ran the suite under it. They were never executed — its own
# header said so — and a job that has never run is not evidence, it is an
# intention with YAML around it. Deleted rather than left staged.
name: ci

on:
push:
pull_request:
workflow_dispatch: # manual trigger — also runs the staged confinement-v9 job
workflow_dispatch:

# Supersede an in-flight run on the same ref (the confinement-vng kernel build
# is long; don't stack two).
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
lint-build:
check:
runs-on: ubuntu-24.04
steps:
# Ring 1 is a rev-pinned git dependency on the maintainer's fork (V10),
# required to compile at all since the V10 floor (D-4b). Unlike the vendored
# copy it replaced, this needs the network at build time — `cargo` fetches
# the pinned revision.
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: rustup show && rustup target add x86_64-unknown-linux-musl
- run: sudo apt-get update && sudo apt-get install -y z3
- run: cargo install cargo-deny cargo-auditable --locked

- name: fmt
run: cargo fmt --check
# NOT `-D warnings`: that would escalate the intentional pedantic=warn
# lints to errors. The Cargo [lints] `clippy all=deny` already makes the
# hard set a build failure; pedantic stays advisory.
# `--features bpf-dump` (NOT --all-features): lint the analysis-only
# bpf-dump code (seccomp::dump_groups, the bpf-dump bin, tests/seccomp_smt.rs)
# too, but do NOT enable `resolve-unix` — that feature needs the out-of-tree
# V9 landlock fork (ABI::V8/V9), which the published crate lacks, so it
# cannot compile in CI (see .cargo/config.toml.example, #135).
run: make fmt
- name: clippy
run: cargo clippy --all-targets --features bpf-dump --locked
# One install of the security tooling from crates.io (--locked). cargo-deny
# comes from crates.io, NOT its marketplace action, whose bundled binary
# was too old to parse the current RustSec DB (CVSS 4.0). cargo-auditable
# embeds the dependency SBOM in the build below; cargo-audit scans it.
- name: install security tooling
run: cargo install cargo-deny cargo-auditable cargo-audit --locked
- name: cargo-deny (advisories/licenses/bans/sources)
run: cargo deny check
- name: static-musl build (with embedded SBOM)
run: cargo auditable build --release --locked --target x86_64-unknown-linux-musl
run: make clippy
# bans + licenses + sources only. `advisories` fetches the live RustSec
# database, so it would turn this red on a day nobody touched the
# repository; it lives in `make advisories` and `make check-full`.
- name: deny
run: make deny
- name: static-musl build with an embedded SBOM
run: make build
env:
# Pin the embedded git SHA to a declared input so the same commit
# yields the same bytes in CI and in a .git-absent tarball build.
# yields the same bytes here and in a .git-absent tarball build.
ORING_GIT_SHA: ${{ github.sha }}
# rustc links musl targets as static-pie, so file(1) reports "static-pie
# linked", NOT "statically linked" — the literal-string form of this
# assertion failed on a real build. Accept both static spellings; the
# thing being excluded is "dynamically linked".
# rustc links musl as static-pie, so file(1) says "static-pie linked", NOT
# "statically linked" — the literal-string form of this assertion failed on
# a real build. What is being excluded is "dynamically linked".
- name: static-link assertion
run: |
file target/x86_64-unknown-linux-musl/release/oring | tee /dev/stderr \
| grep -qE "static-pie linked|statically linked"
# End-to-end smoke of the SHIPPED musl binary (distinct from `cargo test`,
# which builds debug binaries). The hosted runner's kernel varies around
# the V7 floor: on a >=V7 runner oring seals and execs the target (exit 0);
# on a sub-floor runner it refuses (exit 120). Both are healthy fail-closed
# outcomes; anything else (crash/partial seal) is a real failure.
- name: shipped-binary seal smoke test
# Pure computation over the emitted cBPF — the H2 truncation guard
# (CVE-2019-7303's class) and the socket() protocol wall. No kernel needed,
# so this is real evidence even here.
- name: seccomp SMT gate
run: make smt

# The one containment fact a SUB-FLOOR runner can establish: that oring
# refuses rather than under-confines. Exit 120 is the fail-closed path and
# is what this runner should produce; exit 0 would mean the runner fleet
# reached 7.2 and this assertion needs revisiting.
- name: fail-closed refusal below the floor
run: |
set +e
target/x86_64-unknown-linux-musl/release/oring --profile balanced --workdir /tmp -- /bin/true
rc=$?; echo "exit=$rc"
case "$rc" in
0) echo "PASS: sealed and ran the target (runner is >=V7)" ;;
120) echo "PASS: refused below the V7 floor (fail-closed)" ;;
120) echo "PASS: refused below the V10 floor (fail-closed)" ;;
0) echo "FAIL: sealed on a runner expected to be below the floor —"
echo " the fleet moved; re-read this job and make check's kernel gate"; exit 1 ;;
*) echo "FAIL: unexpected exit $rc"; exit 1 ;;
esac
# Audit the SHIPPED binary's embedded SBOM against RustSec (complements the
# source-level cargo-deny advisories check above).
- name: audit the shipped binary (RustSec)
run: cargo audit bin target/x86_64-unknown-linux-musl/release/oring
# Unit tests run everywhere; confinement integration tests skip-and-count
# on this below-floor runner (require_or_skip). Not passing ORING_REQUIRE_KERNEL.
- name: tests
run: cargo test --locked
# Ring-2 truncation-invariance gate — the H2 regression guard (§5). Pure
# computation over the emitted cBPF, so it needs a solver but NOT a Landlock
# kernel, and runs fully on this runner (~2s). ORING_REQUIRE_SMT=1 makes a
# missing z3 fatal rather than a silent skip.
- name: install z3
run: sudo apt-get update && sudo apt-get install -y z3
- name: seccomp SMT gate
run: cargo test --locked --features bpf-dump --test seccomp_smt
env:
ORING_REQUIRE_SMT: "1"

# Witness containment on a real >=V7 kernel — the "proven/gated/witnessed" leg.
# The hosted ubuntu-24.04 runner kernel is now itself >=6.15 (V7), so boot IT
# directly under vng/KVM (~30s) instead of compiling a kernel. Clean guest,
# ORING_REQUIRE_KERNEL=1 so a skip is a HARD FAILURE. The V9/RESOLVE_UNIX path
# needs a 7.1 kernel — see the staged confinement-v9 job below.
confinement-vng:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: rustup show
- name: Enable KVM on the free runner
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
ls -la /dev/kvm
- name: Install virtme-ng
run: |
sudo apt-get update -qq
sudo apt-get install -y qemu-system-x86 >/dev/null
pipx install virtme-ng
vng --version
# Pre-build the test binaries on the host (glibc); the guest shares the
# host FS (hostfs), so the in-guest `cargo test` just executes them.
- name: Build test binaries (host)
run: cargo test --no-run --locked 2>&1 | tail -3
- name: Confinement suite under vng/KVM (runner kernel; skip => fail)
run: |
set -o pipefail
KREL="$(uname -r)"
sudo chmod +r "/boot/vmlinuz-$KREL" "/boot/config-$KREL" 2>/dev/null || true
# Boot the runner's own kernel (~30s) — no build. Propagate HOME +
# ~/.cargo/bin so host cargo/rustup (shared via hostfs) resolve.
run_in_vng() { timeout 600 vng --run "/boot/vmlinuz-$KREL" -- env HOME="$HOME" PATH="$HOME/.cargo/bin:$PATH" "$@"; }
echo "== guest Landlock ABI (proves >=V7) =="
run_in_vng sh -c 'echo "uname=$(uname -r)"; echo "lsm=$(cat /sys/kernel/security/lsm)"; python3 -c "import ctypes; print(\"GUEST_LANDLOCK_ABI=\"+str(ctypes.CDLL(None).syscall(444,0,0,1)))"' 2>&1 \
| grep -E 'uname=|lsm=|GUEST_LANDLOCK_ABI=' | tee guest.info
GABI="$(sed -n 's/.*GUEST_LANDLOCK_ABI=\([0-9]*\).*/\1/p' guest.info | tail -1)"
[ "${GABI:-0}" -ge 7 ] || { echo "FAIL: guest Landlock ABI ${GABI:-unknown} < V7 floor"; exit 1; }
echo "== confinement suite, ORING_REQUIRE_KERNEL=1 =="
run_in_vng ORING_REQUIRE_KERNEL=1 cargo test --locked 2>&1 | tee vng.log | tail -100
grep -qE 'test result: ok' vng.log || { echo "FAIL: suite not ok"; exit 1; }
- name: Live jail suite under vng/KVM (runner kernel)
run: |
set -o pipefail
KREL="$(uname -r)"
timeout 600 vng --run "/boot/vmlinuz-$KREL" -- env HOME="$HOME" PATH="$HOME/.cargo/bin:$PATH" \
ORING_REQUIRE_KERNEL=1 ORING_LIVE=1 \
cargo test --locked --test containment_live 2>&1 | tee live.log | tail -60
grep -qE 'test result: ok' live.log || { echo "FAIL: live suite not ok"; exit 1; }

# STAGED (on-demand: workflow_dispatch only). Builds a mainline 7.1 kernel
# (Landlock ABI V9) and runs the suite on it — the highest ABI, exercising the
# scope/abstract-socket cells at V9. The pathname-AF_UNIX cells still skip
# without `--features resolve-unix` + the out-of-tree fork (the published crate
# can't build V9), which is why this is not per-push: ~20min kernel build.
confinement-v9:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-24.04
timeout-minutes: 75
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: rustup show
- name: Enable KVM on the free runner
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
ls -la /dev/kvm
- name: Install virtme-ng + kernel build deps
run: |
sudo apt-get update -qq
sudo apt-get install -y qemu-system-x86 build-essential flex bison bc \
libelf-dev libssl-dev dwarves cpio >/dev/null
pipx install virtme-ng
vng --version
- name: Build test binaries (host)
run: cargo test --no-run --locked 2>&1 | tail -3
- name: Build the V9 kernel (mainline v7.1)
run: |
set -o pipefail
git clone --depth 1 -b v7.1 https://github.com/torvalds/linux kern
printf 'CONFIG_SECURITY=y\nCONFIG_SECURITY_LANDLOCK=y\nCONFIG_LSM="landlock,yama,bpf"\nCONFIG_NAMESPACES=y\nCONFIG_USER_NS=y\nCONFIG_NET_NS=y\nCONFIG_UTS_NS=y\nCONFIG_IPC_NS=y\nCONFIG_PID_NS=y\n' > landlock.cfg
( cd kern && timeout 3000 vng --build --config ../landlock.cfg --verbose 2>&1 | tail -20 )
- name: Confinement suite on V9 (skip => fail)
run: |
set -o pipefail
run_in_vng() { ( cd kern && timeout 600 vng -- env HOME="$HOME" PATH="$HOME/.cargo/bin:$PATH" "$@" ); }
echo "== guest Landlock ABI (expect V9) =="
run_in_vng sh -c 'echo "uname=$(uname -r)"; echo "lsm=$(cat /sys/kernel/security/lsm)"; python3 -c "import ctypes; print(\"GUEST_LANDLOCK_ABI=\"+str(ctypes.CDLL(None).syscall(444,0,0,1)))"' 2>&1 \
| grep -E 'uname=|lsm=|GUEST_LANDLOCK_ABI=' | tee guest.info
GABI="$(sed -n 's/.*GUEST_LANDLOCK_ABI=\([0-9]*\).*/\1/p' guest.info | tail -1)"
[ "${GABI:-0}" -ge 9 ] || { echo "FAIL: guest Landlock ABI ${GABI:-unknown} < V9"; exit 1; }
run_in_vng ORING_REQUIRE_KERNEL=1 cargo test --locked 2>&1 | tee vng.log | tail -100
grep -qE 'test result: ok' vng.log || { echo "FAIL: suite not ok"; exit 1; }
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ target/
*.sha256
*.swp
.cargo/config.toml
docs-internal/
# docs-internal/ REMOVED 2026-07-29 and deliberately NOT ignored any more.
# It held 75 review documents / 22,008 lines — untracked, inside the product repo,
# grading claims that ship. Corrections there were unrecoverable. It is now evidence
# in the maintainer's research corpus, tracked and gated. If a `docs-internal/`
# reappears here, it should show up in `git status` and be questioned, not hidden.
Loading
Loading