mirror of
https://github.com/containers/podman.git
synced 2025-10-17 03:04:21 +08:00
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>
This commit is contained in:
@ -2,11 +2,8 @@ package abi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
"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]
|
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())
|
policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext())
|
||||||
if len(options.PolicyPath) > 0 {
|
if len(options.PolicyPath) > 0 {
|
||||||
policyPath = options.PolicyPath
|
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, "", " ")
|
return trust.AddPolicyEntries(policyPath, trust.AddPolicyEntriesInput{
|
||||||
if err != nil {
|
Scope: scope,
|
||||||
return fmt.Errorf("error setting trust policy: %w", err)
|
Type: options.Type,
|
||||||
}
|
PubKeyFiles: options.PubKeysFile,
|
||||||
return ioutil.WriteFile(policyPath, data, 0644)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]*trust.Policy, error) {
|
func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]*trust.Policy, error) {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@ -123,3 +124,76 @@ func GetPolicy(policyPath string) (PolicyContent, error) {
|
|||||||
}
|
}
|
||||||
return policyContentStruct, nil
|
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)
|
||||||
|
}
|
||||||
|
71
pkg/trust/policy_test.go
Normal file
71
pkg/trust/policy_test.go
Normal file
@ -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
|
||||||
|
}
|
Reference in New Issue
Block a user