Skip to content

Commit

Permalink
Implement attestation logic for replaced images
Browse files Browse the repository at this point in the history
  • Loading branch information
errordeveloper committed Sep 28, 2023
1 parent cb0267c commit 6028217
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 28 deletions.
34 changes: 22 additions & 12 deletions attest/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@ import (
"github.com/fxamacker/cbor/v2"
)

type PathCheckerRegistryKey struct {
Path string
Digest digest.SHA256
}

type PathCheckerRegistry struct {
newPathChecker func(string, digest.SHA256) types.PathChecker

registry map[PathCheckerRegistryKey]types.PathChecker
statements types.Statements
registry map[types.PathCheckerRegistryKey]types.PathChecker
mutatedPaths types.Mutations
statements types.Statements

baseDir
}
Expand All @@ -39,7 +35,7 @@ func NewPathCheckerRegistry(dir string, newPathChecker func(string, digest.SHA25
return &PathCheckerRegistry{
baseDir: baseDir{fromWorkDir: dir},
newPathChecker: newPathChecker,
registry: map[PathCheckerRegistryKey]types.PathChecker{},
registry: map[types.PathCheckerRegistryKey]types.PathChecker{},
statements: types.Statements{},
}
}
Expand Down Expand Up @@ -71,13 +67,28 @@ func (r *PathCheckerRegistry) Register(path string, digest digest.SHA256) error
return nil
}

func (r *PathCheckerRegistry) RegisterMutated(mutatedPaths types.Mutations) {
r.mutatedPaths = make(types.Mutations, len(mutatedPaths)) // avoid stale entries
for k := range mutatedPaths {
oldDigest := mutatedPaths[k]
k.Path = r.pathFromRepoRoot(k.Path)
r.mutatedPaths[k] = oldDigest
}
}

