implementation of encrypt/decrypt push/pull/bud/from
Signed-off-by: Brandon Lum <lumjjb@gmail.com>
This commit is contained in:
parent
e895bd022d
commit
37df2b9690
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
18
commit.go
18
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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 `<device-on-host>[:<device-on-container>][:<permissions>]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
1
go.mod
1
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
|
||||
|
|
2
go.sum
2
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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
13
new.go
13
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 {
|
||||
|
|
|
@ -57,6 +57,7 @@ type BudResults struct {
|
|||
Creds string
|
||||
DisableCompression bool
|
||||
DisableContentTrust bool
|
||||
DecryptionKeys []string
|
||||
File []string
|
||||
Format string
|
||||
Iidfile string
|
||||
|
|
7
pull.go
7
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
FROM localhost:5000/buildah/busybox_encrypted:latest
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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=<passwordfile>
|
||||
// - pass=<password>
|
||||
// - fd=<filedescriptor>
|
||||
// - <password>
|
||||
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:
|
||||
// - <filename>
|
||||
// - <filename>:file=<passwordfile>
|
||||
// - <filename>:pass=<password>
|
||||
// - <filename>:fd=<filedescriptor>
|
||||
// - <filename>:<password>
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue