fix: layout attestation resolver

This commit is contained in:
mrjoelkamp
2024-08-05 13:24:58 -05:00
parent 58021646e3
commit c7d17faf05
6 changed files with 41 additions and 91 deletions

View File

@@ -10,12 +10,10 @@ import (
"github.com/docker/attest/internal/test" "github.com/docker/attest/internal/test"
"github.com/docker/attest/pkg/attestation" "github.com/docker/attest/pkg/attestation"
"github.com/docker/attest/pkg/config"
"github.com/docker/attest/pkg/mirror"
"github.com/docker/attest/pkg/oci" "github.com/docker/attest/pkg/oci"
"github.com/docker/attest/pkg/policy" "github.com/docker/attest/pkg/policy"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/mutate"
intoto "github.com/in-toto/in-toto-golang/in_toto" intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -84,25 +82,17 @@ func TestVSA(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// output signed attestations // output signed attestations
idx := v1.ImageIndex(empty.Index) spec, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64))
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{ require.NoError(t, err)
Add: signedIndex, err = mirror.SaveIndex([]*oci.ImageSpec{spec}, signedIndex, attIdx.Name)
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OCIReferenceTarget: attIdx.Name,
},
},
})
_, err = layout.Write(outputLayout, idx)
assert.NoError(t, err) assert.NoError(t, err)
// mocked vsa query should pass // mocked vsa query should pass
policyOpts := &policy.Options{ policyOpts := &policy.Options{
LocalPolicyDir: PassPolicyDir, LocalPolicyDir: PassPolicyDir,
AttestationStyle: config.AttestationStyleAttached,
} }
src, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) results, err := Verify(ctx, spec, policyOpts)
require.NoError(t, err)
results, err := Verify(ctx, src, policyOpts)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, OutcomeSuccess, results.Outcome) assert.Equal(t, OutcomeSuccess, results.Outcome)
assert.Empty(t, results.Violations) assert.Empty(t, results.Violations)
@@ -142,25 +132,17 @@ func TestVerificationFailure(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// output signed attestations // output signed attestations
idx := v1.ImageIndex(empty.Index) spec, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64))
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{ require.NoError(t, err)
Add: signedIndex, err = mirror.SaveIndex([]*oci.ImageSpec{spec}, signedIndex, attIdx.Name)
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OCIReferenceTarget: attIdx.Name,
},
},
})
_, err = layout.Write(outputLayout, idx)
assert.NoError(t, err) assert.NoError(t, err)
// mocked vsa query should fail // mocked vsa query should fail
policyOpts := &policy.Options{ policyOpts := &policy.Options{
LocalPolicyDir: FailPolicyDir, LocalPolicyDir: FailPolicyDir,
AttestationStyle: config.AttestationStyleAttached,
} }
src, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) results, err := Verify(ctx, spec, policyOpts)
require.NoError(t, err)
results, err := Verify(ctx, src, policyOpts)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, OutcomeFailure, results.Outcome) assert.Equal(t, OutcomeFailure, results.Outcome)
assert.Len(t, results.Violations, 1) assert.Len(t, results.Violations, 1)
@@ -224,24 +206,15 @@ func TestSignVerify(t *testing.T) {
imageName = attIdx.Name imageName = attIdx.Name
} }
// output signed attestations // output signed attestations
idx := v1.ImageIndex(empty.Index) spec, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64))
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{ require.NoError(t, err)
Add: signedIndex, err = mirror.SaveIndex([]*oci.ImageSpec{spec}, signedIndex, imageName)
Descriptor: v1.Descriptor{ require.NoError(t, err)
Annotations: map[string]string{
oci.OCIReferenceTarget: imageName,
},
},
})
_, err = layout.Write(outputLayout, idx)
assert.NoError(t, err)
policyOpts := &policy.Options{ policyOpts := &policy.Options{
LocalPolicyDir: tc.policyDir, LocalPolicyDir: tc.policyDir,
} }
src, err := oci.ParseImageSpec(oci.LocalPrefix+outputLayout, oci.WithPlatform(LinuxAMD64)) results, err := Verify(ctx, spec, policyOpts)
require.NoError(t, err)
results, err := Verify(ctx, src, policyOpts)
if tc.expectError { if tc.expectError {
require.Error(t, err) require.Error(t, err)
return return

View File

@@ -36,7 +36,7 @@ type Manifest struct {
// accumulated during signing // accumulated during signing
SignedLayers []*Layer SignedLayers []*Layer
// details of subect image // details of subject image
SubjectName string SubjectName string
SubjectDescriptor *v1.Descriptor SubjectDescriptor *v1.Descriptor
} }

View File

@@ -129,6 +129,7 @@ func SaveImage(output *oci.ImageSpec, image v1.Image, imageName string) error {
func SaveReferrers(manifest *attestation.Manifest, outputs []*oci.ImageSpec) error { func SaveReferrers(manifest *attestation.Manifest, outputs []*oci.ImageSpec) error {
for _, output := range outputs { for _, output := range outputs {
// OCI layout output for referrers not supported
if output.Type == oci.OCI { if output.Type == oci.OCI {
continue continue
} }
@@ -147,10 +148,10 @@ func SaveReferrers(manifest *attestation.Manifest, outputs []*oci.ImageSpec) err
return fmt.Errorf("failed to build image: %w", err) return fmt.Errorf("failed to build image: %w", err)
} }
for _, image := range images { for _, image := range images {
err = SaveImage(attOut, image, "") err := PushImageToRegistry(image, attOut.Identifier)
} if err != nil {
if err != nil { return fmt.Errorf("failed to push image: %w", err)
return fmt.Errorf("failed to push image: %w", err) }
} }
} }
return nil return nil

View File

@@ -115,6 +115,9 @@ func attestationManifestFromOCILayout(path string, platform *v1.Platform) (*atte
} }
} }
} }
if subjectDescriptor == nil {
return nil, fmt.Errorf("platform not found in index")
}
for i := range mfs2.Manifests { for i := range mfs2.Manifests {
mf := &mfs2.Manifests[i] mf := &mfs2.Manifests[i]
if mf.Annotations[att.DockerReferenceType] != attestation.AttestationManifestType { if mf.Annotations[att.DockerReferenceType] != attestation.AttestationManifestType {

View File

@@ -3,6 +3,7 @@ package oci
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/docker/attest/pkg/attestation" "github.com/docker/attest/pkg/attestation"
att "github.com/docker/attest/pkg/attestation" att "github.com/docker/attest/pkg/attestation"
@@ -16,14 +17,6 @@ type ReferrersResolver struct {
} }
func NewReferrersAttestationResolver(src ImageDetailsResolver, options ...func(*ReferrersResolver) error) (*ReferrersResolver, error) { func NewReferrersAttestationResolver(src ImageDetailsResolver, options ...func(*ReferrersResolver) error) (*ReferrersResolver, error) {
// currently only supports RegistryImageDetailsResolver
switch src.(type) {
case *RegistryImageDetailsResolver:
case *MockRegistryResolver:
default:
return nil, fmt.Errorf("unsupported referrers image details resolver type: %T", src)
}
res := &ReferrersResolver{ res := &ReferrersResolver{
ImageDetailsResolver: src, ImageDetailsResolver: src,
} }
@@ -66,7 +59,7 @@ func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateTy
} }
var referrersSubjectRef name.Digest var referrersSubjectRef name.Digest
if r.referrersRepo != "" { if r.referrersRepo != "" {
referrersSubjectRef, err = name.NewDigest(fmt.Sprintf("%s@%s", r.referrersRepo, subjectDigest)) referrersSubjectRef, err = name.NewDigest(fmt.Sprintf("%s@%s", strings.TrimPrefix(r.referrersRepo, RegistryPrefix), subjectDigest))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create referrers reference: %w", err) return nil, fmt.Errorf("failed to create referrers reference: %w", err)
} }

View File

@@ -219,40 +219,20 @@ func CreateImageDetailsResolver(imageSource *oci.ImageSpec) (oci.ImageDetailsRes
} }
func CreateAttestationResolver(resolver oci.ImageDetailsResolver, mapping *config.PolicyMapping) (oci.AttestationResolver, error) { func CreateAttestationResolver(resolver oci.ImageDetailsResolver, mapping *config.PolicyMapping) (oci.AttestationResolver, error) {
switch resolver := resolver.(type) { if mapping.Attestations != nil {
case *oci.RegistryImageDetailsResolver: if mapping.Attestations.Style == config.AttestationStyleAttached {
if mapping.Attestations != nil && mapping.Attestations.Style == config.AttestationStyleAttached { switch resolver := resolver.(type) {
return oci.NewRegistryAttestationResolver(resolver) case *oci.RegistryImageDetailsResolver:
} else { return oci.NewRegistryAttestationResolver(resolver)
if mapping.Attestations != nil && mapping.Attestations.Repo != "" { case *oci.LayoutResolver:
return oci.NewReferrersAttestationResolver(resolver, oci.WithReferrersRepo(mapping.Attestations.Repo))
}
return oci.NewReferrersAttestationResolver(resolver)
}
case *oci.LayoutResolver:
if mapping.Attestations != nil {
switch mapping.Attestations.Style {
case config.AttestationStyleAttached:
return resolver, nil return resolver, nil
case config.AttestationStyleReferrers:
if mapping.Attestations.Repo != "" {
referrersSpec, err := oci.ParseImageSpec(mapping.Attestations.Repo)
if err != nil {
return nil, fmt.Errorf("failed to parse referrers image spec: %w", err)
}
referrersResolver, err := CreateImageDetailsResolver(referrersSpec)
if err != nil {
return nil, fmt.Errorf("failed to create referrers resolver: %w", err)
}
return oci.NewReferrersAttestationResolver(referrersResolver, oci.WithReferrersRepo(mapping.Attestations.Repo))
}
return oci.NewReferrersAttestationResolver(resolver)
default: default:
return nil, fmt.Errorf("unsupported attestation style: %s", mapping.Attestations.Style) return nil, fmt.Errorf("unsupported image details resolver type: %T", resolver)
} }
} }
return resolver, nil if mapping.Attestations.Repo != "" {
default: return oci.NewReferrersAttestationResolver(resolver, oci.WithReferrersRepo(mapping.Attestations.Repo))
return nil, fmt.Errorf("unsupported image details resolver type: %T", resolver) }
} }
return oci.NewReferrersAttestationResolver(resolver)
} }