203 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"io/ioutil"
 | 
						|
	"net/url"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/containers/image/signature"
 | 
						|
	"github.com/containers/image/transports"
 | 
						|
	"github.com/containers/image/transports/alltransports"
 | 
						|
	"github.com/containers/libpod/cmd/podman/cliconfig"
 | 
						|
	"github.com/containers/libpod/cmd/podman/libpodruntime"
 | 
						|
	"github.com/containers/libpod/libpod/image"
 | 
						|
	"github.com/containers/libpod/pkg/trust"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
	"github.com/spf13/cobra"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	signCommand     cliconfig.SignValues
 | 
						|
	signDescription = "Create a signature file that can be used later to verify the image."
 | 
						|
	_signCommand    = &cobra.Command{
 | 
						|
		Use:   "sign [flags] IMAGE [IMAGE...]",
 | 
						|
		Short: "Sign an image",
 | 
						|
		Long:  signDescription,
 | 
						|
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
						|
			signCommand.InputArgs = args
 | 
						|
			signCommand.GlobalFlags = MainGlobalOpts
 | 
						|
			return signCmd(&signCommand)
 | 
						|
		},
 | 
						|
		Example: `podman sign --sign-by mykey imageID
 | 
						|
  podman sign --sign-by mykey --directory ./mykeydir imageID`,
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	signCommand.Command = _signCommand
 | 
						|
	signCommand.SetHelpTemplate(HelpTemplate())
 | 
						|
	signCommand.SetUsageTemplate(UsageTemplate())
 | 
						|
	flags := signCommand.Flags()
 | 
						|
	flags.StringVarP(&signCommand.Directory, "directory", "d", "", "Define an alternate directory to store signatures")
 | 
						|
	flags.StringVar(&signCommand.SignBy, "sign-by", "", "Name of the signing key")
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
// SignatureStoreDir defines default directory to store signatures
 | 
						|
const SignatureStoreDir = "/var/lib/containers/sigstore"
 | 
						|
 | 
						|
