k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cluster/gce/gci/apiserver_kms_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 gci 18 19 import ( 20 "bytes" 21 "encoding/base64" 22 "fmt" 23 "os" 24 "path/filepath" 25 "reflect" 26 "strconv" 27 "strings" 28 "testing" 29 30 v1 "k8s.io/api/core/v1" 31 ) 32 33 const ( 34 kubeAPIServerManifestFileName = "kube-apiserver.manifest" 35 kubeAPIServerConfigScriptName = "configure-kubeapiserver.sh" 36 kubeAPIServerStartFuncName = "start-kube-apiserver" 37 ) 38 39 type kubeAPIServerEnv struct { 40 KubeHome string 41 KubeAPIServerRunAsUser string 42 EncryptionProviderConfigPath string 43 EncryptionProviderConfig string 44 CloudKMSIntegration bool 45 } 46 47 func TestEncryptionProviderFlag(t *testing.T) { 48 var ( 49 encryptionConfigFlag = "--encryption-provider-config" 50 ) 51 52 testCases := []struct { 53 desc string 54 encryptionProviderConfig string 55 wantFlag bool 56 }{ 57 { 58 desc: "ENCRYPTION_PROVIDER_CONFIG is set", 59 encryptionProviderConfig: base64.StdEncoding.EncodeToString([]byte("foo")), 60 wantFlag: true, 61 }, 62 { 63 desc: "ENCRYPTION_PROVIDER_CONFIG is not set", 64 encryptionProviderConfig: "", 65 wantFlag: false, 66 }, 67 } 68 69 for _, tc := range testCases { 70 t.Run(tc.desc, func(t *testing.T) { 71 c := newManifestTestCase(t, kubeAPIServerManifestFileName, kubeAPIServerStartFuncName, nil) 72 defer c.tearDown() 73 74 e := kubeAPIServerEnv{ 75 KubeHome: c.kubeHome, 76 KubeAPIServerRunAsUser: strconv.Itoa(os.Getuid()), 77 EncryptionProviderConfigPath: filepath.Join(c.kubeHome, "encryption-provider-config.yaml"), 78 EncryptionProviderConfig: tc.encryptionProviderConfig, 79 } 80 81 c.mustInvokeFunc( 82 e, 83 []string{"configure-helper.sh", kubeAPIServerConfigScriptName}, 84 "kms.template", 85 "testdata/kube-apiserver/base.template", 86 "testdata/kube-apiserver/kms.template") 87 c.mustLoadPodFromManifest() 88 89 execArgs := strings.Join(c.pod.Spec.Containers[0].Command, " ") 90 flagIsInArg := strings.Contains(execArgs, encryptionConfigFlag) 91 flag := fmt.Sprintf("%s=%s", encryptionConfigFlag, e.EncryptionProviderConfigPath) 92 93 switch { 94 case tc.wantFlag && !flagIsInArg: 95 t.Fatalf("Got %q,\n want flags to contain %q", execArgs, flag) 96 case !tc.wantFlag && flagIsInArg: 97 t.Fatalf("Got %q,\n do not want flags to contain %q", execArgs, encryptionConfigFlag) 98 case tc.wantFlag && flagIsInArg && !strings.Contains(execArgs, flag): 99 t.Fatalf("Got flags: %q, want it to contain %q", execArgs, flag) 100 } 101 }) 102 } 103 } 104 105 func TestEncryptionProviderConfig(t *testing.T) { 106 c := newManifestTestCase(t, kubeAPIServerManifestFileName, kubeAPIServerStartFuncName, nil) 107 defer c.tearDown() 108 109 p := filepath.Join(c.kubeHome, "encryption-provider-config.yaml") 110 e := kubeAPIServerEnv{ 111 KubeHome: c.kubeHome, 112 KubeAPIServerRunAsUser: strconv.Itoa(os.Getuid()), 113 EncryptionProviderConfigPath: p, 114 EncryptionProviderConfig: base64.StdEncoding.EncodeToString([]byte("foo")), 115 } 116 117 c.mustInvokeFunc( 118 e, 119 []string{"configure-helper.sh", kubeAPIServerConfigScriptName}, 120 "kms.template", 121 122 "testdata/kube-apiserver/base.template", 123 "testdata/kube-apiserver/kms.template", 124 ) 125 126 if _, err := os.Stat(p); err != nil { 127 c.t.Fatalf("Expected encryption provider config to be written to %s, but stat failed with error: %v", p, err) 128 } 129 130 got, err := os.ReadFile(p) 131 if err != nil { 132 c.t.Fatalf("Failed to read encryption provider config %s", p) 133 } 134 135 want := []byte("foo") 136 if !bytes.Equal(got, want) { 137 c.t.Fatalf("got encryptionConfig:\n%q\n, want encryptionConfig:\n%q", got, want) 138 } 139 } 140 141 func TestKMSIntegration(t *testing.T) { 142 var ( 143 socketPath = "/var/run/kmsplugin" 144 dirOrCreate = v1.HostPathType(v1.HostPathDirectoryOrCreate) 145 socketName = "kmssocket" 146 ) 147 testCases := []struct { 148 desc string 149 cloudKMSIntegration bool 150 wantVolume v1.Volume 151 wantVolMount v1.VolumeMount 152 }{ 153 { 154 desc: "CLOUD_KMS_INTEGRATION is set", 155 cloudKMSIntegration: true, 156 wantVolume: v1.Volume{ 157 Name: socketName, 158 VolumeSource: v1.VolumeSource{ 159 HostPath: &v1.HostPathVolumeSource{ 160 Path: socketPath, 161 Type: &dirOrCreate, 162 }, 163 }, 164 }, 165 wantVolMount: v1.VolumeMount{ 166 Name: socketName, 167 MountPath: socketPath, 168 }, 169 }, 170 { 171 desc: "CLOUD_KMS_INTEGRATION is not set", 172 cloudKMSIntegration: false, 173 }, 174 } 175 176 for _, tc := range testCases { 177 t.Run(tc.desc, func(t *testing.T) { 178 c := newManifestTestCase(t, kubeAPIServerManifestFileName, kubeAPIServerStartFuncName, nil) 179 defer c.tearDown() 180 181 var e = kubeAPIServerEnv{ 182 KubeHome: c.kubeHome, 183 KubeAPIServerRunAsUser: strconv.Itoa(os.Getuid()), 184 EncryptionProviderConfigPath: filepath.Join(c.kubeHome, "encryption-provider-config.yaml"), 185 EncryptionProviderConfig: base64.StdEncoding.EncodeToString([]byte("foo")), 186 CloudKMSIntegration: tc.cloudKMSIntegration, 187 } 188 189 c.mustInvokeFunc( 190 e, 191 []string{"configure-helper.sh", kubeAPIServerConfigScriptName}, 192 "kms.template", 193 194 "testdata/kube-apiserver/base.template", 195 "testdata/kube-apiserver/kms.template", 196 ) 197 c.mustLoadPodFromManifest() 198 // By this point, we can be sure that kube-apiserver manifest is a valid POD. 199 200 var gotVolume v1.Volume 201 for _, v := range c.pod.Spec.Volumes { 202 if v.Name == socketName { 203 gotVolume = v 204 break 205 } 206 } 207 208 if !reflect.DeepEqual(gotVolume, tc.wantVolume) { 209 t.Errorf("got volume %v, want %v", gotVolume, tc.wantVolume) 210 } 211 212 var gotVolumeMount v1.VolumeMount 213 for _, v := range c.pod.Spec.Containers[0].VolumeMounts { 214 if v.Name == socketName { 215 gotVolumeMount = v 216 break 217 } 218 } 219 220 if !reflect.DeepEqual(gotVolumeMount, tc.wantVolMount) { 221 t.Errorf("got volumeMount %v, want %v", gotVolumeMount, tc.wantVolMount) 222 } 223 }) 224 } 225 }