Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4778d3de6a | ||
|
|
a4ac09e7da | ||
|
|
9250552c5b | ||
|
|
2acc30693f | ||
|
|
5db1b5c4c1 |
@@ -6,16 +6,14 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/attest/pkg/policy"
|
||||
"github.com/docker/attest/pkg/signerverifier"
|
||||
"github.com/docker/attest/pkg/tlog"
|
||||
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
||||
)
|
||||
|
||||
const (
|
||||
UseMockTL = true
|
||||
UseMockKMS = true
|
||||
UseMockPolicy = true
|
||||
UseMockTL = true
|
||||
UseMockKMS = true
|
||||
|
||||
AWSRegion = "us-east-1"
|
||||
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
|
||||
@@ -57,15 +55,6 @@ func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
|
||||
|
||||
ctx := tlog.WithTL(context.Background(), tl)
|
||||
|
||||
var policyEvaluator policy.Evaluator
|
||||
if UseMockPolicy {
|
||||
policyEvaluator = policy.GetMockPolicy()
|
||||
} else {
|
||||
policyEvaluator = policy.NewRegoEvaluator(true)
|
||||
}
|
||||
|
||||
ctx = policy.WithPolicyEvaluator(ctx, policyEvaluator)
|
||||
|
||||
var signer dsse.SignerVerifier
|
||||
var err error
|
||||
if UseMockKMS {
|
||||
|
||||
@@ -44,7 +44,7 @@ func NewVerifier(opts *policy.Options) (Verifier, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *tufVerifier) Verify(ctx context.Context, src *oci.ImageSpec) (result *VerificationResult, err error) {
|
||||
func (verifier *tufVerifier) Verify(ctx context.Context, src *oci.ImageSpec) (result *VerificationResult, err error) {
|
||||
// so that we can resolve mapping from the image name earlier
|
||||
detailsResolver, err := policy.CreateImageDetailsResolver(src)
|
||||
if err != nil {
|
||||
@@ -54,35 +54,36 @@ func (v *tufVerifier) Verify(ctx context.Context, src *oci.ImageSpec) (result *V
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve image name: %w", err)
|
||||
}
|
||||
policyResolver := policy.NewResolver(v.tufClient, v.opts)
|
||||
pctx, err := policyResolver.ResolvePolicy(ctx, imageName)
|
||||
policyResolver := policy.NewResolver(verifier.tufClient, verifier.opts)
|
||||
resolvedPolicy, err := policyResolver.ResolvePolicy(ctx, imageName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve policy: %w", err)
|
||||
}
|
||||
|
||||
if pctx == nil {
|
||||
if resolvedPolicy == nil {
|
||||
return &VerificationResult{
|
||||
Outcome: OutcomeNoPolicy,
|
||||
}, nil
|
||||
}
|
||||
// this is overriding the mapping with a referrers config. Useful for testing if nothing else
|
||||
if v.opts.ReferrersRepo != "" {
|
||||
pctx.Mapping.Attestations = &config.AttestationConfig{
|
||||
Repo: v.opts.ReferrersRepo,
|
||||
if verifier.opts.ReferrersRepo != "" {
|
||||
resolvedPolicy.Mapping.Attestations = &config.AttestationConfig{
|
||||
Repo: verifier.opts.ReferrersRepo,
|
||||
Style: config.AttestationStyleReferrers,
|
||||
}
|
||||
} else if v.opts.AttestationStyle == config.AttestationStyleAttached {
|
||||
pctx.Mapping.Attestations = &config.AttestationConfig{
|
||||
Repo: v.opts.ReferrersRepo,
|
||||
} else if verifier.opts.AttestationStyle == config.AttestationStyleAttached {
|
||||
resolvedPolicy.Mapping.Attestations = &config.AttestationConfig{
|
||||
Repo: verifier.opts.ReferrersRepo,
|
||||
Style: config.AttestationStyleAttached,
|
||||
}
|
||||
}
|
||||
// because we have a mapping now, we can select a resolver based on its contents (ie. referrers or attached)
|
||||
resolver, err := policy.CreateAttestationResolver(detailsResolver, pctx.Mapping)
|
||||
resolver, err := policy.CreateAttestationResolver(detailsResolver, resolvedPolicy.Mapping)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create attestation resolver: %w", err)
|
||||
}
|
||||
result, err = VerifyAttestations(ctx, resolver, pctx)
|
||||
evaluator := policy.NewRegoEvaluator(verifier.opts.Debug)
|
||||
result, err = VerifyAttestations(ctx, resolver, evaluator, resolvedPolicy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to evaluate policy: %w", err)
|
||||
}
|
||||
@@ -183,7 +184,7 @@ func toVerificationResult(p *policy.Policy, input *policy.Input, result *policy.
|
||||
}, nil
|
||||
}
|
||||
|
||||
func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, pctx *policy.Policy) (*VerificationResult, error) {
|
||||
func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, evaluator policy.Evaluator, resolvedPolicy *policy.Policy) (*VerificationResult, error) {
|
||||
desc, err := resolver.ImageDescriptor(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get image descriptor: %w", err)
|
||||
@@ -198,7 +199,7 @@ func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, pctx
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pctx.ResolvedName != "" {
|
||||
if resolvedPolicy.ResolvedName != "" {
|
||||
// this means the name we have is not the one we want to use for policy evaluation
|
||||
// so we need to replace it with the one we resolved during policy resolution.
|
||||
// this can happen if the name is an alias for another image, e.g. if it is a mirror
|
||||
@@ -207,7 +208,7 @@ func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, pctx
|
||||
return nil, fmt.Errorf("failed to parse image name: %w", err)
|
||||
}
|
||||
oldName := ref.Name()
|
||||
name = strings.Replace(name, oldName, pctx.ResolvedName, 1)
|
||||
name = strings.Replace(name, oldName, resolvedPolicy.ResolvedName, 1)
|
||||
}
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(name)
|
||||
@@ -239,16 +240,11 @@ func VerifyAttestations(ctx context.Context, resolver attestation.Resolver, pctx
|
||||
if tag != "" {
|
||||
input.Tag = tag
|
||||
}
|
||||
|
||||
evaluator, err := policy.GetPolicyEvaluator(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err := evaluator.Evaluate(ctx, resolver, pctx, input)
|
||||
result, err := evaluator.Evaluate(ctx, resolver, resolvedPolicy, input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("policy evaluation failed: %w", err)
|
||||
}
|
||||
verificationResult, err := toVerificationResult(pctx, input, result)
|
||||
verificationResult, err := toVerificationResult(resolvedPolicy, input, result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert to policy result: %w", err)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestVerifyAttestations(t *testing.T) {
|
||||
{"policy ok", nil, nil},
|
||||
{"policy error", fmt.Errorf("policy error"), fmt.Errorf("policy evaluation failed: policy error")},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockPE := policy.MockPolicyEvaluator{
|
||||
@@ -54,8 +54,7 @@ func TestVerifyAttestations(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
ctx := policy.WithPolicyEvaluator(context.Background(), &mockPE)
|
||||
_, err := VerifyAttestations(ctx, resolver, &policy.Policy{ResolvedName: ""})
|
||||
_, err := VerifyAttestations(ctx, resolver, &mockPE, &policy.Policy{ResolvedName: ""})
|
||||
if tc.expectedError != nil {
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, tc.expectedError.Error(), err.Error())
|
||||
@@ -69,7 +68,6 @@ func TestVerifyAttestations(t *testing.T) {
|
||||
|
||||
func TestVSA(t *testing.T) {
|
||||
ctx, signer := test.Setup(t)
|
||||
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
||||
// setup an image with signed attestations
|
||||
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
||||
|
||||
@@ -122,7 +120,6 @@ func TestVSA(t *testing.T) {
|
||||
|
||||
func TestVerificationFailure(t *testing.T) {
|
||||
ctx, signer := test.Setup(t)
|
||||
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
||||
// setup an image with signed attestations
|
||||
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
||||
|
||||
@@ -175,7 +172,6 @@ func TestVerificationFailure(t *testing.T) {
|
||||
|
||||
func TestSignVerify(t *testing.T) {
|
||||
ctx, signer := test.Setup(t)
|
||||
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
||||
// setup an image with signed attestations
|
||||
outputLayout := test.CreateTempDir(t, "", TestTempDir)
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ var (
|
||||
|
||||
func TestAttestationReferenceTypes(t *testing.T) {
|
||||
ctx, signer := test.Setup(t)
|
||||
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(true))
|
||||
platforms := []string{"linux/amd64", "linux/arm64"}
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
|
||||
@@ -2,29 +2,10 @@ package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/attest/pkg/attestation"
|
||||
)
|
||||
|
||||
type policyEvaluatorCtxKeyType struct{}
|
||||
|
||||
var PolicyEvaluatorCtxKey policyEvaluatorCtxKeyType
|
||||
|
||||
// sets PolicyEvaluator in context.
|
||||
func WithPolicyEvaluator(ctx context.Context, pe Evaluator) context.Context {
|
||||
return context.WithValue(ctx, PolicyEvaluatorCtxKey, pe)
|
||||
}
|
||||
|
||||
// gets PolicyEvaluator from context, defaults to Rego PolicyEvaluator if not set.
|
||||
func GetPolicyEvaluator(ctx context.Context) (Evaluator, error) {
|
||||
t, ok := ctx.Value(PolicyEvaluatorCtxKey).(Evaluator)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no policy evaluator client set on context (set one with policy.WithPolicyEvaluator)")
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
type Evaluator interface {
|
||||
Evaluate(ctx context.Context, resolver attestation.Resolver, pctx *Policy, input *Input) (*Result, error)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
repo string
|
||||
policyPath string
|
||||
expectSuccess bool
|
||||
isCanonical bool
|
||||
resolver attestation.Resolver
|
||||
@@ -50,19 +50,19 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
|
||||
policyID string
|
||||
resolveErrorStr string
|
||||
}{
|
||||
{repo: "testdata/policies/allow", expectSuccess: true, resolver: defaultResolver},
|
||||
{repo: "testdata/policies/allow", expectSuccess: true, resolver: defaultResolver, policyID: "docker-official-images"},
|
||||
{repo: "testdata/policies/allow", resolver: defaultResolver, policyID: "non-existent-policy-id", resolveErrorStr: resolveErrorStr},
|
||||
{repo: "testdata/policies/deny", resolver: defaultResolver},
|
||||
{repo: "testdata/policies/verify-sig", expectSuccess: true, resolver: defaultResolver},
|
||||
{repo: "testdata/policies/wrong-key", resolver: defaultResolver},
|
||||
{repo: "testdata/policies/allow-canonical", expectSuccess: true, isCanonical: true, resolver: defaultResolver},
|
||||
{repo: "testdata/policies/allow-canonical", resolver: defaultResolver},
|
||||
{repo: "testdata/policies/no-rego", resolver: defaultResolver, resolveErrorStr: "no policy file found in policy mapping"},
|
||||
{policyPath: "testdata/policies/allow", expectSuccess: true, resolver: defaultResolver},
|
||||
{policyPath: "testdata/policies/allow", expectSuccess: true, resolver: defaultResolver, policyID: "docker-official-images"},
|
||||
{policyPath: "testdata/policies/allow", resolver: defaultResolver, policyID: "non-existent-policy-id", resolveErrorStr: resolveErrorStr},
|
||||
{policyPath: "testdata/policies/deny", resolver: defaultResolver},
|
||||
{policyPath: "testdata/policies/verify-sig", expectSuccess: true, resolver: defaultResolver},
|
||||
{policyPath: "testdata/policies/wrong-key", resolver: defaultResolver},
|
||||
{policyPath: "testdata/policies/allow-canonical", expectSuccess: true, isCanonical: true, resolver: defaultResolver},
|
||||
{policyPath: "testdata/policies/allow-canonical", resolver: defaultResolver},
|
||||
{policyPath: "testdata/policies/no-rego", resolver: defaultResolver, resolveErrorStr: "no policy file found in policy mapping"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.repo, func(t *testing.T) {
|
||||
t.Run(tc.policyPath, func(t *testing.T) {
|
||||
input := &policy.Input{
|
||||
Digest: "sha256:test-digest",
|
||||
PURL: "test-purl",
|
||||
@@ -75,7 +75,7 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
|
||||
tc.opts = &policy.Options{
|
||||
LocalTargetsDir: test.CreateTempDir(t, "", "tuf-targets"),
|
||||
PolicyID: tc.policyID,
|
||||
LocalPolicyDir: tc.repo,
|
||||
LocalPolicyDir: tc.policyPath,
|
||||
DisableTUF: true,
|
||||
}
|
||||
}
|
||||
|
||||
65
pkg/policy/resolver_test.go
Normal file
65
pkg/policy/resolver_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package policy_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/attest/internal/test"
|
||||
"github.com/docker/attest/pkg/policy"
|
||||
"github.com/docker/attest/pkg/tuf"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestResolvePolicy(t *testing.T) {
|
||||
localPolicyPath := "testdata/policies/allow"
|
||||
tufPolicyPath := "testdata/policies/allow-canonical"
|
||||
noLocalPolicyPath := "testdata/policies/no-policy"
|
||||
testPolicyID := "docker-official-images"
|
||||
testImageName := "localhost:5001/test/repo:tag"
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
policyPath string
|
||||
policyID string
|
||||
localOverridesTUF bool // if a policy is provided locally, it should override TUF
|
||||
DisableTUF bool
|
||||
}{
|
||||
{name: "resolve by id (TUF only)", policyID: testPolicyID, DisableTUF: false},
|
||||
{name: "resolve by id (local mapping, TUF policy)", policyPath: noLocalPolicyPath, policyID: testPolicyID, DisableTUF: false},
|
||||
{name: "resolve by id (local mapping, local policy, no TUF)", policyPath: localPolicyPath, policyID: testPolicyID, DisableTUF: true},
|
||||
{name: "resolve by id (local mapping, local policy)", policyPath: localPolicyPath, policyID: testPolicyID, DisableTUF: false, localOverridesTUF: true},
|
||||
{name: "resolve by match (TUF only)", DisableTUF: false},
|
||||
{name: "resolve by match (local mapping, TUF policy)", policyPath: noLocalPolicyPath, DisableTUF: false},
|
||||
{name: "resolve by match (local mapping, local policy, no TUF)", policyPath: localPolicyPath, DisableTUF: true},
|
||||
{name: "resolve by match (local mapping, local policy)", policyPath: localPolicyPath, DisableTUF: false, localOverridesTUF: true},
|
||||
}
|
||||
|
||||
var tufClient tuf.Downloader
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
opts := &policy.Options{}
|
||||
tempDir := test.CreateTempDir(t, "", "tuf-dest")
|
||||
if !tc.DisableTUF {
|
||||
tufClient = tuf.NewMockTufClient(tufPolicyPath)
|
||||
}
|
||||
if tc.policyID != "" {
|
||||
opts.PolicyID = tc.policyID
|
||||
}
|
||||
if tc.policyPath != "" {
|
||||
opts.LocalPolicyDir = tc.policyPath
|
||||
}
|
||||
opts.DisableTUF = tc.DisableTUF
|
||||
opts.LocalTargetsDir = tempDir
|
||||
resolver := policy.NewResolver(tufClient, opts)
|
||||
policy, err := resolver.ResolvePolicy(context.Background(), testImageName)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, policy)
|
||||
if tc.DisableTUF || tc.localOverridesTUF {
|
||||
assert.Contains(t, policy.URI, localPolicyPath)
|
||||
} else {
|
||||
assert.Contains(t, policy.URI, tufPolicyPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
10
pkg/policy/testdata/policies/no-policy/mapping.yaml
vendored
Normal file
10
pkg/policy/testdata/policies/no-policy/mapping.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# map repos to policies
|
||||
version: v1
|
||||
kind: policy-mapping
|
||||
rules:
|
||||
- pattern: "^docker[.]io/library/(.*)$"
|
||||
policy-id: docker-official-images
|
||||
- pattern: ^localhost:5001/(.*)$
|
||||
rewrite: docker.io/library/$1
|
||||
- pattern: ^registry[.]local:5000/(.*)$
|
||||
rewrite: docker.io/library/$1
|
||||
@@ -34,6 +34,7 @@ type Options struct {
|
||||
PolicyID string
|
||||
ReferrersRepo string
|
||||
AttestationStyle config.AttestationStyle
|
||||
Debug bool
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
|
||||
@@ -1,5 +1,42 @@
|
||||
package tuf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/attest/internal/util"
|
||||
)
|
||||
|
||||
type MockTufClient struct {
|
||||
srcPath string
|
||||
}
|
||||
|
||||
func NewMockTufClient(srcPath string) *MockTufClient {
|
||||
if srcPath == "" {
|
||||
panic("srcPath must be set")
|
||||
}
|
||||
return &MockTufClient{
|
||||
srcPath: srcPath,
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *MockTufClient) DownloadTarget(target string, _ string) (file *TargetFile, err error) {
|
||||
targetPath := filepath.Join(dc.srcPath, target)
|
||||
src, err := os.Open(targetPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
b, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &TargetFile{TargetURI: targetPath, Data: b, Digest: util.SHA256Hex(b)}, nil
|
||||
}
|
||||
|
||||
type MockVersionChecker struct {
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/google/go-containerregistry/pkg/v1/static"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/testcontainers/testcontainers-go/modules/registry"
|
||||
"github.com/theupdateframework/go-tuf/v2/metadata"
|
||||
"github.com/theupdateframework/go-tuf/v2/metadata/config"
|
||||
@@ -55,33 +56,35 @@ func TestRegistryFetcher(t *testing.T) {
|
||||
delegatedDir := CreateTempDir(t, dir, delegatedRole)
|
||||
delegatedTargetFile := fmt.Sprintf("%s/%s", delegatedRole, targetFile)
|
||||
|
||||
cfg, err := config.New(metadataRepo, DockerTUFRootDev.Data)
|
||||
assert.NoError(t, err)
|
||||
// note - url is ignored here - needed to make http url parsing happy even when using oci
|
||||
cfg, err := config.New("", DockerTUFRootDev.Data)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg.Fetcher = NewRegistryFetcher(metadataRepo, metadataImgTag, targetsRepo)
|
||||
cfg.LocalMetadataDir = dir
|
||||
cfg.LocalTargetsDir = dir
|
||||
cfg.RemoteTargetsURL = targetsRepo
|
||||
cfg.RemoteMetadataURL = metadataRepo
|
||||
|
||||
// create a new Updater instance
|
||||
up, err := updater.New(cfg)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// refresh the metadata
|
||||
err = up.Refresh()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// download top-level target
|
||||
targetInfo, err := up.GetTargetInfo(targetFile)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
_, _, err = up.DownloadTarget(targetInfo, filepath.Join(dir, targetInfo.Path), "")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// download delegated target
|
||||
targetInfo, err = up.GetTargetInfo(delegatedTargetFile)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
_, _, err = up.DownloadTarget(targetInfo, filepath.Join(delegatedDir, targetFile), "")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestRoleFromConsistentName(t *testing.T) {
|
||||
@@ -355,9 +358,6 @@ func RunTestRegistry(t *testing.T) (*registry.RegistryContainer, *url.URL) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse container address: %s", err)
|
||||
}
|
||||
if addr.Hostname() == "127.0.0.1" {
|
||||
addr.Host = "localhost:" + addr.Port()
|
||||
}
|
||||
return registryContainer, addr
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/attest/internal/embed"
|
||||
"github.com/docker/attest/internal/util"
|
||||
"github.com/theupdateframework/go-tuf/v2/metadata"
|
||||
@@ -107,20 +108,28 @@ func NewClient(opts *ClientOptions) (*Client, error) {
|
||||
}
|
||||
|
||||
// create updater configuration
|
||||
cfg, err := config.New(opts.MetadataSource, rootBytes) // default config
|
||||
// this is parsed as an HTTP url (which doesn't work for OCI). We're setting this to make TUF happy
|
||||
// and overwriding the configuration below
|
||||
cfg, err := config.New("", rootBytes) // default config
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create TUF updater configuration: %w", err)
|
||||
}
|
||||
cfg.LocalMetadataDir = metadataPath
|
||||
cfg.LocalTargetsDir = filepath.Join(metadataPath, "download")
|
||||
cfg.RemoteMetadataURL = opts.MetadataSource
|
||||
cfg.RemoteTargetsURL = opts.TargetsSource
|
||||
|
||||
if tufSource == OCISource {
|
||||
metadataRepo, metadataTag, found := strings.Cut(opts.MetadataSource, ":")
|
||||
if !found {
|
||||
fmt.Printf("metadata tag not found in URL, using latest\n")
|
||||
metadataTag = LatestTag
|
||||
ref, err := reference.ParseNormalizedNamed(opts.MetadataSource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse metadata source: %w", err)
|
||||
}
|
||||
// add latest tag
|
||||
metadataTag := LatestTag
|
||||
if tag, ok := ref.(reference.Tagged); ok {
|
||||
metadataTag = tag.Tag()
|
||||
}
|
||||
metadataRepo := ref.Name()
|
||||
cfg.Fetcher = NewRegistryFetcher(metadataRepo, metadataTag, opts.TargetsSource)
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ func TestDownloadTarget(t *testing.T) {
|
||||
|
||||
// download delegated target
|
||||
targetInfo, err := tufClient.updater.GetTargetInfo(delegatedTargetFile)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
_, err = tufClient.DownloadTarget(targetInfo.Path, filepath.Join(tufPath, targetInfo.Path))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user