func signCmd(c *cliconfig.SignValues) error {
 | 
						|
	args := c.InputArgs
 | 
						|
	if len(args) < 1 {
 | 
						|
		return errors.Errorf("at least one image name must be specified")
 | 
						|
	}
 | 
						|
	runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
 | 
						|
	if err != nil {
 | 
						|
		return errors.Wrapf(err, "could not create runtime")
 | 
						|
	}
 | 
						|
	defer runtime.Shutdown(false)
 | 
						|
 | 
						|
	signby := c.SignBy
 | 
						|
	if signby == "" {
 | 
						|
		return errors.Errorf("please provide an identity")
 | 
						|
	}
 | 
						|
 | 
						|
	var sigStoreDir string
 | 
						|
	if c.Flag("directory").Changed {
 | 
						|
		sigStoreDir = c.Directory
 | 
						|
		if _, err := os.Stat(sigStoreDir); err != nil {
 | 
						|
			return errors.Wrapf(err, "invalid directory %s", sigStoreDir)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mech, err := signature.NewGPGSigningMechanism()
 | 
						|
	if err != nil {
 | 
						|
		return errors.Wrap(err, "error initializing GPG")
 | 
						|
	}
 | 
						|
	defer mech.Close()
 | 
						|
	if err := mech.SupportsSigning(); err != nil {
 | 
						|
		return errors.Wrap(err, "signing is not supported")
 | 
						|
	}
 | 
						|
 | 
						|
	systemRegistriesDirPath := trust.RegistriesDirPath(runtime.SystemContext())
 | 
						|
	registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath)
 | 
						|
	if err != nil {
 | 
						|
		return errors.Wrapf(err, "error reading registry configuration")
 | 
						|
	}
 | 
						|
 | 
						|
	for _, signimage := range args {
 | 
						|
		srcRef, err := alltransports.ParseImageName(signimage)
 | 
						|
		if err != nil {
 | 
						|
			return errors.Wrapf(err, "error parsing image name")
 | 
						|
		}
 | 
						|
		rawSource, err := srcRef.NewImageSource(getContext(), runtime.SystemContext())
 | 
						|
		if err != nil {
 | 
						|
			return errors.Wrapf(err, "error getting image source")
 | 
						|
		}
 | 
						|
		manifest, _, err := rawSource.GetManifest(getContext(), nil)
 | 
						|
		if err != nil {
 | 
						|
			return errors.Wrapf(err, "error getting manifest")
 | 
						|
		}
 | 
						|
		dockerReference := rawSource.Reference().DockerReference()
 | 
						|
		if dockerReference == nil {
 | 
						|
			return errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference()))
 | 
						|
		}
 | 
						|
 | 
						|
		// create the signstore file
 | 
						|
		newImage, err := runtime.ImageRuntime().New(getContext(), signimage, runtime.GetConfig().SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{SignBy: signby}, false, nil)
 | 
						|
		if err != nil {
 | 
						|
			return errors.Wrapf(err, "error pulling image %s", signimage)
 | 
						|
		}
 | 
						|
 | 
						|
		registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs)
 | 
						|
		if registryInfo != nil {
 | 
						|
			if sigStoreDir == "" {
 | 
						|
				sigStoreDir = registryInfo.SigStoreStaging
 | 
						|
				if sigStoreDir == "" {
 | 
						|
					sigStoreDir = registryInfo.SigStore
 | 
						|
				}
 | 
						|
			}
 | 
						|
			sigStoreDir, err = isValidSigStoreDir(sigStoreDir)
 | 
						|
			if err != nil {
 | 
						|
				return errors.Wrapf(err, "invalid signature storage %s", sigStoreDir)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if sigStoreDir == "" {
 | 
						|
			sigStoreDir = SignatureStoreDir
 | 
						|
		}
 | 
						|
 | 
						|
		repos, err := newImage.RepoDigests()
 | 
						|
		if err != nil {
 | 
						|
			return errors.Wrapf(err, "error calculating repo digests for %s", signimage)
 | 
						|
		}
 | 
						|
		if len(repos) == 0 {
 | 
						|
			logrus.Errorf("no repodigests associated with the image %s", signimage)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// create signature
 | 
						|
		newSig, err := signature.SignDockerManifest(manifest, dockerReference.String(), mech, signby)
 | 
						|
		if err != nil {
 | 
						|
			return errors.Wrapf(err, "error creating new signature")
 | 
						|
		}
 | 
						|
 | 
						|
		trimmedDigest := strings.TrimPrefix(repos[0], strings.Split(repos[0], "/")[0])
 | 
						|
		sigStoreDir = filepath.Join(sigStoreDir, strings.Replace(trimmedDigest, ":", "=", 1))
 | 
						|
		if err := os.MkdirAll(sigStoreDir, 0751); err != nil {
 | 
						|
			// The directory is allowed to exist
 | 
						|
			if !os.IsExist(err) {
 | 
						|
				logrus.Errorf("error creating directory %s: %s", sigStoreDir, err)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		}
 | 
						|
		sigFilename, err := getSigFilename(sigStoreDir)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Errorf("error creating sigstore file: %v", err)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		err = ioutil.WriteFile(filepath.Join(sigStoreDir, sigFilename), newSig, 0644)
 | 
						|
		if err != nil {
 | 
						|
			logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String())
 | 
						|
			continue
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func getSigFilename(sigStoreDirPath string) (string, error) {
 | 
						|
	sigFileSuffix := 1
 | 
						|
	sigFiles, err := ioutil.ReadDir(sigStoreDirPath)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	sigFilenames := make(map[string]bool)
 | 
						|
	for _, file := range sigFiles {
 | 
						|
		sigFilenames[file.Name()] = true
 | 
						|
	}
 | 
						|
	for {
 | 
						|
		sigFilename := "signature-" + strconv.Itoa(sigFileSuffix)
 | 
						|
		if _, exists := sigFilenames[sigFilename]; !exists {
 | 
						|
			return sigFilename, nil
 | 
						|
		}
 | 
						|
		sigFileSuffix++
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func isValidSigStoreDir(sigStoreDir string) (string, error) {
 | 
						|
	writeURIs := map[string]bool{"file": true}
 | 
						|
	url, err := url.Parse(sigStoreDir)
 | 
						|
	if err != nil {
 | 
						|
		return sigStoreDir, errors.Wrapf(err, "invalid directory %s", sigStoreDir)
 | 
						|
	}
 | 
						|
	_, exists := writeURIs[url.Scheme]
 | 
						|
	if !exists {
 | 
						|
		return sigStoreDir, errors.Errorf("writing to %s is not supported. Use a supported scheme", sigStoreDir)
 | 
						|
	}
 | 
						|
	sigStoreDir = url.Path
 | 
						|
	return sigStoreDir, nil
 | 
						|
}
 |