Skip to content

Commit

Permalink
Merge pull request #16329 from gupttaru/encryption-decryption-feature
Browse files Browse the repository at this point in the history
Add encryption decryption feature
  • Loading branch information
openshift-merge-robot authored Nov 28, 2022
2 parents e4e7e41 + 3bb9ed4 commit c1db4f8
Show file tree
Hide file tree
Showing 19 changed files with 391 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 @@ -571,9 +571,18 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
createFlags.StringVar(&cf.PasswdEntry, passwdEntryName, "", "Entry to write to /etc/passwd")
_ = cmd.RegisterFlagCompletionFunc(passwdEntryName, completion.AutocompleteNone)

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

if registry.IsRemote() {
_ = createFlags.MarkHidden("env-host")
_ = createFlags.MarkHidden("http-proxy")
_ = createFlags.MarkHidden(decryptionKeysFlagName)
} else {
createFlags.StringVar(
&cf.SignaturePolicy,
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 @@ -334,15 +334,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
14 changes: 14 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,13 @@ 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 (e.g. /path/to/key.pem)")
_ = cmd.RegisterFlagCompletionFunc(decryptionKeysFlagName, completion.AutocompleteDefault)

if registry.IsRemote() {
_ = flags.MarkHidden(decryptionKeysFlagName)
}
if !registry.IsRemote() {
certDirFlagName := "cert-dir"
flags.StringVar(&pullOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
Expand Down Expand Up @@ -156,6 +164,12 @@ func imagePull(cmd *cobra.Command, args []string) error {
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

if !pullOptions.Quiet {
pullOptions.Writer = os.Stderr
}
Expand Down
20 changes: 20 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 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 All @@ -129,6 +140,8 @@ func pushFlags(cmd *cobra.Command) {
_ = flags.MarkHidden(signByFlagName)
_ = flags.MarkHidden(signBySigstorePrivateKeyFlagName)
_ = flags.MarkHidden(signPassphraseFileFlagName)
_ = flags.MarkHidden(encryptionKeysFlagName)
_ = flags.MarkHidden(encryptLayersFlagName)
}
if !registry.IsRemote() {
flags.StringVar(&pushOptions.SignaturePolicy, "signature-policy", "", "Path to a signature-policy file")
Expand Down Expand Up @@ -172,6 +185,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
7 changes: 7 additions & 0 deletions docs/source/markdown/options/decryption-key.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
####> This option file is used in:
####> podman create, pull, run
####> If you edit this file, make sure your changes
####> are applicable to all of those.
#### **--decryption-key**=*key[:passphrase]*

The [key[:passphrase]] to be used for decryption of images. Key can point to keys and/or certificates. Decryption will be tried with all keys. If the key is protected by a passphrase, it is required to be passed in the argument and omitted otherwise.
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-create.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ and specified with a _tag_.

@@option cpuset-mems

@@option decryption-key

@@option device

Note: if the user only has access rights via a group, accessing the device
Expand Down
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-pull.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ All tagged images in the repository will be pulled.

@@option creds

@@option decryption-key

@@option disable-content-trust

#### **--help**, **-h**
Expand Down
8 changes: 8 additions & 0 deletions docs/source/markdown/podman-push.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ Note: This flag can only be set when using the **dir** transport

@@option disable-content-trust

#### **--encrypt-layer**=*layer(s)*

Layer(s) 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.

#### **--encryption-key**=*key*

The [protocol:keyfile] specifies the encryption protocol, which can be JWE (RFC7516), PGP (RFC4880), and PKCS7 (RFC2315) and the key material required for image encryption. For instance, jwe:/path/to/key.pem or pgp:[email protected] or pkcs7:/path/to/x509-file.

#### **--format**, **-f**=*format*

Manifest Type (oci, v2s2, or v2s1) to use when pushing an image.
Expand Down
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-run.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ and specified with a _tag_.

@@option cpuset-mems

@@option decryption-key

#### **--detach**, **-d**

Detached mode: run the container in the background and print the new container ID. The default is *false*.
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 @@ -158,6 +159,9 @@ type ImagePullOptions struct {
PullPolicy config.PullPolicy
// Writer is used to display copy information including progress bars.
Writer io.Writer
// 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 @@ -227,6 +231,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 @@ -290,6 +290,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 @@ -236,6 +236,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
pullOptions.SignaturePolicyPath = options.SignaturePolicy
pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
pullOptions.Writer = options.Writer
pullOptions.OciDecryptConfig = options.OciDecryptConfig

if !options.Quiet && pullOptions.Writer == nil {
pullOptions.Writer = os.Stderr
Expand Down Expand Up @@ -309,6 +310,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
8 changes: 8 additions & 0 deletions pkg/domain/infra/tunnel/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
}

func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) {
if opts.OciDecryptConfig != nil {
return nil, fmt.Errorf("decryption is not supported for remote clients")
}

options := new(images.PullOptions)
options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithArch(opts.Arch).WithOS(opts.OS)
options.WithVariant(opts.Variant).WithPassword(opts.Password)
Expand Down Expand Up @@ -240,6 +244,10 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti
}

func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, opts entities.ImagePushOptions) error {
if opts.OciEncryptConfig != nil {
return fmt.Errorf("encryption is not supported for remote clients")
}

options := new(images.PushOptions)
options.WithAll(opts.All).WithCompress(opts.Compress).WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithFormat(opts.Format).WithRemoveSignatures(opts.RemoveSignatures).WithQuiet(opts.Quiet).WithCompressionFormat(opts.CompressionFormat).WithProgressWriter(opts.Writer)

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 @@ -756,3 +758,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) {
var 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
}
Loading

0 comments on commit c1db4f8

Please sign in to comment.