secrets_transformation_test.go 4.6 KB

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