Updated docs and code for default nil behavior

Signed-off-by: Brandon Lum <lumjjb@gmail.com>
This commit is contained in:
Brandon Lum 2020-02-24 22:09:43 +00:00
parent 808ae59cf6
commit 8d5a8355d0
9 changed files with 57 additions and 31 deletions

View File

@ -207,16 +207,21 @@ version = 2
[plugins."io.containerd.grpc.v1.cri".image_decryption] [plugins."io.containerd.grpc.v1.cri".image_decryption]
# key_model defines the name of the key model used for how the cri obtains # key_model defines the name of the key model used for how the cri obtains
# keys used for decryption of encrypted container images. # 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. # 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) # 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 # provides information of how to set up stream processors and the containerd imgcrypt decoder
# 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 # * 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" key_model = "node"
``` ```

View File

@ -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: An example for configuring the "node" key model for container image decryption:
Configure `cri` to enable decryption with "node" key model
```toml ```toml
[plugins.cri.image_decryption] [plugins.cri.image_decryption]
key_model = "node" 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]
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"] [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"] 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"] 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. After modify this config, you need restart the `containerd` service.

View File

@ -153,8 +153,15 @@ type RegistryConfig struct {
TLS *TLSConfig `toml:"tls" json:"tls"` TLS *TLSConfig `toml:"tls" json:"tls"`
} }
type ImageEncryption struct { // ImageDecryption contains configuration to handling decryption of encrypted container images.
// KeyModel specifies the model of where keys should reside 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"` KeyModel string `toml:"key_model" json:"keyModel"`
} }
@ -167,8 +174,8 @@ type PluginConfig struct {
CniConfig `toml:"cni" json:"cni"` CniConfig `toml:"cni" json:"cni"`
// Registry contains config related to the registry // Registry contains config related to the registry
Registry Registry `toml:"registry" json:"registry"` Registry Registry `toml:"registry" json:"registry"`
// ImageEncryption contains config related to handling of encrypted images // ImageDecryption contains config related to handling decryption of encrypted container images
ImageEncryption `toml:"image_encryption" json:"imageEncryption"` ImageDecryption `toml:"image_decryption" json:"imageDecryption"`
// DisableTCPService disables serving CRI on the TCP server. // DisableTCPService disables serving CRI on the TCP server.
DisableTCPService bool `toml:"disable_tcp_service" json:"disableTCPService"` DisableTCPService bool `toml:"disable_tcp_service" json:"disableTCPService"`
// StreamServerAddress is the ip address streaming server is listening on. // StreamServerAddress is the ip address streaming server is listening on.
@ -243,9 +250,9 @@ const (
RuntimeUntrusted = "untrusted" RuntimeUntrusted = "untrusted"
// RuntimeDefault is the implicit runtime defined for ContainerdConfig.DefaultRuntime // RuntimeDefault is the implicit runtime defined for ContainerdConfig.DefaultRuntime
RuntimeDefault = "default" 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 // on the worker nodes
EncryptionKeyModelNode = "node" KeyModelNode = "node"
) )
// ValidatePluginConfig validates the given plugin configuration. // ValidatePluginConfig validates the given plugin configuration.

View File

