128 lines
4.7 KiB
Go
128 lines
4.7 KiB
Go
/*
|
|
Copyright Docker attest authors
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package mirror
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/docker/attest/oci"
|
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
|
"github.com/google/go-containerregistry/pkg/v1/empty"
|
|
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
|
"github.com/google/go-containerregistry/pkg/v1/static"
|
|
"github.com/google/go-containerregistry/pkg/v1/types"
|
|
"github.com/theupdateframework/go-tuf/v2/metadata"
|
|
)
|
|
|
|
// GetTUFTargetMirrors returns a list of top-level target files as MirrorImages (image with tag).
|
|
func (m *TUFMirror) GetTUFTargetMirrors() ([]*Image, error) {
|
|
targetMirrors := []*Image{}
|
|
md := m.TUFClient.GetMetadata()
|
|
|
|
// for each top-level target file, create an image with the target file as a layer
|
|
targets := md.Targets[metadata.TARGETS].Signed.Targets
|
|
for _, t := range targets {
|
|
// download target file
|
|
file, err := m.TUFClient.DownloadTarget(t.Path, filepath.Join(m.tufPath, "download"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to download target %s: %w", t.Path, err)
|
|
}
|
|
// create image with target file as layer
|
|
img := empty.Image
|
|
img = mutate.MediaType(img, types.OCIManifestSchema1)
|
|
img = mutate.ConfigMediaType(img, types.OCIConfigJSON)
|
|
// annotate layer
|
|
hash, ok := t.Hashes["sha256"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("missing sha256 hash for target %s", t.Path)
|
|
}
|
|
name := hash.String() + "." + t.Path
|
|
ann := map[string]string{tufFileAnnotation: name}
|
|
layer := mutate.Addendum{Layer: static.NewLayer(file.Data, tufTargetMediaType), Annotations: ann}
|
|
img, err = mutate.Append(img, layer)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to append role layer to image: %w", err)
|
|
}
|
|
targetMirrors = append(targetMirrors, &Image{Image: &oci.EmptyConfigImage{Image: img}, Tag: name})
|
|
}
|
|
return targetMirrors, nil
|
|
}
|
|
|
|
// GetDelegatedTargetMirrors returns a list of delegated target files as MirrorIndexes (image index with tag)
|
|
// each image in the index contains a delegated target file.
|
|
func (m *TUFMirror) GetDelegatedTargetMirrors() ([]*Index, error) {
|
|
mirror := []*Index{}
|
|
md := m.TUFClient.GetMetadata()
|
|
|
|
// for each delegated role, create an image index with target files as images
|
|
roles := md.Targets[metadata.TARGETS].Signed.Delegations.Roles
|
|
for _, role := range roles {
|
|
// create an image index
|
|
index := v1.ImageIndex(empty.Index)
|
|
|
|
// get delegated targets metadata for role
|
|
roleMeta, err := m.TUFClient.LoadDelegatedTargets(role.Name, metadata.TARGETS)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load delegated targets metadata: %w", err)
|
|
}
|
|
|
|
// for each target file, create an image with the target file as a layer
|
|
for _, target := range roleMeta.Signed.Targets {
|
|
// download target file
|
|
file, err := m.TUFClient.DownloadTarget(target.Path, filepath.Join(m.tufPath, "download"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to download target %s: %w", target.Path, err)
|
|
}
|
|
// create image with target file as layer
|
|
img := empty.Image
|
|
img = mutate.MediaType(img, types.OCIManifestSchema1)
|
|
img = mutate.ConfigMediaType(img, types.OCIConfigJSON)
|
|
// annotate layer
|
|
hash, ok := target.Hashes["sha256"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("missing sha256 hash for target %s", target.Path)
|
|
}
|
|
filename := filepath.Base(target.Path)
|
|
subdir, ok := strings.CutSuffix(target.Path, "/"+filename)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to find target subdirectory [%s] in path: %s", subdir, target.Path)
|
|
}
|
|
name := hash.String() + "." + filename
|
|
ann := map[string]string{tufFileAnnotation: name}
|
|
layer := mutate.Addendum{Layer: static.NewLayer(file.Data, tufTargetMediaType), Annotations: ann}
|
|
img, err = mutate.Append(img, layer)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to append role layer to image: %w", err)
|
|
}
|
|
emptyConfigImage := &oci.EmptyConfigImage{Image: img}
|
|
// append image to index with annotation
|
|
index = mutate.AppendManifests(index, mutate.IndexAddendum{
|
|
Add: emptyConfigImage,
|
|
Descriptor: v1.Descriptor{
|
|
Annotations: map[string]string{
|
|
tufFileAnnotation: fmt.Sprintf("%s/%s", subdir, name),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
mirror = append(mirror, &Index{Index: index, Tag: role.Name})
|
|
}
|
|
return mirror, nil
|
|
}
|