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  }