Skip to content

Commit

Permalink
implementation of encrypt/decrypt push/pull/bud/from
Browse files Browse the repository at this point in the history
Signed-off-by: Brandon Lum <lumjjb@gmail.com>
  • Loading branch information
lumjjb committed Apr 15, 2020
1 parent db3ced9 commit db18a06
Show file tree
Hide file tree
Showing 27 changed files with 566 additions and 10 deletions.
4 changes: 4 additions & 0 deletions buildah.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions cmd/buildah/bud.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,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)
Expand Down Expand Up @@ -289,6 +290,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,
Expand Down Expand Up @@ -333,6 +339,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budOptions) error {
SystemContext: systemContext,
Target: iopts.Target,
TransientMounts: iopts.Volumes,
OciDecryptConfig: decConfig,
}

if iopts.Quiet {
Expand Down
34 changes: 34 additions & 0 deletions cmd/buildah/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions cmd/buildah/from.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type fromReply struct {
quiet bool
signaturePolicy string
tlsVerify bool
decryptionKeys []string
*buildahcli.FromAndBudResults
*buildahcli.UserNSResults
*buildahcli.NameSpaceResults
Expand Down Expand Up @@ -72,6 +73,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))
Expand Down Expand Up @@ -262,6 +264,12 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error {

capabilities := defaultContainerConfig.Capabilities("", iopts.CapAdd, iopts.CapDrop)
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,
Expand All @@ -283,6 +291,7 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error {
DefaultEnv: defaultContainerConfig.GetDefaultEnv(),
MaxPullRetries: maxPullPushRetries,
PullRetryDelay: pullPushRetryDelay,
OciDecryptConfig: decConfig,
}

if !iopts.quiet {
Expand Down
8 changes: 8 additions & 0 deletions cmd/buildah/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type pullOptions struct {
quiet bool
removeSignatures bool
tlsVerify bool
decryptionKeys []string
}

func init() {
Expand Down Expand Up @@ -57,6 +58,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))
}
Expand Down Expand Up @@ -101,6 +103,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,
Expand All @@ -111,6 +118,7 @@ func pullCmd(c *cobra.Command, args []string, iopts pullOptions) error {
RemoveSignatures: iopts.removeSignatures,
MaxRetries: maxPullPushRetries,
RetryDelay: pullPushRetryDelay,
OciDecryptConfig: decConfig,
}

if iopts.quiet {
Expand Down
12 changes: 12 additions & 0 deletions cmd/buildah/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type pushOptions struct {
signaturePolicy string
signBy string
tlsVerify bool
encryptionKeys []string
encryptLayers []int
}

func init() {
Expand Down Expand Up @@ -76,6 +78,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))
}
Expand Down Expand Up @@ -164,6 +169,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,
Expand All @@ -175,6 +185,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
Expand Down
19 changes: 17 additions & 2 deletions commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -319,7 +333,8 @@ 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 {
logrus.Debugf("Calling from Commit")
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
Expand Down Expand Up @@ -451,7 +466,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 {
Expand Down
6 changes: 5 additions & 1 deletion common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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,
}
}

Expand Down
4 changes: 4 additions & 0 deletions contrib/completions/bash/buildah
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ return 1
--cpuset-cpus
--cpuset-mems
--creds
--decryption-key
--device
--dns-search
--dns
Expand Down Expand Up @@ -588,6 +589,7 @@ return 1
--authfile
--cert-dir
--creds
--decryption-key
"

local all_options="$options_with_args $boolean_options"
Expand Down Expand Up @@ -615,6 +617,8 @@ return 1
--authfile
--cert-dir
--creds
--encrypt-layer
--encryption-key
--format
-f
--sign-by
Expand Down
4 changes: 4 additions & 0 deletions docs/buildah-bud.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<device-on-host>[:<device-on-container>][:<permissions>]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
Expand Down
4 changes: 4 additions & 0 deletions docs/buildah-pull.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions docs/buildah-push.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784
github.com/containers/common v0.9.0
github.com/containers/image/v5 v5.4.3
github.com/containers/ocicrypt v1.0.2
github.com/containers/storage v1.18.2
github.com/cyphar/filepath-securejoin v0.2.2
github.com/docker/distribution v2.7.1+incompatible
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,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=
Expand Down
4 changes: 4 additions & 0 deletions imagebuildah/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit db18a06

Please sign in to comment.