k8s.io/kubernetes@v1.29.3/test/integration/controlplane/transformation/secrets_transformation_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package transformation 18 19 import ( 20 "context" 21 "crypto/aes" 22 "crypto/cipher" 23 "encoding/base64" 24 "fmt" 25 "testing" 26 27 apiserverconfigv1 "k8s.io/apiserver/pkg/apis/config/v1" 28 "k8s.io/apiserver/pkg/storage/value" 29 aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes" 30 ) 31 32 const ( 33 aesGCMPrefix = "k8s:enc:aesgcm:v1:key1:" 34 aesCBCPrefix = "k8s:enc:aescbc:v1:key1:" 35 36 aesGCMConfigYAML = ` 37 kind: EncryptionConfiguration 38 apiVersion: apiserver.config.k8s.io/v1 39 resources: 40 - resources: 41 - secrets 42 providers: 43 - aesgcm: 44 keys: 45 - name: key1 46 secret: c2VjcmV0IGlzIHNlY3VyZQ== 47 ` 48 49 aesCBCConfigYAML = ` 50 kind: EncryptionConfiguration 51 apiVersion: apiserver.config.k8s.io/v1 52 resources: 53 - resources: 54 - secrets 55 providers: 56 - aescbc: 57 keys: 58 - name: key1 59 secret: c2VjcmV0IGlzIHNlY3VyZQ== 60 ` 61 62 identityConfigYAML = ` 63 kind: EncryptionConfiguration 64 apiVersion: apiserver.config.k8s.io/v1 65 resources: 66 - resources: 67 - secrets 68 providers: 69 - identity: {} 70 ` 71 ) 72 73 // TestSecretsShouldBeEnveloped is an integration test between KubeAPI and etcd that checks: 74 // 1. Secrets are encrypted on write 75 // 2. Secrets are decrypted on read 76 // when EncryptionConfiguration is passed to KubeAPI server. 77 func TestSecretsShouldBeTransformed(t *testing.T) { 78 var testCases = []struct { 79 transformerConfigContent string 80 transformerPrefix string 81 unSealFunc unSealSecret 82 }{ 83 {aesGCMConfigYAML, aesGCMPrefix, unSealWithGCMTransformer}, 84 {aesCBCConfigYAML, aesCBCPrefix, unSealWithCBCTransformer}, 85 // TODO: add secretbox 86 } 87 for _, tt := range testCases { 88 test, err := newTransformTest(t, tt.transformerConfigContent, false, "", nil) 89 if err != nil { 90 t.Fatalf("failed to setup test for envelop %s, error was %v", tt.transformerPrefix, err) 91 continue 92 } 93 test.secret, err = test.createSecret(testSecret, testNamespace) 94 if err != nil { 95 t.Fatalf("Failed to create test secret, error: %v", err) 96 } 97 test.runResource(test.logger, tt.unSealFunc, tt.transformerPrefix, "", "v1", "secrets", test.secret.Name, test.secret.Namespace) 98 test.cleanUp() 99 } 100 } 101 102 // Baseline (no enveloping) - use to contrast with enveloping benchmarks. 103 func BenchmarkBase(b *testing.B) { 104 runBenchmark(b, "") 105 } 106 107 // Identity transformer is a NOOP (crypto-wise) - use to contrast with AESGCM and AESCBC benchmark results. 108 func BenchmarkIdentityWrite(b *testing.B) { 109 runBenchmark(b, identityConfigYAML) 110 } 111 112 func BenchmarkAESGCMEnvelopeWrite(b *testing.B) { 113 runBenchmark(b, aesGCMConfigYAML) 114 } 115 116 func BenchmarkAESCBCEnvelopeWrite(b *testing.B) { 117 runBenchmark(b, aesCBCConfigYAML) 118 } 119 120 func runBenchmark(b *testing.B, transformerConfig string) { 121 b.StopTimer() 122 test, err := newTransformTest(b, transformerConfig, false, "", nil) 123 if err != nil { 124 b.Fatalf("failed to setup benchmark for config %s, error was %v", transformerConfig, err) 125 } 126 defer test.cleanUp() 127 128 b.StartTimer() 129 test.benchmark(b) 130 b.StopTimer() 131 test.printMetrics() 132 } 133 134 func unSealWithGCMTransformer(ctx context.Context, cipherText []byte, dataCtx value.Context, 135 transformerConfig apiserverconfigv1.ProviderConfiguration) ([]byte, error) { 136 137 block, err := newAESCipher(transformerConfig.AESGCM.Keys[0].Secret) 138 if err != nil { 139 return nil, fmt.Errorf("failed to create block cipher: %v", err) 140 } 141 142 gcmTransformer, err := aestransformer.NewGCMTransformer(block) 143 if err != nil { 144 return nil, fmt.Errorf("failed to create transformer from block: %v", err) 145 } 146 147 clearText, _, err := gcmTransformer.TransformFromStorage(ctx, cipherText, dataCtx) 148 if err != nil { 149 return nil, fmt.Errorf("failed to decypt secret: %v", err) 150 } 151 152 return clearText, nil 153 } 154 155 func unSealWithCBCTransformer(ctx context.Context, cipherText []byte, dataCtx value.Context, 156 transformerConfig apiserverconfigv1.ProviderConfiguration) ([]byte, error) { 157 158 block, err := newAESCipher(transformerConfig.AESCBC.Keys[0].Secret) 159 if err != nil { 160 return nil, err 161 } 162 163 cbcTransformer := aestransformer.NewCBCTransformer(block) 164 165 clearText, _, err := cbcTransformer.TransformFromStorage(ctx, cipherText, dataCtx) 166 if err != nil { 167 return nil, fmt.Errorf("failed to decypt secret: %v", err) 168 } 169 170 return clearText, nil 171 } 172 173 func newAESCipher(key string) (cipher.Block, error) { 174 k, err := base64.StdEncoding.DecodeString(key) 175 if err != nil { 176 return nil, fmt.Errorf("failed to decode config secret: %v", err) 177 } 178 179 block, err := aes.NewCipher(k) 180 if err != nil { 181 return nil, fmt.Errorf("failed to create AES cipher: %v", err) 182 } 183 184 return block, nil 185 }