diff --git a/docs/config.md b/docs/config.md index 1b353cd3e..9f008f9d3 100644 --- a/docs/config.md +++ b/docs/config.md @@ -207,16 +207,21 @@ version = 2 [plugins."io.containerd.grpc.v1.cri".image_decryption] # key_model defines the name of the key model used for how the cri obtains # keys used for decryption of encrypted container images. - # Set of available string options: {"node"} + # The [decryption document](https://github.com/containerd/cri/blob/master/docs/decryption.md) + # contains additional information about the key models available. + # + # Set of available string options: {"", "node"} + # Omission of this field defaults to the empty string "", which indicates no key model, + # disabling image decryption. # # In order to use the decryption feature, additional configurations must be made. # The [decryption document](https://github.com/containerd/cri/blob/master/docs/decryption.md) - # provides information of key models and how to set them up with stream processors and the - # containerd imgcrypt decoder. + # provides information of how to set up stream processors and the containerd imgcrypt decoder + # with the appropriate key models. # - # Additional information on stream processors and imgcrypt: + # Additional information: # * Stream processors: https://github.com/containerd/containerd/blob/master/docs/stream_processors.md - # * Containerd imgcrypt : https://github.com/containerd/imgcrypt + # * Containerd imgcrypt: https://github.com/containerd/imgcrypt key_model = "node" ``` diff --git a/docs/decryption.md b/docs/decryption.md index e1e54a4f9..7c73a9735 100644 --- a/docs/decryption.md +++ b/docs/decryption.md @@ -19,10 +19,16 @@ The default configuration does not handle decrypting encrypted container images. An example for configuring the "node" key model for container image decryption: +Configure `cri` to enable decryption with "node" key model ```toml [plugins.cri.image_decryption] key_model = "node" +``` + +Configure `containerd` daemon [`stream_processors`](https://github.com/containerd/containerd/blob/master/docs/stream_processors.md) to handle the +encrypted mediatypes. +```toml [stream_processors] [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"] accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"] @@ -36,6 +42,6 @@ An example for configuring the "node" key model for container image decryption: args = ["--decryption-keys-path", "/keys"] ``` -In this example, container image decryption is set to use the "node" key model. In addition, the decryption `stream_processors` are configured as specified in [containerd/imgcrypt project](https://github.com/containerd/imgcrypt), with the additional field `--decryption-keys-path` configured to specify where decryption keys are located locally in the node. +In this example, container image decryption is set to use the "node" key model. In addition, the decryption [`stream_processors`](https://github.com/containerd/containerd/blob/master/docs/stream_processors.md) are configured as specified in [containerd/imgcrypt project](https://github.com/containerd/imgcrypt), with the additional field `--decryption-keys-path` configured to specify where decryption keys are located locally in the node. After modify this config, you need restart the `containerd` service. diff --git a/pkg/config/config.go b/pkg/config/config.go index e0fa16694..1115ee6fc 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -153,8 +153,15 @@ type RegistryConfig struct { TLS *TLSConfig `toml:"tls" json:"tls"` } -type ImageEncryption struct { - // KeyModel specifies the model of where keys should reside +// ImageDecryption contains configuration to handling decryption of encrypted container images. +type ImageDecryption struct { + // KeyModel specifies the trust model of where keys should reside. + // + // Details of field usage can be found in: + // https://github.com/containerd/cri/tree/master/docs/config.md + // + // Details of key models can be found in: + // https://github.com/containerd/cri/tree/master/docs/decryption.md KeyModel string `toml:"key_model" json:"keyModel"` } @@ -167,8 +174,8 @@ type PluginConfig struct { CniConfig `toml:"cni" json:"cni"` // Registry contains config related to the registry Registry Registry `toml:"registry" json:"registry"` - // ImageEncryption contains config related to handling of encrypted images - ImageEncryption `toml:"image_encryption" json:"imageEncryption"` + // ImageDecryption contains config related to handling decryption of encrypted container images + ImageDecryption `toml:"image_decryption" json:"imageDecryption"` // DisableTCPService disables serving CRI on the TCP server. DisableTCPService bool `toml:"disable_tcp_service" json:"disableTCPService"` // StreamServerAddress is the ip address streaming server is listening on. @@ -243,9 +250,9 @@ const ( RuntimeUntrusted = "untrusted" // RuntimeDefault is the implicit runtime defined for ContainerdConfig.DefaultRuntime RuntimeDefault = "default" - // EncryptionKeyModelNode is the key model where key for encrypted images reside + // KeyModelNode is the key model where key for encrypted images reside // on the worker nodes - EncryptionKeyModelNode = "node" + KeyModelNode = "node" ) // ValidatePluginConfig validates the given plugin configuration. diff --git a/pkg/server/image_pull.go b/pkg/server/image_pull.go index ef4a24e70..5e2df2b24 100644 --- a/pkg/server/image_pull.go +++ b/pkg/server/image_pull.go @@ -411,11 +411,10 @@ func newTransport() *http.Transport { } } -// addEncryptedImagesPullOpts adds the necessary pull options to a list of -// pull options if enabled. +// encryptedImagesPullOpts returns the necessary list of pull options required +// for decryption of encrypted images based on the cri decryption configuration. func (c *criService) encryptedImagesPullOpts() []containerd.RemoteOpt { - if c.config.ImageEncryption.KeyModel == criconfig.EncryptionKeyModelNode || - c.config.ImageEncryption.KeyModel == "" { + if c.config.ImageDecryption.KeyModel == criconfig.KeyModelNode { ltdd := imgcrypt.Payload{} decUnpackOpt := encryption.WithUnpackConfigApplyOpts(encryption.WithDecryptedUnpack(<dd)) opt := containerd.WithUnpackOpts([]containerd.UnpackOpt{decUnpackOpt}) diff --git a/pkg/server/image_pull_test.go b/pkg/server/image_pull_test.go index 8b474d57f..b0ddaa4dd 100644 --- a/pkg/server/image_pull_test.go +++ b/pkg/server/image_pull_test.go @@ -295,17 +295,17 @@ func TestEncryptedImagePullOpts(t *testing.T) { expectedOpts int }{ "node key model should return one unpack opt": { - keyModel: criconfig.EncryptionKeyModelNode, + keyModel: criconfig.KeyModelNode, expectedOpts: 1, }, "no key model selected should default to node key model": { keyModel: "", - expectedOpts: 1, + expectedOpts: 0, }, } { t.Logf("TestCase %q", desc) c := newTestCRIService() - c.config.ImageEncryption.KeyModel = test.keyModel + c.config.ImageDecryption.KeyModel = test.keyModel got := len(c.encryptedImagesPullOpts()) assert.Equal(t, test.expectedOpts, got) } diff --git a/vendor/golang.org/x/crypto/openpgp/armor/armor.go b/vendor/golang.org/x/crypto/openpgp/armor/armor.go index 592d18643..36a680436 100644 --- a/vendor/golang.org/x/crypto/openpgp/armor/armor.go +++ b/vendor/golang.org/x/crypto/openpgp/armor/armor.go @@ -62,10 +62,11 @@ var armorEndOfLine = []byte("-----") // lineReader wraps a line based reader. It watches for the end of an armor // block and records the expected CRC value. type lineReader struct { - in *bufio.Reader - buf []byte - eof bool - crc uint32 + in *bufio.Reader + buf []byte + eof bool + crc uint32 + crcSet bool } func (l *lineReader) Read(p []byte) (n int, err error) { @@ -87,6 +88,11 @@ func (l *lineReader) Read(p []byte) (n int, err error) { return 0, ArmorCorrupt } + if bytes.HasPrefix(line, armorEnd) { + l.eof = true + return 0, io.EOF + } + if len(line) == 5 && line[0] == '=' { // This is the checksum line var expectedBytes [3]byte @@ -108,6 +114,7 @@ func (l *lineReader) Read(p []byte) (n int, err error) { } l.eof = true + l.crcSet = true return 0, io.EOF } @@ -141,10 +148,8 @@ func (r *openpgpReader) Read(p []byte) (n int, err error) { n, err = r.b64Reader.Read(p) r.currentCRC = crc24(r.currentCRC, p[:n]) - if err == io.EOF { - if r.lReader.crc != uint32(r.currentCRC&crc24Mask) { - return 0, ArmorCorrupt - } + if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) { + return 0, ArmorCorrupt } return diff --git a/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go b/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go index 73f4fe378..72a6a7394 100644 --- a/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go +++ b/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go @@ -76,7 +76,9 @@ func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err // Bleichenbacher, Advances in Cryptology (Crypto '98), func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) { s := new(big.Int).Exp(c1, priv.X, priv.P) - s.ModInverse(s, priv.P) + if s.ModInverse(s, priv.P) == nil { + return nil, errors.New("elgamal: invalid private key") + } s.Mul(s, c2) s.Mod(s, priv.P) em := s.Bytes() diff --git a/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go b/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go index 02b372cf3..6d7639722 100644 --- a/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go +++ b/vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go @@ -5,6 +5,7 @@ package packet import ( + "crypto" "crypto/rsa" "encoding/binary" "io" @@ -78,8 +79,9 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { // padding oracle attacks. switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - k := priv.PrivateKey.(*rsa.PrivateKey) - b, err = rsa.DecryptPKCS1v15(config.Random(), k, padToKeySize(&k.PublicKey, e.encryptedMPI1.bytes)) + // Supports both *rsa.PrivateKey and crypto.Decrypter + k := priv.PrivateKey.(crypto.Decrypter) + b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.bytes), nil) case PubKeyAlgoElGamal: c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes) c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes) diff --git a/vendor/golang.org/x/crypto/openpgp/packet/private_key.go b/vendor/golang.org/x/crypto/openpgp/packet/private_key.go index 6f8ec0938..81abb7cef 100644 --- a/vendor/golang.org/x/crypto/openpgp/packet/private_key.go +++ b/vendor/golang.org/x/crypto/openpgp/packet/private_key.go @@ -31,7 +31,7 @@ type PrivateKey struct { encryptedData []byte cipher CipherFunction s2k func(out, in []byte) - PrivateKey interface{} // An *{rsa|dsa|ecdsa}.PrivateKey or a crypto.Signer. + PrivateKey interface{} // An *{rsa|dsa|ecdsa}.PrivateKey or crypto.Signer/crypto.Decrypter (Decryptor RSA only). sha1Checksum bool iv []byte }