From f48bf60abe86ff734017095b94e5def85043b07d Mon Sep 17 00:00:00 2001 From: ChrisJBurns <29541485+ChrisJBurns@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:40:23 +0100 Subject: [PATCH] Adds Keychain to be able to configure registry credentials without a docker config file Signed-off-by: ChrisJBurns <29541485+ChrisJBurns@users.noreply.github.com> --- commands/out.go | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/commands/out.go b/commands/out.go index 796b0bb..052fbd3 100644 --- a/commands/out.go +++ b/commands/out.go @@ -250,6 +250,38 @@ func put(req resource.OutRequest, img partial.WithRawManifest, tags []name.Tag, return nil } +// struct to hold username and password +type userCredentials struct { + username string + password string +} + +// A keychain that is able to return the creds based on the registry url +type InMemoryKeyChain struct { + // An in-memory map of username/passwords + credentials map[string]userCredentials +} + +// Returns a function that is able to produce an AuthConfig struct. This is basically a factory factory +func (k *InMemoryKeyChain) Resolve(resource authn.Resource) (authn.Authenticator, error) { + + // Ask for the registry URL + registryHost := resource.RegistryStr() + + // Find the user credentials by the registry URL + userCreds, ok := k.credentials[registryHost] + + if !ok { + return authn.Anonymous, fmt.Errorf("unable to find credentials for host %s", registryHost) + } + + // Return an authConfig that contains the username and password we looked up + return authn.FromConfig(authn.AuthConfig{ + Username: userCreds.username, + Password: userCreds.password, + }), nil +} + func signImagesCosign(req resource.OutRequest, img v1.Image, tags []name.Tag) { digest, err := img.Digest() if err != nil { @@ -275,6 +307,15 @@ func signImagesCosign(req resource.OutRequest, img v1.Image, tags []name.Tag) { Timeout: options.DefaultTimeout, } + keychain := &InMemoryKeyChain{ + credentials: map[string]userCredentials{ + req.Source.Repository: { + username: req.Source.Username, + password: req.Source.Password, + }, + }, + } + o := options.SignOptions{ Key: "env://COSIGN_KEY", Cert: "", @@ -304,6 +345,7 @@ func signImagesCosign(req resource.OutRequest, img v1.Image, tags []name.Tag) { Registry: options.RegistryOptions{ AllowInsecure: false, KubernetesKeychain: false, + Keychain: keychain, }, } @@ -338,7 +380,7 @@ func signImagesCosign(req resource.OutRequest, img v1.Image, tags []name.Tag) { // tooling (Vault, Azure KeyVault etc) err = os.Setenv("COSIGN_KEY", req.Source.Cosign.Key) if err != nil { - logrus.Fatalf("err %v", err) + fmt.Errorf("err %w", err) } // similiar to the COSIGN_KEY variable we set the password that was used to create the @@ -348,13 +390,13 @@ func signImagesCosign(req resource.OutRequest, img v1.Image, tags []name.Tag) { // input, we have to use the environment variable. err = os.Setenv("COSIGN_PASSWORD", req.Source.Cosign.Password) if err != nil { - logrus.Fatalf("err %v", err) + fmt.Errorf("err %w", err) } logrus.Infof("Signing image with Cosign") err = sign.SignCmd(ro, ko, o, []string{imgDigestUrl}) if err != nil { - logrus.Fatalf("There was an error signing the image with Cosign %w", err) + fmt.Errorf("There was an error signing the image with Cosign %w", err) } logrus.Infof("Image signed with Cosign") }