From 03d9de1528f3a32bea66222b48fff49e24539eb6 Mon Sep 17 00:00:00 2001 From: Ozgur As Date: Mon, 23 Feb 2026 14:27:17 +0300 Subject: [PATCH] Fix container clone with secret type=env ConfigToSpec() serializes the container config to JSON and deserializes it into a SpecGenerator. Both structs use the JSON tag "secret_env" but with incompatible types: the container config uses map[string]*secrets.Secret (complex objects) while the specgen uses map[string]string (env var name to secret name). This causes an unmarshal error when cloning containers that use --secret with type=env. Fix this by saving and clearing EnvSecrets before JSON marshal (same pattern as existing tmpSystemd/tmpMounts), then converting the secret objects to name strings and assigning them to the specgen afterward. Also fix FillOutSpecGen to not overwrite env secrets populated by ConfigToSpec when no new secrets are provided on the command line. Fixes: #28130 Signed-off-by: Ozgur As --- pkg/specgen/generate/container.go | 11 +++++++++++ pkg/specgenutil/specgen.go | 2 +- test/e2e/container_clone_test.go | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index a5b5b8dec1..e714993171 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -379,9 +379,11 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID tmpSystemd := conf.Systemd tmpMounts := conf.Mounts + tmpEnvSecrets := conf.EnvSecrets conf.Systemd = nil conf.Mounts = []string{} + conf.EnvSecrets = nil if specg == nil { specg = &specgen.SpecGenerator{} @@ -401,6 +403,7 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID conf.Systemd = tmpSystemd conf.Mounts = tmpMounts + conf.EnvSecrets = tmpEnvSecrets if conf.Spec != nil { if conf.Spec.Linux != nil && conf.Spec.Linux.Resources != nil { @@ -514,6 +517,14 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID specg.StartupHealthConfig = conf.StartupHealthCheckConfig specg.HealthCheckOnFailureAction = conf.HealthCheckOnFailureAction + if len(tmpEnvSecrets) > 0 { + envSecrets := make(map[string]string, len(tmpEnvSecrets)) + for target, secret := range tmpEnvSecrets { + envSecrets[target] = secret.Name + } + specg.EnvSecrets = envSecrets + } + specg.IDMappings = &conf.IDMappings specg.ContainerCreateCommand = conf.CreateCommand if len(specg.Rootfs) == 0 { diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 7bc5cd86d0..4c033a85db 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -900,7 +900,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions s.RestartRetries = &retries } - if len(s.Secrets) == 0 || len(c.Secrets) != 0 { + if (len(s.Secrets) == 0 && len(s.EnvSecrets) == 0) || len(c.Secrets) != 0 { s.Secrets, s.EnvSecrets, err = parseSecrets(c.Secrets) if err != nil { return err diff --git a/test/e2e/container_clone_test.go b/test/e2e/container_clone_test.go index b0379fcd1e..9b25f9deaf 100644 --- a/test/e2e/container_clone_test.go +++ b/test/e2e/container_clone_test.go @@ -3,6 +3,9 @@ package integration import ( + "os" + "path/filepath" + . "github.com/containers/podman/v6/test/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -299,6 +302,26 @@ var _ = Describe("Podman container clone", func() { Expect(session.OutputToString()).Should(ContainSubstring("12=3")) }) + It("podman container clone with secret env", func() { + secretsString := "somesecretdata" + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := os.WriteFile(secretFilePath, []byte(secretsString), 0o755) + Expect(err).ToNot(HaveOccurred()) + + podmanTest.PodmanExitCleanly("secret", "create", "mysecret", secretFilePath) + + session := podmanTest.PodmanExitCleanly("run", "--secret", "source=mysecret,type=env", "--name", "secr", ALPINE, "printenv", "mysecret") + Expect(session.OutputToString()).To(Equal(secretsString)) + + podmanTest.PodmanExitCleanly("container", "clone", "secr") + + session = podmanTest.PodmanExitCleanly("start", "-a", "secr-clone") + Expect(session.OutputToString()).To(Equal(secretsString)) + + cloneData := podmanTest.PodmanExitCleanly("inspect", "secr-clone").InspectContainerToJSON()[0] + Expect(cloneData.Config.Env).To(ContainElement("mysecret=*******")) + }) + It("podman container clone container with healthcheck", func() { podmanTest.PodmanExitCleanly( "run", "-d", "--rm",