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 <jkaluza@redhat.com>
This commit is contained in:
Jan Kaluza
2026-04-14 10:14:37 +02:00
parent 34a4633d5f
commit 54d61c2f02
28 changed files with 167 additions and 268 deletions

View File

@@ -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.

View File

@@ -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")

View File

@@ -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:

6
go.mod
View File

@@ -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

12
go.sum
View File

@@ -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=

View File

@@ -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)

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -1,10 +0,0 @@
package ocipull
import (
"os"
"path/filepath"
)
func localPolicyOverwrites() []string {
return []string{filepath.Join(os.Getenv("APPDATA"), "containers", policyfile)}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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"))

View File

@@ -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"
}

View File

@@ -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:"-"`

View File

@@ -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",

View File

@@ -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",

View File

@@ -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()
}

View File

@@ -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"
)

View File

@@ -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{},

View File

@@ -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.

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 {

6
vendor/modules.txt vendored
View File

@@ -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