From 37df2b9690e545dd0dd5ac511b6765956baa409f Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Wed, 1 Apr 2020 18:15:56 +0000 Subject: [PATCH 1/6] implementation of encrypt/decrypt push/pull/bud/from Signed-off-by: Brandon Lum --- buildah.go | 4 + cmd/buildah/bud.go | 7 + cmd/buildah/common.go | 34 ++ cmd/buildah/from.go | 9 + cmd/buildah/pull.go | 8 + cmd/buildah/push.go | 12 + commit.go | 18 +- common.go | 6 +- contrib/completions/bash/buildah | 4 + docs/buildah-bud.md | 4 + docs/buildah-from.md | 4 + docs/buildah-pull.md | 4 + docs/buildah-push.md | 4 + go.mod | 1 + go.sum | 2 + imagebuildah/build.go | 4 + imagebuildah/executor.go | 3 + imagebuildah/stage_executor.go | 1 + new.go | 13 +- pkg/cli/common.go | 1 + pull.go | 7 +- tests/bud.bats | 19 ++ tests/bud/from-encrypted-image/Dockerfile | 1 + tests/from.bats | 37 +++ tests/pull.bats | 37 +++ tests/push.bats | 21 ++ .../ocicrypt/helpers/parse_helpers.go | 313 ++++++++++++++++++ vendor/modules.txt | 1 + 28 files changed, 569 insertions(+), 10 deletions(-) create mode 100644 tests/bud/from-encrypted-image/Dockerfile create mode 100644 vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go diff --git a/buildah.go b/buildah.go index 2ece11acdb4..556506e4ad7 100644 --- a/buildah.go +++ b/buildah.go @@ -13,6 +13,7 @@ import ( "github.com/containers/buildah/docker" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" "github.com/containers/storage" "github.com/containers/storage/pkg/ioutils" v1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -413,6 +414,9 @@ type BuilderOptions struct { MaxPullRetries int // PullRetryDelay is how long to wait before retrying a pull attempt. PullRetryDelay time.Duration + // OciDecryptConfig contains the config that can be used to decrypt an image if it is + // encrypted if non-nil. If nil, it does not attempt to decrypt an image. + OciDecryptConfig *encconfig.DecryptConfig } // ImportOptions are used to initialize a Builder from an existing container diff --git a/cmd/buildah/bud.go b/cmd/buildah/bud.go index 039c3da5807..7d39b4158f4 100644 --- a/cmd/buildah/bud.go +++ b/cmd/buildah/bud.go @@ -67,6 +67,7 @@ func init() { // BUD is a all common flags budFlags := buildahcli.GetBudFlags(&budFlagResults) budFlags.StringVar(&budFlagResults.Runtime, "runtime", util.Runtime(), "`path` to an alternate runtime. Use BUILDAH_RUNTIME environment variable to override.") + flags.StringSliceVar(&budFlagResults.DecryptionKeys, "decryption-key", nil, "key needed to decrypt the image") layerFlags := buildahcli.GetLayerFlags(&layerFlagsResults) fromAndBudFlags, err := buildahcli.GetFromAndBudFlags(&fromAndBudResults, &userNSResults, &namespaceResults) @@ -295,6 +296,11 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budOptions) error { return err } + decConfig, err := getDecryptConfig(iopts.DecryptionKeys) + if err != nil { + return errors.Wrapf(err, "unable to obtain decrypt config") + } + options := imagebuildah.BuildOptions{ AddCapabilities: iopts.CapAdd, AdditionalTags: tags, @@ -339,6 +345,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budOptions) error { SystemContext: systemContext, Target: iopts.Target, TransientMounts: iopts.Volumes, + OciDecryptConfig: decConfig, } if iopts.Quiet { diff --git a/cmd/buildah/common.go b/cmd/buildah/common.go index ffc137c06d0..e6cab339c9a 100644 --- a/cmd/buildah/common.go +++ b/cmd/buildah/common.go @@ -11,6 +11,8 @@ import ( "github.com/containers/image/v5/manifest" is "github.com/containers/image/v5/storage" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" + enchelpers "github.com/containers/ocicrypt/helpers" "github.com/containers/storage" "github.com/containers/storage/pkg/unshare" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -411,6 +413,38 @@ func getFormat(format string) (string, error) { } } +func getDecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) { + decConfig := &encconfig.DecryptConfig{} + if len(decryptionKeys) > 0 { + // decryption + dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys) + if err != nil { + return nil, errors.Wrapf(err, "invalid decryption keys") + } + cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc}) + decConfig = cc.DecryptConfig + } + + return decConfig, nil +} + +func getEncryptConfig(encryptionKeys []string, encryptLayers []int) (*encconfig.EncryptConfig, *[]int, error) { + var encLayers *[]int + var encConfig *encconfig.EncryptConfig + + if len(encryptionKeys) > 0 { + // encryption + encLayers = &encryptLayers + ecc, err := enchelpers.CreateCryptoConfig(encryptionKeys, []string{}) + if err != nil { + return nil, nil, errors.Wrapf(err, "invalid encryption keys") + } + cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{ecc}) + encConfig = cc.EncryptConfig + } + return encConfig, encLayers, nil +} + // Tail returns a string slice after the first element unless there are // not enough elements, then it returns an empty slice. This is to replace // the urfavecli Tail method for args diff --git a/cmd/buildah/from.go b/cmd/buildah/from.go index 9697b79286d..abc989ab005 100644 --- a/cmd/buildah/from.go +++ b/cmd/buildah/from.go @@ -31,6 +31,7 @@ type fromReply struct { quiet bool signaturePolicy string tlsVerify bool + decryptionKeys []string *buildahcli.FromAndBudResults *buildahcli.UserNSResults *buildahcli.NameSpaceResults @@ -73,6 +74,7 @@ func init() { flags.BoolVar(&opts.pullAlways, "pull-always", false, "pull the image even if the named image is present in store") flags.BoolVar(&opts.pullNever, "pull-never", false, "do not pull the image, use the image present in store if available") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "don't output progress information when pulling images") + flags.StringSliceVar(&opts.decryptionKeys, "decryption-key", nil, "key needed to decrypt the image") flags.StringVar(&opts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)") if err := flags.MarkHidden("signature-policy"); err != nil { panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err)) @@ -267,6 +269,12 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error { } commonOpts.Ulimit = append(defaultContainerConfig.Containers.DefaultUlimits, commonOpts.Ulimit...) + + decConfig, err := getDecryptConfig(iopts.decryptionKeys) + if err != nil { + return errors.Wrapf(err, "unable to obtain decrypt config") + } + options := buildah.BuilderOptions{ FromImage: args[0], Container: iopts.name, @@ -288,6 +296,7 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error { DefaultEnv: defaultContainerConfig.GetDefaultEnv(), MaxPullRetries: maxPullPushRetries, PullRetryDelay: pullPushRetryDelay, + OciDecryptConfig: decConfig, } if !iopts.quiet { diff --git a/cmd/buildah/pull.go b/cmd/buildah/pull.go index 90a8f40114b..c061c989c12 100644 --- a/cmd/buildah/pull.go +++ b/cmd/buildah/pull.go @@ -25,6 +25,7 @@ type pullOptions struct { quiet bool removeSignatures bool tlsVerify bool + decryptionKeys []string } func init() { @@ -58,6 +59,7 @@ func init() { 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.StringSliceVar(&opts.decryptionKeys, "decryption-key", nil, "key needed to decrypt the image") if err := flags.MarkHidden("signature-policy"); err != nil { panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err)) } @@ -102,6 +104,11 @@ func pullCmd(c *cobra.Command, args []string, iopts pullOptions) error { return err } + decConfig, err := getDecryptConfig(iopts.decryptionKeys) + if err != nil { + return errors.Wrapf(err, "unable to obtain decrypt config") + } + options := buildah.PullOptions{ SignaturePolicyPath: iopts.signaturePolicy, Store: store, @@ -112,6 +119,7 @@ func pullCmd(c *cobra.Command, args []string, iopts pullOptions) error { RemoveSignatures: iopts.removeSignatures, MaxRetries: maxPullPushRetries, RetryDelay: pullPushRetryDelay, + OciDecryptConfig: decConfig, } if iopts.quiet { diff --git a/cmd/buildah/push.go b/cmd/buildah/push.go index 52060a61225..f954a55465a 100644 --- a/cmd/buildah/push.go +++ b/cmd/buildah/push.go @@ -34,6 +34,8 @@ type pushOptions struct { signaturePolicy string signBy string tlsVerify bool + encryptionKeys []string + encryptLayers []int } func init() { @@ -77,6 +79,9 @@ func init() { 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.StringSliceVar(&opts.encryptionKeys, "encryption-key", nil, "key with the encryption protocol to use needed to encrypt the image (e.g. jwe:/path/to/key.pem)") + flags.IntSliceVar(&opts.encryptLayers, "encrypt-layer", nil, "layers to encrypt, 0-indexed layer indices with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer). If not defined, will encrypt all layers if encryption-key flag is specified") + if err := flags.MarkHidden("signature-policy"); err != nil { panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err)) } @@ -165,6 +170,11 @@ func pushCmd(c *cobra.Command, args []string, iopts pushOptions) error { } } + encConfig, encLayers, err := getEncryptConfig(iopts.encryptionKeys, iopts.encryptLayers) + if err != nil { + return errors.Wrapf(err, "unable to obtain encryption config") + } + options := buildah.PushOptions{ Compression: compress, ManifestType: manifestType, @@ -176,6 +186,8 @@ func pushCmd(c *cobra.Command, args []string, iopts pushOptions) error { SignBy: iopts.signBy, MaxRetries: maxPullPushRetries, RetryDelay: pullPushRetryDelay, + OciEncryptConfig: encConfig, + OciEncryptLayers: encLayers, } if !iopts.quiet { options.ReportWriter = os.Stderr diff --git a/commit.go b/commit.go index d25ba110a2b..b919ba7fa68 100644 --- a/commit.go +++ b/commit.go @@ -19,6 +19,7 @@ import ( is "github.com/containers/image/v5/storage" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/stringid" @@ -132,6 +133,19 @@ type PushOptions struct { MaxRetries int // RetryDelay is how long to wait before retrying a push attempt. RetryDelay time.Duration + + // OciEncryptConfig when non-nil indicates that an image should be encrypted. + // The encryption options is derived from the construction of EncryptConfig object. + // Note: During initial encryption process of a layer, the resultant digest is not known + // during creation, so newDigestingReader has to be set with validateDigest = false + OciEncryptConfig *encconfig.EncryptConfig + + // OciEncryptLayers represents the list of layers to encrypt. + // If nil, don't encrypt any layers. + // If non-nil and len==0, denotes encrypt all layers. + // integers in the slice represent 0-indexed layer indices, with support for negative + // indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer. + OciEncryptLayers *[]int } var ( @@ -319,7 +333,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options } var manifestBytes []byte - if manifestBytes, err = retryCopyImage(ctx, policyContext, maybeCachedDest, maybeCachedSrc, dest, "push", getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "", false, options.SignBy), options.MaxRetries, options.RetryDelay); err != nil { + if manifestBytes, err = retryCopyImage(ctx, policyContext, maybeCachedDest, maybeCachedSrc, dest, "push", getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "", false, options.SignBy, nil, nil, nil), options.MaxRetries, options.RetryDelay); err != nil { 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 @@ -451,7 +465,7 @@ func Push(ctx context.Context, image string, dest types.ImageReference, options systemContext.DirForceCompress = true } var manifestBytes []byte - if manifestBytes, err = retryCopyImage(ctx, policyContext, dest, maybeCachedSrc, dest, "push", getCopyOptions(options.Store, options.ReportWriter, nil, systemContext, options.ManifestType, options.RemoveSignatures, options.SignBy), options.MaxRetries, options.RetryDelay); err != nil { + if manifestBytes, err = retryCopyImage(ctx, policyContext, dest, maybeCachedSrc, dest, "push", getCopyOptions(options.Store, options.ReportWriter, nil, systemContext, options.ManifestType, options.RemoveSignatures, options.SignBy, options.OciEncryptLayers, options.OciEncryptConfig, nil), options.MaxRetries, options.RetryDelay); err != nil { return nil, "", errors.Wrapf(err, "error copying layers and metadata from %q to %q", transports.ImageName(maybeCachedSrc), transports.ImageName(dest)) } if options.ReportWriter != nil { diff --git a/common.go b/common.go index 8fb3ebdb7ff..b43cfffc9ad 100644 --- a/common.go +++ b/common.go @@ -14,6 +14,7 @@ import ( "github.com/containers/image/v5/docker" "github.com/containers/image/v5/signature" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" "github.com/containers/storage" "github.com/containers/storage/pkg/unshare" "github.com/docker/distribution/registry/api/errcode" @@ -30,7 +31,7 @@ const ( DOCKER = "docker" ) -func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceSystemContext *types.SystemContext, destinationSystemContext *types.SystemContext, manifestType string, removeSignatures bool, addSigner string) *cp.Options { +func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceSystemContext *types.SystemContext, destinationSystemContext *types.SystemContext, manifestType string, removeSignatures bool, addSigner string, ociEncryptLayers *[]int, ociEncryptConfig *encconfig.EncryptConfig, ociDecryptConfig *encconfig.DecryptConfig) *cp.Options { sourceCtx := getSystemContext(store, nil, "") if sourceSystemContext != nil { *sourceCtx = *sourceSystemContext @@ -47,6 +48,9 @@ func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceSystemCon ForceManifestMIMEType: manifestType, RemoveSignatures: removeSignatures, SignBy: addSigner, + OciEncryptConfig: ociEncryptConfig, + OciDecryptConfig: ociDecryptConfig, + OciEncryptLayers: ociEncryptLayers, } } diff --git a/contrib/completions/bash/buildah b/contrib/completions/bash/buildah index 78310b090ee..d03f1201cc1 100644 --- a/contrib/completions/bash/buildah +++ b/contrib/completions/bash/buildah @@ -392,6 +392,7 @@ return 1 --cpuset-cpus --cpuset-mems --creds + --decryption-key --device --dns-search --dns @@ -588,6 +589,7 @@ return 1 --authfile --cert-dir --creds + --decryption-key " local all_options="$options_with_args $boolean_options" @@ -615,6 +617,8 @@ return 1 --authfile --cert-dir --creds + --encrypt-layer + --encryption-key --format -f --sign-by diff --git a/docs/buildah-bud.md b/docs/buildah-bud.md index 961ba3e0e1b..c85978ea885 100644 --- a/docs/buildah-bud.md +++ b/docs/buildah-bud.md @@ -178,6 +178,10 @@ The [username[:password]] to use to authenticate with the registry if required. If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. +**--decryption-key** *key* + +A reference required to perform decryption of container images. This should point to files which represent keys and/or certificates that can be used for decryption. Decryption will be tried with all keys. This feature is still *experimental*. + **--device**=*device* Add a host device or devices under a directory to the container. The format is `[:][:]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) diff --git a/docs/buildah-from.md b/docs/buildah-from.md index 6a00d8ca6e7..f38fef7f22d 100644 --- a/docs/buildah-from.md +++ b/docs/buildah-from.md @@ -168,6 +168,10 @@ The [username[:password]] to use to authenticate with the registry if required. If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. +**--decryption-key** *key* + +A reference required to perform decryption of container images. This should point to files which represent keys and/or certificates that can be used for decryption. Decryption will be tried with all keys. This feature is still *experimental*. + **--device**=*device* Add a host device or devices under a directory to the container. The format is `[:][:]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) diff --git a/docs/buildah-pull.md b/docs/buildah-pull.md index 4d0d7d8bc1d..2e5bd774d0a 100644 --- a/docs/buildah-pull.md +++ b/docs/buildah-pull.md @@ -62,6 +62,10 @@ The [username[:password]] to use to authenticate with the registry if required. If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. +**--decryption-key** *key* + +A reference required to perform decryption of container images. This should point to files which represent keys and/or certificates that can be used for decryption. Decryption will be tried with all keys. This feature is still *experimental*. + **--quiet, -q** If an image needs to be pulled from the registry, suppress progress output. diff --git a/docs/buildah-push.md b/docs/buildah-push.md index 25de97cf5af..c14923cdf9d 100644 --- a/docs/buildah-push.md +++ b/docs/buildah-push.md @@ -66,6 +66,10 @@ After copying the image, write the digest of the resulting image to the file. Don't compress copies of filesystem layers which will be pushed. +**--encryption-key** *key* + +A reference prefixed with the encryption protocol to use. The supported protocols are JWE, PGP and PKCS7. For instance, jwe:/path/to/key.pem or pgp:admin@example.com or pkcs7:/path/to/x509-file. This feature is still *experimental*. + **--format, -f** Manifest Type (oci, v2s1, or v2s2) to use when saving image to directory using the 'dir:' transport (default is manifest type of source) diff --git a/go.mod b/go.mod index c4885fda3b9..40e036abdcc 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 github.com/containers/common v0.11.2 github.com/containers/image/v5 v5.4.4 + github.com/containers/ocicrypt v1.0.2 github.com/containers/storage v1.19.2 github.com/cyphar/filepath-securejoin v0.2.2 github.com/docker/distribution v2.7.1+incompatible diff --git a/go.sum b/go.sum index ff7ca094c6a..4937336fa2c 100644 --- a/go.sum +++ b/go.sum @@ -308,11 +308,13 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f h1:OtU/w6sBKmXYaw2KEODxjcYi3oPSyyslhgGFgIJVGAI= github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f/go.mod h1:f/98/SnvAzhAEFQJ3u836FePXvcbE8BS0YGMQNn4mhA= github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= diff --git a/imagebuildah/build.go b/imagebuildah/build.go index 8d30367c1b7..eef8c818a79 100644 --- a/imagebuildah/build.go +++ b/imagebuildah/build.go @@ -16,6 +16,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -171,6 +172,9 @@ type BuildOptions struct { MaxPullPushRetries int // PullPushRetryDelay is how long to wait before retrying a pull or push attempt. PullPushRetryDelay time.Duration + // OciDecryptConfig contains the config that can be used to decrypt an image if it is + // encrypted if non-nil. If nil, it does not attempt to decrypt an image. + OciDecryptConfig *encconfig.DecryptConfig } // BuildDockerfiles parses a set of one or more Dockerfiles (which may be diff --git a/imagebuildah/executor.go b/imagebuildah/executor.go index f8ef6401cec..a156313df60 100644 --- a/imagebuildah/executor.go +++ b/imagebuildah/executor.go @@ -20,6 +20,7 @@ import ( "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" v1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -100,6 +101,7 @@ type Executor struct { os string maxPullPushRetries int retryPullPushDelay time.Duration + ociDecryptConfig *encconfig.DecryptConfig } // NewExecutor creates a new instance of the imagebuilder.Executor interface. @@ -188,6 +190,7 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod os: options.OS, maxPullPushRetries: options.MaxPullPushRetries, retryPullPushDelay: options.PullPushRetryDelay, + ociDecryptConfig: options.OciDecryptConfig, } if exec.err == nil { exec.err = os.Stderr diff --git a/imagebuildah/stage_executor.go b/imagebuildah/stage_executor.go index 4f45a4ff212..7ba5e2e964b 100644 --- a/imagebuildah/stage_executor.go +++ b/imagebuildah/stage_executor.go @@ -633,6 +633,7 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo Devices: s.executor.devices, MaxPullRetries: s.executor.maxPullPushRetries, PullRetryDelay: s.executor.retryPullPushDelay, + OciDecryptConfig: s.executor.ociDecryptConfig, } // Check and see if the image is a pseudonym for the end result of a diff --git a/new.go b/new.go index 160c2157d84..4f4b1564bf7 100644 --- a/new.go +++ b/new.go @@ -30,12 +30,13 @@ const ( func pullAndFindImage(ctx context.Context, store storage.Store, srcRef types.ImageReference, options BuilderOptions, sc *types.SystemContext) (*storage.Image, types.ImageReference, error) { pullOptions := PullOptions{ - ReportWriter: options.ReportWriter, - Store: store, - SystemContext: options.SystemContext, - BlobDirectory: options.BlobDirectory, - MaxRetries: options.MaxPullRetries, - RetryDelay: options.PullRetryDelay, + ReportWriter: options.ReportWriter, + Store: store, + SystemContext: options.SystemContext, + BlobDirectory: options.BlobDirectory, + MaxRetries: options.MaxPullRetries, + RetryDelay: options.PullRetryDelay, + OciDecryptConfig: options.OciDecryptConfig, } ref, err := pullImage(ctx, store, srcRef, pullOptions, sc) if err != nil { diff --git a/pkg/cli/common.go b/pkg/cli/common.go index 8e7f7dd4ab3..1a457f34c4b 100644 --- a/pkg/cli/common.go +++ b/pkg/cli/common.go @@ -57,6 +57,7 @@ type BudResults struct { Creds string DisableCompression bool DisableContentTrust bool + DecryptionKeys []string File []string Format string Iidfile string diff --git a/pull.go b/pull.go index cbb98cbcf06..f8d4bdeb67e 100644 --- a/pull.go +++ b/pull.go @@ -19,6 +19,7 @@ import ( is "github.com/containers/image/v5/storage" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/types" + encconfig "github.com/containers/ocicrypt/config" "github.com/containers/storage" multierror "github.com/hashicorp/go-multierror" "github.com/pkg/errors" @@ -56,6 +57,9 @@ type PullOptions struct { MaxRetries int // RetryDelay is how long to wait before retrying a pull attempt. RetryDelay time.Duration + // OciDecryptConfig contains the config that can be used to decrypt an image if it is + // encrypted if non-nil. If nil, it does not attempt to decrypt an image. + OciDecryptConfig *encconfig.DecryptConfig } func localImageNameForReference(ctx context.Context, store storage.Store, srcRef types.ImageReference) (string, error) { @@ -164,6 +168,7 @@ func Pull(ctx context.Context, imageName string, options PullOptions) (imageID s ReportWriter: options.ReportWriter, MaxPullRetries: options.MaxRetries, PullRetryDelay: options.RetryDelay, + OciDecryptConfig: options.OciDecryptConfig, } storageRef, transport, img, err := resolveImage(ctx, systemContext, options.Store, boptions) @@ -275,7 +280,7 @@ func pullImage(ctx context.Context, store storage.Store, srcRef types.ImageRefer }() logrus.Debugf("copying %q to %q", transports.ImageName(srcRef), destName) - if _, err := retryCopyImage(ctx, policyContext, maybeCachedDestRef, srcRef, srcRef, "pull", getCopyOptions(store, options.ReportWriter, sc, nil, "", options.RemoveSignatures, ""), options.MaxRetries, options.RetryDelay); err != nil { + if _, err := retryCopyImage(ctx, policyContext, maybeCachedDestRef, srcRef, srcRef, "pull", getCopyOptions(store, options.ReportWriter, sc, nil, "", options.RemoveSignatures, "", nil, nil, options.OciDecryptConfig), options.MaxRetries, options.RetryDelay); err != nil { logrus.Debugf("error copying src image [%q] to dest image [%q] err: %v", transports.ImageName(srcRef), destName, err) return nil, err } diff --git a/tests/bud.bats b/tests/bud.bats index 25aacfb7e68..f338859042a 100644 --- a/tests/bud.bats +++ b/tests/bud.bats @@ -2079,3 +2079,22 @@ EOM run grep "secretthings" <<< "$output" expect_output "" } + +@test "bud with encrypted FROM image" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl genrsa -out ${TESTDIR}/tmp/mykey2.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah push --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox docker://localhost:5000/buildah/busybox_encrypted:latest + + target=busybox-image + # Try to build from encrypted image without key + run_buildah 1 bud --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword -t ${target} -f ${TESTSDIR}/bud/from-encrypted-image/Dockerfile + # Try to build from encrypted image with wrong key + run_buildah 1 bud --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem -t ${target} -f ${TESTSDIR}/bud/from-encrypted-image/Dockerfile + # Try to build with the correct key + run_buildah bud --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey.pem -t ${target} -f ${TESTSDIR}/bud/from-encrypted-image/Dockerfile + + rm -rf ${TESTDIR}/tmp +} diff --git a/tests/bud/from-encrypted-image/Dockerfile b/tests/bud/from-encrypted-image/Dockerfile new file mode 100644 index 00000000000..7f4547999eb --- /dev/null +++ b/tests/bud/from-encrypted-image/Dockerfile @@ -0,0 +1 @@ +FROM localhost:5000/buildah/busybox_encrypted:latest diff --git a/tests/from.bats b/tests/from.bats index 20376595784..dd3c9c9588a 100644 --- a/tests/from.bats +++ b/tests/from.bats @@ -389,3 +389,40 @@ load helpers run_buildah from --signature-policy ${TESTSDIR}/policy.json --quiet docker.io/busybox expect_output "busybox-working-container" } + +@test "from encrypted local image" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl genrsa -out ${TESTDIR}/tmp/mykey2.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah push --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox oci:${TESTDIR}/tmp/busybox_enc + + # Try encrypted image without key should fail + run_buildah 1 from oci:${TESTDIR}/tmp/busybox_enc + # Try encrypted image with wrong key should fail + run_buildah 1 from --decryption-key ${TESTDIR}/tmp/mykey2.pem oci:${TESTDIR}/tmp/busybox_enc + # Providing the right key should succeed + run_buildah from --decryption-key ${TESTDIR}/tmp/mykey.pem oci:${TESTDIR}/tmp/busybox_enc + + rm -rf ${TESTDIR}/tmp +} + +@test "from encrypted registry image" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl genrsa -out ${TESTDIR}/tmp/mykey2.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah push --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox docker://localhost:5000/buildah/busybox_encrypted:latest + + # Try encrypted image without key should fail + run_buildah 1 from --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest + # Try encrypted image with wrong key should fail + run_buildah 1 from --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest + # Providing the right key should succeed + run_buildah from --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey.pem docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah rmi localhost:5000/buildah/busybox_encrypted:latest + + rm -rf ${TESTDIR}/tmp +} diff --git a/tests/pull.bats b/tests/pull.bats index 6e688c710f1..51f179f7fdd 100644 --- a/tests/pull.bats +++ b/tests/pull.bats @@ -167,3 +167,40 @@ load helpers @test "pull should fail with nonexist authfile" { run_buildah 125 pull --authfile /tmp/nonexist --signature-policy ${TESTSDIR}/policy.json alpine } + +@test "pull encrypted local image" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl genrsa -out ${TESTDIR}/tmp/mykey2.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah push --signature-policy ${TESTSDIR}/policy.json --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox oci:${TESTDIR}/tmp/busybox_enc + + # Try to pull encrypted image without key should fail + run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json oci:${TESTDIR}/tmp/busybox_enc + # Try to pull encrypted image with wrong key should fail + run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --decryption-key ${TESTDIR}/tmp/mykey2.pem oci:${TESTDIR}/tmp/busybox_enc + # Providing the right key should succeed + run_buildah pull --signature-policy ${TESTSDIR}/policy.json --decryption-key ${TESTDIR}/tmp/mykey.pem oci:${TESTDIR}/tmp/busybox_enc + + rm -rf ${TESTDIR}/tmp +} + +@test "pull encrypted registry image" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl genrsa -out ${TESTDIR}/tmp/mykey2.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah push --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox docker://localhost:5000/buildah/busybox_encrypted:latest + + # Try to pull encrypted image without key should fail + run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest + # Try to pull encrypted image with wrong key should fail + run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest + # Providing the right key should succeed + run_buildah pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey.pem docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah rmi localhost:5000/buildah/busybox_encrypted:latest + + rm -rf ${TESTDIR}/tmp +} diff --git a/tests/push.bats b/tests/push.bats index 84f218b48a5..e6b928637ae 100644 --- a/tests/push.bats +++ b/tests/push.bats @@ -159,3 +159,24 @@ load helpers docker rmi localhost:5000/buildah/busybox:latest docker logout localhost:5000 } + +@test "buildah oci encrypt and push local oci" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah push --signature-policy ${TESTSDIR}/policy.json --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox oci:${TESTDIR}/tmp/busybox_enc + imgtype -show-manifest oci:${TESTDIR}/tmp/busybox_enc | grep "+encrypted" + rm -rf ${TESTDIR}/tmp +} + +@test "buildah oci encrypt and push registry" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah push --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox docker://localhost:5000/buildah/busybox_encrypted:latest + # this test, just checks the ability to push an image + # there is no good way to test the details of the image unless with ./buildah pull, test will be in pull.bats + rm -rf ${TESTDIR}/tmp +} diff --git a/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go b/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go new file mode 100644 index 00000000000..476ddfea5e0 --- /dev/null +++ b/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go @@ -0,0 +1,313 @@ +package helpers + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" + + "github.com/containerd/containerd/platforms" + "github.com/containers/ocicrypt" + encconfig "github.com/containers/ocicrypt/config" + encutils "github.com/containers/ocicrypt/utils" + + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// processRecipientKeys sorts the array of recipients by type. Recipients may be either +// x509 certificates, public keys, or PGP public keys identified by email address or name +func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, error) { + var ( + gpgRecipients [][]byte + pubkeys [][]byte + x509s [][]byte + ) + for _, recipient := range recipients { + + idx := strings.Index(recipient, ":") + if idx < 0 { + return nil, nil, nil, errors.New("Invalid recipient format") + } + + protocol := recipient[:idx] + value := recipient[idx+1:] + + switch protocol { + case "pgp": + gpgRecipients = append(gpgRecipients, []byte(value)) + + case "jwe": + tmp, err := ioutil.ReadFile(value) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Unable to read file") + } + if !encutils.IsPublicKey(tmp) { + return nil, nil, nil, errors.New("File provided is not a public key") + } + pubkeys = append(pubkeys, tmp) + + case "pkcs7": + tmp, err := ioutil.ReadFile(value) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Unable to read file") + } + if !encutils.IsCertificate(tmp) { + return nil, nil, nil, errors.New("File provided is not an x509 cert") + } + x509s = append(x509s, tmp) + + default: + return nil, nil, nil, errors.New("Provided protocol not recognized") + } + } + return gpgRecipients, pubkeys, x509s, nil +} + +// processPwdString process a password that may be in any of the following formats: +// - file= +// - pass= +// - fd= +// - +func processPwdString(pwdString string) ([]byte, error) { + if strings.HasPrefix(pwdString, "file=") { + return ioutil.ReadFile(pwdString[5:]) + } else if strings.HasPrefix(pwdString, "pass=") { + return []byte(pwdString[5:]), nil + } else if strings.HasPrefix(pwdString, "fd=") { + fdStr := pwdString[3:] + fd, err := strconv.Atoi(fdStr) + if err != nil { + return nil, errors.Wrapf(err, "could not parse file descriptor %s", fdStr) + } + f := os.NewFile(uintptr(fd), "pwdfile") + if f == nil { + return nil, fmt.Errorf("%s is not a valid file descriptor", fdStr) + } + defer f.Close() + pwd := make([]byte, 64) + n, err := f.Read(pwd) + if err != nil { + return nil, errors.Wrapf(err, "could not read from file descriptor") + } + return pwd[:n], nil + } + return []byte(pwdString), nil +} + +// processPrivateKeyFiles sorts the different types of private key files; private key files may either be +// private keys or GPG private key ring files. The private key files may include the password for the +// private key and take any of the following forms: +// - +// - :file= +// - :pass= +// - :fd= +// - : +func processPrivateKeyFiles(keyFilesAndPwds []string) ([][]byte, [][]byte, [][]byte, [][]byte, error) { + var ( + gpgSecretKeyRingFiles [][]byte + gpgSecretKeyPasswords [][]byte + privkeys [][]byte + privkeysPasswords [][]byte + err error + ) + // keys needed for decryption in case of adding a recipient + for _, keyfileAndPwd := range keyFilesAndPwds { + var password []byte + + parts := strings.Split(keyfileAndPwd, ":") + if len(parts) == 2 { + password, err = processPwdString(parts[1]) + if err != nil { + return nil, nil, nil, nil, err + } + } + + keyfile := parts[0] + tmp, err := ioutil.ReadFile(keyfile) + if err != nil { + return nil, nil, nil, nil, err + } + isPrivKey, err := encutils.IsPrivateKey(tmp, password) + if encutils.IsPasswordError(err) { + return nil, nil, nil, nil, err + } + if isPrivKey { + privkeys = append(privkeys, tmp) + privkeysPasswords = append(privkeysPasswords, password) + } else if encutils.IsGPGPrivateKeyRing(tmp) { + gpgSecretKeyRingFiles = append(gpgSecretKeyRingFiles, tmp) + gpgSecretKeyPasswords = append(gpgSecretKeyPasswords, password) + } else { + return nil, nil, nil, nil, fmt.Errorf("unidentified private key in file %s (password=%s)", keyfile, string(password)) + } + } + return gpgSecretKeyRingFiles, gpgSecretKeyPasswords, privkeys, privkeysPasswords, nil +} + +func createGPGClient(context context.Context) (ocicrypt.GPGClient, error) { + return ocicrypt.NewGPGClient(context.Value("gpg-version").(string), context.Value("gpg-homedir").(string)) +} + +func getGPGPrivateKeys(context context.Context, gpgSecretKeyRingFiles [][]byte, descs []ocispec.Descriptor, mustFindKey bool) (gpgPrivKeys [][]byte, gpgPrivKeysPwds [][]byte, err error) { + gpgClient, err := createGPGClient(context) + if err != nil { + return nil, nil, err + } + + var gpgVault ocicrypt.GPGVault + if len(gpgSecretKeyRingFiles) > 0 { + gpgVault = ocicrypt.NewGPGVault() + err = gpgVault.AddSecretKeyRingDataArray(gpgSecretKeyRingFiles) + if err != nil { + return nil, nil, err + } + } + return ocicrypt.GPGGetPrivateKey(descs, gpgClient, gpgVault, mustFindKey) +} + +// CreateDecryptCryptoConfig creates the CryptoConfig object that contains the necessary +// information to perform decryption from command line options and possibly +// LayerInfos describing the image and helping us to query for the PGP decryption keys +func CreateDecryptCryptoConfig(keys []string, decRecipients []string) (encconfig.CryptoConfig, error) { + ccs := []encconfig.CryptoConfig{} + + // x509 cert is needed for PKCS7 decryption + _, _, x509s, err := processRecipientKeys(decRecipients) + if err != nil { + return encconfig.CryptoConfig{}, err + } + + gpgSecretKeyRingFiles, gpgSecretKeyPasswords, privKeys, privKeysPasswords, err := processPrivateKeyFiles(keys) + if err != nil { + return encconfig.CryptoConfig{}, err + } + + if len(gpgSecretKeyRingFiles) > 0 { + gpgCc, err := encconfig.DecryptWithGpgPrivKeys(gpgSecretKeyRingFiles, gpgSecretKeyPasswords) + if err != nil { + return encconfig.CryptoConfig{}, err + } + ccs = append(ccs, gpgCc) + } + + /* TODO: Add in GPG client query for secret keys in the future. + _, err = createGPGClient(context) + gpgInstalled := err == nil + if gpgInstalled { + if len(gpgSecretKeyRingFiles) == 0 && len(privKeys) == 0 && descs != nil { + // Get pgp private keys from keyring only if no private key was passed + gpgPrivKeys, gpgPrivKeyPasswords, err := getGPGPrivateKeys(context, gpgSecretKeyRingFiles, descs, true) + if err != nil { + return encconfig.CryptoConfig{}, err + } + + gpgCc, err := encconfig.DecryptWithGpgPrivKeys(gpgPrivKeys, gpgPrivKeyPasswords) + if err != nil { + return encconfig.CryptoConfig{}, err + } + ccs = append(ccs, gpgCc) + + } else if len(gpgSecretKeyRingFiles) > 0 { + gpgCc, err := encconfig.DecryptWithGpgPrivKeys(gpgSecretKeyRingFiles, gpgSecretKeyPasswords) + if err != nil { + return encconfig.CryptoConfig{}, err + } + ccs = append(ccs, gpgCc) + + } + } + */ + + x509sCc, err := encconfig.DecryptWithX509s(x509s) + if err != nil { + return encconfig.CryptoConfig{}, err + } + ccs = append(ccs, x509sCc) + + privKeysCc, err := encconfig.DecryptWithPrivKeys(privKeys, privKeysPasswords) + if err != nil { + return encconfig.CryptoConfig{}, err + } + ccs = append(ccs, privKeysCc) + + return encconfig.CombineCryptoConfigs(ccs), nil +} + +// parsePlatformArray parses an array of specifiers and converts them into an array of specs.Platform +func parsePlatformArray(specifiers []string) ([]ocispec.Platform, error) { + var speclist []ocispec.Platform + + for _, specifier := range specifiers { + spec, err := platforms.Parse(specifier) + if err != nil { + return []ocispec.Platform{}, err + } + speclist = append(speclist, spec) + } + return speclist, nil +} + +// CreateCryptoConfig from the list of recipient strings and list of key paths of private keys +func CreateCryptoConfig(recipients []string, keys []string) (encconfig.CryptoConfig, error) { + var decryptCc *encconfig.CryptoConfig + ccs := []encconfig.CryptoConfig{} + if len(keys) > 0 { + dcc, err := CreateDecryptCryptoConfig(keys, []string{}) + if err != nil { + return encconfig.CryptoConfig{}, err + } + decryptCc = &dcc + ccs = append(ccs, dcc) + } + + if len(recipients) > 0 { + gpgRecipients, pubKeys, x509s, err := processRecipientKeys(recipients) + if err != nil { + return encconfig.CryptoConfig{}, err + } + encryptCcs := []encconfig.CryptoConfig{} + + // Create GPG client with guessed GPG version and default homedir + gpgClient, err := ocicrypt.NewGPGClient("", "") + gpgInstalled := err == nil + if len(gpgRecipients) > 0 && gpgInstalled { + gpgPubRingFile, err := gpgClient.ReadGPGPubRingFile() + if err != nil { + return encconfig.CryptoConfig{}, err + } + + gpgCc, err := encconfig.EncryptWithGpg(gpgRecipients, gpgPubRingFile) + if err != nil { + return encconfig.CryptoConfig{}, err + } + encryptCcs = append(encryptCcs, gpgCc) + } + + // Create Encryption Crypto Config + pkcs7Cc, err := encconfig.EncryptWithPkcs7(x509s) + if err != nil { + return encconfig.CryptoConfig{}, err + } + encryptCcs = append(encryptCcs, pkcs7Cc) + + jweCc, err := encconfig.EncryptWithJwe(pubKeys) + if err != nil { + return encconfig.CryptoConfig{}, err + } + encryptCcs = append(encryptCcs, jweCc) + ecc := encconfig.CombineCryptoConfigs(encryptCcs) + if decryptCc != nil { + ecc.EncryptConfig.AttachDecryptConfig(decryptCc.DecryptConfig) + } + ccs = append(ccs, ecc) + } + + if len(ccs) > 0 { + return encconfig.CombineCryptoConfigs(ccs), nil + } else { + return encconfig.CryptoConfig{}, nil + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index c71aa045185..cdf4351ea84 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -108,6 +108,7 @@ github.com/containers/libtrust github.com/containers/ocicrypt github.com/containers/ocicrypt/blockcipher github.com/containers/ocicrypt/config +github.com/containers/ocicrypt/helpers github.com/containers/ocicrypt/keywrap github.com/containers/ocicrypt/keywrap/jwe github.com/containers/ocicrypt/keywrap/pgp From 57706e134d56ad19717d5713dac8c2e043146a34 Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Fri, 17 Apr 2020 15:48:14 +0000 Subject: [PATCH 2/6] Implement commit for encryption Signed-off-by: Brandon Lum --- cmd/buildah/commit.go | 11 +++++++++++ commit.go | 17 +++++++++++++++-- tests/commit.bats | 26 ++++++++++++++++++++++++++ tests/pull.bats | 20 ++++++++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/cmd/buildah/commit.go b/cmd/buildah/commit.go index 9ea0c370479..c6def14dfef 100644 --- a/cmd/buildah/commit.go +++ b/cmd/buildah/commit.go @@ -35,6 +35,8 @@ type commitInputOptions struct { signBy string squash bool tlsVerify bool + encryptionKeys []string + encryptLayers []int } func init() { @@ -59,6 +61,8 @@ func init() { flags.StringVar(&opts.authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&opts.blobCache, "blob-cache", "", "assume image blobs in the specified directory will be available for pushing") + flags.StringSliceVar(&opts.encryptionKeys, "encryption-key", nil, "key with the encryption protocol to use needed to encrypt the image (e.g. jwe:/path/to/key.pem)") + flags.IntSliceVar(&opts.encryptLayers, "encrypt-layer", nil, "layers to encrypt, 0-indexed layer indices with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer). If not defined, will encrypt all layers if encryption-key flag is specified") if err := flags.MarkHidden("blob-cache"); err != nil { panic(fmt.Sprintf("error marking blob-cache as hidden: %v", err)) @@ -168,6 +172,11 @@ func commitCmd(c *cobra.Command, args []string, iopts commitInputOptions) error // Add builder identity information. builder.SetLabel(buildah.BuilderIdentityAnnotation, buildah.Version) + encConfig, encLayers, err := getEncryptConfig(iopts.encryptionKeys, iopts.encryptLayers) + if err != nil { + return errors.Wrapf(err, "unable to obtain encryption config") + } + options := buildah.CommitOptions{ PreferredManifestType: format, Compression: compress, @@ -179,6 +188,8 @@ func commitCmd(c *cobra.Command, args []string, iopts commitInputOptions) error BlobDirectory: iopts.blobCache, OmitTimestamp: iopts.omitTimestamp, SignBy: iopts.signBy, + OciEncryptConfig: encConfig, + OciEncryptLayers: encLayers, } if !iopts.quiet { options.ReportWriter = os.Stderr diff --git a/commit.go b/commit.go index b919ba7fa68..5e08b8345bc 100644 --- a/commit.go +++ b/commit.go @@ -89,6 +89,17 @@ type CommitOptions struct { // RetryDelay is how long to wait before retrying a commit attempt to a // registry. RetryDelay time.Duration + // OciEncryptConfig when non-nil indicates that an image should be encrypted. + // The encryption options is derived from the construction of EncryptConfig object. + // Note: During initial encryption process of a layer, the resultant digest is not known + // during creation, so newDigestingReader has to be set with validateDigest = false + OciEncryptConfig *encconfig.EncryptConfig + // OciEncryptLayers represents the list of layers to encrypt. + // If nil, don't encrypt any layers. + // If non-nil and len==0, denotes encrypt all layers. + // integers in the slice represent 0-indexed layer indices, with support for negative + // indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer. + OciEncryptLayers *[]int } // PushOptions can be used to alter how an image is copied somewhere. @@ -284,7 +295,9 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options // Check if the base image is already in the destination and it's some kind of local // storage. If so, we can skip recompressing any layers that come from the base image. exportBaseLayers := true - if transport, destIsStorage := dest.Transport().(is.StoreTransport); destIsStorage && b.FromImageID != "" { + if transport, destIsStorage := dest.Transport().(is.StoreTransport); destIsStorage && options.OciEncryptConfig != nil { + return imgID, nil, "", errors.New("unable to use local storage with image encryption") + } else if destIsStorage && b.FromImageID != "" { if baseref, err := transport.ParseReference(b.FromImageID); baseref != nil && err == nil { if img, err := transport.GetImage(baseref); img != nil && err == nil { logrus.Debugf("base image %q is already present in local storage, no need to copy its layers", b.FromImageID) @@ -333,7 +346,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options } var manifestBytes []byte - if manifestBytes, err = retryCopyImage(ctx, policyContext, maybeCachedDest, maybeCachedSrc, dest, "push", getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "", false, options.SignBy, nil, nil, nil), options.MaxRetries, options.RetryDelay); err != nil { + if manifestBytes, err = retryCopyImage(ctx, policyContext, maybeCachedDest, maybeCachedSrc, dest, "push", getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "", false, options.SignBy, options.OciEncryptLayers, options.OciEncryptConfig, nil), options.MaxRetries, options.RetryDelay); err != nil { 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 diff --git a/tests/commit.bats b/tests/commit.bats index 6bf6010c5d1..d03da42b7be 100644 --- a/tests/commit.bats +++ b/tests/commit.bats @@ -174,3 +174,29 @@ load helpers run_buildah commit --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword busyboxc docker://localhost:5000/commit/busybox run_buildah from --signature-policy ${TESTSDIR}/policy.json --name fromdocker --tls-verify=false --creds testuser:testpassword docker://localhost:5000/commit/busybox } + +@test "commit encrypted local oci image" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah from --quiet --pull=false --signature-policy ${TESTSDIR}/policy.json busybox + cid=$output + run_buildah commit --iidfile /dev/null --signature-policy ${TESTSDIR}/policy.json --encryption-key jwe:${TESTDIR}/tmp/mykey.pub -q $cid oci:${TESTDIR}/tmp/busybox_enc + imgtype -show-manifest oci:${TESTDIR}/tmp/busybox_enc | grep "+encrypted" + rm -rf ${TESTDIR}/tmp +} + + +@test "commit oci encrypt to registry" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah from --quiet --pull=false --signature-policy ${TESTSDIR}/policy.json busybox + cid=$output + run_buildah commit --iidfile /dev/null --tls-verify=false --creds testuser:testpassword --signature-policy ${TESTSDIR}/policy.json --encryption-key jwe:${TESTDIR}/tmp/mykey.pub -q $cid docker://localhost:5000/buildah/busybox_encrypted:latest + # this test, just checks the ability to commit an image to a registry + # there is no good way to test the details of the image unless with ./buildah pull, test will be in pull.bats + rm -rf ${TESTDIR}/tmp +} diff --git a/tests/pull.bats b/tests/pull.bats index 51f179f7fdd..dc0ce69cec6 100644 --- a/tests/pull.bats +++ b/tests/pull.bats @@ -204,3 +204,23 @@ load helpers rm -rf ${TESTDIR}/tmp } + +@test "pull encrypted registry image from commit" { + _prefetch busybox + mkdir ${TESTDIR}/tmp + openssl genrsa -out ${TESTDIR}/tmp/mykey.pem 1024 + openssl rsa -in ${TESTDIR}/tmp/mykey.pem -pubout > ${TESTDIR}/tmp/mykey.pub + run_buildah from --quiet --pull=false --signature-policy ${TESTSDIR}/policy.json busybox + cid=$output + run_buildah commit --iidfile /dev/null --tls-verify=false --creds testuser:testpassword --signature-policy ${TESTSDIR}/policy.json --encryption-key jwe:${TESTDIR}/tmp/mykey.pub -q $cid docker://localhost:5000/buildah/busybox_encrypted:latest + + # Try to pull encrypted image without key should fail + run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest + # Try to pull encrypted image with wrong key should fail + run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest + # Providing the right key should succeed + run_buildah pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey.pem docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah rmi localhost:5000/buildah/busybox_encrypted:latest + + rm -rf ${TESTDIR}/tmp +} From 853a35c6482022d915d419c97299c812880529b0 Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Tue, 28 Apr 2020 18:07:41 +0000 Subject: [PATCH 3/6] update exit code for tests Signed-off-by: Brandon Lum --- tests/bud.bats | 4 ++-- tests/from.bats | 8 ++++---- tests/pull.bats | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/bud.bats b/tests/bud.bats index f338859042a..42662d5631f 100644 --- a/tests/bud.bats +++ b/tests/bud.bats @@ -2090,9 +2090,9 @@ EOM target=busybox-image # Try to build from encrypted image without key - run_buildah 1 bud --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword -t ${target} -f ${TESTSDIR}/bud/from-encrypted-image/Dockerfile + run_buildah 125 bud --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword -t ${target} -f ${TESTSDIR}/bud/from-encrypted-image/Dockerfile # Try to build from encrypted image with wrong key - run_buildah 1 bud --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem -t ${target} -f ${TESTSDIR}/bud/from-encrypted-image/Dockerfile + run_buildah 125 bud --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem -t ${target} -f ${TESTSDIR}/bud/from-encrypted-image/Dockerfile # Try to build with the correct key run_buildah bud --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey.pem -t ${target} -f ${TESTSDIR}/bud/from-encrypted-image/Dockerfile diff --git a/tests/from.bats b/tests/from.bats index dd3c9c9588a..3e22ae7e936 100644 --- a/tests/from.bats +++ b/tests/from.bats @@ -399,9 +399,9 @@ load helpers run_buildah push --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox oci:${TESTDIR}/tmp/busybox_enc # Try encrypted image without key should fail - run_buildah 1 from oci:${TESTDIR}/tmp/busybox_enc + run_buildah 125 from oci:${TESTDIR}/tmp/busybox_enc # Try encrypted image with wrong key should fail - run_buildah 1 from --decryption-key ${TESTDIR}/tmp/mykey2.pem oci:${TESTDIR}/tmp/busybox_enc + run_buildah 125 from --decryption-key ${TESTDIR}/tmp/mykey2.pem oci:${TESTDIR}/tmp/busybox_enc # Providing the right key should succeed run_buildah from --decryption-key ${TESTDIR}/tmp/mykey.pem oci:${TESTDIR}/tmp/busybox_enc @@ -417,9 +417,9 @@ load helpers run_buildah push --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox docker://localhost:5000/buildah/busybox_encrypted:latest # Try encrypted image without key should fail - run_buildah 1 from --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah 125 from --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest # Try encrypted image with wrong key should fail - run_buildah 1 from --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah 125 from --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest # Providing the right key should succeed run_buildah from --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey.pem docker://localhost:5000/buildah/busybox_encrypted:latest run_buildah rmi localhost:5000/buildah/busybox_encrypted:latest diff --git a/tests/pull.bats b/tests/pull.bats index dc0ce69cec6..cf994938432 100644 --- a/tests/pull.bats +++ b/tests/pull.bats @@ -177,9 +177,9 @@ load helpers run_buildah push --signature-policy ${TESTSDIR}/policy.json --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox oci:${TESTDIR}/tmp/busybox_enc # Try to pull encrypted image without key should fail - run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json oci:${TESTDIR}/tmp/busybox_enc + run_buildah 125 pull --signature-policy ${TESTSDIR}/policy.json oci:${TESTDIR}/tmp/busybox_enc # Try to pull encrypted image with wrong key should fail - run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --decryption-key ${TESTDIR}/tmp/mykey2.pem oci:${TESTDIR}/tmp/busybox_enc + run_buildah 125 pull --signature-policy ${TESTSDIR}/policy.json --decryption-key ${TESTDIR}/tmp/mykey2.pem oci:${TESTDIR}/tmp/busybox_enc # Providing the right key should succeed run_buildah pull --signature-policy ${TESTSDIR}/policy.json --decryption-key ${TESTDIR}/tmp/mykey.pem oci:${TESTDIR}/tmp/busybox_enc @@ -195,9 +195,9 @@ load helpers run_buildah push --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --encryption-key jwe:${TESTDIR}/tmp/mykey.pub busybox docker://localhost:5000/buildah/busybox_encrypted:latest # Try to pull encrypted image without key should fail - run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah 125 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest # Try to pull encrypted image with wrong key should fail - run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah 125 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest # Providing the right key should succeed run_buildah pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey.pem docker://localhost:5000/buildah/busybox_encrypted:latest run_buildah rmi localhost:5000/buildah/busybox_encrypted:latest @@ -215,9 +215,9 @@ load helpers run_buildah commit --iidfile /dev/null --tls-verify=false --creds testuser:testpassword --signature-policy ${TESTSDIR}/policy.json --encryption-key jwe:${TESTDIR}/tmp/mykey.pub -q $cid docker://localhost:5000/buildah/busybox_encrypted:latest # Try to pull encrypted image without key should fail - run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah 125 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword docker://localhost:5000/buildah/busybox_encrypted:latest # Try to pull encrypted image with wrong key should fail - run_buildah 1 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest + run_buildah 125 pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey2.pem docker://localhost:5000/buildah/busybox_encrypted:latest # Providing the right key should succeed run_buildah pull --signature-policy ${TESTSDIR}/policy.json --tls-verify=false --creds testuser:testpassword --decryption-key ${TESTDIR}/tmp/mykey.pem docker://localhost:5000/buildah/busybox_encrypted:latest run_buildah rmi localhost:5000/buildah/busybox_encrypted:latest From 054a14b63d58b7cb96a63fd2b944d61fa1bf3fd6 Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Tue, 12 May 2020 19:09:05 +0000 Subject: [PATCH 4/6] clean up comments Signed-off-by: Brandon Lum --- commit.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/commit.go b/commit.go index 5e08b8345bc..dd6a527ada7 100644 --- a/commit.go +++ b/commit.go @@ -91,8 +91,6 @@ type CommitOptions struct { RetryDelay time.Duration // OciEncryptConfig when non-nil indicates that an image should be encrypted. // The encryption options is derived from the construction of EncryptConfig object. - // Note: During initial encryption process of a layer, the resultant digest is not known - // during creation, so newDigestingReader has to be set with validateDigest = false OciEncryptConfig *encconfig.EncryptConfig // OciEncryptLayers represents the list of layers to encrypt. // If nil, don't encrypt any layers. @@ -144,13 +142,9 @@ type PushOptions struct { MaxRetries int // RetryDelay is how long to wait before retrying a push attempt. RetryDelay time.Duration - // OciEncryptConfig when non-nil indicates that an image should be encrypted. // The encryption options is derived from the construction of EncryptConfig object. - // Note: During initial encryption process of a layer, the resultant digest is not known - // during creation, so newDigestingReader has to be set with validateDigest = false OciEncryptConfig *encconfig.EncryptConfig - // OciEncryptLayers represents the list of layers to encrypt. // If nil, don't encrypt any layers. // If non-nil and len==0, denotes encrypt all layers. From 0ee5ea325ab27144f262c8895b9b0724041a49ff Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Tue, 12 May 2020 19:20:13 +0000 Subject: [PATCH 5/6] Updated docs Signed-off-by: Brandon Lum --- docs/buildah-bud.md | 2 +- docs/buildah-from.md | 2 +- docs/buildah-pull.md | 2 +- docs/buildah-push.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/buildah-bud.md b/docs/buildah-bud.md index c85978ea885..874f23a6ebb 100644 --- a/docs/buildah-bud.md +++ b/docs/buildah-bud.md @@ -180,7 +180,7 @@ value can be entered. The password is entered without echo. **--decryption-key** *key* -A reference required to perform decryption of container images. This should point to files which represent keys and/or certificates that can be used for decryption. Decryption will be tried with all keys. This feature is still *experimental*. +The [keyfile[:passphrase]] to be used for decryption of images. Keyfile can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. **--device**=*device* diff --git a/docs/buildah-from.md b/docs/buildah-from.md index f38fef7f22d..40cbf2343a7 100644 --- a/docs/buildah-from.md +++ b/docs/buildah-from.md @@ -170,7 +170,7 @@ value can be entered. The password is entered without echo. **--decryption-key** *key* -A reference required to perform decryption of container images. This should point to files which represent keys and/or certificates that can be used for decryption. Decryption will be tried with all keys. This feature is still *experimental*. +The [keyfile[:passphrase]] to be used for decryption of images. Keyfile can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. **--device**=*device* diff --git a/docs/buildah-pull.md b/docs/buildah-pull.md index 2e5bd774d0a..5764190cbbe 100644 --- a/docs/buildah-pull.md +++ b/docs/buildah-pull.md @@ -64,7 +64,7 @@ value can be entered. The password is entered without echo. **--decryption-key** *key* -A reference required to perform decryption of container images. This should point to files which represent keys and/or certificates that can be used for decryption. Decryption will be tried with all keys. This feature is still *experimental*. +The [keyfile[:passphrase]] to be used for decryption of images. Keyfile can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. **--quiet, -q** diff --git a/docs/buildah-push.md b/docs/buildah-push.md index c14923cdf9d..7b9b7805a2f 100644 --- a/docs/buildah-push.md +++ b/docs/buildah-push.md @@ -68,7 +68,7 @@ Don't compress copies of filesystem layers which will be pushed. **--encryption-key** *key* -A reference prefixed with the encryption protocol to use. The supported protocols are JWE, PGP and PKCS7. For instance, jwe:/path/to/key.pem or pgp:admin@example.com or pkcs7:/path/to/x509-file. This feature is still *experimental*. +The [protocol:keyfile] specifies the encryption protocol, which can be JWE (RFC7516), PGP (RFC4880), and PKCS7 (RFC2315) and the key material required for image encryption. For instance, jwe:/path/to/key.pem or pgp:admin@example.com or pkcs7:/path/to/x509-file. **--format, -f** From 2054b5df8dcabf74654f89331ce9058b36ed740b Mon Sep 17 00:00:00 2001 From: Brandon Lum Date: Tue, 19 May 2020 19:12:29 +0000 Subject: [PATCH 6/6] Ammended docs Signed-off-by: Brandon Lum --- docs/buildah-bud.md | 4 ++-- docs/buildah-from.md | 4 ++-- docs/buildah-pull.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/buildah-bud.md b/docs/buildah-bud.md index 874f23a6ebb..337fcf1e177 100644 --- a/docs/buildah-bud.md +++ b/docs/buildah-bud.md @@ -178,9 +178,9 @@ The [username[:password]] to use to authenticate with the registry if required. If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. -**--decryption-key** *key* +**--decryption-key** *key[:passphrase]* -The [keyfile[:passphrase]] to be used for decryption of images. Keyfile can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. +The [key[:passphrase]] to be used for decryption of images. Key can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. **--device**=*device* diff --git a/docs/buildah-from.md b/docs/buildah-from.md index 40cbf2343a7..87798f60b4b 100644 --- a/docs/buildah-from.md +++ b/docs/buildah-from.md @@ -168,9 +168,9 @@ The [username[:password]] to use to authenticate with the registry if required. If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. -**--decryption-key** *key* +**--decryption-key** *key[:passphrase]* -The [keyfile[:passphrase]] to be used for decryption of images. Keyfile can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. +The [key[:passphrase]] to be used for decryption of images. Key can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. **--device**=*device* diff --git a/docs/buildah-pull.md b/docs/buildah-pull.md index 5764190cbbe..669a6148527 100644 --- a/docs/buildah-pull.md +++ b/docs/buildah-pull.md @@ -62,9 +62,9 @@ The [username[:password]] to use to authenticate with the registry if required. If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. -**--decryption-key** *key* +**--decryption-key** *key[:passphrase]* -The [keyfile[:passphrase]] to be used for decryption of images. Keyfile can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. +The [key[:passphrase]] to be used for decryption of images. Key can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and ommitted otherwise. **--quiet, -q**