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/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 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 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})