func (r *PathCheckerRegistry) AssociateStatements(statements ...types.Statement) error {
for i := range statements {
if err := statements[i].SetSubjects(func(subject *types.Subject) error {
path := r.pathFromRepoRoot(subject.Name)
key := r.makeKey(path, subject.Digest)
if _, ok := r.registry[key]; !ok {
return fmt.Errorf("statement with subject %#v is not relevant (path resoved to %q)", subject, path)
err := fmt.Errorf("statement with subject %#v is not relevant (path resoved to %q)", subject, path)
if r.mutatedPaths == nil {
return err
}
if _, ok := r.mutatedPaths[key]; !ok {
return err
}
}
subject.Name = path
return nil
Expand All @@ -86,7 +97,6 @@ func (r *PathCheckerRegistry) AssociateStatements(statements ...types.Statement)
}
}
r.statements = append(r.statements, statements...)

return nil
}

Expand Down Expand Up @@ -178,6 +188,6 @@ func (r *PathCheckerRegistry) pathFromWorkDir(path string) string {
return filepath.Join(r.fromWorkDir, path)
}

func (r *PathCheckerRegistry) makeKey(path string, digest digest.SHA256) PathCheckerRegistryKey {
return PathCheckerRegistryKey{Path: path, Digest: digest}
func (r *PathCheckerRegistry) makeKey(path string, digest digest.SHA256) types.PathCheckerRegistryKey {
return types.PathCheckerRegistryKey{Path: path, Digest: digest}
}
1 change: 1 addition & 0 deletions attest/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type (
Path string
Digest digest.SHA256
}
Mutations = map[PathCheckerRegistryKey]digest.SHA256

PathChecker interface {
ProviderName() string
Expand Down
5 changes: 4 additions & 1 deletion manifest/imagescanner/imagescanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ func (s *DefaultImageScanner) Scan(dir string, manifests []string) error {

pipeline := kio.Pipeline{
Inputs: []kio.Reader{
&kio.ByteReader{Reader: io.TeeReader(manifest, s.hash)},
&kio.ByteReader{
Reader: io.TeeReader(manifest, s.hash),
OmitReaderAnnotations: true,
},
},
Filters: []kio.Filter{filter},
}
Expand Down
72 changes: 58 additions & 14 deletions manifest/updater/updater.go
Original file line number Diff line number Diff line change
@@ -1,53 +1,85 @@
package updater

import (
"crypto/sha256"
"fmt"
"hash"

"sigs.k8s.io/kustomize/api/filters/imagetag"
kustomize "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"

"github.com/docker/labs-brown-tape/attest/digest"
attestTypes "github.com/docker/labs-brown-tape/attest/types"
"github.com/docker/labs-brown-tape/manifest/types"
manifestTypes "github.com/docker/labs-brown-tape/manifest/types"
)

type Updater interface {
Update(*types.ImageList) error
Update(*manifestTypes.ImageList) error
Mutations() attestTypes.Mutations
}

func NewFileUpdater() Updater { return &FileUpdater{} }
func NewFileUpdater() Updater {
return &FileUpdater{
hash: sha256.New(),
mutations: attestTypes.Mutations{},
}
}

type FileUpdater struct{}
type FileUpdater struct {
hash hash.Hash
mutations attestTypes.Mutations
}

func (u *FileUpdater) Update(images *types.ImageList) error {
for manifest, images := range images.GroupByManifest() {
if err := u.doUpdate(manifest, images.Items()); err != nil {
func (u *FileUpdater) Update(images *manifestTypes.ImageList) error {
groups := images.GroupByManifest()
for manifestPath := range groups {
if err := u.doUpdate(manifestPath, groups[manifestPath].Items()); err != nil {
return err
}
}
return nil
}

func (u *FileUpdater) doUpdate(manifest string, images []types.Image) error {
func (u *FileUpdater) doUpdate(manifestPath string, images []types.Image) error {
if len(images) == 0 {
return fmt.Errorf("no images to update")
}

u.hash.Reset()

pipeline := kio.Pipeline{
Inputs: []kio.Reader{
kio.LocalPackageReader{
PackagePath: manifest,
PackagePath: manifestPath,
},
},
Filters: make([]kio.Filter, len(images)),
Outputs: []kio.Writer{
kio.LocalPackageWriter{
PackagePath: manifest,
PackagePath: manifestPath,
},
kio.ByteWriter{
Writer: u.hash,
KeepReaderAnnotations: false,
ClearAnnotations: []string{
kioutil.PathAnnotation,
kioutil.LegacyPathAnnotation,
},
},
},
}

for i, image := range images {
for i := range images {
pipeline.Filters[i] = imagetag.Filter{
ImageTag: kustomize.Image{
Name: image.OriginalName,
NewName: image.NewName,
Name: images[i].OriginalName,
NewName: images[i].NewName,
// NB: docs say NewTag is ignored when digest is set, but it's not true
NewTag: image.NewTag,
Digest: image.Digest,
NewTag: images[i].NewTag,
Digest: images[i].Digest,
},
// this is not optimal, however `(*yaml.RNode).FieldPath()` only returns a flat slice
// where `contianers[]` is presented as `containers` for some reason; but having
Expand All @@ -60,5 +92,17 @@ func (u *FileUpdater) doUpdate(manifest string, images []types.Image) error {
if err := pipeline.Execute(); err != nil {
return err
}

key := attestTypes.PathCheckerRegistryKey{
Path: images[0].Manifest(),
Digest: digest.MakeSHA256(u.hash),
}
if _, ok := u.mutations[key]; ok {
return fmt.Errorf("mutations with key %#v is already registered", key)
}
u.mutations[key] = images[0].ManifestDigest()

return nil
}

func (u *FileUpdater) Mutations() attestTypes.Mutations { return u.mutations }
2 changes: 1 addition & 1 deletion tape/app/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (c *TapePackageCommand) Execute(args []string) error {
if err := updater.Update(images); err != nil {
return fmt.Errorf("failed to update manifest files: %w", err)
}

attreg.RegisterMutated(updater.Mutations())
scanner.Reset()
if err := scanner.Scan(loader.RelPaths()); err != nil {
return fmt.Errorf("failed to scan updated manifest files: %w", err)
Expand Down

0 comments on commit 6028217

Please sign in to comment.