k8s.io/client-go@v0.31.1/util/certificate/certificate_store_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 certificate
    18  
    19  import (
    20  	"bytes"
    21  	"os"
    22  	"path/filepath"
    23  	"testing"
    24  )
    25  
    26  func TestUpdateSymlinkExistingFileError(t *testing.T) {
    27  	dir, err := os.MkdirTemp("", "k8s-test-update-symlink")
    28  	if err != nil {
    29  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
    30  	}
    31  	defer func() {
    32  		if err := os.RemoveAll(dir); err != nil {
    33  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
    34  		}
    35  	}()
    36  	pairFile := filepath.Join(dir, "kubelet-current.pem")
    37  	if err := os.WriteFile(pairFile, nil, 0600); err != nil {
    38  		t.Fatalf("Unable to create the file %q: %v", pairFile, err)
    39  	}
    40  
    41  	s := fileStore{
    42  		certDirectory:  dir,
    43  		pairNamePrefix: "kubelet",
    44  	}
    45  	if err := s.updateSymlink(pairFile); err == nil {
    46  		t.Errorf("Got no error, wanted to fail updating the symlink because there is a file there.")
    47  	}
    48  }
    49  
    50  func TestUpdateSymlinkNewFileNotExist(t *testing.T) {
    51  	dir, err := os.MkdirTemp("", "k8s-test-update-symlink")
    52  	if err != nil {
    53  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
    54  	}
    55  	defer func() {
    56  		if err := os.RemoveAll(dir); err != nil {
    57  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
    58  		}
    59  	}()
    60  	oldPairFile := filepath.Join(dir, "kubelet-oldpair.pem")
    61  	if err := os.WriteFile(oldPairFile, nil, 0600); err != nil {
    62  		t.Fatalf("Unable to create the file %q: %v", oldPairFile, err)
    63  	}
    64  
    65  	s := fileStore{
    66  		certDirectory:  dir,
    67  		pairNamePrefix: "kubelet",
    68  	}
    69  	if err := s.updateSymlink(oldPairFile); err != nil {
    70  		t.Errorf("Got error %v, wanted successful update of the symlink to point to %q", err, oldPairFile)
    71  	}
    72  
    73  	if _, err := os.Stat(oldPairFile); err != nil {
    74  		t.Errorf("Got error %v, wanted file %q to be there.", err, oldPairFile)
    75  	}
    76  
    77  	currentPairFile := filepath.Join(dir, "kubelet-current.pem")
    78  	if fi, err := os.Lstat(currentPairFile); err != nil {
    79  		t.Errorf("Got error %v, wanted file %q to be there", err, currentPairFile)
    80  	} else if fi.Mode()&os.ModeSymlink != os.ModeSymlink {
    81  		t.Errorf("Got %q not a symlink.", currentPairFile)
    82  	}
    83  
    84  	newPairFile := filepath.Join(dir, "kubelet-newpair.pem")
    85  	if err := s.updateSymlink(newPairFile); err == nil {
    86  		t.Errorf("Got no error, wanted to fail updating the symlink the file %q does not exist.", newPairFile)
    87  	}
    88  }
    89  
    90  func TestUpdateSymlinkNoSymlink(t *testing.T) {
    91  	dir, err := os.MkdirTemp("", "k8s-test-update-symlink")
    92  	if err != nil {
    93  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
    94  	}
    95  	defer func() {
    96  		if err := os.RemoveAll(dir); err != nil {
    97  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
    98  		}
    99  	}()
   100  	pairFile := filepath.Join(dir, "kubelet-newfile.pem")
   101  	if err := os.WriteFile(pairFile, nil, 0600); err != nil {
   102  		t.Fatalf("Unable to create the file %q: %v", pairFile, err)
   103  	}
   104  
   105  	s := fileStore{
   106  		certDirectory:  dir,
   107  		pairNamePrefix: "kubelet",
   108  	}
   109  	if err := s.updateSymlink(pairFile); err != nil {
   110  		t.Errorf("Got error %v, wanted a new symlink to be created", err)
   111  	}
   112  
   113  	if _, err := os.Stat(pairFile); err != nil {
   114  		t.Errorf("Got error %v, wanted file %q to be there", err, pairFile)
   115  	}
   116  	currentPairFile := filepath.Join(dir, "kubelet-current.pem")
   117  	if fi, err := os.Lstat(currentPairFile); err != nil {
   118  		t.Errorf("Got %v, wanted %q to be there", currentPairFile, err)
   119  	} else if fi.Mode()&os.ModeSymlink != os.ModeSymlink {
   120  		t.Errorf("%q not a symlink, wanted a symlink.", currentPairFile)
   121  	}
   122  }
   123  
   124  func TestUpdateSymlinkReplaceExistingSymlink(t *testing.T) {
   125  	prefix := "kubelet"
   126  	dir, err := os.MkdirTemp("", "k8s-test-update-symlink")
   127  	if err != nil {
   128  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   129  	}
   130  	defer func() {
   131  		if err := os.RemoveAll(dir); err != nil {
   132  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   133  		}
   134  	}()
   135  	oldPairFile := filepath.Join(dir, prefix+"-oldfile.pem")
   136  	if err := os.WriteFile(oldPairFile, nil, 0600); err != nil {
   137  		t.Fatalf("Unable to create the file %q: %v", oldPairFile, err)
   138  	}
   139  	newPairFile := filepath.Join(dir, prefix+"-newfile.pem")
   140  	if err := os.WriteFile(newPairFile, nil, 0600); err != nil {
   141  		t.Fatalf("Unable to create the file %q: %v", newPairFile, err)
   142  	}
   143  	currentPairFile := filepath.Join(dir, prefix+"-current.pem")
   144  	if err := os.Symlink(oldPairFile, currentPairFile); err != nil {
   145  		t.Fatalf("unable to create a symlink from %q to %q: %v", currentPairFile, oldPairFile, err)
   146  	}
   147  	if resolved, err := os.Readlink(currentPairFile); err != nil {
   148  		t.Fatalf("Got %v when attempting to resolve symlink %q", err, currentPairFile)
   149  	} else if resolved != oldPairFile {
   150  		t.Fatalf("Got %q as resolution of symlink %q, wanted %q", resolved, currentPairFile, oldPairFile)
   151  	}
   152  
   153  	s := fileStore{
   154  		certDirectory:  dir,
   155  		pairNamePrefix: prefix,
   156  	}
   157  	if err := s.updateSymlink(newPairFile); err != nil {
   158  		t.Errorf("Got error %v, wanted a new symlink to be created", err)
   159  	}
   160  
   161  	if _, err := os.Stat(oldPairFile); err != nil {
   162  		t.Errorf("Got error %v, wanted file %q to be there", oldPairFile, err)
   163  	}
   164  	if _, err := os.Stat(newPairFile); err != nil {
   165  		t.Errorf("Got error %v, wanted file %q to be there", newPairFile, err)
   166  	}
   167  	if fi, err := os.Lstat(currentPairFile); err != nil {
   168  		t.Errorf("Got %v, wanted %q to be there", currentPairFile, err)
   169  	} else if fi.Mode()&os.ModeSymlink != os.ModeSymlink {
   170  		t.Errorf("%q not a symlink, wanted a symlink.", currentPairFile)
   171  	}
   172  	if resolved, err := os.Readlink(currentPairFile); err != nil {
   173  		t.Fatalf("Got %v when attempting to resolve symlink %q", err, currentPairFile)
   174  	} else if resolved != newPairFile {
   175  		t.Fatalf("Got %q as resolution of symlink %q, wanted %q", resolved, currentPairFile, newPairFile)
   176  	}
   177  }
   178  
   179  func TestLoadFile(t *testing.T) {
   180  	dir, err := os.MkdirTemp("", "k8s-test-load-cert-key-blocks")
   181  	if err != nil {
   182  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   183  	}
   184  	defer func() {
   185  		if err := os.RemoveAll(dir); err != nil {
   186  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   187  		}
   188  	}()
   189  
   190  	pairFile := filepath.Join(dir, "kubelet-pair.pem")
   191  
   192  	tests := []struct {
   193  		desc string
   194  		data []byte
   195  	}{
   196  		{desc: "cert and key", data: bytes.Join([][]byte{storeCertData.certificatePEM, storeCertData.keyPEM}, []byte("\n"))},
   197  		{desc: "key and cert", data: bytes.Join([][]byte{storeCertData.keyPEM, storeCertData.certificatePEM}, []byte("\n"))},
   198  	}
   199  	for _, tt := range tests {
   200  		t.Run(tt.desc, func(t *testing.T) {
   201  			if err := os.WriteFile(pairFile, tt.data, 0600); err != nil {
   202  				t.Fatalf("Unable to create the file %q: %v", pairFile, err)
   203  			}
   204  			cert, err := loadFile(pairFile)
   205  			if err != nil {
   206  				t.Fatalf("Could not load certificate from disk: %v", err)
   207  			}
   208  			if cert == nil {
   209  				t.Fatalf("There was no error, but no certificate data was returned.")
   210  			}
   211  			if cert.Leaf == nil {
   212  				t.Fatalf("Got an empty leaf, expected private data.")
   213  			}
   214  		})
   215  	}
   216  }
   217  
   218  func TestUpdateNoRotation(t *testing.T) {
   219  	prefix := "kubelet-server"
   220  	dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
   221  	if err != nil {
   222  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   223  	}
   224  	defer func() {
   225  		if err := os.RemoveAll(dir); err != nil {
   226  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   227  		}
   228  	}()
   229  	keyFile := filepath.Join(dir, "kubelet.key")
   230  	if err := os.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
   231  		t.Fatalf("Unable to create the file %q: %v", keyFile, err)
   232  	}
   233  	certFile := filepath.Join(dir, "kubelet.crt")
   234  	if err := os.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
   235  		t.Fatalf("Unable to create the file %q: %v", certFile, err)
   236  	}
   237  
   238  	s, err := NewFileStore(prefix, dir, dir, certFile, keyFile)
   239  	if err != nil {
   240  		t.Fatalf("Got %v while creating a new store.", err)
   241  	}
   242  
   243  	cert, err := s.Update(storeCertData.certificatePEM, storeCertData.keyPEM)
   244  	if err != nil {
   245  		t.Errorf("Got %v while updating certificate store.", err)
   246  	}
   247  	if cert == nil {
   248  		t.Errorf("Got nil certificate, expected something real.")
   249  	}
   250  }
   251  
   252  func TestUpdateRotation(t *testing.T) {
   253  	prefix := "kubelet-server"
   254  	dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
   255  	if err != nil {
   256  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   257  	}
   258  	defer func() {
   259  		if err := os.RemoveAll(dir); err != nil {
   260  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   261  		}
   262  	}()
   263  	keyFile := filepath.Join(dir, "kubelet.key")
   264  	if err := os.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
   265  		t.Fatalf("Unable to create the file %q: %v", keyFile, err)
   266  	}
   267  	certFile := filepath.Join(dir, "kubelet.crt")
   268  	if err := os.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
   269  		t.Fatalf("Unable to create the file %q: %v", certFile, err)
   270  	}
   271  
   272  	s, err := NewFileStore(prefix, dir, dir, certFile, keyFile)
   273  	if err != nil {
   274  		t.Fatalf("Got %v while creating a new store.", err)
   275  	}
   276  
   277  	cert, err := s.Update(storeCertData.certificatePEM, storeCertData.keyPEM)
   278  	if err != nil {
   279  		t.Fatalf("Got %v while updating certificate store.", err)
   280  	}
   281  	if cert == nil {
   282  		t.Fatalf("Got nil certificate, expected something real.")
   283  	}
   284  }
   285  
   286  func TestUpdateTwoCerts(t *testing.T) {
   287  	prefix := "kubelet-server"
   288  	dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
   289  	if err != nil {
   290  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   291  	}
   292  	defer func() {
   293  		if err := os.RemoveAll(dir); err != nil {
   294  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   295  		}
   296  	}()
   297  	keyFile := filepath.Join(dir, "kubelet.key")
   298  	if err := os.WriteFile(keyFile, storeTwoCertsData.keyPEM, 0600); err != nil {
   299  		t.Fatalf("Unable to create the file %q: %v", keyFile, err)
   300  	}
   301  	certFile := filepath.Join(dir, "kubelet.crt")
   302  	if err := os.WriteFile(certFile, storeTwoCertsData.certificatePEM, 0600); err != nil {
   303  		t.Fatalf("Unable to create the file %q: %v", certFile, err)
   304  	}
   305  
   306  	s, err := NewFileStore(prefix, dir, dir, certFile, keyFile)
   307  	if err != nil {
   308  		t.Fatalf("Got %v while creating a new store.", err)
   309  	}
   310  
   311  	cert, err := s.Update(storeTwoCertsData.certificatePEM, storeTwoCertsData.keyPEM)
   312  	if err != nil {
   313  		t.Errorf("Got %v while updating certificate store.", err)
   314  	}
   315  	if cert == nil {
   316  		t.Fatalf("Got nil certificate, expected something real.")
   317  	}
   318  	if len(cert.Certificate) != 2 {
   319  		t.Fatalf("Unexpected number of certificates, expected 2, got %v", len(cert.Certificate))
   320  	}
   321  }
   322  
   323  func TestUpdateWithBadCertKeyData(t *testing.T) {
   324  	prefix := "kubelet-server"
   325  	dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
   326  	if err != nil {
   327  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   328  	}
   329  	defer func() {
   330  		if err := os.RemoveAll(dir); err != nil {
   331  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   332  		}
   333  	}()
   334  	keyFile := filepath.Join(dir, "kubelet.key")
   335  	if err := os.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
   336  		t.Fatalf("Unable to create the file %q: %v", keyFile, err)
   337  	}
   338  	certFile := filepath.Join(dir, "kubelet.crt")
   339  	if err := os.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
   340  		t.Fatalf("Unable to create the file %q: %v", certFile, err)
   341  	}
   342  
   343  	s, err := NewFileStore(prefix, dir, dir, certFile, keyFile)
   344  	if err != nil {
   345  		t.Fatalf("Got %v while creating a new store.", err)
   346  	}
   347  
   348  	cert, err := s.Update([]byte{0, 0}, storeCertData.keyPEM)
   349  	if err == nil {
   350  		t.Fatalf("Got no error while updating certificate store with invalid data.")
   351  	}
   352  	if cert != nil {
   353  		t.Fatalf("Got %v certificate returned from the update, expected nil.", cert)
   354  	}
   355  }
   356  
   357  func TestCurrentPairFile(t *testing.T) {
   358  	prefix := "kubelet-server"
   359  	dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
   360  	if err != nil {
   361  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   362  	}
   363  	defer func() {
   364  		if err := os.RemoveAll(dir); err != nil {
   365  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   366  		}
   367  	}()
   368  	pairFile := filepath.Join(dir, prefix+"-pair.pem")
   369  	data := append(storeCertData.certificatePEM, []byte("\n")...)
   370  	data = append(data, storeCertData.keyPEM...)
   371  	if err := os.WriteFile(pairFile, data, 0600); err != nil {
   372  		t.Fatalf("Unable to create the file %q: %v", pairFile, err)
   373  	}
   374  	currentFile := filepath.Join(dir, prefix+"-current.pem")
   375  	if err := os.Symlink(pairFile, currentFile); err != nil {
   376  		t.Fatalf("unable to create a symlink from %q to %q: %v", currentFile, pairFile, err)
   377  	}
   378  
   379  	store, err := NewFileStore("kubelet-server", dir, dir, "", "")
   380  	if err != nil {
   381  		t.Fatalf("Failed to initialize certificate store: %v", err)
   382  	}
   383  
   384  	cert, err := store.Current()
   385  	if err != nil {
   386  		t.Fatalf("Could not load certificate from disk: %v", err)
   387  	}
   388  	if cert == nil {
   389  		t.Fatalf("There was no error, but no certificate data was returned.")
   390  	}
   391  	if cert.Leaf == nil {
   392  		t.Fatalf("Got an empty leaf, expected private data.")
   393  	}
   394  }
   395  
   396  func TestCurrentCertKeyFiles(t *testing.T) {
   397  	prefix := "kubelet-server"
   398  	dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
   399  	if err != nil {
   400  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   401  	}
   402  	defer func() {
   403  		if err := os.RemoveAll(dir); err != nil {
   404  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   405  		}
   406  	}()
   407  	certFile := filepath.Join(dir, "kubelet.crt")
   408  	if err := os.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
   409  		t.Fatalf("Unable to create the file %q: %v", certFile, err)
   410  	}
   411  	keyFile := filepath.Join(dir, "kubelet.key")
   412  	if err := os.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
   413  		t.Fatalf("Unable to create the file %q: %v", keyFile, err)
   414  	}
   415  
   416  	store, err := NewFileStore(prefix, dir, dir, certFile, keyFile)
   417  	if err != nil {
   418  		t.Fatalf("Failed to initialize certificate store: %v", err)
   419  	}
   420  
   421  	cert, err := store.Current()
   422  	if err != nil {
   423  		t.Fatalf("Could not load certificate from disk: %v", err)
   424  	}
   425  	if cert == nil {
   426  		t.Fatalf("There was no error, but no certificate data was returned.")
   427  	}
   428  	if cert.Leaf == nil {
   429  		t.Fatalf("Got an empty leaf, expected private data.")
   430  	}
   431  }
   432  
   433  func TestCurrentTwoCerts(t *testing.T) {
   434  	prefix := "kubelet-server"
   435  	dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
   436  	if err != nil {
   437  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   438  	}
   439  	defer func() {
   440  		if err := os.RemoveAll(dir); err != nil {
   441  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   442  		}
   443  	}()
   444  	certFile := filepath.Join(dir, "kubelet.crt")
   445  	if err := os.WriteFile(certFile, storeTwoCertsData.certificatePEM, 0600); err != nil {
   446  		t.Fatalf("Unable to create the file %q: %v", certFile, err)
   447  	}
   448  	keyFile := filepath.Join(dir, "kubelet.key")
   449  	if err := os.WriteFile(keyFile, storeTwoCertsData.keyPEM, 0600); err != nil {
   450  		t.Fatalf("Unable to create the file %q: %v", keyFile, err)
   451  	}
   452  
   453  	store, err := NewFileStore(prefix, dir, dir, certFile, keyFile)
   454  	if err != nil {
   455  		t.Fatalf("Failed to initialize certificate store: %v", err)
   456  	}
   457  
   458  	cert, err := store.Current()
   459  	if err != nil {
   460  		t.Fatalf("Could not load certificate from disk: %v", err)
   461  	}
   462  	if cert == nil {
   463  		t.Fatalf("There was no error, but no certificate data was returned.")
   464  	}
   465  	if cert.Leaf == nil {
   466  		t.Fatalf("Got an empty leaf, expected private data.")
   467  	}
   468  	if len(cert.Certificate) != 2 {
   469  		t.Fatalf("Unexpected number of certificates, expected 2, got %v", len(cert.Certificate))
   470  	}
   471  }
   472  
   473  func TestCurrentNoFiles(t *testing.T) {
   474  	dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
   475  	if err != nil {
   476  		t.Fatalf("Unable to create the test directory %q: %v", dir, err)
   477  	}
   478  	defer func() {
   479  		if err := os.RemoveAll(dir); err != nil {
   480  			t.Errorf("Unable to clean up test directory %q: %v", dir, err)
   481  		}
   482  	}()
   483  
   484  	store, err := NewFileStore("kubelet-server", dir, dir, "", "")
   485  	if err != nil {
   486  		t.Fatalf("Failed to initialize certificate store: %v", err)
   487  	}
   488  
   489  	cert, err := store.Current()
   490  	if err == nil {
   491  		t.Fatalf("Got no error, expected an error because the cert/key files don't exist.")
   492  	}
   493  	if _, ok := err.(*NoCertKeyError); !ok {
   494  		t.Fatalf("Got error %v, expected NoCertKeyError.", err)
   495  	}
   496  	if cert != nil {
   497  		t.Fatalf("Got certificate, expected no certificate because the cert/key files don't exist.")
   498  	}
   499  }