diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/BUILD b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/BUILD index de403c56180..ce6df23f97a 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/BUILD @@ -21,6 +21,7 @@ go_library( "//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/aes:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/identity:go_default_library", + "//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/secretbox:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go index 4986e3c1b05..bb34dd5846a 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go @@ -18,6 +18,7 @@ package encryptionconfig import ( "crypto/aes" + "crypto/cipher" "encoding/base64" "fmt" "io" @@ -30,10 +31,13 @@ import ( "k8s.io/apiserver/pkg/storage/value" aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes" "k8s.io/apiserver/pkg/storage/value/encrypt/identity" + "k8s.io/apiserver/pkg/storage/value/encrypt/secretbox" ) const ( - aesTransformerPrefixV1 = "k8s:enc:aes:v1:" + aesCBCTransformerPrefixV1 = "k8s:enc:aescbc:v1:" + aesGCMTransformerPrefixV1 = "k8s:enc:aesgcm:v1:" + secretboxTransformerPrefixV1 = "k8s:enc:secretbox:v1" ) // GetTransformerOverrides returns the transformer overrides by reading and parsing the encryption provider configuration file @@ -102,26 +106,49 @@ func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, e for _, provider := range config.Providers { found := false - if provider.AES != nil { - transformer, err := GetAESPrefixTransformer(provider.AES) - found = true + var transformer value.PrefixTransformer + var err error + + if provider.AESGCM != nil { + transformer, err = GetAESPrefixTransformer(provider.AESGCM, aestransformer.NewGCMTransformer, aesGCMTransformerPrefixV1) if err != nil { return result, err } - result = append(result, transformer) + found = true + } + + if provider.AESCBC != nil { + if found == true { + return result, fmt.Errorf("more than one provider specified in a single element, should split into different list elements") + } + transformer, err = GetAESPrefixTransformer(provider.AESCBC, aestransformer.NewCBCTransformer, aesCBCTransformerPrefixV1) + found = true + } + + if provider.Secretbox != nil { + if found == true { + return result, fmt.Errorf("more than one provider specified in a single element, should split into different list elements") + } + transformer, err = GetSecretboxPrefixTransformer(provider.Secretbox) + found = true } if provider.Identity != nil { if found == true { return result, fmt.Errorf("more than one provider specified in a single element, should split into different list elements") } - found = true - result = append(result, value.PrefixTransformer{ + transformer = value.PrefixTransformer{ Transformer: identity.NewEncryptCheckTransformer(), Prefix: []byte{}, - }) + } + found = true } + if err != nil { + return result, err + } + result = append(result, transformer) + if found == false { return result, fmt.Errorf("invalid provider configuration provided") } @@ -129,8 +156,12 @@ func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, e return result, nil } -// GetAESPrefixTransformer returns a prefix transformer from the provided configuration -func GetAESPrefixTransformer(config *AESConfig) (value.PrefixTransformer, error) { +// BlockTransformerFunc taske an AES cipher block and returns a value transformer. +type BlockTransformerFunc func(cipher.Block) value.Transformer + +// GetAESPrefixTransformer returns a prefix transformer from the provided configuration. +// Returns an AES transformer based on the provided prefix and block transformer. +func GetAESPrefixTransformer(config *AESConfig, fn BlockTransformerFunc, prefix string) (value.PrefixTransformer, error) { var result value.PrefixTransformer if len(config.Keys) == 0 { @@ -160,7 +191,7 @@ func GetAESPrefixTransformer(config *AESConfig) (value.PrefixTransformer, error) // Create a new PrefixTransformer for this key keyTransformers = append(keyTransformers, value.PrefixTransformer{ - Transformer: aestransformer.NewGCMTransformer(block), + Transformer: fn(block), Prefix: []byte(keyData.Name + ":"), }) } @@ -172,7 +203,58 @@ func GetAESPrefixTransformer(config *AESConfig) (value.PrefixTransformer, error) // Create a PrefixTransformer which shall later be put in a list with other providers result = value.PrefixTransformer{ Transformer: keyTransformer, - Prefix: []byte(aesTransformerPrefixV1), + Prefix: []byte(prefix), + } + return result, nil +} + +// GetSecretboxPrefixTransformer returns a prefix transformer from the provided configuration +func GetSecretboxPrefixTransformer(config *SecretboxConfig) (value.PrefixTransformer, error) { + var result value.PrefixTransformer + + if len(config.Keys) == 0 { + return result, fmt.Errorf("secretbox provider has no valid keys") + } + for _, key := range config.Keys { + if key.Name == "" { + return result, fmt.Errorf("key with invalid name provided") + } + if key.Secret == "" { + return result, fmt.Errorf("key %v has no provided secret", key.Name) + } + } + + keyTransformers := []value.PrefixTransformer{} + + for _, keyData := range config.Keys { + key, err := base64.StdEncoding.DecodeString(keyData.Secret) + if err != nil { + return result, fmt.Errorf("could not obtain secret for named key %s: %s", keyData.Name, err) + } + + if len(key) != 32 { + return result, fmt.Errorf("expected key size 32 for aes-cbc provider, got %v", len(key)) + } + + keyArray := [32]byte{} + copy(keyArray[:], key) + + // Create a new PrefixTransformer for this key + keyTransformers = append(keyTransformers, + value.PrefixTransformer{ + Transformer: secretbox.NewSecretboxTransformer(keyArray), + Prefix: []byte(keyData.Name + ":"), + }) + } + + // Create a prefixTransformer which can choose between these keys + keyTransformer := value.NewPrefixTransformers( + fmt.Errorf("no matching key was found for the provided Secretbox transformer"), keyTransformers...) + + // Create a PrefixTransformer which shall later be put in a list with other providers + result = value.PrefixTransformer{ + Transformer: keyTransformer, + Prefix: []byte(secretboxTransformerPrefixV1), } return result, nil } diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/encryptionconfig_test.go b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/encryptionconfig_test.go index ae7f10c140c..7ffa32fdeca 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/encryptionconfig_test.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/encryptionconfig_test.go @@ -39,7 +39,69 @@ resources: - namespaces providers: - identity: {} - - aes: + - aesgcm: + keys: + - name: key1 + secret: c2VjcmV0IGlzIHNlY3VyZQ== + - name: key2 + secret: dGhpcyBpcyBwYXNzd29yZA== + - aescbc: + keys: + - name: key1 + secret: c2VjcmV0IGlzIHNlY3VyZQ== + - name: key2 + secret: dGhpcyBpcyBwYXNzd29yZA== + - secretbox: + keys: + - name: key1 + secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY= +` + + correctConfigWithAesGcmFirst = ` +kind: EncryptionConfig +apiVersion: v1 +resources: + - resources: + - secrets + providers: + - aesgcm: + keys: + - name: key1 + secret: c2VjcmV0IGlzIHNlY3VyZQ== + - name: key2 + secret: dGhpcyBpcyBwYXNzd29yZA== + - secretbox: + keys: + - name: key1 + secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY= + - aescbc: + keys: + - name: key1 + secret: c2VjcmV0IGlzIHNlY3VyZQ== + - name: key2 + secret: dGhpcyBpcyBwYXNzd29yZA== + - identity: {} +` + + correctConfigWithAesCbcFirst = ` +kind: EncryptionConfig +apiVersion: v1 +resources: + - resources: + - secrets + providers: + - aescbc: + keys: + - name: key1 + secret: c2VjcmV0IGlzIHNlY3VyZQ== + - name: key2 + secret: dGhpcyBpcyBwYXNzd29yZA== + - identity: {} + - secretbox: + keys: + - name: key1 + secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY= + - aesgcm: keys: - name: key1 secret: c2VjcmV0IGlzIHNlY3VyZQ== @@ -47,20 +109,30 @@ resources: secret: dGhpcyBpcyBwYXNzd29yZA== ` - correctConfigWithAesFirst = ` + correctConfigWithSecretboxFirst = ` kind: EncryptionConfig apiVersion: v1 resources: - resources: - secrets providers: - - aes: + - secretbox: + keys: + - name: key1 + secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY= + - aescbc: keys: - name: key1 secret: c2VjcmV0IGlzIHNlY3VyZQ== - name: key2 secret: dGhpcyBpcyBwYXNzd29yZA== - identity: {} + - aesgcm: + keys: + - name: key1 + secret: c2VjcmV0IGlzIHNlY3VyZQ== + - name: key2 + secret: dGhpcyBpcyBwYXNzd29yZA== ` incorrectConfigNoSecretForKey = ` @@ -71,7 +143,7 @@ resources: - namespaces - secrets providers: - - aes: + - aesgcm: keys: - name: key1 ` @@ -84,7 +156,7 @@ resources: - namespaces - secrets providers: - - aes: + - aesgcm: keys: - name: key1 secret: c2VjcmV0IGlzIHNlY3VyZQ== @@ -103,58 +175,60 @@ func TestEncryptionProviderConfigCorrect(t *testing.T) { t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithIdentityFirst) } - aesFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithAesFirst)) + aesGcmFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithAesGcmFirst)) if err != nil { - t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithAesFirst) + t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithAesGcmFirst) + } + + aesCbcFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithAesCbcFirst)) + if err != nil { + t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithAesCbcFirst) + } + + secretboxFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithSecretboxFirst)) + if err != nil { + t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithSecretboxFirst) } // Pick the transformer for any of the returned resources. identityFirstTransformer := identityFirstTransformerOverrides[schema.ParseGroupResource("secrets")] - aesFirstTransformer := aesFirstTransformerOverrides[schema.ParseGroupResource("secrets")] + aesGcmFirstTransformer := aesGcmFirstTransformerOverrides[schema.ParseGroupResource("secrets")] + aesCbcFirstTransformer := aesCbcFirstTransformerOverrides[schema.ParseGroupResource("secrets")] + secretboxFirstTransformer := secretboxFirstTransformerOverrides[schema.ParseGroupResource("secrets")] context := value.DefaultContext([]byte(sampleContextText)) originalText := []byte(sampleText) - testCases := []struct { - WritingTransformer value.Transformer - Name string - AesStale bool - IdentityStale bool + transformers := []struct { + Transformer value.Transformer + Name string }{ - {aesFirstTransformer, "aesFirst", false, true}, - {identityFirstTransformer, "identityFirst", true, false}, + {aesGcmFirstTransformer, "aesGcmFirst"}, + {aesCbcFirstTransformer, "aesCbcFirst"}, + {secretboxFirstTransformer, "secretboxFirst"}, + {identityFirstTransformer, "identityFirst"}, } - for _, testCase := range testCases { - transformedData, err := testCase.WritingTransformer.TransformToStorage(originalText, context) + for _, testCase := range transformers { + transformedData, err := testCase.Transformer.TransformToStorage(originalText, context) if err != nil { t.Fatalf("%s: error while transforming data to storage: %s", testCase.Name, err) } - aesUntransformedData, stale, err := aesFirstTransformer.TransformFromStorage(transformedData, context) - if err != nil { - t.Fatalf("%s: error while reading using aesFirst transformer: %s", testCase.Name, err) - } - if stale != testCase.AesStale { - t.Fatalf("%s: wrong stale information on reading using aesFirst transformer, should be %v", testCase.Name, testCase.AesStale) - } - - identityUntransformedData, stale, err := identityFirstTransformer.TransformFromStorage(transformedData, context) - if err != nil { - t.Fatalf("%s: error while reading using identityFirst transformer: %s", testCase.Name, err) - } - if stale != testCase.IdentityStale { - t.Fatalf("%s: wrong stale information on reading using identityFirst transformer, should be %v", testCase.Name, testCase.IdentityStale) - } - - if bytes.Compare(aesUntransformedData, originalText) != 0 { - t.Fatalf("%s: aesFirst transformer transformed data incorrectly. Expected: %v, got %v", testCase.Name, originalText, aesUntransformedData) - } - - if bytes.Compare(identityUntransformedData, originalText) != 0 { - t.Fatalf("%s: identityFirst transformer transformed data incorrectly. Expected: %v, got %v", testCase.Name, originalText, aesUntransformedData) + for _, transformer := range transformers { + untransformedData, stale, err := transformer.Transformer.TransformFromStorage(transformedData, context) + if err != nil { + t.Fatalf("%s: error while reading using %s transformer: %s", testCase.Name, transformer.Name, err) + } + if stale != (transformer.Name != testCase.Name) { + t.Fatalf("%s: wrong stale information on reading using %s transformer, should be %v", testCase.Name, transformer.Name, testCase.Name == transformer.Name) + } + if bytes.Compare(untransformedData, originalText) != 0 { + t.Fatalf("%s: %s transformer transformed data incorrectly. Expected: %v, got %v", testCase.Name, transformer.Name, originalText, untransformedData) + } } } + } // Throw error if key has no secret diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/types.go b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/types.go index ea23ac40539..2e19261ec20 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/types.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/types.go @@ -37,8 +37,12 @@ type ResourceConfig struct { // ProviderConfig stores the provided configuration for an encryption provider. type ProviderConfig struct { - // aes is the configuration for the AEAD-GCM transformer. - AES *AESConfig `json:"aes,omitempty"` + // aesgcm is the configuration for the AES-GCM transformer. + AESGCM *AESConfig `json:"aesgcm,omitempty"` + // aescbc is the configuration for the AES-CBC transformer. + AESCBC *AESConfig `json:"aescbc,omitempty"` + // secretbox is the configuration for the Secretbox based transformer. + Secretbox *SecretboxConfig `json:"secretbox,omitempty"` // identity is the (empty) configuration for the identity transformer. Identity *IdentityConfig `json:"identity,omitempty"` } @@ -49,6 +53,12 @@ type AESConfig struct { Keys []Key `json:"keys"` } +// SECRETBOXConfig contains the API configuration for an Secretbox transformer. +type SecretboxConfig struct { + // keys is a list of keys to be used for creating the Secretbox transformer. + Keys []Key `json:"keys"` +} + // Key contains name and secret of the provided key for AES transformer. type Key struct { // name is the name of the key to be used while storing data to disk.