Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
11 changes: 11 additions & 0 deletions cmd/buildah/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type commitInputOptions struct {
signBy string
squash bool
tlsVerify bool
encryptionKeys []string
encryptLayers []int
}

func init() {
Expand All @@ -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))
Expand Down Expand Up @@ -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,
Expand All @@ -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
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 @@ -31,6 +31,7 @@ type fromReply struct {
quiet bool
signaturePolicy string
tlsVerify bool
decryptionKeys []string
*buildahcli.FromAndBudResults
*buildahcli.UserNSResults
*buildahcli.NameSpaceResults
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
8 changes: 8 additions & 0 deletions cmd/buildah/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type pullOptions struct {
quiet bool
removeSignatures bool
tlsVerify bool
decryptionKeys []string
}

func init() {
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions cmd/buildah/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type pushOptions struct {
signaturePolicy string
signBy string
tlsVerify bool
encryptionKeys []string
encryptLayers []int
}

func init() {
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down
27 changes: 24 additions & 3 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 @@ -88,6 +89,15 @@ 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.
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.
Expand Down Expand Up @@ -132,6 +142,15 @@ 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.
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 @@ -270,7 +289,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")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This should end up as a native functionality in c/image eventually, somehow dealing with API stability. But this check can give users better error messages, so it’s fine to leave it in.)

} 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)
Expand Down Expand Up @@ -319,7 +340,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, 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
Expand Down Expand Up @@ -451,7 +472,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*
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest the following, ditto for the other man pages with decryption-key

Suggested change
**--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.

**--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-from.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*

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*

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*

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**

If an image needs to be pulled from the registry, suppress progress output.
Expand Down
Loading