fix: layout attestation resolver
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user