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  }