Reset Podman qualification storage on teardown

This commit is contained in:
rcourtman
2026-09-01 20:48:13 +01:00
parent 54cd77d3b1
commit f364cfc73b
3 changed files with 46 additions and 5 deletions
@@ -7347,10 +7347,12 @@ semantic-only report and racing the evidence assertion.
Cleanup removes the exact fixture containers through both still-live runtime
APIs before stopping their services or deleting storage, so Podman namespace
and shared-memory mounts cannot outlive the state they protect. After both
runtime services and the delegated user manager stop, cleanup waits until the
kernel mount table contains no mount at or below any dedicated runtime state
root before deleting that state; an empty runtime inventory or a successful
systemd stop alone is not mount-release evidence.
runtime services stop, Podman cleanup resets each rootless and rootful storage
identity through the local runtime CLI before the delegated user manager is
stopped. Cleanup then waits until the kernel mount table contains no mount at
or below any dedicated runtime state root before deleting that state; an empty
runtime inventory or a successful systemd stop alone is not mount-release
evidence.
The wrapper must compile every Go artifact with mandatory VCS stamping so a
missing revision or unavailable clean-worktree proof fails before live evidence
can be emitted; the receipt and validator retain the exact artifact hashes and
@@ -269,7 +269,9 @@ Direct recovery is complete only after the full stable telemetry digest is
observed; the first semantic-only report cannot satisfy the qualification wait.
Final cleanup removes the exact rootless and rootful fixture containers while
both runtime APIs are live, then stops the services and delegated user manager.
It deletes runtime state only after the kernel mount table proves that no mount
After the services stop, Podman cleanup uses the local runtime CLI to reset each
rootless and rootful storage identity before stopping the user manager. It
deletes runtime state only after the kernel mount table proves that no mount
remains at or below any dedicated runtime root; container inventory and unit
state alone cannot authorize storage deletion.
The outer wrapper forgets a tracked container only after strict labeled removal
@@ -429,6 +429,7 @@ func TestSecureRuntimeRootlessQualification(t *testing.T) {
rootlessQualRemoveFixtures(t, daemon)
rootlessQualStopUnit(t, daemon.rootlessUnit)
rootlessQualStopUnit(t, daemon.rootfulUnit)
rootlessQualResetRuntimeStorage(t, daemon)
rootlessQualStopUserManager(t, daemon)
rootlessQualWaitRuntimeMountsReleased(t, daemon, 30*time.Second)
rootlessQualRemoveRuntimeState(t, daemon)
@@ -1263,6 +1264,27 @@ func rootlessQualRemoveFixtures(t *testing.T, d rootlessQualDaemon) {
rootlessQualRemoveFixtureSet(t, rootlessQualCLI(d, false))
}
func rootlessQualResetRuntimeStorage(t *testing.T, d rootlessQualDaemon) {
t.Helper()
for _, command := range rootlessQualRuntimeResetCommands(d) {
if len(command) == 0 {
t.Fatal("empty runtime storage reset command")
}
rootlessQualCommand(t, 60*time.Second, command[0], command[1:]...)
}
}
func rootlessQualRuntimeResetCommands(d rootlessQualDaemon) [][]string {
if d.runtime != "podman" {
return nil
}
const cleanPath = "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
return [][]string{
{"runuser", "-u", "pulse-agent", "--", "env", "-i", "HOME=" + d.home, "XDG_RUNTIME_DIR=" + filepath.Join("/run/user", strconv.Itoa(d.uid)), cleanPath, "/usr/bin/podman", "system", "reset", "--force"},
{"/usr/bin/env", "-i", cleanPath, "/usr/bin/podman", "system", "reset", "--force"},
}
}
func rootlessQualRemoveFixtureSet(t *testing.T, cli []string) {
t.Helper()
if len(cli) == 0 {
@@ -1625,6 +1647,21 @@ func TestRootlessQualificationMountReleaseUsesExactKernelPaths(t *testing.T) {
}
}
func TestRootlessQualificationPodmanResetsBothStorageIdentitiesLocally(t *testing.T) {
d := rootlessQualDaemon{runtime: "podman", uid: 996, home: "/var/lib/pulse-rootless"}
got := rootlessQualRuntimeResetCommands(d)
want := [][]string{
{"runuser", "-u", "pulse-agent", "--", "env", "-i", "HOME=/var/lib/pulse-rootless", "XDG_RUNTIME_DIR=/run/user/996", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "/usr/bin/podman", "system", "reset", "--force"},
{"/usr/bin/env", "-i", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "/usr/bin/podman", "system", "reset", "--force"},
}
if !slices.EqualFunc(got, want, slices.Equal[[]string]) {
t.Fatalf("Podman reset commands = %q, want %q", got, want)
}
if commands := rootlessQualRuntimeResetCommands(rootlessQualDaemon{runtime: "docker"}); len(commands) != 0 {
t.Fatalf("Docker cleanup unexpectedly gained Podman reset commands: %q", commands)
}
}
func TestRootlessQualificationStopAcceptsCollectedTransientUnit(t *testing.T) {
tempDir := t.TempDir()
logPath := filepath.Join(tempDir, "systemctl.log")