@ -411,11 +411,10 @@ func newTransport() *http.Transport {
} }
} }
// addEncryptedImagesPullOpts adds the necessary pull options to a list of // encryptedImagesPullOpts returns the necessary list of pull options required
// pull options if enabled. // for decryption of encrypted images based on the cri decryption configuration.
func (c *criService) encryptedImagesPullOpts() []containerd.RemoteOpt { func (c *criService) encryptedImagesPullOpts() []containerd.RemoteOpt {
if c.config.ImageEncryption.KeyModel == criconfig.EncryptionKeyModelNode || if c.config.ImageDecryption.KeyModel == criconfig.KeyModelNode {
c.config.ImageEncryption.KeyModel == "" {
ltdd := imgcrypt.Payload{} ltdd := imgcrypt.Payload{}
decUnpackOpt := encryption.WithUnpackConfigApplyOpts(encryption.WithDecryptedUnpack(&ltdd)) decUnpackOpt := encryption.WithUnpackConfigApplyOpts(encryption.WithDecryptedUnpack(&ltdd))
opt := containerd.WithUnpackOpts([]containerd.UnpackOpt{decUnpackOpt}) opt := containerd.WithUnpackOpts([]containerd.UnpackOpt{decUnpackOpt})

View File

@ -295,17 +295,17 @@ func TestEncryptedImagePullOpts(t *testing.T) {
expectedOpts int expectedOpts int
}{ }{
"node key model should return one unpack opt": { "node key model should return one unpack opt": {
keyModel: criconfig.EncryptionKeyModelNode, keyModel: criconfig.KeyModelNode,
expectedOpts: 1, expectedOpts: 1,
}, },
"no key model selected should default to node key model": { "no key model selected should default to node key model": {
keyModel: "", keyModel: "",
expectedOpts: 1, expectedOpts: 0,
}, },
} { } {
t.Logf("TestCase %q", desc) t.Logf("TestCase %q", desc)
c := newTestCRIService() c := newTestCRIService()
c.config.ImageEncryption.KeyModel = test.keyModel c.config.ImageDecryption.KeyModel = test.keyModel
got := len(c.encryptedImagesPullOpts()) got := len(c.encryptedImagesPullOpts())
assert.Equal(t, test.expectedOpts, got) assert.Equal(t, test.expectedOpts, got)
} }

View File

@ -66,6 +66,7 @@ type lineReader struct {
buf []byte buf []byte
eof bool eof bool
crc uint32 crc uint32
crcSet bool
} }
func (l *lineReader) Read(p []byte) (n int, err error) { 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 return 0, ArmorCorrupt
} }
if bytes.HasPrefix(line, armorEnd) {
l.eof = true
return 0, io.EOF
}
if len(line) == 5 && line[0] == '=' { if len(line) == 5 && line[0] == '=' {
// This is the checksum line // This is the checksum line
var expectedBytes [3]byte var expectedBytes [3]byte
@ -108,6 +114,7 @@ func (l *lineReader) Read(p []byte) (n int, err error) {
} }
l.eof = true l.eof = true
l.crcSet = true
return 0, io.EOF return 0, io.EOF
} }
@ -141,11 +148,9 @@ func (r *openpgpReader) Read(p []byte) (n int, err error) {
n, err = r.b64Reader.Read(p) n, err = r.b64Reader.Read(p)
r.currentCRC = crc24(r.currentCRC, p[:n]) r.currentCRC = crc24(r.currentCRC, p[:n])
if err == io.EOF { if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
if r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
return 0, ArmorCorrupt return 0, ArmorCorrupt
} }
}
return return
} }

View File

@ -76,7 +76,9 @@ func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err
// Bleichenbacher, Advances in Cryptology (Crypto '98), // Bleichenbacher, Advances in Cryptology (Crypto '98),
func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) { func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) {
s := new(big.Int).Exp(c1, priv.X, priv.P) 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.Mul(s, c2)
s.Mod(s, priv.P) s.Mod(s, priv.P)
em := s.Bytes() em := s.Bytes()

View File

@ -5,6 +5,7 @@
package packet package packet
import ( import (
"crypto"
"crypto/rsa" "crypto/rsa"
"encoding/binary" "encoding/binary"
"io" "io"
@ -78,8 +79,9 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
// padding oracle attacks. // padding oracle attacks.
switch priv.PubKeyAlgo { switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
k := priv.PrivateKey.(*rsa.PrivateKey) // Supports both *rsa.PrivateKey and crypto.Decrypter
b, err = rsa.DecryptPKCS1v15(config.Random(), k, padToKeySize(&k.PublicKey, e.encryptedMPI1.bytes)) k := priv.PrivateKey.(crypto.Decrypter)
b, err = k.Decrypt(config.Random(), padToKeySize(k.Public().(*rsa.PublicKey), e.encryptedMPI1.bytes), nil)
case PubKeyAlgoElGamal: case PubKeyAlgoElGamal:
c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes) c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes) c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes)

View File

@ -31,7 +31,7 @@ type PrivateKey struct {
encryptedData []byte encryptedData []byte
cipher CipherFunction cipher CipherFunction
s2k func(out, in []byte) 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 sha1Checksum bool
iv []byte iv []byte
} }