slice 8C Phase A: agent disk endpoints + data-bearing classifier gate + mkfs (v0.12.0)

internal/storage: mkfs executor (Format, device-pinned, narrow FELHOM_FORMAT
sudoers) + data-bearing device inspection (InspectDevice/DeviceProbe via
blkid+lsblk; conservative — ambiguous=data-bearing). internal/localapi: /disks
(+ data-bearing flag), /disks/assign (EnsureMount), /disks/eject (Unmount +
dependent guests), /disks/format. SECURITY CENTERPIECE: the agent inspects the
device itself; data-bearing format -> ClassStorageWipe gate -> pending_signature
refused; the caller's claim is never trusted. Additive (no controller change yet).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 12:52:22 +02:00
parent 4d9e76e66a
commit c17cfde236
10 changed files with 1074 additions and 10 deletions
+29
View File
@@ -25,6 +25,16 @@ var (
// smartctl is run against. Anything else is refused.
reSMARTDevice = regexp.MustCompile(`^/dev/(sd[a-z]+|nvme[0-9]+n[0-9]+|hd[a-z]+|vd[a-z]+)$`)
// Block device for inspection / mkfs: a raw disk OR a partition under /dev. Like the SMART
// whitelist but also allows the trailing partition number (sda1, nvme0n1p2, vdb3). No
// by-* symlinks, no device-mapper, no traversal. This is the mkfs/inspect target — it is
// validated AND the agent device-inspects it before any destructive decision (8C).
reBlockDevice = regexp.MustCompile(`^/dev/(sd[a-z]+[0-9]*|nvme[0-9]+n[0-9]+(p[0-9]+)?|hd[a-z]+[0-9]*|vd[a-z]+[0-9]*)$`)
// Filesystem types the agent will mkfs. Deliberately tiny — the sudoers mkfs entries are
// per-fstype binaries (mkfs.ext4 / mkfs.xfs), so this set MUST match those entries.
reFSType = regexp.MustCompile(`^(ext4|xfs)$`)
// LVM VG / pool names: LVM permits [A-Za-z0-9._+-]; we forbid leading '-' (would look
// like a flag) and cap the length.
reLVMName = regexp.MustCompile(`^[A-Za-z0-9_+.][A-Za-z0-9_+.-]*$`)
@@ -106,6 +116,25 @@ func ValidateSMARTDevice(device string) error {
return nil
}
// ValidateBlockDevice accepts only a raw disk or partition path under /dev (the mkfs / inspect
// target). The same strict-whitelist discipline as ValidateSMARTDevice: no symlinks, no
// device-mapper, no traversal — refused before any command is built.
func ValidateBlockDevice(device string) error {
if !reBlockDevice.MatchString(device) {
return fmt.Errorf("storage: refusing to operate on non-whitelisted block device %q", device)
}
return nil
}
// ValidateFSType accepts only a filesystem type the agent is configured to mkfs (ext4|xfs). The
// set MUST match the per-fstype sudoers entries.
func ValidateFSType(fstype string) error {
if !reFSType.MatchString(fstype) {
return fmt.Errorf("storage: unsupported filesystem type %q (want ext4|xfs)", fstype)
}
return nil
}
// ValidateLVMName accepts an LVM VG or LV (pool) name.
func ValidateLVMName(name string) error {
if name == "" {