67 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			67 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
| package common
 | ||
| 
 | ||
| import (
 | ||
| 	"fmt"
 | ||
| 	"os"
 | ||
| 
 | ||
| 	"github.com/containers/common/pkg/ssh"
 | ||
| 	"github.com/containers/image/v5/pkg/cli"
 | ||
| 	"github.com/containers/image/v5/pkg/cli/sigstore"
 | ||
| 	"github.com/containers/image/v5/signature/signer"
 | ||
| 	"github.com/containers/podman/v4/pkg/domain/entities"
 | ||
| )
 | ||
| 
 | ||
| // PrepareSigning updates pushOpts.Signers, pushOpts.SignPassphrase and SignSigstorePrivateKeyPassphrase based on a --sign-passphrase-file
 | ||
| // value signPassphraseFile and a --sign-by-sigsstore value signBySigstoreParamFile, and validates pushOpts.Sign* consistency.
 | ||
| // It may interactively prompt for a passphrase if one is required and wasn’t provided otherwise;
 | ||
| // or it may interactively trigger an OIDC authentication, using standard input/output, or even open a web browser.
 | ||
| // Returns a cleanup callback on success, which must be called when done.
 | ||
| func PrepareSigning(pushOpts *entities.ImagePushOptions,
 | ||
| 	signPassphraseFile, signBySigstoreParamFile string) (func(), error) {
 | ||
| 	// c/common/libimage.Image does allow creating both simple signing and sigstore signatures simultaneously,
 | ||
| 	// with independent passphrases, but that would make the CLI probably too confusing.
 | ||
| 	// For now, use the passphrase with either, but only one of them.
 | ||
| 	if signPassphraseFile != "" && pushOpts.SignBy != "" && pushOpts.SignBySigstorePrivateKeyFile != "" {
 | ||
| 		return nil, fmt.Errorf("only one of --sign-by and sign-by-sigstore-private-key can be used with --sign-passphrase-file")
 | ||
| 	}
 | ||
| 
 | ||
| 	var passphrase string
 | ||
| 	if signPassphraseFile != "" {
 | ||
| 		p, err := cli.ReadPassphraseFile(signPassphraseFile)
 | ||
| 		if err != nil {
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 		passphrase = p
 | ||
| 	} else if pushOpts.SignBySigstorePrivateKeyFile != "" {
 | ||
| 		p := ssh.ReadPassphrase()
 | ||
| 		passphrase = string(p)
 | ||
| 	} // pushOpts.SignBy triggers a GPG-agent passphrase prompt, possibly using a more secure channel, so we usually shouldn’t prompt ourselves if no passphrase was explicitly provided.
 | ||
| 	pushOpts.SignPassphrase = passphrase
 | ||
| 	pushOpts.SignSigstorePrivateKeyPassphrase = []byte(passphrase)
 | ||
| 	cleanup := signingCleanup{}
 | ||
| 	if signBySigstoreParamFile != "" {
 | ||
| 		signer, err := sigstore.NewSignerFromParameterFile(signBySigstoreParamFile, &sigstore.Options{
 | ||
| 			PrivateKeyPassphrasePrompt: cli.ReadPassphraseFile,
 | ||
| 			Stdin:                      os.Stdin,
 | ||
| 			Stdout:                     os.Stdout,
 | ||
| 		})
 | ||
| 		if err != nil {
 | ||
| 			return nil, err
 | ||
| 		}
 | ||
| 		pushOpts.Signers = append(pushOpts.Signers, signer)
 | ||
| 		cleanup.signers = append(cleanup.signers, signer)
 | ||
| 	}
 | ||
| 	return cleanup.cleanup, nil
 | ||
| }
 | ||
| 
 | ||
| // signingCleanup carries state for cleanup after PrepareSigning
 | ||
| type signingCleanup struct {
 | ||
| 	signers []*signer.Signer
 | ||
| }
 | ||
| 
 | ||
| func (c *signingCleanup) cleanup() {
 | ||
| 	for _, s := range c.signers {
 | ||
| 		s.Close()
 | ||
| 	}
 | ||
| }
 |