mirror of
https://github.com/containers/podman.git
synced 2025-10-16 18:53:19 +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 (
|
||||
"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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
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