From 7728ba0a90094408ad9078e36c3622a651d49616 Mon Sep 17 00:00:00 2001 From: Matt Heon Date: Mon, 28 Sep 2026 07:48:00 -0400 Subject: [PATCH 1/3] Revert "Enable 'podman run' for checkpoint images" This reverts commit f4401567cdf6d7ccc1ef9f50345c865bc6262c22. This commit has been identified as a serious security issue. Running checkpoints via `podman run` introduces potentially confusing behavior where all security for a container, even options specified by the user at the command line, can be turned off by the checkpoint. Checkpoints have special security considerations and must be explicitly requested by the user to ensure they are used safely. We recommend ensuring the lifecycle of a checkpoint is fully monitored and integrity is ensured at each stage to guarantee safety. Signed-off-by: Matt Heon --- pkg/domain/infra/abi/containers.go | 39 --------------------------- pkg/domain/infra/tunnel/containers.go | 25 ----------------- 2 files changed, 64 deletions(-) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 36ceb1921f..ea123ea9b5 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -9,7 +9,6 @@ import ( "fmt" "maps" "os" - "reflect" "strconv" "sync" "time" @@ -1198,44 +1197,6 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta fmt.Fprintf(os.Stderr, "%s\n", w) } - if opts.Spec != nil && !reflect.ValueOf(opts.Spec).IsNil() { - // If this is a checkpoint image, restore it. - img, resolvedImageName := opts.Spec.GetImage() - if img != nil && resolvedImageName != "" { - imgData, err := img.Inspect(ctx, nil) - if err != nil { - return nil, err - } - if imgData != nil { - _, isCheckpointImage := imgData.Annotations[define.CheckpointAnnotationRuntimeName] - if isCheckpointImage { - var restoreOptions entities.RestoreOptions - restoreOptions.Name = opts.Spec.Name - restoreOptions.Pod = opts.Spec.Pod - responses, err := ic.ContainerRestore(ctx, []string{resolvedImageName}, restoreOptions) - if err != nil { - return nil, err - } - - report := entities.ContainerRunReport{} - for _, r := range responses { - report.Id = r.Id - report.ExitCode = 0 - if r.Err != nil { - logrus.Errorf("Failed to restore checkpoint image %s: %v", resolvedImageName, r.Err) - report.ExitCode = 126 - } - if r.RawInput != "" { - logrus.Errorf("Failed to restore checkpoint image %s: %v", resolvedImageName, r.RawInput) - report.ExitCode = 126 - } - } - return &report, nil - } - } - } - } - rtSpec, spec, optsN, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec, false, nil) if err != nil { return nil, err diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index f130bbbc4d..ec814c369d 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "os" - "reflect" "strconv" "strings" "time" @@ -882,30 +881,6 @@ func (ic *ContainerEngine) ContainerListExternal(_ context.Context) ([]entities. } func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { - if opts.Spec != nil && !reflect.ValueOf(opts.Spec).IsNil() && opts.Spec.RawImageName != "" { - // If this is a checkpoint image, restore it. - getImageOptions := new(images.GetOptions).WithSize(false) - inspectReport, err := images.GetImage(ic.ClientCtx, opts.Spec.RawImageName, getImageOptions) - if err != nil { - return nil, fmt.Errorf("no such container or image: %s", opts.Spec.RawImageName) - } - if inspectReport != nil { - _, isCheckpointImage := inspectReport.Annotations[define.CheckpointAnnotationRuntimeName] - if isCheckpointImage { - restoreOptions := new(containers.RestoreOptions) - restoreOptions.WithName(opts.Spec.Name) - restoreOptions.WithPod(opts.Spec.Pod) - - restoreReport, err := containers.Restore(ic.ClientCtx, inspectReport.ID, restoreOptions) - if err != nil { - return nil, err - } - runReport := entities.ContainerRunReport{Id: restoreReport.Id} - return &runReport, nil - } - } - } - con, err := containers.CreateWithSpec(ic.ClientCtx, opts.Spec, nil) if err != nil { return nil, err From 24706290d5c974f078d8ca284026802515f28e92 Mon Sep 17 00:00:00 2001 From: Matt Heon Date: Mon, 28 Sep 2026 09:26:35 -0400 Subject: [PATCH 2/3] Error when a checkpoint image is run Support for these was removed in 3721ff82ffb as the feature is inherently insecure. Checkpoints imply an extremely privileged operation that is incompatible with the constraints of a `podman run` command. However, we should still tell folks that it no longer works with a clear error message. This is done during Specgen processing, server side, which should be safer than the previous client-side processing for these. Signed-off-by: Matt Heon --- docs/source/markdown/podman-container-restore.1.md | 6 ++++++ pkg/specgen/generate/container_create.go | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md index 96ff14aa43..a70ea92e4e 100644 --- a/docs/source/markdown/podman-container-restore.1.md +++ b/docs/source/markdown/podman-container-restore.1.md @@ -10,6 +10,12 @@ podman\-container\-restore - Restore one or more containers from a checkpoint **podman container restore** restores a container from a container checkpoint or checkpoint image. The *container IDs*, *image IDs* or *names* are used as input. +Please note that restoring a checkpoint will use the complete security context specified in the checkpoint. +If a checkpoint specifies that it should be run with full capabilities added and all security features like Seccomp, SELinux, and Apparmor disabled, this will be done. +Security configuration from **containers.conf** is not used, as the checkpoint must be restored with the originally-specified security configuration. +As such, restoring a checkpoint is a security sensitive operation and should only be done on checkpoints that are fully trusted. +It is recommended that checkpoint integrity be ensured at each lifecycle step - creation, storage, retrieval, and restoration. + ## OPTIONS #### **--all**, **-a** diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index d06cf7559b..3ab520994a 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -164,6 +164,16 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener return nil, nil, nil, err } + // Check for checkpoint annotation. + // This was removed in 3721ff82ffb44a4ceeaecc5abd3808e924a6b669 as a result of CVE-2026-94603. + // If we get it, throw an error stating that `podman container restore` must be used instead. + if imageData != nil { + _, isCheckpointImage := imageData.Annotations[define.CheckpointAnnotationRuntimeName] + if isCheckpointImage { + return nil, nil, nil, fmt.Errorf("%s is a checkpoint and must be started using `podman container restore` instead", s.Image) + } + } + if imageData != nil { ociRuntimeVariant := rtc.Engine.ImagePlatformToRuntime(imageData.Os, imageData.Architecture) // Don't unnecessarily set and invoke additional libpod From 29c8c6e50607df7a7d735ace09ff0ea099c0b3e0 Mon Sep 17 00:00:00 2001 From: Matt Heon Date: Mon, 28 Sep 2026 10:28:29 -0400 Subject: [PATCH 3/3] Rework podman run checkpoint test Needs to properly handle the error now that we've removed the functionality. Signed-off-by: Matt Heon --- test/e2e/checkpoint_image_test.go | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/test/e2e/checkpoint_image_test.go b/test/e2e/checkpoint_image_test.go index 2481e2226e..c8320f0491 100644 --- a/test/e2e/checkpoint_image_test.go +++ b/test/e2e/checkpoint_image_test.go @@ -275,7 +275,7 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) - It("podman run with checkpoint image", func() { + It("podman run with checkpoint image throws an error", func() { // Container image must be lowercase checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6)) containerName := "alpine-container-" + RandomString(6) @@ -301,19 +301,7 @@ var _ = Describe("Podman checkpoint", func() { // Restore containers from image using `podman run` result = podmanTest.Podman([]string{"run", checkpointImage}) result.WaitWithDefaultTimeout() - Expect(result).Should(ExitCleanly()) - Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) - - // Check if the container is running - status := podmanTest.Podman([]string{"inspect", containerName, "--format={{.State.Status}}"}) - status.WaitWithDefaultTimeout() - Expect(status).Should(ExitCleanly()) - Expect(status.OutputToString()).To(Equal("running")) - - // Clean-up - result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) - result.WaitWithDefaultTimeout() - Expect(result).Should(ExitCleanly()) + Expect(result).Should(ExitWithError(125, "is a checkpoint and must be started using `podman container restore` instead")) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) result = podmanTest.Podman([]string{"rmi", checkpointImage})