k8s.io/kubernetes@v1.29.3/test/integration/apiserver/certreload/certreload_test.go (about) 1 /* 2 Copyright 2019 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 podlogs 18 19 import ( 20 "bytes" 21 "context" 22 "crypto/rand" 23 "crypto/rsa" 24 "crypto/x509" 25 "crypto/x509/pkix" 26 "encoding/pem" 27 "fmt" 28 "math/big" 29 "os" 30 "path" 31 "strings" 32 "testing" 33 "time" 34 35 authorizationv1 "k8s.io/api/authorization/v1" 36 apierrors "k8s.io/apimachinery/pkg/api/errors" 37 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 "k8s.io/apimachinery/pkg/util/wait" 39 "k8s.io/client-go/kubernetes" 40 "k8s.io/client-go/kubernetes/scheme" 41 "k8s.io/client-go/rest" 42 "k8s.io/client-go/util/cert" 43 "k8s.io/component-base/cli/flag" 44 "k8s.io/kubernetes/cmd/kube-apiserver/app/options" 45 "k8s.io/kubernetes/test/integration/framework" 46 "k8s.io/kubernetes/test/utils/ktesting" 47 ) 48 49 type caWithClient struct { 50 CACert []byte 51 ClientCert []byte 52 ClientKey []byte 53 } 54 55 func newTestCAWithClient(caSubject pkix.Name, caSerial *big.Int, clientSubject pkix.Name, subjectSerial *big.Int) (*caWithClient, error) { 56 ca := &x509.Certificate{ 57 SerialNumber: caSerial, 58 Subject: caSubject, 59 NotBefore: time.Now(), 60 NotAfter: time.Now().Add(24 * time.Hour), 61 IsCA: true, 62 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, 63 KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 64 BasicConstraintsValid: true, 65 } 66 67 caPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096) 68 if err != nil { 69 return nil, err 70 } 71 72 caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivateKey.PublicKey, caPrivateKey) 73 if err != nil { 74 return nil, err 75 } 76 77 caPEM := new(bytes.Buffer) 78 err = pem.Encode(caPEM, &pem.Block{ 79 Type: "CERTIFICATE", 80 Bytes: caBytes, 81 }) 82 if err != nil { 83 return nil, err 84 } 85 86 clientCert := &x509.Certificate{ 87 SerialNumber: subjectSerial, 88 Subject: clientSubject, 89 NotBefore: time.Now(), 90 NotAfter: time.Now().Add(24 * time.Hour), 91 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, 92 KeyUsage: x509.KeyUsageDigitalSignature, 93 } 94 95 clientCertPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096) 96 if err != nil { 97 return nil, err 98 } 99 100 clientCertPrivateKeyPEM := new(bytes.Buffer) 101 err = pem.Encode(clientCertPrivateKeyPEM, &pem.Block{ 102 Type: "RSA PRIVATE KEY", 103 Bytes: x509.MarshalPKCS1PrivateKey(clientCertPrivateKey), 104 }) 105 if err != nil { 106 return nil, err 107 } 108 109 clientCertBytes, err := x509.CreateCertificate(rand.Reader, clientCert, ca, &clientCertPrivateKey.PublicKey, caPrivateKey) 110 if err != nil { 111 return nil, err 112 } 113 114 clientCertPEM := new(bytes.Buffer) 115 err = pem.Encode(clientCertPEM, &pem.Block{ 116 Type: "CERTIFICATE", 117 Bytes: clientCertBytes, 118 }) 119 if err != nil { 120 return nil, err 121 } 122 123 return &caWithClient{ 124 CACert: caPEM.Bytes(), 125 ClientCert: clientCertPEM.Bytes(), 126 ClientKey: clientCertPrivateKeyPEM.Bytes(), 127 }, nil 128 } 129 130 func TestClientCAUpdate(t *testing.T) { 131 testClientCA(t, false) 132 } 133 134 func TestClientCARecreate(t *testing.T) { 135 testClientCA(t, true) 136 } 137 138 func testClientCA(t *testing.T, recreate bool) { 139 _, ctx := ktesting.NewTestContext(t) 140 ctx, cancel := context.WithCancel(ctx) 141 defer cancel() 142 143 frontProxyCA, err := newTestCAWithClient( 144 pkix.Name{ 145 CommonName: "test-front-proxy-ca", 146 }, 147 big.NewInt(43), 148 pkix.Name{ 149 CommonName: "test-aggregated-apiserver", 150 Organization: []string{"system:masters"}, 151 }, 152 big.NewInt(86), 153 ) 154 if err != nil { 155 t.Error(err) 156 return 157 } 158 159 clientCA, err := newTestCAWithClient( 160 pkix.Name{ 161 CommonName: "test-client-ca", 162 }, 163 big.NewInt(42), 164 pkix.Name{ 165 CommonName: "system:admin", 166 Organization: []string{"system:masters"}, 167 }, 168 big.NewInt(84), 169 ) 170 if err != nil { 171 t.Error(err) 172 return 173 } 174 175 clientCAFilename := "" 176 frontProxyCAFilename := "" 177 178 kubeClient, kubeconfig, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{ 179 ModifyServerRunOptions: func(opts *options.ServerRunOptions) { 180 opts.GenericServerRunOptions.MaxRequestBodyBytes = 1024 * 1024 181 clientCAFilename = opts.Authentication.ClientCert.ClientCA 182 frontProxyCAFilename = opts.Authentication.RequestHeader.ClientCAFile 183 opts.Authentication.RequestHeader.AllowedNames = append(opts.Authentication.RequestHeader.AllowedNames, "test-aggregated-apiserver") 184 }, 185 }) 186 defer tearDownFn() 187 188 // wait for request header info 189 err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "requestheader-client-ca-file", "-----BEGIN CERTIFICATE-----", 1)) 190 if err != nil { 191 t.Fatal(err) 192 } 193 // wait for client cert info 194 err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "client-ca-file", "-----BEGIN CERTIFICATE-----", 1)) 195 if err != nil { 196 t.Fatal(err) 197 } 198 199 if recreate { 200 if err := os.Remove(path.Join(clientCAFilename)); err != nil { 201 t.Fatal(err) 202 } 203 if err := os.Remove(path.Join(frontProxyCAFilename)); err != nil { 204 t.Fatal(err) 205 } 206 } 207 208 // when we run this the second time, we know which one we are expecting 209 if err := os.WriteFile(clientCAFilename, clientCA.CACert, 0644); err != nil { 210 t.Fatal(err) 211 } 212 if err := os.WriteFile(frontProxyCAFilename, frontProxyCA.CACert, 0644); err != nil { 213 t.Fatal(err) 214 } 215 216 time.Sleep(4 * time.Second) 217 218 acceptableCAs, err := cert.GetClientCANamesForURL(kubeconfig.Host) 219 if err != nil { 220 t.Fatal(err) 221 } 222 223 expectedCAs := []string{"test-client-ca", "test-front-proxy-ca"} 224 if len(expectedCAs) != len(acceptableCAs) { 225 t.Fatal(strings.Join(acceptableCAs, ":")) 226 } 227 for i := range expectedCAs { 228 if !strings.Contains(acceptableCAs[i], expectedCAs[i]) { 229 t.Errorf("expected %q, got %q", expectedCAs[i], acceptableCAs[i]) 230 } 231 } 232 233 // wait for updated request header info that contains both 234 err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "requestheader-client-ca-file", "-----BEGIN CERTIFICATE-----", 2)) 235 if err != nil { 236 t.Error(err) 237 } 238 err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "requestheader-client-ca-file", string(frontProxyCA.CACert), 1)) 239 if err != nil { 240 t.Error(err) 241 } 242 // wait for updated client cert info that contains both 243 err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "client-ca-file", "-----BEGIN CERTIFICATE-----", 2)) 244 if err != nil { 245 t.Error(err) 246 } 247 err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "client-ca-file", string(clientCA.CACert), 1)) 248 if err != nil { 249 t.Error(err) 250 } 251 252 // Test an aggregated apiserver client (signed by the new front proxy CA) is authorized 253 extensionApiserverClient, err := kubernetes.NewForConfig(&rest.Config{ 254 Host: kubeconfig.Host, 255 TLSClientConfig: rest.TLSClientConfig{ 256 CAData: kubeconfig.TLSClientConfig.CAData, 257 CAFile: kubeconfig.TLSClientConfig.CAFile, 258 ServerName: kubeconfig.TLSClientConfig.ServerName, 259 KeyData: frontProxyCA.ClientKey, 260 CertData: frontProxyCA.ClientCert, 261 }, 262 }) 263 if err != nil { 264 t.Error(err) 265 return 266 } 267 268 // Call an endpoint to make sure we are authenticated 269 err = extensionApiserverClient.AuthorizationV1().RESTClient(). 270 Post(). 271 Resource("subjectaccessreviews"). 272 VersionedParams(&metav1.CreateOptions{}, scheme.ParameterCodec). 273 Body(&authorizationv1.SubjectAccessReview{ 274 Spec: authorizationv1.SubjectAccessReviewSpec{ 275 ResourceAttributes: &authorizationv1.ResourceAttributes{ 276 Verb: "create", 277 Resource: "pods", 278 Namespace: "default", 279 }, 280 User: "deads2k", 281 }, 282 }). 283 SetHeader("X-Remote-User", "test-aggregated-apiserver"). 284 SetHeader("X-Remote-Group", "system:masters"). 285 Do(context.Background()). 286 Into(&authorizationv1.SubjectAccessReview{}) 287 if err != nil { 288 t.Error(err) 289 } 290 291 // Test a client signed by the new ClientCA is authorized 292 testClient, err := kubernetes.NewForConfig(&rest.Config{ 293 Host: kubeconfig.Host, 294 TLSClientConfig: rest.TLSClientConfig{ 295 CAData: kubeconfig.TLSClientConfig.CAData, 296 CAFile: kubeconfig.TLSClientConfig.CAFile, 297 ServerName: kubeconfig.TLSClientConfig.ServerName, 298 KeyData: clientCA.ClientKey, 299 CertData: clientCA.ClientCert, 300 }, 301 }) 302 if err != nil { 303 t.Error(err) 304 return 305 } 306 307 // Call an endpoint to make sure we are authenticated 308 _, err = testClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) 309 if err != nil { 310 t.Error(err) 311 } 312 } 313 314 func waitForConfigMapCAContent(t *testing.T, kubeClient kubernetes.Interface, key, content string, count int) func() (bool, error) { 315 return func() (bool, error) { 316 clusterAuthInfo, err := kubeClient.CoreV1().ConfigMaps("kube-system").Get(context.TODO(), "extension-apiserver-authentication", metav1.GetOptions{}) 317 if apierrors.IsNotFound(err) { 318 return false, nil 319 } 320 if err != nil { 321 return false, err 322 } 323 324 ca := clusterAuthInfo.Data[key] 325 if strings.Count(ca, content) == count { 326 return true, nil 327 } 328 t.Log(ca) 329 return false, nil 330 } 331 } 332 333 var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY----- 334 MIIEowIBAAKCAQEA13f50PPWuR/InxLIoJjHdNSG+jVUd25CY7ZL2J023X2BAY+1 335 M6jkLR6C2nSFZnn58ubiB74/d1g/Fg1Twd419iR615A013f+qOoyFx3LFHxU1S6e 336 v22fgJ6ntK/+4QD5MwNgOwD8k1jN2WxHqNWn16IF4Tidbv8M9A35YHAdtYDYaOJC 337 kzjVztzRw1y6bKRakpMXxHylQyWmAKDJ2GSbRTbGtjr7Ji54WBfG43k94tO5X8K4 338 VGbz/uxrKe1IFMHNOlrjR438dbOXusksx9EIqDA9a42J3qjr5NKSqzCIbgBFl6qu 339 45V3A7cdRI/sJ2G1aqlWIXh2fAQiaFQAEBrPfwIDAQABAoIBAAZbxgWCjJ2d8H+x 340 QDZtC8XI18redAWqPU9P++ECkrHqmDoBkalanJEwS1BDDATAKL4gTh9IX/sXoZT3 341 A7e+5PzEitN9r/GD2wIFF0FTYcDTAnXgEFM52vEivXQ5lV3yd2gn+1kCaHG4typp 342 ZZv34iIc5+uDjjHOWQWCvA86f8XxX5EfYH+GkjfixTtN2xhWWlfi9vzYeESS4Jbt 343 tqfH0iEaZ1Bm/qvb8vFgKiuSTOoSpaf+ojAdtPtXDjf1bBtQQG+RSQkP59O/taLM 344 FCVuRrU8EtdB0+9anwmAP+O2UqjL5izA578lQtdIh13jHtGEgOcnfGNUphK11y9r 345 Mg5V28ECgYEA9fwI6Xy1Rb9b9irp4bU5Ec99QXa4x2bxld5cDdNOZWJQu9OnaIbg 346 kw/1SyUkZZCGMmibM/BiWGKWoDf8E+rn/ujGOtd70sR9U0A94XMPqEv7iHxhpZmD 347 rZuSz4/snYbOWCZQYXFoD/nqOwE7Atnz7yh+Jti0qxBQ9bmkb9o0QW8CgYEA4D3d 348 okzodg5QQ1y9L0J6jIC6YysoDedveYZMd4Un9bKlZEJev4OwiT4xXmSGBYq/7dzo 349 OJOvN6qgPfibr27mSB8NkAk6jL/VdJf3thWxNYmjF4E3paLJ24X31aSipN1Ta6K3 350 KKQUQRvixVoI1q+8WHAubBDEqvFnNYRHD+AjKvECgYBkekjhpvEcxme4DBtw+OeQ 351 4OJXJTmhKemwwB12AERboWc88d3GEqIVMEWQJmHRotFOMfCDrMNfOxYv5+5t7FxL 352 gaXHT1Hi7CQNJ4afWrKgmjjqrXPtguGIvq2fXzjVt8T9uNjIlNxe+kS1SXFjXsgH 353 ftDY6VgTMB0B4ozKq6UAvQKBgQDER8K5buJHe+3rmMCMHn+Qfpkndr4ftYXQ9Kn4 354 MFiy6sV0hdfTgRzEdOjXu9vH/BRVy3iFFVhYvIR42iTEIal2VaAUhM94Je5cmSyd 355 eE1eFHTqfRPNazmPaqttmSc4cfa0D4CNFVoZR6RupIl6Cect7jvkIaVUD+wMXxWo 356 osOFsQKBgDLwVhZWoQ13RV/jfQxS3veBUnHJwQJ7gKlL1XZ16mpfEOOVnJF7Es8j 357 TIIXXYhgSy/XshUbsgXQ+YGliye/rXSCTXHBXvWShOqxEMgeMYMRkcm8ZLp/DH7C 358 kC2pemkLPUJqgSh1PASGcJbDJIvFGUfP69tUCYpHpk3nHzexuAg3 359 -----END RSA PRIVATE KEY-----`) 360 361 var serverCert = []byte(`-----BEGIN CERTIFICATE----- 362 MIIDQDCCAiigAwIBAgIJANWw74P5KJk2MA0GCSqGSIb3DQEBCwUAMDQxMjAwBgNV 363 BAMMKWdlbmVyaWNfd2ViaG9va19hZG1pc3Npb25fcGx1Z2luX3Rlc3RzX2NhMCAX 364 DTE3MTExNjAwMDUzOVoYDzIyOTEwOTAxMDAwNTM5WjAjMSEwHwYDVQQDExh3ZWJo 365 b29rLXRlc3QuZGVmYXVsdC5zdmMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 366 AoIBAQDXd/nQ89a5H8ifEsigmMd01Ib6NVR3bkJjtkvYnTbdfYEBj7UzqOQtHoLa 367 dIVmefny5uIHvj93WD8WDVPB3jX2JHrXkDTXd/6o6jIXHcsUfFTVLp6/bZ+Anqe0 368 r/7hAPkzA2A7APyTWM3ZbEeo1afXogXhOJ1u/wz0DflgcB21gNho4kKTONXO3NHD 369 XLpspFqSkxfEfKVDJaYAoMnYZJtFNsa2OvsmLnhYF8bjeT3i07lfwrhUZvP+7Gsp 370 7UgUwc06WuNHjfx1s5e6ySzH0QioMD1rjYneqOvk0pKrMIhuAEWXqq7jlXcDtx1E 371 j+wnYbVqqVYheHZ8BCJoVAAQGs9/AgMBAAGjZDBiMAkGA1UdEwQCMAAwCwYDVR0P 372 BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATApBgNVHREEIjAg 373 hwR/AAABghh3ZWJob29rLXRlc3QuZGVmYXVsdC5zdmMwDQYJKoZIhvcNAQELBQAD 374 ggEBAD/GKSPNyQuAOw/jsYZesb+RMedbkzs18sSwlxAJQMUrrXwlVdHrA8q5WhE6 375 ABLqU1b8lQ8AWun07R8k5tqTmNvCARrAPRUqls/ryER+3Y9YEcxEaTc3jKNZFLbc 376 T6YtcnkdhxsiO136wtiuatpYL91RgCmuSpR8+7jEHhuFU01iaASu7ypFrUzrKHTF 377 bKwiLRQi1cMzVcLErq5CDEKiKhUkoDucyARFszrGt9vNIl/YCcBOkcNvM3c05Hn3 378 M++C29JwS3Hwbubg6WO3wjFjoEhpCwU6qRYUz3MRp4tHO4kxKXx+oQnUiFnR7vW0 379 YkNtGc1RUDHwecCTFpJtPb7Yu/E= 380 -----END CERTIFICATE-----`) 381 382 var anotherServerKey = []byte(`-----BEGIN RSA PRIVATE KEY----- 383 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 432 -----END RSA PRIVATE KEY----- 433 `) 434 435 var anotherServerCert = []byte(`-----BEGIN CERTIFICATE----- 436 MIIFJjCCAw6gAwIBAgIJAOcEAbv8NslfMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV 437 BAYTAlVTMQswCQYDVQQIDAJDQTETMBEGA1UECgwKQWNtZSwgSW5jLjEPMA0GA1UE 438 AwwGc29tZUNBMCAXDTE4MDYwODEzMzkyNFoYDzIyMTgwNDIxMTMzOTI0WjBDMQsw 439 CQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEzARBgNVBAoMCkFjbWUsIEluYy4xEjAQ 440 BgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB 441 AJWSTkcwo28xdUmglxR8orbnZRPqt2eWxoUtk6Vxunei2qKrcDaOFAt4vk5zJ3ba 442 z9qTZFf2WIr7KdXx8/dhahQE/l6WHBvKS5uIi9JNFLnzlnSz01/OxwiYymeK+35s 443 7Lco6iuLL/SN8ntYyotFRNQS6xdSlbxBP9T1wroOvBAT/2hdOvrr3K+qPIWzGM4H 444 Sks4UWJvQt5BnGsDaH6gA/ypP85/qTJOAZaMuFILVxV4I9mxJ5lRPrUVsi79vkzj 445 ZZi512gVt/zL2bSgt5iLNihvumgTbmPD3Vs1rzLad9K/4m4Jx87jCrb1yeV0YU2k 446 k+EGFzWxsG9WyFdcpZ1/M3VzxEJLmE0A3LWmEAntEV7dSPRad4FUEcckzPtCHrW7 447 FNJPVbwcENO5oFPEYpNw8VZ327iWP8ERJxEU5IL+qI7UcIrM6xiGOcd+qXkbN9Aq 448 kTL97ibagvxVOpwkGx8cLLEMgf7pjei+23KbEBQ8nXiveYb0UMurW9r/7zHMOzDK 449 OZEhVB46C+WXOAzm8IqrPqexe0tFiCTAcyTnI5oAjM4GeyZnO2zJq5uNxi8zmlSy 450 I7j1VaTqe2ICf5akX+yt4WV6zHWl5AptNWi/C+fXft1X3Y6dxx75mKQofG3Pio7P 451 50fUXPtiSivx9dyx91uQq/BZ3PxT+k4G77zcLEFdNYBvAgMBAAGjHjAcMBoGA1Ud 452 EQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAgEABL8kffi7 453 48qSD+/l/UwCYdmqta1vAbOkvLnPtfXe1XlDpJipNuPxUBc8nNTemtrbg0erNJnC 454 jQHodqmdKBJJOdaEKTwAGp5pYvvjlU3WasmhfJy+QwOWgeqjJcTUo3+DEaHRls16 455 AZXlsp3hB6z0gzR/qzUuZwpMbL477JpuZtAcwLYeVvLG8bQRyWyEy8JgGDoYSn8s 456 Z16s+r6AX+cnL/2GHkZ+oc3iuXJbnac4xfWTKDiYnyzK6RWRnoyro7X0jiPz6XX3 457 wyoWzB1uMSCXscrW6ZcKyKqz75lySLuwGxOMhX4nGOoYHY0ZtrYn5WK2ZAJxsQnn 458 8QcjPB0nq37U7ifk1uebmuXe99iqyKnWaLvlcpe+HnO5pVxFkSQEf7Zh+hEnRDkN 459 IBzLFnqwDS1ug/oQ1aSvc8oBh2ylKDJuGtPNqGKibNJyb2diXO/aEUOKRUKPAxKa 460 dbKsc4Y1bhZNN3/MICMoyghwAOiuwUQMR5uhxTkQmZUwNrPFa+eW6GvyoYLFUsZs 461 hZfWLNGD5mLADElxs0HF7F9Zk6pSocTDXba4d4lfxsq88SyZZ7PbjJYFRfLQPzd1 462 CfvpRPqolEmZo1Y5Q644PELYiJRKpBxmX5GtC5j5eaUD9XdGKvXsGhb0m0gW75rq 463 iUnnLkZt2ya1cDJDiCnJjo7r5KxMo0XXFDc= 464 -----END CERTIFICATE----- 465 `) 466 467 func TestServingCertUpdate(t *testing.T) { 468 testServingCert(t, false) 469 } 470 471 func TestServingCertRecreate(t *testing.T) { 472 testServingCert(t, true) 473 } 474 475 func testServingCert(t *testing.T, recreate bool) { 476 _, ctx := ktesting.NewTestContext(t) 477 ctx, cancel := context.WithCancel(ctx) 478 defer cancel() 479 480 var servingCertPath string 481 482 _, kubeconfig, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{ 483 ModifyServerRunOptions: func(opts *options.ServerRunOptions) { 484 opts.GenericServerRunOptions.MaxRequestBodyBytes = 1024 * 1024 485 servingCertPath = opts.SecureServing.ServerCert.CertDirectory 486 }, 487 }) 488 defer tearDownFn() 489 490 if recreate { 491 if err := os.Remove(path.Join(servingCertPath, "apiserver.key")); err != nil { 492 t.Fatal(err) 493 } 494 if err := os.Remove(path.Join(servingCertPath, "apiserver.crt")); err != nil { 495 t.Fatal(err) 496 } 497 } 498 499 if err := os.WriteFile(path.Join(servingCertPath, "apiserver.key"), serverKey, 0644); err != nil { 500 t.Fatal(err) 501 } 502 if err := os.WriteFile(path.Join(servingCertPath, "apiserver.crt"), serverCert, 0644); err != nil { 503 t.Fatal(err) 504 } 505 506 time.Sleep(4 * time.Second) 507 508 // get the certs we're actually serving with 509 _, actualCerts, err := cert.GetServingCertificatesForURL(kubeconfig.Host, "") 510 if err != nil { 511 t.Fatal(err) 512 } 513 if err := checkServingCerts(serverCert, actualCerts); err != nil { 514 t.Fatal(err) 515 } 516 } 517 518 func TestSNICert(t *testing.T) { 519 var servingCertPath string 520 521 _, ctx := ktesting.NewTestContext(t) 522 ctx, cancel := context.WithCancel(ctx) 523 defer cancel() 524 525 _, kubeconfig, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{ 526 ModifyServerRunOptions: func(opts *options.ServerRunOptions) { 527 opts.GenericServerRunOptions.MaxRequestBodyBytes = 1024 * 1024 528 servingCertPath = opts.SecureServing.ServerCert.CertDirectory 529 530 if err := os.WriteFile(path.Join(servingCertPath, "foo.key"), anotherServerKey, 0644); err != nil { 531 t.Fatal(err) 532 } 533 if err := os.WriteFile(path.Join(servingCertPath, "foo.crt"), anotherServerCert, 0644); err != nil { 534 t.Fatal(err) 535 } 536 537 opts.SecureServing.SNICertKeys = []flag.NamedCertKey{{ 538 Names: []string{"foo"}, 539 CertFile: path.Join(servingCertPath, "foo.crt"), 540 KeyFile: path.Join(servingCertPath, "foo.key"), 541 }} 542 }, 543 }) 544 defer tearDownFn() 545 546 // When we run this the second time, we know which one we are expecting. 547 _, actualCerts, err := cert.GetServingCertificatesForURL(kubeconfig.Host, "foo") 548 if err != nil { 549 t.Fatal(err) 550 } 551 if err := checkServingCerts(anotherServerCert, actualCerts); err != nil { 552 t.Fatal(err) 553 } 554 555 if err := os.WriteFile(path.Join(servingCertPath, "foo.key"), serverKey, 0644); err != nil { 556 t.Fatal(err) 557 } 558 if err := os.WriteFile(path.Join(servingCertPath, "foo.crt"), serverCert, 0644); err != nil { 559 t.Fatal(err) 560 } 561 562 time.Sleep(4 * time.Second) 563 564 _, actualCerts, err = cert.GetServingCertificatesForURL(kubeconfig.Host, "foo") 565 if err != nil { 566 t.Fatal(err) 567 } 568 if err := checkServingCerts(serverCert, actualCerts); err != nil { 569 t.Fatal(err) 570 } 571 } 572 573 func checkServingCerts(expectedBytes []byte, actual [][]byte) error { 574 expectedCerts, err := cert.ParseCertsPEM(expectedBytes) 575 if err != nil { 576 return err 577 } 578 expected := [][]byte{} 579 for _, curr := range expectedCerts { 580 currBytes, err := cert.EncodeCertificates(curr) 581 if err != nil { 582 return err 583 } 584 expected = append(expected, []byte(strings.TrimSpace(string(currBytes)))) 585 } 586 587 if len(expected) != len(actual) { 588 var certs []string 589 for _, a := range actual { 590 certs = append(certs, string(a)) 591 } 592 return fmt.Errorf("unexpected number of certs %d vs %d: %v", len(expected), len(actual), strings.Join(certs, "\n")) 593 } 594 for i := range expected { 595 if !bytes.Equal(actual[i], expected[i]) { 596 return fmt.Errorf("expected %q, got %q", string(expected[i]), string(actual[i])) 597 } 598 } 599 return nil 600 }