...

Source file src/k8s.io/kubernetes/test/integration/controlplane/transformation/secrets_transformation_test.go

Documentation: k8s.io/kubernetes/test/integration/controlplane/transformation

     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  	apiserverv1 "k8s.io/apiserver/pkg/apis/apiserver/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 apiserverv1.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 apiserverv1.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  }
   186  

View as plain text