...

Source file src/k8s.io/kubernetes/test/integration/apiserver/certreload/certreload_test.go

Documentation: k8s.io/kubernetes/test/integration/apiserver/certreload

     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  	tCtx := ktesting.Init(t)
   140  
   141  	frontProxyCA, err := newTestCAWithClient(
   142  		pkix.Name{
   143  			CommonName: "test-front-proxy-ca",
   144  		},
   145  		big.NewInt(43),
   146  		pkix.Name{
   147  			CommonName:   "test-aggregated-apiserver",
   148  			Organization: []string{"system:masters"},
   149  		},
   150  		big.NewInt(86),
   151  	)
   152  	if err != nil {
   153  		t.Error(err)
   154  		return
   155  	}
   156  
   157  	clientCA, err := newTestCAWithClient(
   158  		pkix.Name{
   159  			CommonName: "test-client-ca",
   160  		},
   161  		big.NewInt(42),
   162  		pkix.Name{
   163  			CommonName:   "system:admin",
   164  			Organization: []string{"system:masters"},
   165  		},
   166  		big.NewInt(84),
   167  	)
   168  	if err != nil {
   169  		t.Error(err)
   170  		return
   171  	}
   172  
   173  	clientCAFilename := ""
   174  	frontProxyCAFilename := ""
   175  
   176  	kubeClient, kubeconfig, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{
   177  		ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
   178  			opts.GenericServerRunOptions.MaxRequestBodyBytes = 1024 * 1024
   179  			clientCAFilename = opts.Authentication.ClientCert.ClientCA
   180  			frontProxyCAFilename = opts.Authentication.RequestHeader.ClientCAFile
   181  			opts.Authentication.RequestHeader.AllowedNames = append(opts.Authentication.RequestHeader.AllowedNames, "test-aggregated-apiserver")
   182  		},
   183  	})
   184  	defer tearDownFn()
   185  
   186  	// wait for request header info
   187  	err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "requestheader-client-ca-file", "-----BEGIN CERTIFICATE-----", 1))
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	// wait for client cert info
   192  	err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "client-ca-file", "-----BEGIN CERTIFICATE-----", 1))
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  
   197  	if recreate {
   198  		if err := os.Remove(path.Join(clientCAFilename)); err != nil {
   199  			t.Fatal(err)
   200  		}
   201  		if err := os.Remove(path.Join(frontProxyCAFilename)); err != nil {
   202  			t.Fatal(err)
   203  		}
   204  	}
   205  
   206  	// when we run this the second time, we know which one we are expecting
   207  	if err := os.WriteFile(clientCAFilename, clientCA.CACert, 0644); err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	if err := os.WriteFile(frontProxyCAFilename, frontProxyCA.CACert, 0644); err != nil {
   211  		t.Fatal(err)
   212  	}
   213  
   214  	time.Sleep(4 * time.Second)
   215  
   216  	acceptableCAs, err := cert.GetClientCANamesForURL(kubeconfig.Host)
   217  	if err != nil {
   218  		t.Fatal(err)
   219  	}
   220  
   221  	expectedCAs := []string{"test-client-ca", "test-front-proxy-ca"}
   222  	if len(expectedCAs) != len(acceptableCAs) {
   223  		t.Fatal(strings.Join(acceptableCAs, ":"))
   224  	}
   225  	for i := range expectedCAs {
   226  		if !strings.Contains(acceptableCAs[i], expectedCAs[i]) {
   227  			t.Errorf("expected %q, got %q", expectedCAs[i], acceptableCAs[i])
   228  		}
   229  	}
   230  
   231  	// wait for updated request header info that contains both
   232  	err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "requestheader-client-ca-file", "-----BEGIN CERTIFICATE-----", 2))
   233  	if err != nil {
   234  		t.Error(err)
   235  	}
   236  	err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "requestheader-client-ca-file", string(frontProxyCA.CACert), 1))
   237  	if err != nil {
   238  		t.Error(err)
   239  	}
   240  	// wait for updated client cert info that contains both
   241  	err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "client-ca-file", "-----BEGIN CERTIFICATE-----", 2))
   242  	if err != nil {
   243  		t.Error(err)
   244  	}
   245  	err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, waitForConfigMapCAContent(t, kubeClient, "client-ca-file", string(clientCA.CACert), 1))
   246  	if err != nil {
   247  		t.Error(err)
   248  	}
   249  
   250  	// Test an aggregated apiserver client (signed by the new front proxy CA) is authorized
   251  	extensionApiserverClient, err := kubernetes.NewForConfig(&rest.Config{
   252  		Host: kubeconfig.Host,
   253  		TLSClientConfig: rest.TLSClientConfig{
   254  			CAData:     kubeconfig.TLSClientConfig.CAData,
   255  			CAFile:     kubeconfig.TLSClientConfig.CAFile,
   256  			ServerName: kubeconfig.TLSClientConfig.ServerName,
   257  			KeyData:    frontProxyCA.ClientKey,
   258  			CertData:   frontProxyCA.ClientCert,
   259  		},
   260  	})
   261  	if err != nil {
   262  		t.Error(err)
   263  		return
   264  	}
   265  
   266  	// Call an endpoint to make sure we are authenticated
   267  	err = extensionApiserverClient.AuthorizationV1().RESTClient().
   268  		Post().
   269  		Resource("subjectaccessreviews").
   270  		VersionedParams(&metav1.CreateOptions{}, scheme.ParameterCodec).
   271  		Body(&authorizationv1.SubjectAccessReview{
   272  			Spec: authorizationv1.SubjectAccessReviewSpec{
   273  				ResourceAttributes: &authorizationv1.ResourceAttributes{
   274  					Verb:      "create",
   275  					Resource:  "pods",
   276  					Namespace: "default",
   277  				},
   278  				User: "deads2k",
   279  			},
   280  		}).
   281  		SetHeader("X-Remote-User", "test-aggregated-apiserver").
   282  		SetHeader("X-Remote-Group", "system:masters").
   283  		Do(context.Background()).
   284  		Into(&authorizationv1.SubjectAccessReview{})
   285  	if err != nil {
   286  		t.Error(err)
   287  	}
   288  
   289  	// Test a client signed by the new ClientCA is authorized
   290  	testClient, err := kubernetes.NewForConfig(&rest.Config{
   291  		Host: kubeconfig.Host,
   292  		TLSClientConfig: rest.TLSClientConfig{
   293  			CAData:     kubeconfig.TLSClientConfig.CAData,
   294  			CAFile:     kubeconfig.TLSClientConfig.CAFile,
   295  			ServerName: kubeconfig.TLSClientConfig.ServerName,
   296  			KeyData:    clientCA.ClientKey,
   297  			CertData:   clientCA.ClientCert,
   298  		},
   299  	})
   300  	if err != nil {
   301  		t.Error(err)
   302  		return
   303  	}
   304  
   305  	// Call an endpoint to make sure we are authenticated
   306  	_, err = testClient.CoreV1().Nodes().List(tCtx, metav1.ListOptions{})
   307  	if err != nil {
   308  		t.Error(err)
   309  	}
   310  }
   311  
   312  func waitForConfigMapCAContent(t *testing.T, kubeClient kubernetes.Interface, key, content string, count int) func() (bool, error) {
   313  	return func() (bool, error) {
   314  		clusterAuthInfo, err := kubeClient.CoreV1().ConfigMaps("kube-system").Get(context.TODO(), "extension-apiserver-authentication", metav1.GetOptions{})
   315  		if apierrors.IsNotFound(err) {
   316  			return false, nil
   317  		}
   318  		if err != nil {
   319  			return false, err
   320  		}
   321  
   322  		ca := clusterAuthInfo.Data[key]
   323  		if strings.Count(ca, content) == count {
   324  			return true, nil
   325  		}
   326  		t.Log(ca)
   327  		return false, nil
   328  	}
   329  }
   330  
   331  var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
   332  MIIEowIBAAKCAQEA13f50PPWuR/InxLIoJjHdNSG+jVUd25CY7ZL2J023X2BAY+1
   333  M6jkLR6C2nSFZnn58ubiB74/d1g/Fg1Twd419iR615A013f+qOoyFx3LFHxU1S6e
   334  v22fgJ6ntK/+4QD5MwNgOwD8k1jN2WxHqNWn16IF4Tidbv8M9A35YHAdtYDYaOJC
   335  kzjVztzRw1y6bKRakpMXxHylQyWmAKDJ2GSbRTbGtjr7Ji54WBfG43k94tO5X8K4
   336  VGbz/uxrKe1IFMHNOlrjR438dbOXusksx9EIqDA9a42J3qjr5NKSqzCIbgBFl6qu
   337  45V3A7cdRI/sJ2G1aqlWIXh2fAQiaFQAEBrPfwIDAQABAoIBAAZbxgWCjJ2d8H+x
   338  QDZtC8XI18redAWqPU9P++ECkrHqmDoBkalanJEwS1BDDATAKL4gTh9IX/sXoZT3
   339  A7e+5PzEitN9r/GD2wIFF0FTYcDTAnXgEFM52vEivXQ5lV3yd2gn+1kCaHG4typp
   340  ZZv34iIc5+uDjjHOWQWCvA86f8XxX5EfYH+GkjfixTtN2xhWWlfi9vzYeESS4Jbt
   341  tqfH0iEaZ1Bm/qvb8vFgKiuSTOoSpaf+ojAdtPtXDjf1bBtQQG+RSQkP59O/taLM
   342  FCVuRrU8EtdB0+9anwmAP+O2UqjL5izA578lQtdIh13jHtGEgOcnfGNUphK11y9r
   343  Mg5V28ECgYEA9fwI6Xy1Rb9b9irp4bU5Ec99QXa4x2bxld5cDdNOZWJQu9OnaIbg
   344  kw/1SyUkZZCGMmibM/BiWGKWoDf8E+rn/ujGOtd70sR9U0A94XMPqEv7iHxhpZmD
   345  rZuSz4/snYbOWCZQYXFoD/nqOwE7Atnz7yh+Jti0qxBQ9bmkb9o0QW8CgYEA4D3d
   346  okzodg5QQ1y9L0J6jIC6YysoDedveYZMd4Un9bKlZEJev4OwiT4xXmSGBYq/7dzo
   347  OJOvN6qgPfibr27mSB8NkAk6jL/VdJf3thWxNYmjF4E3paLJ24X31aSipN1Ta6K3
   348  KKQUQRvixVoI1q+8WHAubBDEqvFnNYRHD+AjKvECgYBkekjhpvEcxme4DBtw+OeQ
   349  4OJXJTmhKemwwB12AERboWc88d3GEqIVMEWQJmHRotFOMfCDrMNfOxYv5+5t7FxL
   350  gaXHT1Hi7CQNJ4afWrKgmjjqrXPtguGIvq2fXzjVt8T9uNjIlNxe+kS1SXFjXsgH
   351  ftDY6VgTMB0B4ozKq6UAvQKBgQDER8K5buJHe+3rmMCMHn+Qfpkndr4ftYXQ9Kn4
   352  MFiy6sV0hdfTgRzEdOjXu9vH/BRVy3iFFVhYvIR42iTEIal2VaAUhM94Je5cmSyd
   353  eE1eFHTqfRPNazmPaqttmSc4cfa0D4CNFVoZR6RupIl6Cect7jvkIaVUD+wMXxWo
   354  osOFsQKBgDLwVhZWoQ13RV/jfQxS3veBUnHJwQJ7gKlL1XZ16mpfEOOVnJF7Es8j
   355  TIIXXYhgSy/XshUbsgXQ+YGliye/rXSCTXHBXvWShOqxEMgeMYMRkcm8ZLp/DH7C
   356  kC2pemkLPUJqgSh1PASGcJbDJIvFGUfP69tUCYpHpk3nHzexuAg3
   357  -----END RSA PRIVATE KEY-----`)
   358  
   359  var serverCert = []byte(`-----BEGIN CERTIFICATE-----
   360  MIIDQDCCAiigAwIBAgIJANWw74P5KJk2MA0GCSqGSIb3DQEBCwUAMDQxMjAwBgNV
   361  BAMMKWdlbmVyaWNfd2ViaG9va19hZG1pc3Npb25fcGx1Z2luX3Rlc3RzX2NhMCAX
   362  DTE3MTExNjAwMDUzOVoYDzIyOTEwOTAxMDAwNTM5WjAjMSEwHwYDVQQDExh3ZWJo
   363  b29rLXRlc3QuZGVmYXVsdC5zdmMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
   364  AoIBAQDXd/nQ89a5H8ifEsigmMd01Ib6NVR3bkJjtkvYnTbdfYEBj7UzqOQtHoLa
   365  dIVmefny5uIHvj93WD8WDVPB3jX2JHrXkDTXd/6o6jIXHcsUfFTVLp6/bZ+Anqe0
   366  r/7hAPkzA2A7APyTWM3ZbEeo1afXogXhOJ1u/wz0DflgcB21gNho4kKTONXO3NHD
   367  XLpspFqSkxfEfKVDJaYAoMnYZJtFNsa2OvsmLnhYF8bjeT3i07lfwrhUZvP+7Gsp
   368  7UgUwc06WuNHjfx1s5e6ySzH0QioMD1rjYneqOvk0pKrMIhuAEWXqq7jlXcDtx1E
   369  j+wnYbVqqVYheHZ8BCJoVAAQGs9/AgMBAAGjZDBiMAkGA1UdEwQCMAAwCwYDVR0P
   370  BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATApBgNVHREEIjAg
   371  hwR/AAABghh3ZWJob29rLXRlc3QuZGVmYXVsdC5zdmMwDQYJKoZIhvcNAQELBQAD
   372  ggEBAD/GKSPNyQuAOw/jsYZesb+RMedbkzs18sSwlxAJQMUrrXwlVdHrA8q5WhE6
   373  ABLqU1b8lQ8AWun07R8k5tqTmNvCARrAPRUqls/ryER+3Y9YEcxEaTc3jKNZFLbc
   374  T6YtcnkdhxsiO136wtiuatpYL91RgCmuSpR8+7jEHhuFU01iaASu7ypFrUzrKHTF
   375  bKwiLRQi1cMzVcLErq5CDEKiKhUkoDucyARFszrGt9vNIl/YCcBOkcNvM3c05Hn3
   376  M++C29JwS3Hwbubg6WO3wjFjoEhpCwU6qRYUz3MRp4tHO4kxKXx+oQnUiFnR7vW0
   377  YkNtGc1RUDHwecCTFpJtPb7Yu/E=
   378  -----END CERTIFICATE-----`)
   379  
   380  var anotherServerKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
   381  MIIJKAIBAAKCAgEAlZJORzCjbzF1SaCXFHyitudlE+q3Z5bGhS2TpXG6d6Laoqtw
   382  No4UC3i+TnMndtrP2pNkV/ZYivsp1fHz92FqFAT+XpYcG8pLm4iL0k0UufOWdLPT
   383  X87HCJjKZ4r7fmzstyjqK4sv9I3ye1jKi0VE1BLrF1KVvEE/1PXCug68EBP/aF06
   384  +uvcr6o8hbMYzgdKSzhRYm9C3kGcawNofqAD/Kk/zn+pMk4Bloy4UgtXFXgj2bEn
   385  mVE+tRWyLv2+TONlmLnXaBW3/MvZtKC3mIs2KG+6aBNuY8PdWzWvMtp30r/ibgnH
   386  zuMKtvXJ5XRhTaST4QYXNbGwb1bIV1ylnX8zdXPEQkuYTQDctaYQCe0RXt1I9Fp3
   387  gVQRxyTM+0IetbsU0k9VvBwQ07mgU8Rik3DxVnfbuJY/wREnERTkgv6ojtRwiszr
   388  GIY5x36peRs30CqRMv3uJtqC/FU6nCQbHxwssQyB/umN6L7bcpsQFDydeK95hvRQ
   389  y6tb2v/vMcw7MMo5kSFUHjoL5Zc4DObwiqs+p7F7S0WIJMBzJOcjmgCMzgZ7Jmc7
   390  bMmrm43GLzOaVLIjuPVVpOp7YgJ/lqRf7K3hZXrMdaXkCm01aL8L59d+3Vfdjp3H
   391  HvmYpCh8bc+Kjs/nR9Rc+2JKK/H13LH3W5Cr8Fnc/FP6TgbvvNwsQV01gG8CAwEA
   392  AQKCAgBLBQn8DPo8YDsqxcBhRy45vQ/mkHiTHX3O+JAwkD1tmiI9Ku3qfxKwukwB
   393  fyKRK6jLQdg3gljgxJ80Ltol/xc8mVCYUoQgsDOB/FfdEEpQBkw1lqhzSnxr5G7I
   394  xl3kCHAmYgAp/PL9n2C620sj1YdzM1X06bgupy+D+gxEU/WhvtYBG5nklv6moSUg
   395  DjdnxyJNXh7710Bbx97Tke8Ma+f0B1P4l/FeSN/lCgm9JPD11L9uhbuN28EvBIXN
   396  qfmUCQ5BLx1KmHIi+n/kaCQN/+0XFQsS/oQEyA2znNaWFBu7egDxHji4nQoXwGoW
   397  i2vujJibafmkNc5/2bA8mTx8JXvCLhU2L9j2ZumpKOda0g+pfMauesL+9rvZdqwW
   398  gjdjndOHZlg3qm40hGCDBVmmV3mdnvXrk1BbuB4Y0N7qGo3PyYtJHGwJILaNQVGR
   399  Sj75uTatxJwFXsqSaJaErV3Q90IiyXX4AOFGnWHOs29GEwtnDbCvT/rzqutTYSXD
   400  Yv0XFDznzJelhZTH7FbaW3FW3YGEG1ER/0MtKpsAH4i7H9q3KKK8yrzUsgUkGwXt
   401  xtoLckh91xilPIGbzARdELTEdHrjlFL+qaz3PIqEQScWz3WBu2JcIzGbp6PQfMZ+
   402  FZXarEb/ADZuX0+WoKFYR5jzwMoQfF/fxe2Ib/37ETNw4BgfSQKCAQEAxOw64XgO
   403  nUVJslzGK/H5fqTVpD1rfRmvVAiSDLAuWpClbpDZXqEPuoPPYsiccuUWu9VkJE1F
   404  6MZEexGx1jFkN08QUHD1Bobzu6ThaBc2PrWHRjFGKM60d0AkhOiL4N04FGwVeCN6
   405  xzIJFk1E4VOOo1+lzeAWRvi1lwuWTgQi+m25nwBJtmYdBLGeS+DXy80Fi6deECei
   406  ipDzJ4rxJsZ61uqBeYC4CfuHW9m5rCzJWPMMMFrPdl3OxEyZzKng4Co5EYc5i/QH
   407  piXD6IJayKcTPRK3tBJZp2YCIIdtQLcjAwmDEDowQtelHkbTihXMGRarf3VcOEoN
   408  ozMRgcLEEynuKwKCAQEAwnF5ZkkJEL/1MCOZ6PZfSKl35ZMIz/4Umk8hOMAQGhCT
   409  cnxlDUfGSBu4OihdBbIuBSBsYDjgcev8uyiIPDVy0FIkBKRGfgrNCLDh19aHljvE
   410  bUc3akvbft0mro86AvSd/Rpc7sj841bru37RDUm6AJOtIvb6DWUpMOZgMm0WMmSI
   411  kNs/UT+7rqg+AZPP8lumnJIFnRK38xOehQAaS1FHWGP//38py8yo8eXpMsoCWMch
   412  c+kZD2jsAYV+SWjjkZjcrv/52+asd4AotRXIShV8E8xItQeq6vLHKOaIe0tC2Y44
   413  ONAKiu4dgABt1voy8I5J63MwgeNmgAUS+KsgUclYzQKCAQEAlt/3bPAzIkQH5uQ1
   414  4U2PvnxEQ4XbaQnYzyWR4K7LlQ/l8ASCxoHYLyr2JdVWKKFk/ZzNERMzUNk3dqNk
   415  AZvuEII/GaKx2MJk04vMN5gxM3KZpinyeymEEynN0RbqtOpJITx+ZoGofB3V4IRr
   416  FciTLJEH0+iwqMe9OXDjQ/rfYcfXw/7QezNZYFNF2RT3wWnfqdQduXrkig3sfotx
   417  oCfJzgf2E0WPu/Y/CxyRqVzXF5N/7zxkX2gYF0YpQCmX5afz+X4FlTju81lT9DyL
   418  mdiIYO6KWSkGD7+UOaAJEOA/rwAGrtQmTdAy7jONt+pjaYV4+DrO4UG7mSJzc1vq
   419  JlSl6QKCAQARqwPv8mT7e6XI2QNMMs7XqGZ3mtOrKpguqVAIexM7exQazAjWmxX+
   420  SV6FElPZh6Y82wRd/e0PDPVrADTY27ZyDXSuY0rwewTEbGYpGZo6YXXoxBbZ9sic
   421  D3ZLWEJaMGYGsJWPMP4hni1PXSebwH5BPSn3Sl/QRcfnZJeLHXRt4cqy9uka9eKU
   422  7T6tIAQ+LmvGQFJ4QlIqqTa3ORoqi9kiw/tn+OMQXKlhSZXWApsR/A4jHSQkzVDc
   423  loeyHfDHsw8ia6oFfEFhnmiUg8UuTiN3HRHiOS8jqCnGoqP2KBGL+StMpkK++wH9
   424  NozEgvmL+DHpTg8zTjlrGortw4btR5FlAoIBABVni+EsGA5K/PM1gIct2pDm+6Kq
   425  UCYScTwIjftuwKLk/KqermG9QJLiJouKO3ZSz7iCelu87Dx1cKeXrc2LQ1pnQzCB
   426  JnI6BCT+zRnQFXjLokJXD2hIS2hXhqV6/9FRXLKKMYePcDxWt/etLNGmpLnhDfb3
   427  sMOH/9pnaGmtk36Ce03Hh7E1C6io/MKfTq+KKUV1UGwO1BdNQCiclkYzAUqn1O+Y
   428  c8BaeGKc2c6as8DKrPTGGQGmzo/ZUxQVfVFl2g7+HXISWBBcui/G5gtnU1afZqbW
   429  mTmDoqs4510vhlkhN9XZ0DyhewDIqNNGEY2vS1x2fJz1XC2Eve4KpSyUsiE=
   430  -----END RSA PRIVATE KEY-----
   431  `)
   432  
   433  var anotherServerCert = []byte(`-----BEGIN CERTIFICATE-----
   434  MIIFJjCCAw6gAwIBAgIJAOcEAbv8NslfMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
   435  BAYTAlVTMQswCQYDVQQIDAJDQTETMBEGA1UECgwKQWNtZSwgSW5jLjEPMA0GA1UE
   436  AwwGc29tZUNBMCAXDTE4MDYwODEzMzkyNFoYDzIyMTgwNDIxMTMzOTI0WjBDMQsw
   437  CQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEzARBgNVBAoMCkFjbWUsIEluYy4xEjAQ
   438  BgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
   439  AJWSTkcwo28xdUmglxR8orbnZRPqt2eWxoUtk6Vxunei2qKrcDaOFAt4vk5zJ3ba
   440  z9qTZFf2WIr7KdXx8/dhahQE/l6WHBvKS5uIi9JNFLnzlnSz01/OxwiYymeK+35s
   441  7Lco6iuLL/SN8ntYyotFRNQS6xdSlbxBP9T1wroOvBAT/2hdOvrr3K+qPIWzGM4H
   442  Sks4UWJvQt5BnGsDaH6gA/ypP85/qTJOAZaMuFILVxV4I9mxJ5lRPrUVsi79vkzj
   443  ZZi512gVt/zL2bSgt5iLNihvumgTbmPD3Vs1rzLad9K/4m4Jx87jCrb1yeV0YU2k
   444  k+EGFzWxsG9WyFdcpZ1/M3VzxEJLmE0A3LWmEAntEV7dSPRad4FUEcckzPtCHrW7
   445  FNJPVbwcENO5oFPEYpNw8VZ327iWP8ERJxEU5IL+qI7UcIrM6xiGOcd+qXkbN9Aq
   446  kTL97ibagvxVOpwkGx8cLLEMgf7pjei+23KbEBQ8nXiveYb0UMurW9r/7zHMOzDK
   447  OZEhVB46C+WXOAzm8IqrPqexe0tFiCTAcyTnI5oAjM4GeyZnO2zJq5uNxi8zmlSy
   448  I7j1VaTqe2ICf5akX+yt4WV6zHWl5AptNWi/C+fXft1X3Y6dxx75mKQofG3Pio7P
   449  50fUXPtiSivx9dyx91uQq/BZ3PxT+k4G77zcLEFdNYBvAgMBAAGjHjAcMBoGA1Ud
   450  EQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAgEABL8kffi7
   451  48qSD+/l/UwCYdmqta1vAbOkvLnPtfXe1XlDpJipNuPxUBc8nNTemtrbg0erNJnC
   452  jQHodqmdKBJJOdaEKTwAGp5pYvvjlU3WasmhfJy+QwOWgeqjJcTUo3+DEaHRls16
   453  AZXlsp3hB6z0gzR/qzUuZwpMbL477JpuZtAcwLYeVvLG8bQRyWyEy8JgGDoYSn8s
   454  Z16s+r6AX+cnL/2GHkZ+oc3iuXJbnac4xfWTKDiYnyzK6RWRnoyro7X0jiPz6XX3
   455  wyoWzB1uMSCXscrW6ZcKyKqz75lySLuwGxOMhX4nGOoYHY0ZtrYn5WK2ZAJxsQnn
   456  8QcjPB0nq37U7ifk1uebmuXe99iqyKnWaLvlcpe+HnO5pVxFkSQEf7Zh+hEnRDkN
   457  IBzLFnqwDS1ug/oQ1aSvc8oBh2ylKDJuGtPNqGKibNJyb2diXO/aEUOKRUKPAxKa
   458  dbKsc4Y1bhZNN3/MICMoyghwAOiuwUQMR5uhxTkQmZUwNrPFa+eW6GvyoYLFUsZs
   459  hZfWLNGD5mLADElxs0HF7F9Zk6pSocTDXba4d4lfxsq88SyZZ7PbjJYFRfLQPzd1
   460  CfvpRPqolEmZo1Y5Q644PELYiJRKpBxmX5GtC5j5eaUD9XdGKvXsGhb0m0gW75rq
   461  iUnnLkZt2ya1cDJDiCnJjo7r5KxMo0XXFDc=
   462  -----END CERTIFICATE-----
   463  `)
   464  
   465  func TestServingCertUpdate(t *testing.T) {
   466  	testServingCert(t, false)
   467  }
   468  
   469  func TestServingCertRecreate(t *testing.T) {
   470  	testServingCert(t, true)
   471  }
   472  
   473  func testServingCert(t *testing.T, recreate bool) {
   474  	tCtx := ktesting.Init(t)
   475  
   476  	var servingCertPath string
   477  
   478  	_, kubeconfig, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{
   479  		ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
   480  			opts.GenericServerRunOptions.MaxRequestBodyBytes = 1024 * 1024
   481  			servingCertPath = opts.SecureServing.ServerCert.CertDirectory
   482  		},
   483  	})
   484  	defer tearDownFn()
   485  
   486  	if recreate {
   487  		if err := os.Remove(path.Join(servingCertPath, "apiserver.key")); err != nil {
   488  			t.Fatal(err)
   489  		}
   490  		if err := os.Remove(path.Join(servingCertPath, "apiserver.crt")); err != nil {
   491  			t.Fatal(err)
   492  		}
   493  	}
   494  
   495  	if err := os.WriteFile(path.Join(servingCertPath, "apiserver.key"), serverKey, 0644); err != nil {
   496  		t.Fatal(err)
   497  	}
   498  	if err := os.WriteFile(path.Join(servingCertPath, "apiserver.crt"), serverCert, 0644); err != nil {
   499  		t.Fatal(err)
   500  	}
   501  
   502  	time.Sleep(4 * time.Second)
   503  
   504  	// get the certs we're actually serving with
   505  	_, actualCerts, err := cert.GetServingCertificatesForURL(kubeconfig.Host, "")
   506  	if err != nil {
   507  		t.Fatal(err)
   508  	}
   509  	if err := checkServingCerts(serverCert, actualCerts); err != nil {
   510  		t.Fatal(err)
   511  	}
   512  }
   513  
   514  func TestSNICert(t *testing.T) {
   515  	var servingCertPath string
   516  
   517  	tCtx := ktesting.Init(t)
   518  
   519  	_, kubeconfig, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{
   520  		ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
   521  			opts.GenericServerRunOptions.MaxRequestBodyBytes = 1024 * 1024
   522  			servingCertPath = opts.SecureServing.ServerCert.CertDirectory
   523  
   524  			if err := os.WriteFile(path.Join(servingCertPath, "foo.key"), anotherServerKey, 0644); err != nil {
   525  				t.Fatal(err)
   526  			}
   527  			if err := os.WriteFile(path.Join(servingCertPath, "foo.crt"), anotherServerCert, 0644); err != nil {
   528  				t.Fatal(err)
   529  			}
   530  
   531  			opts.SecureServing.SNICertKeys = []flag.NamedCertKey{{
   532  				Names:    []string{"foo"},
   533  				CertFile: path.Join(servingCertPath, "foo.crt"),
   534  				KeyFile:  path.Join(servingCertPath, "foo.key"),
   535  			}}
   536  		},
   537  	})
   538  	defer tearDownFn()
   539  
   540  	// When we run this the second time, we know which one we are expecting.
   541  	_, actualCerts, err := cert.GetServingCertificatesForURL(kubeconfig.Host, "foo")
   542  	if err != nil {
   543  		t.Fatal(err)
   544  	}
   545  	if err := checkServingCerts(anotherServerCert, actualCerts); err != nil {
   546  		t.Fatal(err)
   547  	}
   548  
   549  	if err := os.WriteFile(path.Join(servingCertPath, "foo.key"), serverKey, 0644); err != nil {
   550  		t.Fatal(err)
   551  	}
   552  	if err := os.WriteFile(path.Join(servingCertPath, "foo.crt"), serverCert, 0644); err != nil {
   553  		t.Fatal(err)
   554  	}
   555  
   556  	time.Sleep(4 * time.Second)
   557  
   558  	_, actualCerts, err = cert.GetServingCertificatesForURL(kubeconfig.Host, "foo")
   559  	if err != nil {
   560  		t.Fatal(err)
   561  	}
   562  	if err := checkServingCerts(serverCert, actualCerts); err != nil {
   563  		t.Fatal(err)
   564  	}
   565  }
   566  
   567  func checkServingCerts(expectedBytes []byte, actual [][]byte) error {
   568  	expectedCerts, err := cert.ParseCertsPEM(expectedBytes)
   569  	if err != nil {
   570  		return err
   571  	}
   572  	expected := [][]byte{}
   573  	for _, curr := range expectedCerts {
   574  		currBytes, err := cert.EncodeCertificates(curr)
   575  		if err != nil {
   576  			return err
   577  		}
   578  		expected = append(expected, []byte(strings.TrimSpace(string(currBytes))))
   579  	}
   580  
   581  	if len(expected) != len(actual) {
   582  		var certs []string
   583  		for _, a := range actual {
   584  			certs = append(certs, string(a))
   585  		}
   586  		return fmt.Errorf("unexpected number of certs %d vs %d: %v", len(expected), len(actual), strings.Join(certs, "\n"))
   587  	}
   588  	for i := range expected {
   589  		if !bytes.Equal(actual[i], expected[i]) {
   590  			return fmt.Errorf("expected %q, got %q", string(expected[i]), string(actual[i]))
   591  		}
   592  	}
   593  	return nil
   594  }
   595  

View as plain text