5.5 KiB
Trusted Candidate Builder — CAP_SYS_PTRACE security contract
Status: formal (V2R2C2) · Applies to:
deploy/systemd/aios-trusted-candidate-builder.service (the live baseline unit)
0. Live baseline (rea truth, not nominal)
The authoritative deployed unit is the live systemd unit on the hw control
plane host:
User=root/Group=root(the builder runs as root)- socket:
aios-trusted-candidate-builder.socketListenStream=/run/aios-trusted-candidate-builder.sock,SocketUser=root,SocketGroup=gitops,SocketMode=0660 - credentials:
LoadCredentialEncryptedforsigning_private_key/builder_hmac(under/etc/credstore.encrypted/aios-trusted-candidate-builder/) andsource_admission_hmac(under/etc/credstore.encrypted/aios-release-source-admission/) - hardening:
NoNewPrivileges,PrivateTmp,PrivateNetwork,ProtectSystem=strict,ProtectHome,ProtectControlGroups,ProtectKernelTunables/Modules/Logs,RestrictAddressFamilies=AF_UNIX,LockPersonality,MemoryDenyWriteExecute,RestrictSUIDSGID,SystemCallArchitectures=native - capability:
AmbientCapabilities=CAP_SYS_PTRACE+CapabilityBoundingSet=CAP_SYS_PTRACE(live current state)
The tracked deploy/systemd/aios-trusted-candidate-builder.service is kept
byte-identical (LF-normalized) to the live unit so sha256 drift is detectable:
live sha256 = 9d194c23ec981528d29d55f601d1be858635ac6229c7e25c10736df4e44042d3.
This contract deliberately does NOT introduce a separate non-root
-ptraceunit. §2 of the V2R2C2 instruction forbids inventing a distinct service uid/group,ProtectProc/ProcSubset, credential paths or socket ownership that diverge from the live baseline. The live baseline is root + the single minimal capability, and that is the only variant tracked.
1. Why a capability is declared at all
TrustedCandidateBuilder._verify_peer validates the connecting peer before
signing any candidate. It reads three /proc/<pid> views of the peer:
| view | function | failure → error |
|---|---|---|
/proc/<pid>/cgroup |
_resolve_cgroups |
PEER_CGROUP_UNAVAILABLE |
/proc/<pid>/exe |
_resolve_process_identity |
PEER_PROCESS_UNAVAILABLE |
/proc/<pid>/cmdline |
_resolve_process_identity |
PEER_PROCESS_UNAVAILABLE |
A cross-uid readlink("/proc/<pid>/exe") for a peer of a different uid is
subject to the Yama ptrace_scope sysctl and returns EACCES for a process
without the minimal CAP_SYS_PTRACE. Result: the builder fails closed
(PEER_PROCESS_UNAVAILABLE), which is safe but loses availability for
legitimate cross-uid peers.
The minimal CAP_SYS_PTRACE restores the ability to inspect the peer's
/proc view across uids.
2. Capability sensitivity — MANDATORY reading
CAP_SYS_PTRACE is a high-sensitivity capability. It is not a read-only
helper: it broadens the process's observation and debugging authority across
uids, including ptrace, reading /proc/<pid>/mem, and inspecting another
user's process state. It is therefore:
- limited to the single capability
CAP_SYS_PTRACE(bothAmbientCapabilitiesandCapabilityBoundingSet), and - scoped to the dedicated builder service unit only.
It must never be used to imply "read-only" or "no control authority." It
carries real inspection/debug power and is declared for one concrete purpose:
restoring cross-uid /proc/<pid>/exe identity resolution for the builder's
peer validation. Nothing more.
Three-state verification contract
- A. No CAP_SYS_PTRACE (fail-closed): cross-uid
/proc/<pid>/exeraisesPermissionError(EACCES)→ resolvers fail closed →PEER_PROCESS_UNAVAILABLE(andPEER_CGROUP_UNAVAILABLEfor cgroup). Never degrade to allow. - B. Minimal CAP_SYS_PTRACE + legitimate peer: peer uid in
allowed_peer_uid, cgroup inallowed_peer_cgroups, executable matchesallowed_peer_executable_prefix, argv matches the runtime argument contract → readinessREADY. - C. CAP present but peer illegitimate: any identity check fails →
PEER_IDENTITY_DENIED/PEER_CGROUP_DENIED/PEER_PROCESS_DENIED. The capability must never bypass the identity policy — it restores the ability to perform identity validation, not the authorization itself.
3. Operational rules
- Keep
CapabilityBoundingSetandAmbientCapabilitiespinned to exactlyCAP_SYS_PTRACE. Never use capability wildcards (e.g.CAP_*applying to every capability). - Keep
NoNewPrivileges=true; ambient capabilities are the only inheritance path into the exec'd Python process. - Do not re-introduce a separate non-root unit with a different
uid/group/
ProtectProc/ProcSubset/credential paths/socket ownership (see §0). The root + single-capability unit is the live baseline. - Runtime verification (three-state) MUST be executed on the real Linux host that runs systemd; the adversarial states require real uids/cgroups/argv.
4. Testing
- Local (no
/procpeer needed): the resolvers are exercised with monkeypatchedPermissionError(EACCES)— mirroring the exact bytes a missingCAP_SYS_PTRACE/ cross-uid read produces — coveringcgroup → PEER_CGROUP_UNAVAILABLE,exe → PEER_PROCESS_UNAVAILABLE,cmdline → PEER_PROCESS_UNAVAILABLE(seetests/test_trusted_candidate_builder_captrace.py). - Host (Linux, required for final acceptance): run
systemd-analyze verifyon the tracked unit; confirm the unit matches the live sha256; launch with/without the capability; confirm A →PEER_PROCESS_UNAVAILABLE, B →READY, C →*_DENIED.