Skip to content

Commit

Permalink
Encryption decryption feature (containers#1)
Browse files Browse the repository at this point in the history
* first draft of changes to perform encryption decryption

* capitalised first letters as per convention

* first draft of changes for podman run --decryption-key
  • Loading branch information
gupttaru authored and GitHub Enterprise committed Aug 26, 2022
1 parent 1f0c3d5 commit d5e18de
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 8 deletions.
9 changes: 9 additions & 0 deletions cmd/podman/common/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,15 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
"replace", false,
`If a container with the same name exists, replace it`,
)

decryptionKeysFlagName := "decryption-key"
createFlags.StringSliceVar(
&cf.DecryptionKeys,
decryptionKeysFlagName, []string{},
"key needed to decrypt the image",
)
_ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone)

}
if isInfra || (!clone && !isInfra) { // infra container flags, create should also pick these up
shmSizeFlagName := "shm-size"
Expand Down
22 changes: 14 additions & 8 deletions cmd/podman/containers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,15 +327,21 @@ func PullImage(imageName string, cliVals *entities.ContainerCreateOptions) (stri
skipTLSVerify = types.NewOptionalBool(!cliVals.TLSVerify.Value())
}

decConfig, err := util.DecryptConfig(cliVals.DecryptionKeys)
if err != nil {
return "unable to obtain decryption config", err
}

pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{
Authfile: cliVals.Authfile,
Quiet: cliVals.Quiet,
Arch: cliVals.Arch,
OS: cliVals.OS,
Variant: cliVals.Variant,
SignaturePolicy: cliVals.SignaturePolicy,
PullPolicy: pullPolicy,
SkipTLSVerify: skipTLSVerify,
Authfile: cliVals.Authfile,
Quiet: cliVals.Quiet,
Arch: cliVals.Arch,
OS: cliVals.OS,
Variant: cliVals.Variant,
SignaturePolicy: cliVals.SignaturePolicy,
PullPolicy: pullPolicy,
SkipTLSVerify: skipTLSVerify,
OciDecryptConfig: decConfig,
})
if pullErr != nil {
return "", pullErr
Expand Down
11 changes: 11 additions & 0 deletions cmd/podman/images/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type pullOptionsWrapper struct {
entities.ImagePullOptions
TLSVerifyCLI bool // CLI only
CredentialsCLI string
DecryptionKeys []string
}

var (
Expand Down Expand Up @@ -107,6 +108,9 @@ func pullFlags(cmd *cobra.Command) {
flags.StringVar(&pullOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
_ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault)

decryptionKeysFlagName := "decryption-key"
flags.StringSliceVar(&pullOptions.DecryptionKeys, decryptionKeysFlagName, nil, "key needed to decrypt the image")

if !registry.IsRemote() {
certDirFlagName := "cert-dir"
flags.StringVar(&pullOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
Expand Down Expand Up @@ -155,6 +159,13 @@ func imagePull(cmd *cobra.Command, args []string) error {
pullOptions.Username = creds.Username
pullOptions.Password = creds.Password
}

decConfig, err := util.DecryptConfig(pullOptions.DecryptionKeys)
if err != nil {
return fmt.Errorf("unable to obtain decryption config: %w", err)
}
pullOptions.OciDecryptConfig = decConfig

// Let's do all the remaining Yoga in the API to prevent us from
// scattering logic across (too) many parts of the code.
var errs utils.OutputErrors
Expand Down
18 changes: 18 additions & 0 deletions cmd/podman/images/push.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package images

import (
"fmt"
"os"

"github.com/containers/common/pkg/auth"
Expand All @@ -20,6 +21,8 @@ type pushOptionsWrapper struct {
TLSVerifyCLI bool // CLI only
CredentialsCLI string
SignPassphraseFileCLI string
EncryptionKeys []string
EncryptLayers []int
}

var (
Expand Down Expand Up @@ -121,6 +124,14 @@ func pushFlags(cmd *cobra.Command) {
flags.StringVar(&pushOptions.CompressionFormat, compressionFormat, "", "compression format to use")
_ = cmd.RegisterFlagCompletionFunc(compressionFormat, common.AutocompleteCompressionFormat)

encryptionKeysFlagName := "encryption-key"
flags.StringSliceVar(&pushOptions.EncryptionKeys, encryptionKeysFlagName, nil, "key with the encryption protocol to use needed to encrypt the image (e.g. jwe:/path/to/key.pem)")
_ = cmd.RegisterFlagCompletionFunc(encryptionKeysFlagName, completion.AutocompleteDefault)

encryptLayersFlagName := "encrypt-layer"
flags.IntSliceVar(&pushOptions.EncryptLayers, encryptLayersFlagName, 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")
_ = cmd.RegisterFlagCompletionFunc(encryptLayersFlagName, completion.AutocompleteDefault)

if registry.IsRemote() {
_ = flags.MarkHidden("cert-dir")
_ = flags.MarkHidden("compress")
Expand Down Expand Up @@ -168,6 +179,13 @@ func imagePush(cmd *cobra.Command, args []string) error {
return err
}

encConfig, encLayers, err := util.EncryptConfig(pushOptions.EncryptionKeys, pushOptions.EncryptLayers)
if err != nil {
return fmt.Errorf("unable to obtain encryption config: %w", err)
}
pushOptions.OciEncryptConfig = encConfig
pushOptions.OciEncryptLayers = encLayers

// Let's do all the remaining Yoga in the API to prevent us from scattering
// logic across (too) many parts of the code.
return registry.ImageEngine().Push(registry.GetContext(), source, destination, pushOptions.ImagePushOptions)
Expand Down
13 changes: 13 additions & 0 deletions pkg/domain/entities/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
encconfig "github.com/containers/ocicrypt/config"
"github.com/containers/podman/v4/pkg/inspect"
"github.com/containers/podman/v4/pkg/trust"
"github.com/docker/docker/api/types/container"
Expand Down Expand Up @@ -156,6 +157,9 @@ type ImagePullOptions struct {
SkipTLSVerify types.OptionalBool
// PullPolicy whether to pull new image
PullPolicy config.PullPolicy
// 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
}

// ImagePullReport is the response from pulling one or more images.
Expand Down Expand Up @@ -225,6 +229,15 @@ type ImagePushOptions struct {
CompressionFormat string
// Writer is used to display copy information including progress bars.
Writer io.Writer
// 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
}

// ImagePushReport is the response from pushing an image.
Expand Down
1 change: 1 addition & 0 deletions pkg/domain/entities/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ type ContainerCreateOptions struct {
ChrootDirs []string
IsInfra bool
IsClone bool
DecryptionKeys []string

Net *NetOptions `json:"net,omitempty"`

Expand Down
3 changes: 3 additions & 0 deletions pkg/domain/infra/abi/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
pullOptions.Variant = options.Variant
pullOptions.SignaturePolicyPath = options.SignaturePolicy
pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
pullOptions.OciDecryptConfig = options.OciDecryptConfig

if !options.Quiet {
pullOptions.Writer = os.Stderr
Expand Down Expand Up @@ -310,6 +311,8 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri
pushOptions.SignSigstorePrivateKeyPassphrase = options.SignSigstorePrivateKeyPassphrase
pushOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
pushOptions.Writer = options.Writer
pushOptions.OciEncryptConfig = options.OciEncryptConfig
pushOptions.OciEncryptLayers = options.OciEncryptLayers

compressionFormat := options.CompressionFormat
if compressionFormat == "" {
Expand Down
36 changes: 36 additions & 0 deletions pkg/util/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/util"
"github.com/containers/image/v5/types"
encconfig "github.com/containers/ocicrypt/config"
enchelpers "github.com/containers/ocicrypt/helpers"
"github.com/containers/podman/v4/pkg/errorhandling"
"github.com/containers/podman/v4/pkg/namespaces"
"github.com/containers/podman/v4/pkg/rootless"
Expand Down Expand Up @@ -751,3 +753,37 @@ func SizeOfPath(path string) (uint64, error) {
})
return size, err
}

// EncryptConfig translates encryptionKeys into a EncriptionsConfig structure
func EncryptConfig(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, fmt.Errorf("invalid encryption keys: %w", err)
}
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{ecc})
encConfig = cc.EncryptConfig
}
return encConfig, encLayers, nil
}

// DecryptConfig translates decryptionKeys into a DescriptionConfig structure
func DecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) {
decryptConfig := &encconfig.DecryptConfig{}
if len(decryptionKeys) > 0 {
// decryption
dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys)
if err != nil {
return nil, fmt.Errorf("invalid decryption keys: %w", err)
}
cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc})
decryptConfig = cc.DecryptConfig
}

return decryptConfig, nil
}

0 comments on commit d5e18de

Please sign in to comment.