From 54d61c2f0239944cd116c290f2e5bc2dd150fe32 Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Tue, 14 Apr 2026 10:14:37 +0200 Subject: [PATCH] trust: switch policy.json lookup to configfile Use shared configfile instead of custom policy.json path handling. This updates ocipull to rely on signature.DefaultPolicy(), removes explicit SignaturePolicyPath, and replaces trust's custom default-policy path logic with common configfile code. Replace hidden `--policypath` with --signature-policy` and require it for `trust set` command instead of path resolution based on configfile. For `trust get`, the `--signature-policy` is optional. Signed-off-by: Jan Kaluza --- cmd/podman/images/trust_set.go | 5 +- cmd/podman/images/trust_show.go | 5 +- .../markdown/podman-image-trust.1.md.in | 10 ++- go.mod | 6 +- go.sum | 12 +-- libpod/runtime.go | 1 - pkg/api/handlers/compat/images.go | 7 +- pkg/api/handlers/libpod/images.go | 7 +- pkg/domain/infra/abi/containers.go | 5 -- pkg/domain/infra/abi/trust.go | 61 ++++++++++++--- pkg/machine/ocipull/policy.go | 33 -------- pkg/machine/ocipull/policy_unix.go | 19 ----- pkg/machine/ocipull/policy_windows.go | 10 --- pkg/machine/ocipull/pull.go | 19 ++--- pkg/trust/policy.go | 27 ------- test/e2e/trust_test.go | 8 +- test/system/750-trust.bats | 22 +++--- .../go.podman.io/common/pkg/config/config.go | 5 -- .../common/pkg/config/config_bsd.go | 6 -- .../common/pkg/config/config_darwin.go | 6 -- .../common/pkg/config/config_linux.go | 6 -- .../common/pkg/config/config_windows.go | 4 - .../go.podman.io/common/pkg/config/default.go | 21 ------ .../image/v5/signature/policy_config.go | 75 +++++++++---------- .../image/v5/signature/policy_paths_common.go | 7 -- .../v5/signature/policy_paths_freebsd.go | 7 -- .../storage/pkg/configfile/parse.go | 35 ++++++++- vendor/modules.txt | 6 +- 28 files changed, 167 insertions(+), 268 deletions(-) delete mode 100644 pkg/machine/ocipull/policy.go delete mode 100644 pkg/machine/ocipull/policy_unix.go delete mode 100644 pkg/machine/ocipull/policy_windows.go delete mode 100644 vendor/go.podman.io/image/v5/signature/policy_paths_common.go delete mode 100644 vendor/go.podman.io/image/v5/signature/policy_paths_freebsd.go diff --git a/cmd/podman/images/trust_set.go b/cmd/podman/images/trust_set.go index d5cab6106b..876fcd165d 100644 --- a/cmd/podman/images/trust_set.go +++ b/cmd/podman/images/trust_set.go @@ -35,8 +35,9 @@ func init() { Parent: trustCmd, }) setFlags := setTrustCommand.Flags() - setFlags.StringVar(&setOptions.PolicyPath, "policypath", "", "") - _ = setFlags.MarkHidden("policypath") + signaturePolicyFlagName := "signature-policy" + setFlags.StringVar(&setOptions.PolicyPath, signaturePolicyFlagName, "", "Path to a signature-policy file") + _ = setTrustCommand.RegisterFlagCompletionFunc(signaturePolicyFlagName, completion.AutocompleteDefault) pubkeysfileFlagName := "pubkeysfile" setFlags.StringArrayVarP(&setOptions.PubKeysFile, pubkeysfileFlagName, "f", []string{}, `Path of installed public key(s) to trust for TARGET. diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go index 2b4ad4b0f3..75582cedd7 100644 --- a/cmd/podman/images/trust_show.go +++ b/cmd/podman/images/trust_show.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v6/cmd/podman/registry" "github.com/containers/podman/v6/pkg/domain/entities" "github.com/spf13/cobra" + "go.podman.io/common/pkg/completion" "go.podman.io/common/pkg/report" ) @@ -35,9 +36,9 @@ func init() { }) showFlags := showTrustCommand.Flags() showFlags.BoolVarP(&showTrustOptions.JSON, "json", "j", false, "Output as json") - showFlags.StringVar(&showTrustOptions.PolicyPath, "policypath", "", "") + showFlags.StringVar(&showTrustOptions.PolicyPath, "signature-policy", "", "Path to a signature-policy file") + _ = showTrustCommand.RegisterFlagCompletionFunc("signature-policy", completion.AutocompleteDefault) showFlags.BoolVar(&showTrustOptions.Raw, "raw", false, "Output raw policy file") - _ = showFlags.MarkHidden("policypath") showFlags.StringVar(&showTrustOptions.RegistryPath, "registrypath", "", "") showFlags.BoolVarP(&noHeading, "noheading", "n", false, "Do not print column headings") _ = showFlags.MarkHidden("registrypath") diff --git a/docs/source/markdown/podman-image-trust.1.md.in b/docs/source/markdown/podman-image-trust.1.md.in index ab9e3f8d25..cd4b954dfb 100644 --- a/docs/source/markdown/podman-image-trust.1.md.in +++ b/docs/source/markdown/podman-image-trust.1.md.in @@ -55,6 +55,9 @@ Trust may be updated using the command **podman image trust set** for an existin are referenced in policy.json. Any path to a file may be used but locating the file in **/etc/pki/containers** is recommended. Options may be used multiple times to require an image be signed by multiple keys. The **--pubkeysfile** option is required for the **signedBy** and **sigstoreSigned** types. +#### **--signature-policy**=*path* + Path to the signature policy file (**policy.json**) that Podman updates. This option is required for **set**. + #### **--type**, **-t**=*value* The trust type for this policy entry. Accepted values: @@ -76,16 +79,19 @@ Trust may be updated using the command **podman image trust set** for an existin #### **--raw** Output trust policy file as raw JSON +#### **--signature-policy**=*path* + Path to the signature policy file (**policy.json**) to display. When omitted, Podman resolves the policy path from the system configuration the same way it selects a policy for image pulls. + ## EXAMPLES Accept all unsigned images from a registry: ``` -sudo podman image trust set --type accept docker.io +sudo podman image trust set --signature-policy /etc/containers/policy.json --type accept docker.io ``` Modify default trust policy: ``` -sudo podman image trust set -t reject default +sudo podman image trust set --signature-policy /etc/containers/policy.json -t reject default ``` Display system trust policy: diff --git a/go.mod b/go.mod index e6ecd3e131..2ead6be5be 100644 --- a/go.mod +++ b/go.mod @@ -64,9 +64,9 @@ require ( github.com/stretchr/testify v1.11.1 github.com/vbauerster/mpb/v8 v8.12.0 github.com/vishvananda/netlink v1.3.1 - go.podman.io/common v0.67.2-0.20260416202057-7d53666cdee9 - go.podman.io/image/v5 v5.39.3-0.20260416202057-7d53666cdee9 - go.podman.io/storage v1.62.1-0.20260416202057-7d53666cdee9 + go.podman.io/common v0.67.2-0.20260420103546-618304d6f83d + go.podman.io/image/v5 v5.39.3-0.20260420103546-618304d6f83d + go.podman.io/storage v1.62.1-0.20260420103546-618304d6f83d golang.org/x/crypto v0.50.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 diff --git a/go.sum b/go.sum index 87c5a3b809..10a1653f9e 100644 --- a/go.sum +++ b/go.sum @@ -429,12 +429,12 @@ go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9 go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= -go.podman.io/common v0.67.2-0.20260416202057-7d53666cdee9 h1:WOO740hWFwfvA+vcpXO9TaW9v/kMzWSiEx8Ulb0kX4U= -go.podman.io/common v0.67.2-0.20260416202057-7d53666cdee9/go.mod h1:PIlj/bShUaMV9Dz4ygGPtUWZ3Zx4nukPO82Q/AnZrRA= -go.podman.io/image/v5 v5.39.3-0.20260416202057-7d53666cdee9 h1:7R6x8/GH9BprC+VjmU8BhGGxn9w/AZBmJqMm059S9s0= -go.podman.io/image/v5 v5.39.3-0.20260416202057-7d53666cdee9/go.mod h1:E3RVHxc/Hmyq3U4DUpv+MSdmk/tkmAcSjp61K6YmAIU= -go.podman.io/storage v1.62.1-0.20260416202057-7d53666cdee9 h1:JK0umBWWQSgd8cx1YgOQeiIwtLTprW+iZrNKUxZX5kM= -go.podman.io/storage v1.62.1-0.20260416202057-7d53666cdee9/go.mod h1:13aOBf6782/fbAzH7QNEqlVzFu+X4sS4MxDM/VdJGZU= +go.podman.io/common v0.67.2-0.20260420103546-618304d6f83d h1:zryioAYWqUu1BUPraJJ6q5mLiclNZ3kTuQyyBO/5vYs= +go.podman.io/common v0.67.2-0.20260420103546-618304d6f83d/go.mod h1:Pom4Io+8C+CCZzr3CRUkr6A5inTMY8nVaFS3X1Y5NbI= +go.podman.io/image/v5 v5.39.3-0.20260420103546-618304d6f83d h1:NeMM89Fwaw19guPj/hXEjBtoCeH/mGgbAWoBnpnRiIo= +go.podman.io/image/v5 v5.39.3-0.20260420103546-618304d6f83d/go.mod h1:Yl9kZ8QslAOPc2rMkCrjPENCpSox6frxScCEzpmsQX4= +go.podman.io/storage v1.62.1-0.20260420103546-618304d6f83d h1:aMAdj9gLrVUz+Zb+nOxcoeT6+CC+8Rk2lHs1MC3kcQM= +go.podman.io/storage v1.62.1-0.20260420103546-618304d6f83d/go.mod h1:13aOBf6782/fbAzH7QNEqlVzFu+X4sS4MxDM/VdJGZU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= diff --git a/libpod/runtime.go b/libpod/runtime.go index 7b1a7d4cfb..833264d1ef 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -439,7 +439,6 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { if runtime.imageContext.BigFilesTemporaryDir == "" { runtime.imageContext.BigFilesTemporaryDir = parse.GetTempDir() } - runtime.imageContext.SignaturePolicyPath = runtime.config.Engine.SignaturePolicyPath // Get us at least one working OCI runtime. runtime.ociRuntimes = make(map[string]OCIRuntime) diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 1cdffdf5eb..9047b5331d 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -100,6 +100,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { } func CommitContainer(w http.ResponseWriter, r *http.Request) { + var err error decoder := utils.GetDecoder(r) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) @@ -121,17 +122,11 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } - rtc, err := runtime.GetConfig() - if err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) - return - } sc := runtime.SystemContext() options := libpod.ContainerCommitOptions{ Pause: true, } options.CommitOptions = buildah.CommitOptions{ - SignaturePolicyPath: rtc.Engine.SignaturePolicyPath, ReportWriter: os.Stderr, SystemContext: sc, PreferredManifestType: manifest.DockerV2Schema2MediaType, diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 5c7ca56feb..b16fe23456 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -487,6 +487,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { var ( destImage string mimeType string + err error ) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) @@ -510,11 +511,6 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } - rtc, err := runtime.GetConfig() - if err != nil { - utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to get runtime config: %w", err)) - return - } sc := runtime.SystemContext() tag := "latest" options := libpod.ContainerCommitOptions{ @@ -534,7 +530,6 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { return } options.CommitOptions = buildah.CommitOptions{ - SignaturePolicyPath: rtc.Engine.SignaturePolicyPath, ReportWriter: os.Stderr, SystemContext: sc, PreferredManifestType: mimeType, diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 0d06e132d7..b9c506b66d 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -642,10 +642,6 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, if err != nil { return nil, err } - rtc, err := ic.Libpod.GetConfig() - if err != nil { - return nil, err - } switch options.Format { case "oci": mimeType = buildah.OCIv1ImageManifest @@ -670,7 +666,6 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, } } coptions := buildah.CommitOptions{ - SignaturePolicyPath: rtc.Engine.SignaturePolicyPath, ReportWriter: options.Writer, SystemContext: sc, PreferredManifestType: mimeType, diff --git a/pkg/domain/infra/abi/trust.go b/pkg/domain/infra/abi/trust.go index f6cf470fa0..cf88c5fc94 100644 --- a/pkg/domain/infra/abi/trust.go +++ b/pkg/domain/infra/abi/trust.go @@ -6,20 +6,63 @@ import ( "context" "fmt" "os" + "path/filepath" "github.com/containers/podman/v6/pkg/domain/entities" "github.com/containers/podman/v6/pkg/trust" + "github.com/sirupsen/logrus" + "go.podman.io/image/v5/types" + "go.podman.io/storage/pkg/configfile" + "go.podman.io/storage/pkg/homedir" ) +// policyPathFromConfigfile resolves policy.json the same way as [signature.DefaultPolicy] +// (via [configfile.Read]); overridePath, if non-empty, wins. +func policyPathFromConfigfile(sys *types.SystemContext, overridePath string) string { + if overridePath != "" { + return overridePath + } + if sys != nil && sys.SignaturePolicyPath != "" { + return sys.SignaturePolicyPath + } + + root := "" + if sys != nil { + root = sys.RootForImplicitAbsolutePaths + } + + policyFiles := configfile.File{ + Name: "policy", + Extension: "json", + DoNotLoadDropInFiles: true, + EnvironmentName: "CONTAINERS_POLICY_JSON", + RootForImplicitAbsolutePaths: root, + ErrorIfNotFound: false, + } + + for item, err := range configfile.Read(&policyFiles) { + if err != nil { + logrus.Warnf("Resolving default policy path: %v", err) + break + } + if item != nil { + return item.Name + } + } + userDir, err := configfile.UserConfigPath() + if err != nil { + logrus.Warnf("Error resolving user config path for policy: %v", err) + return filepath.Join(homedir.Get(), filepath.FromSlash(".config/containers/policy.json")) + } + return filepath.Join(userDir, "policy.json") +} + func (ir *ImageEngine) ShowTrust(_ context.Context, _ []string, options entities.ShowTrustOptions) (*entities.ShowTrustReport, error) { var ( err error report entities.ShowTrustReport ) - policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext()) - if len(options.PolicyPath) > 0 { - policyPath = options.PolicyPath - } + policyPath := policyPathFromConfigfile(ir.Libpod.SystemContext(), options.PolicyPath) report.Raw, err = os.ReadFile(policyPath) if err != nil { return nil, err @@ -42,14 +85,12 @@ func (ir *ImageEngine) SetTrust(_ context.Context, args []string, options entiti if len(args) != 1 { return fmt.Errorf("SetTrust called with unexpected %d args", len(args)) } + if options.PolicyPath == "" { + return fmt.Errorf("signature-policy path must be provided") + } scope := args[0] - policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext()) - if len(options.PolicyPath) > 0 { - policyPath = options.PolicyPath - } - - return trust.AddPolicyEntries(policyPath, trust.AddPolicyEntriesInput{ + return trust.AddPolicyEntries(options.PolicyPath, trust.AddPolicyEntriesInput{ Scope: scope, Type: options.Type, PubKeyFiles: options.PubKeysFile, diff --git a/pkg/machine/ocipull/policy.go b/pkg/machine/ocipull/policy.go deleted file mode 100644 index 6fc3a5d4e9..0000000000 --- a/pkg/machine/ocipull/policy.go +++ /dev/null @@ -1,33 +0,0 @@ -package ocipull - -import ( - "os" - "path/filepath" - - "github.com/sirupsen/logrus" -) - -// DefaultPolicyJSONPath should be overwritten at build time with the real path to the directory where -// the shipped policy.json file is located. This can either be absolute path or a relative path. If it -// is relative it will be resolved relative to the podman binary and NOT the CWD. -// -// use "-X github.com/containers/podman/v6/pkg/machine/ocipull.DefaultPolicyJSONPath=/somepath" in go ldflags to overwrite this -var DefaultPolicyJSONPath = "" - -const policyfile = "policy.json" - -// policyPaths returns a slice of possible directories where a policy.json might live -func policyPaths() []string { - paths := localPolicyOverwrites() - if DefaultPolicyJSONPath != "" { - if filepath.IsAbs(DefaultPolicyJSONPath) { - return append(paths, filepath.Join(DefaultPolicyJSONPath, policyfile)) - } - p, err := os.Executable() - if err != nil { - logrus.Warnf("could not resolve relative path to binary: %q", err) - } - paths = append(paths, filepath.Join(filepath.Dir(p), DefaultPolicyJSONPath, policyfile)) - } - return paths -} diff --git a/pkg/machine/ocipull/policy_unix.go b/pkg/machine/ocipull/policy_unix.go deleted file mode 100644 index 6a94b76e48..0000000000 --- a/pkg/machine/ocipull/policy_unix.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !windows - -package ocipull - -import ( - "path/filepath" - - "go.podman.io/common/pkg/config" - "go.podman.io/storage/pkg/homedir" -) - -func localPolicyOverwrites() []string { - var dirs []string - if p, err := homedir.GetConfigHome(); err == nil { - dirs = append(dirs, filepath.Join(p, "containers", policyfile)) - } - dirs = append(dirs, config.DefaultSignaturePolicyPath) - return dirs -} diff --git a/pkg/machine/ocipull/policy_windows.go b/pkg/machine/ocipull/policy_windows.go deleted file mode 100644 index 3a1c31932c..0000000000 --- a/pkg/machine/ocipull/policy_windows.go +++ /dev/null @@ -1,10 +0,0 @@ -package ocipull - -import ( - "os" - "path/filepath" -) - -func localPolicyOverwrites() []string { - return []string{filepath.Join(os.Getenv("APPDATA"), "containers", policyfile)} -} diff --git a/pkg/machine/ocipull/pull.go b/pkg/machine/ocipull/pull.go index 120c221ef2..9690429a3e 100644 --- a/pkg/machine/ocipull/pull.go +++ b/pkg/machine/ocipull/pull.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "io/fs" "os" "github.com/containers/buildah/pkg/parse" @@ -14,6 +13,7 @@ import ( "go.podman.io/image/v5/oci/layout" "go.podman.io/image/v5/signature" "go.podman.io/image/v5/types" + "go.podman.io/storage/pkg/configfile" ) // pullOptions includes data to alter certain knobs when pulling a source @@ -48,7 +48,6 @@ var noSignaturePolicy string = `{"default":[{"type":"insecureAcceptAnything"}]}` // pull `imageInput` from a container registry to `sourcePath`. func pull(ctx context.Context, imageInput types.ImageReference, localDestPath *define.VMFile, options *pullOptions) error { - var policy *signature.Policy destRef, err := layout.ParseReference(localDestPath.GetPath()) if err != nil { return err @@ -59,21 +58,13 @@ func pull(ctx context.Context, imageInput types.ImageReference, localDestPath *d return err } - // Policy paths returns a slice of directories where the policy.json - // may live. Iterate those directories and try to see if any are - // valid ignoring when the file does not exist - for _, path := range policyPaths() { - policy, err = signature.NewPolicyFromFile(path) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - continue - } + policy, err := signature.DefaultPolicy(sysCtx) + if err != nil { + if !errors.Is(err, configfile.ErrConfigFileNotFound) { return fmt.Errorf("reading signature policy: %w", err) } - } - // If no policy has been found yet, we use a no signature policy automatically - if policy == nil { + // If no policy has been found yet, we use a no signature policy automatically logrus.Debug("no signature policy file found: using default allow everything signature policy") policy, err = signature.NewPolicyFromBytes([]byte(noSignaturePolicy)) if err != nil { diff --git a/pkg/trust/policy.go b/pkg/trust/policy.go index 6f222f312f..9e5d8d27ec 100644 --- a/pkg/trust/policy.go +++ b/pkg/trust/policy.go @@ -7,17 +7,12 @@ import ( "encoding/json" "errors" "fmt" - "io/fs" "os" "os/exec" - "path/filepath" "strings" "github.com/sirupsen/logrus" - "go.podman.io/common/pkg/config" - "go.podman.io/image/v5/types" "go.podman.io/storage/pkg/fileutils" - "go.podman.io/storage/pkg/homedir" ) // policyContent is the overall structure of a policy.json file (= c/image/v5/signature.Policy) @@ -55,28 +50,6 @@ type genericTransportsContent map[string]genericRepoMap // genericRepoMap maps a scope name to requirements that apply to that scope (= c/image/v5/signature.PolicyTransportScopes) type genericRepoMap map[string]json.RawMessage -// DefaultPolicyPath returns a path to the default policy of the system. -func DefaultPolicyPath(sys *types.SystemContext) string { - if sys != nil && sys.SignaturePolicyPath != "" { - return sys.SignaturePolicyPath - } - - userPolicyFilePath := filepath.Join(homedir.Get(), filepath.FromSlash(".config/containers/policy.json")) - err := fileutils.Exists(userPolicyFilePath) - if err == nil { - return userPolicyFilePath - } - if !errors.Is(err, fs.ErrNotExist) { - logrus.Warnf("Error trying to read local config file: %s", err.Error()) - } - - systemDefaultPolicyPath := config.DefaultSignaturePolicyPath - if sys != nil && sys.RootForImplicitAbsolutePaths != "" { - return filepath.Join(sys.RootForImplicitAbsolutePaths, systemDefaultPolicyPath) - } - return systemDefaultPolicyPath -} - // gpgIDReader returns GPG key IDs of keys stored at the provided path. // It exists only for tests, production code should always use getGPGIdFromKeyPath. type gpgIDReader func(string) []string diff --git a/test/e2e/trust_test.go b/test/e2e/trust_test.go index c8ca66620a..d58b58f326 100644 --- a/test/e2e/trust_test.go +++ b/test/e2e/trust_test.go @@ -19,7 +19,7 @@ var _ = Describe("Podman trust", Ordered, func() { }) It("podman image trust show", func() { - session := podmanTest.Podman([]string{"image", "trust", "show", "-n", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json")}) + session := podmanTest.Podman([]string{"image", "trust", "show", "-n", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--signature-policy", filepath.Join(INTEGRATION_ROOT, "test/policy.json")}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) outArray := session.OutputToStringArray() @@ -34,7 +34,7 @@ var _ = Describe("Podman trust", Ordered, func() { It("podman image trust set", func() { policyJSON := filepath.Join(podmanTest.TempDir, "trust_set_test.json") - session := podmanTest.Podman([]string{"image", "trust", "set", "--policypath", policyJSON, "-t", "accept", "default"}) + session := podmanTest.Podman([]string{"image", "trust", "set", "--signature-policy", policyJSON, "-t", "accept", "default"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) var teststruct map[string][]map[string]string @@ -50,7 +50,7 @@ var _ = Describe("Podman trust", Ordered, func() { }) It("podman image trust show --json", func() { - session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--json"}) + session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--signature-policy", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--json"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) Expect(session.OutputToString()).To(BeValidJSON()) @@ -89,7 +89,7 @@ var _ = Describe("Podman trust", Ordered, func() { }) It("podman image trust show --raw", func() { - session := podmanTest.Podman([]string{"image", "trust", "show", "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--raw"}) + session := podmanTest.Podman([]string{"image", "trust", "show", "--signature-policy", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--raw"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) contents, err := os.ReadFile(filepath.Join(INTEGRATION_ROOT, "test/policy.json")) diff --git a/test/system/750-trust.bats b/test/system/750-trust.bats index 0d04c33dca..d99343beaa 100644 --- a/test/system/750-trust.bats +++ b/test/system/750-trust.bats @@ -8,37 +8,37 @@ load helpers @test "podman image trust set" { skip_if_remote "trust only works locally" policypath=$PODMAN_TMPDIR/policy.json - run_podman 125 image trust set --policypath=$policypath --type=bogus default + run_podman 125 image trust set --signature-policy=$policypath --type=bogus default is "$output" "Error: invalid choice: bogus.*" "error from --type=bogus" - run_podman image trust set --policypath=$policypath --type=accept default - run_podman image trust show --policypath=$policypath + run_podman image trust set --signature-policy=$policypath --type=accept default + run_podman image trust show --signature-policy=$policypath is "$output" ".*all *default *accept" "default policy should be accept" - run_podman image trust set --policypath=$policypath --type=reject default - run_podman image trust show --policypath=$policypath + run_podman image trust set --signature-policy=$policypath --type=reject default + run_podman image trust show --signature-policy=$policypath is "$output" ".*all *default *reject" "default policy should be reject" - run_podman image trust set --policypath=$policypath --type=reject docker.io - run_podman image trust show --policypath=$policypath + run_podman image trust set --signature-policy=$policypath --type=reject docker.io + run_podman image trust show --signature-policy=$policypath is "$output" ".*all *default *reject" "default policy should still be reject" is "$output" ".*repository *docker.io *reject" "docker.io should also be reject" - run_podman image trust show --policypath=$policypath --json + run_podman image trust show --signature-policy=$policypath --json subset=$(jq -r '.[0] | .repo_name, .type' <<<"$output" | fmt) is "$subset" "default reject" "--json also shows default" subset=$(jq -r '.[1] | .repo_name, .type' <<<"$output" | fmt) is "$subset" "docker.io reject" "--json also shows docker.io" - run_podman image trust set --policypath=$policypath --type=accept docker.io - run_podman image trust show --policypath=$policypath --json + run_podman image trust set --signature-policy=$policypath --type=accept docker.io + run_podman image trust show --signature-policy=$policypath --json subset=$(jq -r '.[0] | .repo_name, .type' <<<"$output" | fmt) is "$subset" "default reject" "--json, default is still reject" subset=$(jq -r '.[1] | .repo_name, .type' <<<"$output" | fmt) is "$subset" "docker.io accept" "--json, docker.io should now be accept" policy="$(< $policypath)" - run_podman image trust show --policypath=$policypath --raw + run_podman image trust show --signature-policy=$policypath --raw is "$output" "$policy" "output should show match content of policy.json" } diff --git a/vendor/go.podman.io/common/pkg/config/config.go b/vendor/go.podman.io/common/pkg/config/config.go index 7864f9f381..2ab2613495 100644 --- a/vendor/go.podman.io/common/pkg/config/config.go +++ b/vendor/go.podman.io/common/pkg/config/config.go @@ -488,11 +488,6 @@ type EngineConfig struct { // backwards compat with older version of libpod and Podman. SetOptions - // SignaturePolicyPath is the path to a signature policy to use for - // validating images. If left empty, the containers/image default signature - // policy will be used. - SignaturePolicyPath string `toml:"-"` - // SDNotify tells container engine to allow containers to notify the host systemd of // readiness using the SD_NOTIFY mechanism. SDNotify bool `toml:"-"` diff --git a/vendor/go.podman.io/common/pkg/config/config_bsd.go b/vendor/go.podman.io/common/pkg/config/config_bsd.go index fec17a72fe..c925675b8c 100644 --- a/vendor/go.podman.io/common/pkg/config/config_bsd.go +++ b/vendor/go.podman.io/common/pkg/config/config_bsd.go @@ -2,12 +2,6 @@ package config -const ( - // DefaultSignaturePolicyPath is the default value for the - // policy.json file. - DefaultSignaturePolicyPath = "/usr/local/etc/containers/policy.json" -) - var defaultHelperBinariesDir = []string{ "/usr/local/bin", "/usr/local/libexec/podman", diff --git a/vendor/go.podman.io/common/pkg/config/config_darwin.go b/vendor/go.podman.io/common/pkg/config/config_darwin.go index 858187b043..a544f5fed7 100644 --- a/vendor/go.podman.io/common/pkg/config/config_darwin.go +++ b/vendor/go.podman.io/common/pkg/config/config_darwin.go @@ -1,11 +1,5 @@ package config -const ( - // DefaultSignaturePolicyPath is the default value for the - // policy.json file. - DefaultSignaturePolicyPath = "/etc/containers/policy.json" -) - var defaultHelperBinariesDir = []string{ // Relative to the binary directory "$BINDIR/../libexec/podman", diff --git a/vendor/go.podman.io/common/pkg/config/config_linux.go b/vendor/go.podman.io/common/pkg/config/config_linux.go index 655837864c..b3386fdc7a 100644 --- a/vendor/go.podman.io/common/pkg/config/config_linux.go +++ b/vendor/go.podman.io/common/pkg/config/config_linux.go @@ -5,12 +5,6 @@ import ( "go.podman.io/common/pkg/capabilities" ) -const ( - // DefaultSignaturePolicyPath is the default value for the - // policy.json file. - DefaultSignaturePolicyPath = "/etc/containers/policy.json" -) - func selinuxEnabled() bool { return selinux.GetEnabled() } diff --git a/vendor/go.podman.io/common/pkg/config/config_windows.go b/vendor/go.podman.io/common/pkg/config/config_windows.go index 3319ffbbd0..8855b7049c 100644 --- a/vendor/go.podman.io/common/pkg/config/config_windows.go +++ b/vendor/go.podman.io/common/pkg/config/config_windows.go @@ -7,10 +7,6 @@ import ( ) const ( - // DefaultSignaturePolicyPath is the default value for the - // policy.json file. - DefaultSignaturePolicyPath = "/etc/containers/policy.json" - // Mount type for mounting host dir _typeBind = "bind" ) diff --git a/vendor/go.podman.io/common/pkg/config/default.go b/vendor/go.podman.io/common/pkg/config/default.go index e0006cc681..7ea362f6bc 100644 --- a/vendor/go.podman.io/common/pkg/config/default.go +++ b/vendor/go.podman.io/common/pkg/config/default.go @@ -15,7 +15,6 @@ import ( nettypes "go.podman.io/common/libnetwork/types" "go.podman.io/common/pkg/apparmor" "go.podman.io/storage/pkg/configfile" - "go.podman.io/storage/pkg/fileutils" "go.podman.io/storage/pkg/homedir" "go.podman.io/storage/pkg/unshare" "go.podman.io/storage/types" @@ -177,9 +176,6 @@ const ( // DefaultSubnet is the subnet that will be used for the default // network. DefaultSubnet = "10.88.0.0/16" - // DefaultRootlessSignaturePolicyPath is the location within - // XDG_CONFIG_HOME of the rootless policy.json file. - DefaultRootlessSignaturePolicyPath = "containers/policy.json" // DefaultShmSize is the default upper limit on the size of tmpfs mounts. DefaultShmSize = "65536k" // DefaultUserNSSize indicates the default number of UIDs allocated for user namespace within a container. @@ -205,23 +201,6 @@ func defaultConfig() (*Config, error) { return nil, err } - defaultEngineConfig.SignaturePolicyPath = DefaultSignaturePolicyPath - // NOTE: For now we want Windows to use system locations. - // GetRootlessUID == -1 on Windows, so exclude negative range - if unshare.GetRootlessUID() > 0 { - configHome, err := homedir.GetConfigHome() - if err != nil { - return nil, err - } - sigPath := filepath.Join(configHome, DefaultRootlessSignaturePolicyPath) - defaultEngineConfig.SignaturePolicyPath = sigPath - if err := fileutils.Exists(sigPath); err != nil { - if err := fileutils.Exists(DefaultSignaturePolicyPath); err == nil { - defaultEngineConfig.SignaturePolicyPath = DefaultSignaturePolicyPath - } - } - } - return &Config{ Containers: ContainersConfig{ Annotations: configfile.Slice{}, diff --git a/vendor/go.podman.io/image/v5/signature/policy_config.go b/vendor/go.podman.io/image/v5/signature/policy_config.go index 5e06531192..8144c8d9c1 100644 --- a/vendor/go.podman.io/image/v5/signature/policy_config.go +++ b/vendor/go.podman.io/image/v5/signature/policy_config.go @@ -17,26 +17,17 @@ import ( "encoding/json" "errors" "fmt" + "io" "os" - "path/filepath" "go.podman.io/image/v5/docker/reference" "go.podman.io/image/v5/signature/internal" "go.podman.io/image/v5/transports" "go.podman.io/image/v5/types" - "go.podman.io/storage/pkg/fileutils" - "go.podman.io/storage/pkg/homedir" + "go.podman.io/storage/pkg/configfile" "go.podman.io/storage/pkg/regexp" ) -// systemDefaultPolicyPath is the policy path used for DefaultPolicy(). -// You can override this at build time with -// -ldflags '-X go.podman.io/image/v5/signature.systemDefaultPolicyPath=$your_path' -var systemDefaultPolicyPath = builtinDefaultPolicyPath - -// userPolicyFile is the path to the per user policy path. -var userPolicyFile = filepath.FromSlash(".config/containers/policy.json") - // InvalidPolicyFormatError is returned when parsing an invalid policy configuration. type InvalidPolicyFormatError string @@ -51,39 +42,45 @@ func (err InvalidPolicyFormatError) Error() string { // NOTE: When this function returns an error, report it to the user and abort. // DO NOT hard-code fallback policies in your application. func DefaultPolicy(sys *types.SystemContext) (*Policy, error) { - policyPath, err := defaultPolicyPath(sys) - if err != nil { - return nil, err - } - return NewPolicyFromFile(policyPath) -} - -// defaultPolicyPath returns a path to the relevant policy of the system, or an error if the policy is missing. -func defaultPolicyPath(sys *types.SystemContext) (string, error) { - policyFilePath, err := defaultPolicyPathWithHomeDir(sys, homedir.Get(), systemDefaultPolicyPath) - if err != nil { - return "", err - } - return policyFilePath, nil -} - -// defaultPolicyPathWithHomeDir is an internal implementation detail of defaultPolicyPath, -// it exists only to allow testing it with artificial paths. -func defaultPolicyPathWithHomeDir(sys *types.SystemContext, homeDir string, systemPolicyPath string) (string, error) { if sys != nil && sys.SignaturePolicyPath != "" { - return sys.SignaturePolicyPath, nil + return NewPolicyFromFile(sys.SignaturePolicyPath) } - userPolicyFilePath := filepath.Join(homeDir, userPolicyFile) - if err := fileutils.Exists(userPolicyFilePath); err == nil { - return userPolicyFilePath, nil + + var rootForImplicitAbsPaths string + if sys != nil { + rootForImplicitAbsPaths = sys.RootForImplicitAbsolutePaths } - if sys != nil && sys.RootForImplicitAbsolutePaths != "" { - return filepath.Join(sys.RootForImplicitAbsolutePaths, systemPolicyPath), nil + + policyFiles := configfile.File{ + Name: "policy", + Extension: "json", + DoNotLoadDropInFiles: true, + EnvironmentName: "CONTAINERS_POLICY_JSON", + RootForImplicitAbsolutePaths: rootForImplicitAbsPaths, + ErrorIfNotFound: true, } - if err := fileutils.Exists(systemPolicyPath); err == nil { - return systemPolicyPath, nil + + var policy *Policy + for item, err := range configfile.Read(&policyFiles) { + if err != nil { + return nil, err + } + if policy != nil { + // Coverage: This should never happen, configfile.Read ensures at most one policy file. + return nil, fmt.Errorf("internal error: expected at most one policy file, got another item %q", item.Name) + } + + contents, err := io.ReadAll(item.Reader) + if err != nil { + return nil, err + } + policy, err = NewPolicyFromBytes(contents) + if err != nil { + return nil, fmt.Errorf("invalid policy in %q: %w", item.Name, err) + } } - return "", fmt.Errorf("no policy.json file found at any of the following: %q, %q", userPolicyFilePath, systemPolicyPath) + + return policy, nil } // NewPolicyFromFile returns a policy configured in the specified file. diff --git a/vendor/go.podman.io/image/v5/signature/policy_paths_common.go b/vendor/go.podman.io/image/v5/signature/policy_paths_common.go deleted file mode 100644 index 038351cb74..0000000000 --- a/vendor/go.podman.io/image/v5/signature/policy_paths_common.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !freebsd - -package signature - -// builtinDefaultPolicyPath is the policy path used for DefaultPolicy(). -// DO NOT change this, instead see systemDefaultPolicyPath above. -const builtinDefaultPolicyPath = "/etc/containers/policy.json" diff --git a/vendor/go.podman.io/image/v5/signature/policy_paths_freebsd.go b/vendor/go.podman.io/image/v5/signature/policy_paths_freebsd.go deleted file mode 100644 index 6a45a78fa1..0000000000 --- a/vendor/go.podman.io/image/v5/signature/policy_paths_freebsd.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build freebsd - -package signature - -// builtinDefaultPolicyPath is the policy path used for DefaultPolicy(). -// DO NOT change this, instead see systemDefaultPolicyPath above. -const builtinDefaultPolicyPath = "/usr/local/etc/containers/policy.json" diff --git a/vendor/go.podman.io/storage/pkg/configfile/parse.go b/vendor/go.podman.io/storage/pkg/configfile/parse.go index f54f926926..b80ed3fbf5 100644 --- a/vendor/go.podman.io/storage/pkg/configfile/parse.go +++ b/vendor/go.podman.io/storage/pkg/configfile/parse.go @@ -31,6 +31,10 @@ var ( // This can be overridden at build time with the following go linker flag: // -ldflags '-X go.podman.io/storage/pkg/configfile.adminOverrideConfigPath=$your_path' adminOverrideConfigPath = getAdminOverrideConfigPath() + + // ErrConfigFileNotFound is returned when ErrorIfNotFound is true and no config + // file could be loaded. + ErrConfigFileNotFound = errors.New("config file not found") ) type File struct { @@ -43,6 +47,8 @@ type File struct { Extension string // EnvironmentName is the name of environment variable that can be set to specify the override. + // If EnvironmentName is set, the variable with _OVERRIDE suffix is also checked for an override + // unless DoNotLoadDropInFiles is set. // Optional. EnvironmentName string @@ -55,6 +61,7 @@ type File struct { DoNotLoadMainFiles bool // DoNotLoadDropInFiles should be set if only the main files should be loaded. + // If DoNotLoadDropInFiles is set, the _OVERRIDE environment variable is ignored. DoNotLoadDropInFiles bool // DoNotUseExtensionForConfigName makes it so that the extension is only consulted for the drop in @@ -70,6 +77,9 @@ type File struct { // For compatibility reasons this field is written to with the fully resolved paths // of each module as this is what podman expects today. Modules []string + + // ErrorIfNotFound is true if an error should be returned if no file is found. + ErrorIfNotFound bool } // Item is a single config file that is being read once at a time and returned by the iterator from [Read]. @@ -113,10 +123,14 @@ func Read(conf *File) iter.Seq2[*Item, error] { } return func(yield func(*Item, error) bool) { + usedPaths := make([]string, 0, 8) + foundAny := false + shouldLoadMainFile := !conf.DoNotLoadMainFiles shouldLoadDropIns := !conf.DoNotLoadDropInFiles yieldAndClose := func(f *os.File) bool { + foundAny = true ok := yield(&Item{ Reader: f, Name: f.Name(), @@ -134,6 +148,7 @@ func Read(conf *File) iter.Seq2[*Item, error] { if conf.EnvironmentName != "" { if path := os.Getenv(conf.EnvironmentName); path != "" { + usedPaths = append(usedPaths, path) f, err := os.Open(path) // Do not ignore ErrNotExist here, we want to hard error if users set a wrong path here. if err != nil { @@ -165,6 +180,7 @@ func Read(conf *File) iter.Seq2[*Item, error] { if path == "" { continue } + usedPaths = append(usedPaths, path) f, err := os.Open(path) // only ignore ErrNotExist, all other errors get return to the caller via yield if err != nil { @@ -191,6 +207,7 @@ func Read(conf *File) iter.Seq2[*Item, error] { return } for _, file := range files { + usedPaths = append(usedPaths, file) f, err := os.Open(file) // only ignore ErrNotExist, all other errors get return to the caller via yield if err != nil { @@ -211,7 +228,7 @@ func Read(conf *File) iter.Seq2[*Item, error] { dirs := moduleDirectories(defaultConfig, overrideConfig, userConfig) resolvedModules := make([]string, 0, len(conf.Modules)) for _, module := range conf.Modules { - f, err := resolveModule(module, dirs) + f, err := resolveModule(module, dirs, &usedPaths) if err != nil { yield(nil, fmt.Errorf("could not resolve module: %w", err)) return @@ -224,9 +241,10 @@ func Read(conf *File) iter.Seq2[*Item, error] { conf.Modules = resolvedModules } - if conf.EnvironmentName != "" { + if conf.EnvironmentName != "" && !conf.DoNotLoadDropInFiles { // The _OVERRIDE env must be appended after loading all files, even modules. if path := os.Getenv(conf.EnvironmentName + "_OVERRIDE"); path != "" { + usedPaths = append(usedPaths, path) f, err := os.Open(path) // Do not ignore ErrNotExist here, we want to hard error if users set a wrong path here. if err != nil { @@ -238,6 +256,11 @@ func Read(conf *File) iter.Seq2[*Item, error] { } } } + + if conf.ErrorIfNotFound && !foundAny { + yield(nil, fmt.Errorf("%w: no %s file found; searched paths: %q", ErrConfigFileNotFound, configFileName, usedPaths)) + return + } } } @@ -324,8 +347,11 @@ func moduleDirectories(defaultConfig, overrideConfig, userConfig string) []strin } // Resolve the specified path to a module. -func resolveModule(path string, dirs []string) (*os.File, error) { +func resolveModule(path string, dirs []string, usedPaths *[]string) (*os.File, error) { if filepath.IsAbs(path) { + if usedPaths != nil { + *usedPaths = append(*usedPaths, path) + } return os.Open(path) } @@ -334,6 +360,9 @@ func resolveModule(path string, dirs []string) (*os.File, error) { var multiErr error for _, d := range dirs { candidate := filepath.Join(d, path) + if usedPaths != nil { + *usedPaths = append(*usedPaths, candidate) + } f, err := os.Open(candidate) if err == nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 1e823650a5..330b2e27d3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -733,7 +733,7 @@ go.opentelemetry.io/otel/trace go.opentelemetry.io/otel/trace/embedded go.opentelemetry.io/otel/trace/internal/telemetry go.opentelemetry.io/otel/trace/noop -# go.podman.io/common v0.67.2-0.20260416202057-7d53666cdee9 +# go.podman.io/common v0.67.2-0.20260420103546-618304d6f83d ## explicit; go 1.25.6 go.podman.io/common/internal go.podman.io/common/libimage @@ -799,7 +799,7 @@ go.podman.io/common/pkg/umask go.podman.io/common/pkg/util go.podman.io/common/pkg/version go.podman.io/common/version -# go.podman.io/image/v5 v5.39.3-0.20260416202057-7d53666cdee9 +# go.podman.io/image/v5 v5.39.3-0.20260420103546-618304d6f83d ## explicit; go 1.25.6 go.podman.io/image/v5/copy go.podman.io/image/v5/directory @@ -876,7 +876,7 @@ go.podman.io/image/v5/transports go.podman.io/image/v5/transports/alltransports go.podman.io/image/v5/types go.podman.io/image/v5/version -# go.podman.io/storage v1.62.1-0.20260416202057-7d53666cdee9 +# go.podman.io/storage v1.62.1-0.20260420103546-618304d6f83d ## explicit; go 1.25.0 go.podman.io/storage go.podman.io/storage/drivers