Repository navigation
docs: security-hardening program — ConfigService.Set advisory and phased hardening plan - #291
Merged
Merged
Conversation
randomizedcoder
force-pushed
the
docs/security-hardening
branch
from
November 9, 2026 01:27
9be3b52 to
f4e53a4
Compare
…sed hardening plan Adds docs/security-hardening/ with the full security assessment of the gRPC ConfigService.Set filesystem-write exposure and the program to close it: - security-vulnerability-configservice-set-fs-write.md — the advisory: the Set -> filesystem flow, the five Set-reachable write primitives, the fleet container deployment and permission model (NET_ADMIN/ SYS_ADMIN, read-only rootfs, private pidns), what the Nov 5 incident IOCs prove, and the defense layers L0-L6 - plan-configservice-set-hardening.md — workstreams W0-W10, issue register I1-I16, open decisions OD-1-OD-9, fleet rollout runbook - implementation-plan.md — phases 0-4 mapped to PRs with gates and a rollback matrix - phase-1-code-hardening.md — build-time elimination (dest_file/capture tags), protovalidate path patterns, runtime path policy + frozen fields, universal validating interceptor, Set audit, CI gates - phase-2-kernel-confinement.md — Landlock self-confinement: the ruleset, ABI degradation policy, re-exec persistence, and the full table-driven test matrices - phase-3-writer-split.md — privilege separation: writer process with a path-free proto envelope over a UDS (protovalidate both ends), SO_PEERCRED auth, monotonic seq, crash-safe commit dance, collector :ro flip, the complete write inventory, and the target admission checklist - phase-4-egress-fleet-lockdown.md — egress pinning in three layers (build tags closing OD-9, Landlock v4 network rules, security groups), escape-hatch removal, v1.x retirement, standing CI gates docs/README.md links the set from the security section.
randomizedcoder
force-pushed
the
docs/security-hardening
branch
from
November 9, 2026 01:51
f4e53a4 to
b70210a
Compare
…er perms - Narrow L4/L5 claims: docker.sock bypasses both; the writer accepts schema-valid content at the next seq, so L5 bounds shape, not truth. - Landlock: chmod/chown unmediated at every ABI, truncate from ABI 3, pathname-UDS connect from ABI 9 (RESOLVE_UNIX); tests B10/B14/B24, D7. - Apply the ruleset once and inherit it across soft restarts (16-layer cap); tests C6 (20 restarts) and C7 (forged marker). - Writer files 0640 group xtcp2-state; collector joins the group and keeps a read-only state-dir grant; writer-init chowns legacy files. - Split -writableDir / -readableDir; EvalSymlinks on the deepest existing ancestor so not-yet-created outputs validate. - unix_socket_mode allowlist in: [0, 384]; lte 384 accepted 0577. - Scope egress pinning to the collector's cgroup (nftables), not host-wide SG/UFW; fix the Test D anchor and -setAllowFrozenFields. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ency - setns: SYS_ADMIN enters net/UTS/IPC/cgroup/time namespaces only; mount needs CAP_SYS_CHROOT, PID only descendants. - :ro binds and the ro rootfs hold against SYS_ADMIN only because AppArmor docker-default denies mount; add it to the runtime table, restrictions and a fleet-gate regression guard (PidMode, ReadonlyRootfs, AppArmorProfile, remount probe). - dest pattern uses one colon (matches Phase 1); ipmeta cap 64 MiB everywhere; OD-3 aligned with W0; Set on an uncompiled scheme returns FailedPrecondition, not a restart loop; I15 renumbered to I14. - Phase 3 frame tests match the [len u32] + proto envelope (min_len 1, oneof required). Co-Authored-By: Claude Opus 5.5 <[email protected]>
…e, severity - New phase-5-privilege-brokers.md: netlink broker (proto over SOCK_SEQPACKET, SCM_RIGHTS fds), Docker broker (typed gRPC, fixed GET set, projection), per-process seccomp/AppArmor profiles, three enforcement layers per request, test tables P-U, rollout and decisions - Advisory: L7 section and matrix column; setns limited to network namespaces in the container; bpf/perf_event_open and NET_ADMIN reach spelled out; severity scoped to deployment shape - Plan: W10-W12, I15-I18, OD-10-OD-12; implementation plan Phase 5 (PR-18-PR-23); Phase 4 egress rule holds against collector RCE only after Phase 5 Co-Authored-By: Claude Opus 5.5 <[email protected]>
The daemon refuses to start (exit 78) without no-new-privs, a seccomp filter, a read-only root and a bounded capability set, and later Landlock. It checks confinement rather than container markers: those pass --privileged and fail a hardened systemd unit. A per-check -insecureAllowUnconfined / XTCP_INSECURE_ALLOW_UNCONFINED waiver is alerted on, and the NixOS module is hardened to pass without one. Phase 2's OD-2 changes from warn-and-continue to refuse unless waived. Co-Authored-By: Claude Opus 5.5 <[email protected]>
- allowed capability set per mode (writer, both brokers, broker-mode collector) so one list cannot wave through a privileged writer - in-repo deployments that would fail the check: k8s DaemonSet (also drops the unused CAP_SYS_CHROOT), dev docker-compose, quickstart sudo examples, nix capability-check - startup-failure tests assert the message, not a non-zero exit (PR-1 acceptance, image smoke test), since exit 78 would also match - Phase 3 writer container and bare-metal units carry read_only, no-new-privileges and systemd hardening; Xtcp2ConfinementWaived alert - stale Landlock degrade-not-exit wording in W4 and the advisory - W9 docs/metrics; W7 gate noted as the check's pre-deploy twin Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Netlink broker becomes a gRPC service (ListNetns, DumpSockets, DumpRoute) that builds and sends every netlink request from typed, validated fields and relays replies unfiltered as raw chunks; the collector no longer holds any netlink socket in a namespace - Kernel fact 5: a netlink socket has no receive-only mode and dump replies go only to the requester, so fd passing would leave sends filtered by the kernel alone - New pkg/ipc: gRPC over UDS for the writer and both brokers; peer uid checked in the transport handshake before HTTP/2 parsing - Writer moves to a WriterService (ipmeta as a client stream) - Netns ids become netns cookies; netns_direct build tag; collector seccomp forbids SOCK_DIAG; P12 relay benchmark gate with a shared-memory fallback; OD-10 revised, OD-14 added - Bare metal marked unsupported for the broker design Co-Authored-By: Claude Opus 5.5 <[email protected]>
Re-prioritizes the phased plan for container deployments with the admin surface off by default: what the code does today versus those assumptions, the remaining inputs, ranked risks, a Parquet-path audit, and a focused set — a listener model (endpoint classes mapped to listeners, UDS + bearer auth by default), the container spec, tenant-volume bounds, the Docker broker pulled forward from Phase 5, the capture writer as a compile-time opt-in, govulncheck — with table-driven tests. The writer split and netlink broker are deferred. Co-Authored-By: Claude Fable 5.1 <[email protected]>
- the gap table names the development compose and the production role's actual deltas; there is no in-tree reference container spec - B1 records that the daemon currently requires CAP_NET_ADMIN (init_capabilities.go:36), cites Phase 5 kernel fact 2 for what the cap buys, and orders B1 after the PR-18 capability settlement, now pulled forward - assumption 1: PR-24 refuses unconfined shapes unless waived; a hardened systemd unit passes - the S3 key flag is accepted silently, not warned - listener-config errors exit 64, never PR-24's 78 - Docker broker residual: host-userns deployments make uid 0 a weak SO_PEERCRED identity until the collector is non-root - scrape-topology migration step before the UDS default ships; Phase 0 retirement conditional on the fleet inventory Co-Authored-By: Claude Fable 5.1 <[email protected]>
…er hardening Phases F0-F6 and 17 PRs (FP-1..FP-17) covering the focused set: build tags for the capture and file writers, secrets and S3 transport, the listener model, volume bounds, the goip_write tag and capability settlement, the Docker broker on pkg/ipc, the confinement check, reduced Landlock and the reference container spec. Two releases (R1 defaults, R2 container shape), rollout order, gates and rollback. Co-Authored-By: Claude <[email protected]>
UDS scrape is the reference (tcp listener is the fallback), the production image is the Parquet-only flavor, the collector needs every socket so network_mode: host stays, and a PutObject-only key with S3_SKIP_BUCKET_PROBE is the recommended configuration. F0.1-F0.4 are marked decided in the plan. Co-Authored-By: Claude <[email protected]>
…ODOs Co-Authored-By: Claude Opus 5.5 <[email protected]>
This was referenced Nov 12, 2026
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
… quota; kernel facts, tests, benchmarks, TODOs Co-Authored-By: Claude Opus 5.5 <[email protected]>
…mespace port range Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
The full security assessment of the gRPC
ConfigService.Setfilesystem-write exposure, and the program to close it — eight documents underdocs/security-hardening/, linked fromdocs/README.md. Docs only: no code, proto, or build changes in this PR.Documents
Set→ filesystem flow; the fiveSet-reachable write primitives; who can callSet(auth off by default); the fleet container deployment and permission model (NET_ADMIN/SYS_ADMIN, read-only rootfs, private pidns — what each does and does not stop); how the classic container-escape path idioms fare; defense layers L0–L7dest_file/capturetags), protovalidate path patterns, runtime path policy + frozen fields, universal validating interceptor,Setaudit logging, golden-config CI gate, and a startup confinement check (PR-24)pkg/ipcstack (peer uid checked before HTTP/2 parsing, protovalidate both ends, monotonic seq, streamed ipmeta, crash-safe commit dance), collector:roflip, the complete write inventory, and the target admission checklistCAP_NET_ADMIN,CAP_SYS_ADMINanddocker.sockout of the collector; per-process seccomp + AppArmor profilesprivilegedreference spec, rtnetlink write encoders linked), the remaining inputs, ranked risks, a Parquet-path audit, and a focused set A–D — a listener model (endpoint classes → listeners, UDS + bearer auth by default), the container spec, tenant-volume bounds, the Docker broker pulled forward, the capture writer as a compile-time opt-in,govulncheck— with test tables; the writer split and netlink broker deferredFP-1…FP-17) with file-level checklists, test tables and acceptance lines; two releases (R1 defaults, R2 container shape); rollout order, gates and rollbackHeadline findings
Sethas no path or scheme validation, and listener auth is off by default — one unauthenticated call switchesdesttofile:<path>and the daemon writes root-owned files at that path on the next poll.Set-reachable; three are debug tooling with no build gate at all today (file:,write_files/capture_path, UDS binds)./proc/1/root/...escape targets fail on role-deployed containers (private pidns + read-only rootfs make/proc/1/root/...the container's own root); outside that deployment shape the sameSetcalls land as root.tcp/udp/http/https/unix/unixgram) are always compiled in — thedest-repoint egress/exfil class is structural (OD-9 closes it in Phase 4).docker.sock,CAP_NET_ADMINin the host netns, andCAP_SYS_ADMIN(bpf/perf_event_openunder Docker's default seccomp) make a compromised collector host-root-equivalent — Phase 5 removes all three.--privileged, a pod without seccomp) is now refused at startup — PR-24 checks no-new-privs, seccomp, read-only root and the capability bound; a per-check-insecureAllowUnconfinedwaiver is alerted on.Design decisions worth reviewing closely
execve, includingSet's soft restart; not bypassable by a privileged process) — seccomp cannot express path rules.pkg/ipc) — one typed, validated stack; the peer uid is checked in the transport handshake, so only the expected process reaches the HTTP/2 parser.CAP_NET_ADMIN, so the kernel still refusesSOCK_DESTROY, route changes and MD5 keys even if the broker is buggy./.dockerenv-style checks pass--privilegedand fail a hardened systemd unit; the in-tree NixOS module is hardened to pass with no waiver.privilegedblast radius, tenant-driven volume and strings, S3 credential scope, and the debug writers; the focused doc lands those in 4–5 weeks and defers the writer split and netlink broker without conflicting with them.Verification
docs/README.md+ cross-links between all eight docs).mainat97bed69c(call sites, line references, build tags, nix flavors, protobuf-go semantics).🤖 Generated with Claude Code