feat: add support for ecr, gcp, acr authn

This commit is contained in:
mrjoelkamp
2024-06-18 09:59:04 -05:00
parent 8e3c6a2ec5
commit f611f81fff
7 changed files with 113 additions and 27 deletions

View File

@@ -5,12 +5,15 @@ import (
"log"
"os"
ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
acr "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper"
"github.com/docker/attest/internal/embed"
"github.com/docker/attest/pkg/tuf"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/google"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
@@ -27,18 +30,19 @@ func NewTufMirror(root []byte, tufPath, metadataURL, targetsURL string, versionC
}
func PushImageToRegistry(image v1.Image, imageName string) error {
// Parse the image name
ref, err := name.ParseReference(imageName)
if err != nil {
log.Fatalf("Failed to parse image name: %v", err)
}
// Get the authenticator from the default Docker keychain
auth, err := authn.DefaultKeychain.Resolve(ref.Context())
if err != nil {
log.Fatalf("Failed to get authenticator: %v", err)
}
// Create a multi-keychain that will use the default Docker, Google, ECR or ACR keychain
keychain := authn.NewMultiKeychain(
authn.DefaultKeychain,
google.Keychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper()),
authn.NewKeychainFromHelper(acr.NewACRCredentialsHelper()),
)
// Push the image to the registry
return remote.Write(ref, image, remote.WithAuth(auth))
return remote.Write(ref, image, remote.WithAuthFromKeychain(keychain))
}
func PushIndexToRegistry(image v1.ImageIndex, imageName string) error {
@@ -47,13 +51,15 @@ func PushIndexToRegistry(image v1.ImageIndex, imageName string) error {
if err != nil {
log.Fatalf("Failed to parse image name: %v", err)
}
// Get the authenticator from the default Docker keychain
auth, err := authn.DefaultKeychain.Resolve(ref.Context())
if err != nil {
log.Fatalf("Failed to get authenticator: %v", err)
}
// Create a multi-keychain that will use the default Docker, Google, ECR or ACR keychain
keychain := authn.NewMultiKeychain(
authn.DefaultKeychain,
google.Keychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper()),
authn.NewKeychainFromHelper(acr.NewACRCredentialsHelper()),
)
// Push the index to the registry
return remote.WriteIndex(ref, image, remote.WithAuth(auth))
return remote.WriteIndex(ref, image, remote.WithAuthFromKeychain(keychain))
}
func SaveImageAsOCILayout(image v1.Image, path string) error {

View File

@@ -6,12 +6,15 @@ import (
"fmt"
"strings"
ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
acr "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper"
"github.com/containerd/containerd/platforms"
"github.com/distribution/reference"
att "github.com/docker/attest/pkg/attestation"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/google"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
@@ -456,7 +459,14 @@ func FetchAttestationManifest(ctx context.Context, image string, platform *v1.Pl
func WithOptions(ctx context.Context, platform *v1.Platform) []remote.Option {
// prepare options
options := []remote.Option{remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithTransport(HttpTransport()), remote.WithContext(ctx)}
// Create a multi-keychain that will use the default Docker, Google, ECR or ACR keychain
keychain := authn.NewMultiKeychain(
authn.DefaultKeychain,
google.Keychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper()),
authn.NewKeychainFromHelper(acr.NewACRCredentialsHelper()),
)
options := []remote.Option{remote.WithAuthFromKeychain(keychain), remote.WithTransport(HttpTransport()), remote.WithContext(ctx)}
// add in platform into remote Get operation; this might conflict with an explicit digest, but we are trying anyway
if platform != nil {

View File

@@ -4,9 +4,12 @@ import (
"fmt"
"log"
ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
acr "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/google"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
@@ -50,13 +53,15 @@ func SubjectIndexFromRemote(image string) (*SubjectIndex, error) {
if err != nil {
log.Fatalf("Failed to parse image name: %v", err)
}
// Get the authenticator from the default Docker keychain
auth, err := authn.DefaultKeychain.Resolve(ref.Context())
if err != nil {
log.Fatalf("Failed to get authenticator: %v", err)
}
// Create a multi-keychain that will use the default Docker, Google, ECR or ACR keychain
keychain := authn.NewMultiKeychain(
authn.DefaultKeychain,
google.Keychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper()),
authn.NewKeychainFromHelper(acr.NewACRCredentialsHelper()),
)
// Pull the image from the registry
idx, err := remote.Index(ref, remote.WithAuth(auth))
idx, err := remote.Index(ref, remote.WithAuthFromKeychain(keychain))
if err != nil {
return nil, fmt.Errorf("failed to pull image %s: %w", image, err)
}

View File

@@ -10,9 +10,12 @@ import (
"strings"
"time"
ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
acr "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/google"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/theupdateframework/go-tuf/v2/metadata"
)
@@ -118,13 +121,20 @@ func (d *RegistryFetcher) getManifest(ref string) ([]byte, error) {
var err error
var found bool
var mf []byte
// Create a multi-keychain that will use the default Docker, Google, ECR or ACR keychain
keychain := authn.NewMultiKeychain(
authn.DefaultKeychain,
google.Keychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper()),
authn.NewKeychainFromHelper(acr.NewACRCredentialsHelper()),
)
// Check cache for manifest and only pull if not found
if mf, found = d.cache.Get(ref); !found {
mf, err = crane.Manifest(ref,
crane.WithUserAgent(d.httpUserAgent),
crane.WithTransport(transportWithTimeout(d.timeout)),
crane.WithAuth(authn.Anonymous),
crane.WithAuthFromKeychain(authn.DefaultKeychain))
crane.WithAuthFromKeychain(keychain))
if err != nil {
return nil, err
}

View File

@@ -9,6 +9,8 @@ import (
"strings"
"testing"
ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
acr "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper"
"github.com/docker/attest/internal/embed"
"github.com/docker/attest/internal/util"
"github.com/google/go-containerregistry/pkg/authn"
@@ -16,6 +18,7 @@ import (
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/google"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
@@ -368,14 +371,22 @@ func LoadRegistryTestData(t *testing.T, registry *url.URL, path string) {
TARGETS_REPO := "tuf-targets"
DELEGATED_ROLE := "test-role"
// Create a multi-keychain that will use the default Docker, Google, ECR or ACR keychain
keychain := authn.NewMultiKeychain(
authn.DefaultKeychain,
google.Keychain,
authn.NewKeychainFromHelper(ecr.NewECRHelper()),
authn.NewKeychainFromHelper(acr.NewACRCredentialsHelper()),
)
// push top-level metadata -> metadata:latest
err := LoadMetadata(filepath.Join(path, "metadata"), registry.Host, METADATA_REPO, METADATA_TAG)
err := LoadMetadata(filepath.Join(path, "metadata"), registry.Host, METADATA_REPO, METADATA_TAG, keychain)
if err != nil {
t.Fatal(err)
}
// push delegated metadata -> metadata:<DELEGATED_ROLE>
err = LoadMetadata(filepath.Join(path, "metadata", DELEGATED_ROLE), registry.Host, METADATA_REPO, DELEGATED_ROLE)
err = LoadMetadata(filepath.Join(path, "metadata", DELEGATED_ROLE), registry.Host, METADATA_REPO, DELEGATED_ROLE, keychain)
if err != nil {
t.Fatal(err)
}
@@ -407,13 +418,13 @@ func LoadRegistryTestData(t *testing.T, registry *url.URL, path string) {
if err != nil {
t.Fatal(err)
}
err = remote.Write(ref, img, remote.WithAuthFromKeychain(authn.DefaultKeychain))
err = remote.Write(ref, img, remote.WithAuthFromKeychain(keychain))
if err != nil {
t.Fatal(err)
}
} else if len(mf.Manifests) > 1 {
// delegated target
err = remote.WriteIndex(ref, tIdx, remote.WithAuthFromKeychain(authn.DefaultKeychain))
err = remote.WriteIndex(ref, tIdx, remote.WithAuthFromKeychain(keychain))
if err != nil {
t.Fatal(err)
}
@@ -424,7 +435,7 @@ func LoadRegistryTestData(t *testing.T, registry *url.URL, path string) {
}
// LoadMetadata loads TUF metadata from a local path and pushes to a registry
func LoadMetadata(path, host, repo, tag string) error {
func LoadMetadata(path, host, repo, tag string, keychain authn.Keychain) error {
mIdx, err := layout.ImageIndexFromPath(path)
if err != nil {
return err
@@ -441,5 +452,5 @@ func LoadMetadata(path, host, repo, tag string) error {
if err != nil {
return err
}
return remote.Write(ref, img, remote.WithAuthFromKeychain(authn.DefaultKeychain))
return remote.Write(ref, img, remote.WithAuthFromKeychain(keychain))
}