summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiloslav Trmač <mitr@redhat.com>2022-08-24 21:39:14 +0200
committerMiloslav Trmač <mitr@redhat.com>2022-08-25 01:50:42 +0200
commitcbdbb025a3f6e6e5417cdade032075d679842056 (patch)
tree0430bbab018b9699b22495ecb3ebb0fb3855dd34
parent4f68075306efb9381de3c5ea5762a3f843137b56 (diff)
downloadpodman-cbdbb025a3f6e6e5417cdade032075d679842056.tar.gz
podman-cbdbb025a3f6e6e5417cdade032075d679842056.tar.bz2
podman-cbdbb025a3f6e6e5417cdade032075d679842056.zip
Move most of imageEngine.SetTrust to pkg/trust.AddPolicyEntries
This will allow us to write unit tests without setting up the complete Podman runtime (and without the Linux dependency). Also, actually add a basic smoke test of the core functionality. Should not change behavior. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
-rw-r--r--pkg/domain/infra/abi/trust.go68
-rw-r--r--pkg/trust/policy.go74
-rw-r--r--pkg/trust/policy_test.go71
3 files changed, 150 insertions, 63 deletions
diff --git a/pkg/domain/infra/abi/trust.go b/pkg/domain/infra/abi/trust.go
index 61bf03727..381ea5deb 100644
--- a/pkg/domain/infra/abi/trust.go
+++ b/pkg/domain/infra/abi/trust.go
@@ -2,11 +2,8 @@ package abi
import (
"context"
- "encoding/json"
- "errors"
"fmt"
"io/ioutil"
- "os"
"strings"
"github.com/containers/podman/v4/pkg/domain/entities"
@@ -51,71 +48,16 @@ func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options enti
}
scope := args[0]
- var (
- policyContentStruct trust.PolicyContent
- newReposContent []trust.RepoContent
- )
- trustType := options.Type
- if trustType == "accept" {
- trustType = "insecureAcceptAnything"
- }
-
- pubkeysfile := options.PubKeysFile
- if len(pubkeysfile) == 0 && trustType == "signedBy" {
- return errors.New("at least one public key must be defined for type 'signedBy'")
- }
-
policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext())
if len(options.PolicyPath) > 0 {
policyPath = options.PolicyPath
}
- _, err := os.Stat(policyPath)
- if !os.IsNotExist(err) {
- policyContent, err := ioutil.ReadFile(policyPath)
- if err != nil {
- return err
- }
- if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil {
- return errors.New("could not read trust policies")
- }
- }
- if len(pubkeysfile) != 0 {
- for _, filepath := range pubkeysfile {
- newReposContent = append(newReposContent, trust.RepoContent{Type: trustType, KeyType: "GPGKeys", KeyPath: filepath})
- }
- } else {
- newReposContent = append(newReposContent, trust.RepoContent{Type: trustType})
- }
- if scope == "default" {
- policyContentStruct.Default = newReposContent
- } else {
- if len(policyContentStruct.Default) == 0 {
- return errors.New("default trust policy must be set")
- }
- registryExists := false
- for transport, transportval := range policyContentStruct.Transports {
- _, registryExists = transportval[scope]
- if registryExists {
- policyContentStruct.Transports[transport][scope] = newReposContent
- break
- }
- }
- if !registryExists {
- if policyContentStruct.Transports == nil {
- policyContentStruct.Transports = make(map[string]trust.RepoMap)
- }
- if policyContentStruct.Transports["docker"] == nil {
- policyContentStruct.Transports["docker"] = make(map[string][]trust.RepoContent)
- }
- policyContentStruct.Transports["docker"][scope] = append(policyContentStruct.Transports["docker"][scope], newReposContent...)
- }
- }
- data, err := json.MarshalIndent(policyContentStruct, "", " ")
- if err != nil {
- return fmt.Errorf("error setting trust policy: %w", err)
- }
- return ioutil.WriteFile(policyPath, data, 0644)
+ return trust.AddPolicyEntries(policyPath, trust.AddPolicyEntriesInput{
+ Scope: scope,
+ Type: options.Type,
+ PubKeyFiles: options.PubKeysFile,
+ })
}
func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]*trust.Policy, error) {
diff --git a/pkg/trust/policy.go b/pkg/trust/policy.go
index 62950131d..352be781c 100644
--- a/pkg/trust/policy.go
+++ b/pkg/trust/policy.go
@@ -5,6 +5,7 @@ import (
"bytes"
"encoding/base64"
"encoding/json"
+ "errors"
"fmt"
"io/ioutil"
"os"
@@ -123,3 +124,76 @@ func GetPolicy(policyPath string) (PolicyContent, error) {
}
return policyContentStruct, nil
}
+
+// AddPolicyEntriesInput collects some parameters to AddPolicyEntries,
+// primarily so that the callers use named values instead of just strings in a sequence.
+type AddPolicyEntriesInput struct {
+ Scope string // "default" or a docker/atomic scope name
+ Type string
+ PubKeyFiles []string // For signature enforcement types, paths to public keys files (where the image needs to be signed by at least one key from _each_ of the files). File format depends on Type.
+}
+
+// AddPolicyEntries adds one or more policy entries necessary to implement AddPolicyEntriesInput.
+func AddPolicyEntries(policyPath string, input AddPolicyEntriesInput) error {
+ var (
+ policyContentStruct PolicyContent
+ newReposContent []RepoContent
+ )
+ trustType := input.Type
+ if trustType == "accept" {
+ trustType = "insecureAcceptAnything"
+ }
+
+ pubkeysfile := input.PubKeyFiles
+ if len(pubkeysfile) == 0 && trustType == "signedBy" {
+ return errors.New("at least one public key must be defined for type 'signedBy'")
+ }
+
+ _, err := os.Stat(policyPath)
+ if !os.IsNotExist(err) {
+ policyContent, err := ioutil.ReadFile(policyPath)
+ if err != nil {
+ return err
+ }
+ if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil {
+ return errors.New("could not read trust policies")
+ }
+ }
+ if len(pubkeysfile) != 0 {
+ for _, filepath := range pubkeysfile {
+ newReposContent = append(newReposContent, RepoContent{Type: trustType, KeyType: "GPGKeys", KeyPath: filepath})
+ }
+ } else {
+ newReposContent = append(newReposContent, RepoContent{Type: trustType})
+ }
+ if input.Scope == "default" {
+ policyContentStruct.Default = newReposContent
+ } else {
+ if len(policyContentStruct.Default) == 0 {
+ return errors.New("default trust policy must be set")
+ }
+ registryExists := false
+ for transport, transportval := range policyContentStruct.Transports {
+ _, registryExists = transportval[input.Scope]
+ if registryExists {
+ policyContentStruct.Transports[transport][input.Scope] = newReposContent
+ break
+ }
+ }
+ if !registryExists {
+ if policyContentStruct.Transports == nil {
+ policyContentStruct.Transports = make(map[string]RepoMap)
+ }
+ if policyContentStruct.Transports["docker"] == nil {
+ policyContentStruct.Transports["docker"] = make(map[string][]RepoContent)
+ }
+ policyContentStruct.Transports["docker"][input.Scope] = append(policyContentStruct.Transports["docker"][input.Scope], newReposContent...)
+ }
+ }
+
+ data, err := json.MarshalIndent(policyContentStruct, "", " ")
+ if err != nil {
+ return fmt.Errorf("error setting trust policy: %w", err)
+ }
+ return ioutil.WriteFile(policyPath, data, 0644)
+}
diff --git a/pkg/trust/policy_test.go b/pkg/trust/policy_test.go
new file mode 100644
index 000000000..1f2f585c8
--- /dev/null
+++ b/pkg/trust/policy_test.go
@@ -0,0 +1,71 @@
+package trust
+
+import (
+ "encoding/json"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/containers/image/v5/signature"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestAddPolicyEntries(t *testing.T) {
+ tempDir := t.TempDir()
+ policyPath := filepath.Join(tempDir, "policy.json")
+
+ minimalPolicy := &signature.Policy{
+ Default: []signature.PolicyRequirement{
+ signature.NewPRInsecureAcceptAnything(),
+ },
+ }
+ minimalPolicyJSON, err := json.Marshal(minimalPolicy)
+ require.NoError(t, err)
+ err = os.WriteFile(policyPath, minimalPolicyJSON, 0600)
+ require.NoError(t, err)
+
+ err = AddPolicyEntries(policyPath, AddPolicyEntriesInput{
+ Scope: "default",
+ Type: "reject",
+ })
+ assert.NoError(t, err)
+ err = AddPolicyEntries(policyPath, AddPolicyEntriesInput{
+ Scope: "quay.io/accepted",
+ Type: "accept",
+ })
+ assert.NoError(t, err)
+ err = AddPolicyEntries(policyPath, AddPolicyEntriesInput{
+ Scope: "quay.io/multi-signed",
+ Type: "signedBy",
+ PubKeyFiles: []string{"/1.pub", "/2.pub"},
+ })
+ assert.NoError(t, err)
+
+ // Test that the outcome is consumable, and compare it with the expected values.
+ parsedPolicy, err := signature.NewPolicyFromFile(policyPath)
+ require.NoError(t, err)
+ assert.Equal(t, &signature.Policy{
+ Default: signature.PolicyRequirements{
+ signature.NewPRReject(),
+ },
+ Transports: map[string]signature.PolicyTransportScopes{
+ "docker": {
+ "quay.io/accepted": {
+ signature.NewPRInsecureAcceptAnything(),
+ },
+ "quay.io/multi-signed": {
+ xNewPRSignedByKeyPath(t, "/1.pub", signature.NewPRMMatchRepoDigestOrExact()),
+ xNewPRSignedByKeyPath(t, "/2.pub", signature.NewPRMMatchRepoDigestOrExact()),
+ },
+ },
+ },
+ }, parsedPolicy)
+}
+
+// xNewPRSignedByKeyPath is a wrapper for NewPRSignedByKeyPath which must not fail.
+func xNewPRSignedByKeyPath(t *testing.T, keyPath string, signedIdentity signature.PolicyReferenceMatch) signature.PolicyRequirement {
+ pr, err := signature.NewPRSignedByKeyPath(signature.SBKeyTypeGPGKeys, keyPath, signedIdentity)
+ require.NoError(t, err)
+ return pr
+}