1
0
mirror of https://github.com/containers/buildah.git synced 2025-07-31 15:24:26 +03:00

add --sign-by to bud/commit/push, --remove-signatures for pull/push

Add the --sign-by option to `buildah build-using-dockerfile`,
`buildah commit`, `buildah push`, and `buildah manifest push`.  Add the
`--remove-signatures` option to `buildah pull`, `buildah push`, and
`buildah manifest push`.  We just pass them to the image library, which
does all of the heavy lifting.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>

Closes: #2085
Approved by: rhatdan
This commit is contained in:
Nalin Dahyabhai
2020-01-15 12:23:38 -05:00
committed by Atomic Bot
parent ca0819f640
commit a925f79cc3
21 changed files with 200 additions and 31 deletions

View File

@ -16,7 +16,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type budResults struct { type budOptions struct {
*buildahcli.LayerResults *buildahcli.LayerResults
*buildahcli.BudResults *buildahcli.BudResults
*buildahcli.UserNSResults *buildahcli.UserNSResults
@ -45,7 +45,7 @@ func init() {
Long: budDescription, Long: budDescription,
//Flags: sortFlags(append(append(buildahcli.BudFlags, buildahcli.LayerFlags...), buildahcli.FromAndBudFlags...)), //Flags: sortFlags(append(append(buildahcli.BudFlags, buildahcli.LayerFlags...), buildahcli.FromAndBudFlags...)),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
br := budResults{ br := budOptions{
&layerFlagsResults, &layerFlagsResults,
&budFlagResults, &budFlagResults,
&userNSResults, &userNSResults,
@ -90,7 +90,7 @@ func getDockerfiles(files []string) []string {
return dockerfiles return dockerfiles
} }
func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error { func budCmd(c *cobra.Command, inputArgs []string, iopts budOptions) error {
output := "" output := ""
tags := []string{} tags := []string{}
if c.Flag("tag").Changed { if c.Flag("tag").Changed {
@ -340,6 +340,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error {
TransientMounts: transientMounts, TransientMounts: transientMounts,
Devices: devices, Devices: devices,
DefaultEnv: defaultContainerConfig.GetDefaultEnv(), DefaultEnv: defaultContainerConfig.GetDefaultEnv(),
SignBy: iopts.SignBy,
} }
if iopts.Quiet { if iopts.Quiet {

View File

@ -31,6 +31,7 @@ type commitInputOptions struct {
referenceTime string referenceTime string
rm bool rm bool
signaturePolicy string signaturePolicy string
signBy string
squash bool squash bool
tlsVerify bool tlsVerify bool
} }
@ -70,6 +71,7 @@ func init() {
flags.BoolVar(&opts.omitTimestamp, "omit-timestamp", false, "set created timestamp to epoch 0 to allow for deterministic builds") flags.BoolVar(&opts.omitTimestamp, "omit-timestamp", false, "set created timestamp to epoch 0 to allow for deterministic builds")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "don't output progress information when writing images") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "don't output progress information when writing images")
flags.StringVar(&opts.referenceTime, "reference-time", "", "set the timestamp on the image to match the named `file`") flags.StringVar(&opts.referenceTime, "reference-time", "", "set the timestamp on the image to match the named `file`")
flags.StringVar(&opts.signBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`")
if err := flags.MarkHidden("reference-time"); err != nil { if err := flags.MarkHidden("reference-time"); err != nil {
panic(fmt.Sprintf("error marking reference-time as hidden: %v", err)) panic(fmt.Sprintf("error marking reference-time as hidden: %v", err))
@ -175,6 +177,7 @@ func commitCmd(c *cobra.Command, args []string, iopts commitInputOptions) error
Squash: iopts.squash, Squash: iopts.squash,
BlobDirectory: iopts.blobCache, BlobDirectory: iopts.blobCache,
OmitTimestamp: iopts.omitTimestamp, OmitTimestamp: iopts.omitTimestamp,
SignBy: iopts.signBy,
} }
if !iopts.quiet { if !iopts.quiet {
options.ReportWriter = os.Stderr options.ReportWriter = os.Stderr

View File

@ -38,8 +38,8 @@ type manifestAnnotateOpts = struct {
} }
type manifestInspectOpts = struct{} type manifestInspectOpts = struct{}
type manifestPushOpts = struct { type manifestPushOpts = struct {
purge, quiet, all, tlsVerify bool purge, quiet, all, tlsVerify, removeSignatures bool
authfile, certDir, creds, digestfile, signaturePolicy string authfile, certDir, creds, digestfile, signaturePolicy, signBy string
} }
func init() { func init() {
@ -194,6 +194,8 @@ func init() {
flags.StringVar(&manifestPushOpts.certDir, "cert-dir", "", "use certificates at the specified path to access the registry") flags.StringVar(&manifestPushOpts.certDir, "cert-dir", "", "use certificates at the specified path to access the registry")
flags.StringVar(&manifestPushOpts.creds, "creds", "", "use `[username[:password]]` for accessing the registry") flags.StringVar(&manifestPushOpts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
flags.StringVar(&manifestPushOpts.digestfile, "digestfile", "", "after copying the image, write the digest of the resulting digest to the file") flags.StringVar(&manifestPushOpts.digestfile, "digestfile", "", "after copying the image, write the digest of the resulting digest to the file")
flags.BoolVarP(&manifestPushOpts.removeSignatures, "remove-signatures", "", false, "don't copy signatures when pushing images")
flags.StringVar(&manifestPushOpts.signBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`")
flags.StringVar(&manifestPushOpts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)") flags.StringVar(&manifestPushOpts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
if err := flags.MarkHidden("signature-policy"); err != nil { if err := flags.MarkHidden("signature-policy"); err != nil {
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err)) panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
@ -642,6 +644,8 @@ func manifestPushCmd(c *cobra.Command, args []string, opts manifestPushOpts) err
SystemContext: systemContext, SystemContext: systemContext,
ImageListSelection: cp.CopySpecificImages, ImageListSelection: cp.CopySpecificImages,
Instances: nil, Instances: nil,
RemoveSignatures: opts.removeSignatures,
SignBy: opts.signBy,
} }
if opts.all { if opts.all {
options.ImageListSelection = cp.CopyAllImages options.ImageListSelection = cp.CopyAllImages

View File

@ -12,22 +12,23 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type pullResults struct { type pullOptions struct {
allTags bool allTags bool
authfile string authfile string
blobCache string blobCache string
certDir string certDir string
creds string creds string
overrideArch string overrideArch string
overrideOS string overrideOS string
signaturePolicy string signaturePolicy string
quiet bool quiet bool
tlsVerify bool removeSignatures bool
tlsVerify bool
} }
func init() { func init() {
var ( var (
opts pullResults opts pullOptions
pullDescription = ` Pulls an image from a registry and stores it locally. pullDescription = ` Pulls an image from a registry and stores it locally.
An image can be pulled using its tag or digest. If a tag is not An image can be pulled using its tag or digest. If a tag is not
@ -54,6 +55,7 @@ func init() {
flags.StringVar(&opts.blobCache, "blob-cache", "", "store copies of pulled image blobs in the specified directory") flags.StringVar(&opts.blobCache, "blob-cache", "", "store copies of pulled image blobs in the specified directory")
flags.StringVar(&opts.certDir, "cert-dir", "", "use certificates at the specified path to access the registry") flags.StringVar(&opts.certDir, "cert-dir", "", "use certificates at the specified path to access the registry")
flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing the registry") flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
flags.BoolVarP(&opts.removeSignatures, "remove-signatures", "", false, "don't copy signatures when pulling image")
flags.StringVar(&opts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)") flags.StringVar(&opts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
if err := flags.MarkHidden("signature-policy"); err != nil { if err := flags.MarkHidden("signature-policy"); err != nil {
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err)) panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
@ -75,7 +77,7 @@ func init() {
rootCmd.AddCommand(pullCommand) rootCmd.AddCommand(pullCommand)
} }
func pullCmd(c *cobra.Command, args []string, iopts pullResults) error { func pullCmd(c *cobra.Command, args []string, iopts pullOptions) error {
if len(args) == 0 { if len(args) == 0 {
return errors.Errorf("an image name must be specified") return errors.Errorf("an image name must be specified")
} }
@ -106,6 +108,7 @@ func pullCmd(c *cobra.Command, args []string, iopts pullResults) error {
BlobDirectory: iopts.blobCache, BlobDirectory: iopts.blobCache,
AllTags: iopts.allTags, AllTags: iopts.allTags,
ReportWriter: os.Stderr, ReportWriter: os.Stderr,
RemoveSignatures: iopts.removeSignatures,
} }
if iopts.quiet { if iopts.quiet {

View File

@ -20,7 +20,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type pushResults struct { type pushOptions struct {
authfile string authfile string
blobCache string blobCache string
certDir string certDir string
@ -29,13 +29,15 @@ type pushResults struct {
disableCompression bool disableCompression bool
format string format string
quiet bool quiet bool
removeSignatures bool
signaturePolicy string signaturePolicy string
signBy string
tlsVerify bool tlsVerify bool
} }
func init() { func init() {
var ( var (
opts pushResults opts pushOptions
pushDescription = fmt.Sprintf(` pushDescription = fmt.Sprintf(`
Pushes an image to a specified location. Pushes an image to a specified location.
@ -71,6 +73,8 @@ func init() {
flags.BoolVarP(&opts.disableCompression, "disable-compression", "D", false, "don't compress layers") flags.BoolVarP(&opts.disableCompression, "disable-compression", "D", false, "don't compress layers")
flags.StringVarP(&opts.format, "format", "f", "", "manifest type (oci, v2s1, or v2s2) to use when saving image using the 'dir:' transport (default is manifest type of source)") flags.StringVarP(&opts.format, "format", "f", "", "manifest type (oci, v2s1, or v2s2) to use when saving image using the 'dir:' transport (default is manifest type of source)")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "don't output progress information when pushing images") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "don't output progress information when pushing images")
flags.BoolVarP(&opts.removeSignatures, "remove-signatures", "", false, "don't copy signatures when pushing image")
flags.StringVar(&opts.signBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`")
flags.StringVar(&opts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)") flags.StringVar(&opts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
if err := flags.MarkHidden("signature-policy"); err != nil { if err := flags.MarkHidden("signature-policy"); err != nil {
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err)) panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
@ -83,7 +87,7 @@ func init() {
rootCmd.AddCommand(pushCommand) rootCmd.AddCommand(pushCommand)
} }
func pushCmd(c *cobra.Command, args []string, iopts pushResults) error { func pushCmd(c *cobra.Command, args []string, iopts pushOptions) error {
var src, destSpec string var src, destSpec string
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil { if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
@ -167,6 +171,8 @@ func pushCmd(c *cobra.Command, args []string, iopts pushResults) error {
Store: store, Store: store,
SystemContext: systemContext, SystemContext: systemContext,
BlobDirectory: iopts.blobCache, BlobDirectory: iopts.blobCache,
RemoveSignatures: iopts.removeSignatures,
SignBy: iopts.signBy,
} }
if !iopts.quiet { if !iopts.quiet {
options.ReportWriter = os.Stderr options.ReportWriter = os.Stderr

View File

@ -81,6 +81,8 @@ type CommitOptions struct {
// OmitTimestamp forces epoch 0 as created timestamp to allow for // OmitTimestamp forces epoch 0 as created timestamp to allow for
// deterministic, content-addressable builds. // deterministic, content-addressable builds.
OmitTimestamp bool OmitTimestamp bool
// SignBy is the fingerprint of a GPG key to use for signing the image.
SignBy string
} }
// PushOptions can be used to alter how an image is copied somewhere. // PushOptions can be used to alter how an image is copied somewhere.
@ -115,6 +117,11 @@ type PushOptions struct {
// the user will be displayed, this is best used for logging. // the user will be displayed, this is best used for logging.
// The default is false. // The default is false.
Quiet bool Quiet bool
// SignBy is the fingerprint of a GPG key to use for signing the image.
SignBy string
// RemoveSignatures causes any existing signatures for the image to be
// discarded for the pushed copy.
RemoveSignatures bool
} }
var ( var (
@ -294,7 +301,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
systemContext.DirForceCompress = true systemContext.DirForceCompress = true
} }
var manifestBytes []byte var manifestBytes []byte
if manifestBytes, err = cp.Image(ctx, policyContext, maybeCachedDest, maybeCachedSrc, getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "")); err != nil { if manifestBytes, err = cp.Image(ctx, policyContext, maybeCachedDest, maybeCachedSrc, getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "", false, options.SignBy)); err != nil {
return imgID, nil, "", errors.Wrapf(err, "error copying layers and metadata for container %q", b.ContainerID) return imgID, nil, "", errors.Wrapf(err, "error copying layers and metadata for container %q", b.ContainerID)
} }
// If we've got more names to attach, and we know how to do that for // If we've got more names to attach, and we know how to do that for
@ -426,7 +433,7 @@ func Push(ctx context.Context, image string, dest types.ImageReference, options
systemContext.DirForceCompress = true systemContext.DirForceCompress = true
} }
var manifestBytes []byte var manifestBytes []byte
if manifestBytes, err = cp.Image(ctx, policyContext, dest, maybeCachedSrc, getCopyOptions(options.Store, options.ReportWriter, nil, systemContext, options.ManifestType)); err != nil { if manifestBytes, err = cp.Image(ctx, policyContext, dest, maybeCachedSrc, getCopyOptions(options.Store, options.ReportWriter, nil, systemContext, options.ManifestType, options.RemoveSignatures, options.SignBy)); err != nil {
return nil, "", errors.Wrapf(err, "error copying layers and metadata from %q to %q", transports.ImageName(maybeCachedSrc), transports.ImageName(dest)) return nil, "", errors.Wrapf(err, "error copying layers and metadata from %q to %q", transports.ImageName(maybeCachedSrc), transports.ImageName(dest))
} }
if options.ReportWriter != nil { if options.ReportWriter != nil {

View File

@ -18,7 +18,7 @@ const (
DOCKER = "docker" DOCKER = "docker"
) )
func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceSystemContext *types.SystemContext, destinationSystemContext *types.SystemContext, manifestType string) *cp.Options { func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceSystemContext *types.SystemContext, destinationSystemContext *types.SystemContext, manifestType string, removeSignatures bool, addSigner string) *cp.Options {
sourceCtx := getSystemContext(store, nil, "") sourceCtx := getSystemContext(store, nil, "")
if sourceSystemContext != nil { if sourceSystemContext != nil {
*sourceCtx = *sourceSystemContext *sourceCtx = *sourceSystemContext
@ -33,6 +33,8 @@ func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceSystemCon
SourceCtx: sourceCtx, SourceCtx: sourceCtx,
DestinationCtx: destinationCtx, DestinationCtx: destinationCtx,
ForceManifestMIMEType: manifestType, ForceManifestMIMEType: manifestType,
RemoveSignatures: removeSignatures,
SignBy: addSigner,
} }
} }

View File

@ -326,6 +326,7 @@ return 1
--format --format
-f -f
--iidfile --iidfile
--sign-by
" "
local all_options="$options_with_args $boolean_options" local all_options="$options_with_args $boolean_options"
@ -415,6 +416,7 @@ return 1
--runtime-flag --runtime-flag
--security-opt --security-opt
--shm-size --shm-size
--sign-by
-t -t
--tag --tag
--target --target
@ -577,6 +579,7 @@ return 1
--quiet --quiet
-q -q
--tls-verify --tls-verify
--remove-signatures
" "
local options_with_args=" local options_with_args="
@ -603,6 +606,7 @@ return 1
--quiet --quiet
-q -q
--tls-verify --tls-verify
--remove-signatures
" "
local options_with_args=" local options_with_args="
@ -611,6 +615,7 @@ return 1
--creds --creds
--format --format
-f -f
--sign-by
" "
local all_options="$options_with_args $boolean_options" local all_options="$options_with_args $boolean_options"
@ -810,6 +815,7 @@ return 1
--help --help
-h -h
--all --all
--remove-signatures
" "
local options_with_args=" local options_with_args="
@ -818,6 +824,7 @@ return 1
--creds --creds
--digestfile --digestfile
--purge --purge
--sign-by
--tls-verify --tls-verify
" "

View File

@ -414,6 +414,10 @@ Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater tha
Unit is optional and can be `b` (bytes), `k` (kilobytes), `m`(megabytes), or `g` (gigabytes). Unit is optional and can be `b` (bytes), `k` (kilobytes), `m`(megabytes), or `g` (gigabytes).
If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`. If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`.
**--sign-by** *fingerprint*
Sign the built image using the GPG key that matches the specified fingerprint.
**--squash** **--squash**
Squash all of the new image's layers (including those inherited from a base image) into a single new layer. Squash all of the new image's layers (including those inherited from a base image) into a single new layer.

View File

@ -64,6 +64,10 @@ When writing the output image, suppress progress output.
Remove the container and its content after committing it to an image. Remove the container and its content after committing it to an image.
Default leaves the container and its content in place. Default leaves the container and its content in place.
**--sign-by** *fingerprint*
Sign the new image using the GPG key that matches the specified fingerprint.
**--squash** **--squash**
Squash all of the new image's layers (including those inherited from a base image) into a single new layer. Squash all of the new image's layers (including those inherited from a base image) into a single new layer.

View File

@ -47,6 +47,14 @@ After copying the image, write the digest of the resulting image to the file.
Delete the manifest list or image index from local storage if pushing succeeds. Delete the manifest list or image index from local storage if pushing succeeds.
**--remove-signatures**
Don't copy signatures when pushing images.
**--sign-by** *fingerprint*
Sign the pushed images using the GPG key that matches the specified fingerprint.
**--tls-verify** *bool-value* **--tls-verify** *bool-value*
Require HTTPS and verify certificates when talking to container registries (defaults to true) Require HTTPS and verify certificates when talking to container registries (defaults to true)

View File

@ -66,6 +66,10 @@ value can be entered. The password is entered without echo.
If an image needs to be pulled from the registry, suppress progress output. If an image needs to be pulled from the registry, suppress progress output.
**--remove-signatures**
Don't copy signatures when pulling images.
**--shm-size**="" **--shm-size**=""
Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`. Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`.

View File

@ -1,4 +1,4 @@
# buildah-push"1" "June 2017" "buildah" # buildah-push "1" "June 2017" "buildah"
## NAME ## NAME
buildah\-push - Push an image from local storage to elsewhere. buildah\-push - Push an image from local storage to elsewhere.
@ -74,6 +74,14 @@ Manifest Type (oci, v2s1, or v2s2) to use when saving image to directory using t
When writing the output image, suppress progress output. When writing the output image, suppress progress output.
**--remove-signatures**
Don't copy signatures when pushing images.
**--sign-by** *fingerprint*
Sign the pushed image using the GPG key that matches the specified fingerprint.
**--tls-verify** *bool-value* **--tls-verify** *bool-value*
Require HTTPS and verify certificates when talking to container registries (defaults to true) Require HTTPS and verify certificates when talking to container registries (defaults to true)

View File

@ -1,4 +1,4 @@
# buildah-rename "9" "July 2018" "buildah" # buildah-rename "1" "July 2018" "buildah"
## NAME ## NAME
buildah\-rename - Rename a local container. buildah\-rename - Rename a local container.

View File

@ -152,12 +152,14 @@ type BuildOptions struct {
ForceRmIntermediateCtrs bool ForceRmIntermediateCtrs bool
// BlobDirectory is a directory which we'll use for caching layer blobs. // BlobDirectory is a directory which we'll use for caching layer blobs.
BlobDirectory string BlobDirectory string
// Target the targeted FROM in the Dockerfile to build // Target the targeted FROM in the Dockerfile to build.
Target string Target string
// Devices are the additional devices to add to the containers // Devices are the additional devices to add to the containers.
Devices []configs.Device Devices []configs.Device
//DefaultEnv for containers // DefaultEnv for containers.
DefaultEnv []string DefaultEnv []string
// SignBy is the fingerprint of a GPG key to use for signing images.
SignBy string
} }
// BuildDockerfiles parses a set of one or more Dockerfiles (which may be // BuildDockerfiles parses a set of one or more Dockerfiles (which may be

View File

@ -93,6 +93,7 @@ type Executor struct {
buildArgs map[string]string buildArgs map[string]string
capabilities []string capabilities []string
devices []configs.Device devices []configs.Device
signBy string
} }
// NewExecutor creates a new instance of the imagebuilder.Executor interface. // NewExecutor creates a new instance of the imagebuilder.Executor interface.
@ -149,6 +150,7 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod
buildArgs: options.Args, buildArgs: options.Args,
capabilities: options.Capabilities, capabilities: options.Capabilities,
devices: options.Devices, devices: options.Devices,
signBy: options.SignBy,
} }
if exec.err == nil { if exec.err == nil {
exec.err = os.Stderr exec.err = os.Stderr

View File

@ -1203,6 +1203,7 @@ func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, cr
Squash: s.executor.squash, Squash: s.executor.squash,
EmptyLayer: emptyLayer, EmptyLayer: emptyLayer,
BlobDirectory: s.executor.blobDirectory, BlobDirectory: s.executor.blobDirectory,
SignBy: s.executor.signBy,
} }
imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options) imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options)
if err != nil { if err != nil {

View File

@ -53,6 +53,8 @@ type PushOptions struct {
ImageListSelection cp.ImageListSelection // set to either CopySystemImage, CopyAllImages, or CopySpecificImages ImageListSelection cp.ImageListSelection // set to either CopySystemImage, CopyAllImages, or CopySpecificImages
Instances []digest.Digest // instances to copy if ImageListSelection == CopySpecificImages Instances []digest.Digest // instances to copy if ImageListSelection == CopySpecificImages
ReportWriter io.Writer // will be used to log the writing of the list and any blobs ReportWriter io.Writer // will be used to log the writing of the list and any blobs
SignBy string // fingerprint of GPG key to use to sign images
RemoveSignatures bool // true to discard signatures in images
} }
// Create creates a new list containing information about the specified image, // Create creates a new list containing information about the specified image,
@ -211,6 +213,8 @@ func (l *list) Push(ctx context.Context, dest types.ImageReference, options Push
SourceCtx: options.SystemContext, SourceCtx: options.SystemContext,
DestinationCtx: options.SystemContext, DestinationCtx: options.SystemContext,
ReportWriter: options.ReportWriter, ReportWriter: options.ReportWriter,
RemoveSignatures: options.RemoveSignatures,
SignBy: options.SignBy,
} }
// Copy whatever we were asked to copy. // Copy whatever we were asked to copy.

View File

@ -70,6 +70,7 @@ type BudResults struct {
Runtime string Runtime string
RuntimeFlags []string RuntimeFlags []string
SignaturePolicy string SignaturePolicy string
SignBy string
Squash bool Squash bool
Tag []string Tag []string
Target string Target string
@ -168,8 +169,9 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.BoolVar(&flags.Rm, "rm", true, "Remove intermediate containers after a successful build") fs.BoolVar(&flags.Rm, "rm", true, "Remove intermediate containers after a successful build")
// "runtime" definition moved to avoid name collision in podman build. Defined in cmd/buildah/bud.go. // "runtime" definition moved to avoid name collision in podman build. Defined in cmd/buildah/bud.go.
fs.StringSliceVar(&flags.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime") fs.StringSliceVar(&flags.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime")
fs.StringVar(&flags.SignBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`")
fs.StringVar(&flags.SignaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)") fs.StringVar(&flags.SignaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
fs.BoolVar(&flags.Squash, "squash", false, "Squash newly built layers into a single new layer.") fs.BoolVar(&flags.Squash, "squash", false, "squash newly built layers into a single new layer")
fs.StringArrayVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image") fs.StringArrayVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image")
fs.StringVar(&flags.Target, "target", "", "set the target build stage to build") fs.StringVar(&flags.Target, "target", "", "set the target build stage to build")
fs.BoolVar(&flags.TLSVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") fs.BoolVar(&flags.TLSVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")

View File

@ -49,6 +49,9 @@ type PullOptions struct {
// AllTags is a boolean value that determines if all tagged images // AllTags is a boolean value that determines if all tagged images
// will be downloaded from the repository. The default is false. // will be downloaded from the repository. The default is false.
AllTags bool AllTags bool
// RemoveSignatures causes any existing signatures for the image to be
// discarded when pulling it.
RemoveSignatures bool
} }
func localImageNameForReference(ctx context.Context, store storage.Store, srcRef types.ImageReference) (string, error) { func localImageNameForReference(ctx context.Context, store storage.Store, srcRef types.ImageReference) (string, error) {
@ -260,7 +263,7 @@ func pullImage(ctx context.Context, store storage.Store, srcRef types.ImageRefer
}() }()
logrus.Debugf("copying %q to %q", transports.ImageName(srcRef), destName) logrus.Debugf("copying %q to %q", transports.ImageName(srcRef), destName)
if _, err := cp.Image(ctx, policyContext, maybeCachedDestRef, srcRef, getCopyOptions(store, options.ReportWriter, sc, nil, "")); err != nil { if _, err := cp.Image(ctx, policyContext, maybeCachedDestRef, srcRef, getCopyOptions(store, options.ReportWriter, sc, nil, "", options.RemoveSignatures, "")); err != nil {
logrus.Debugf("error copying src image [%q] to dest image [%q] err: %v", transports.ImageName(srcRef), destName, err) logrus.Debugf("error copying src image [%q] to dest image [%q] err: %v", transports.ImageName(srcRef), destName, err)
return nil, err return nil, err
} }

94
tests/sign.bats Normal file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env bats
load helpers
@test "commit-pull-push-signatures" {
if ! which gpg > /dev/null 2> /dev/null ; then
skip 'gpg command not found in $PATH'
fi
export GNUPGHOME=${TESTDIR}/.gnupg
cat > genkey-answers <<- EOF
%echo Generating a basic OpenPGP key
Key-Type: RSA
Key-Length: 512
Name-Real: Amanda Lorian
Name-Comment: Mandy to her friends
Name-Email: amanda@localhost
%commit
%echo done
EOF
gpg --batch --gen-key < genkey-answers
mkdir -p ${TESTDIR}/signed-image ${TESTDIR}/unsigned-image
run_buildah from --quiet --pull=false --signature-policy ${TESTSDIR}/policy.json alpine
cid=$output
run_buildah commit --signature-policy ${TESTSDIR}/policy.json --sign-by amanda@localhost $cid signed-alpine-image
# Pushing should preserve the signature.
run_buildah push --signature-policy ${TESTSDIR}/policy.json signed-alpine-image dir:${TESTDIR}/signed-image
ls -l ${TESTDIR}/signed-image/
test -s ${TESTDIR}/signed-image/signature-1
# Pushing with --remove-signatures should remove the signature.
run_buildah push --signature-policy ${TESTSDIR}/policy.json --remove-signatures signed-alpine-image dir:${TESTDIR}/unsigned-image
ls -l ${TESTDIR}/unsigned-image/
! test -s ${TESTDIR}/unsigned-image/signature-1
run_buildah commit --signature-policy ${TESTSDIR}/policy.json $cid unsigned-alpine-image
# Pushing with --sign-by should fail add the signature to a dir: location, if it tries to add them.
run_buildah 1 push --signature-policy ${TESTSDIR}/policy.json --sign-by amanda@localhost unsigned-alpine-image dir:${TESTDIR}/signed-image
expect_output --substring "Cannot determine canonical Docker reference"
# Clear out images, so that we don't have leftover signatures when we pull in an image that will end up
# causing us to merge its contents with the image with the same ID.
run_buildah rmi -a -f
# Pulling with --remove-signatures should remove signatures, and pushing should have none to keep.
run_buildah pull --signature-policy ${TESTSDIR}/policy.json --quiet dir:${TESTDIR}/signed-image
imageID="$output"
run_buildah push --signature-policy ${TESTSDIR}/policy.json "$imageID" dir:${TESTDIR}/unsigned-image
ls -l ${TESTDIR}/unsigned-image/
! test -s ${TESTDIR}/unsigned-image/signature-1
# Build a manifest list and try to push the list with signatures.
run_buildah manifest create list
run_buildah manifest add list $imageID
run_buildah 1 manifest push --signature-policy ${TESTSDIR}/policy.json --sign-by amanda@localhost --all list dir:${TESTDIR}/signed-image
expect_output --substring "Cannot determine canonical Docker reference"
run_buildah manifest push --signature-policy ${TESTSDIR}/policy.json --all list dir:${TESTDIR}/unsigned-image
}
@test "build-with-dockerfile-signatures" {
if ! which gpg > /dev/null 2> /dev/null ; then
skip 'gpg command not found in $PATH'
fi
export GNUPGHOME=${TESTDIR}/.gnupg
cat > genkey-answers <<- EOF
%echo Generating a basic OpenPGP key
Key-Type: RSA
Key-Length: 512
Name-Real: Amanda Lorian
Name-Comment: Mandy to her friends
Name-Email: amanda@localhost
%commit
%echo done
EOF
gpg --batch --gen-key < genkey-answers
cat > Dockerfile <<- EOF
FROM scratch
ADD Dockerfile /
EOF
# We should be able to sign at build-time.
run_buildah bud --signature-policy ${TESTSDIR}/policy.json --sign-by amanda@localhost -t signed-scratch-image .
mkdir -p ${TESTDIR}/signed-image
# Pushing should preserve the signature.
run_buildah push --signature-policy ${TESTSDIR}/policy.json signed-scratch-image dir:${TESTDIR}/signed-image
ls -l ${TESTDIR}/signed-image/
test -s ${TESTDIR}/signed-image/signature-1
}