k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/phases/certs/certlist_test.go (about) 1 /* 2 Copyright 2018 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 certs 18 19 import ( 20 "crypto" 21 "crypto/tls" 22 "crypto/x509" 23 "os" 24 "path/filepath" 25 "testing" 26 "time" 27 28 "github.com/google/go-cmp/cmp" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 certutil "k8s.io/client-go/util/cert" 31 32 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" 33 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" 34 "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" 35 ) 36 37 func TestCertListOrder(t *testing.T) { 38 tests := []struct { 39 certs Certificates 40 name string 41 }{ 42 { 43 name: "Default Certificate List", 44 certs: GetDefaultCertList(), 45 }, 46 { 47 name: "Cert list less etcd", 48 certs: GetCertsWithoutEtcd(), 49 }, 50 } 51 52 for _, test := range tests { 53 t.Run(test.name, func(t *testing.T) { 54 var lastCA *KubeadmCert 55 for i, cert := range test.certs { 56 if i > 0 && lastCA == nil { 57 t.Fatalf("CA not present in list before certificate %q", cert.Name) 58 } 59 if cert.CAName == "" { 60 lastCA = cert 61 } else { 62 if cert.CAName != lastCA.Name { 63 t.Fatalf("expected CA name %q, got %q, for certificate %q", lastCA.Name, cert.CAName, cert.Name) 64 } 65 } 66 } 67 }) 68 } 69 } 70 71 func TestCAPointersValid(t *testing.T) { 72 tests := []struct { 73 certs Certificates 74 name string 75 }{ 76 { 77 name: "Default Certificate List", 78 certs: GetDefaultCertList(), 79 }, 80 { 81 name: "Cert list less etcd", 82 certs: GetCertsWithoutEtcd(), 83 }, 84 } 85 86 for _, test := range tests { 87 t.Run(test.name, func(t *testing.T) { 88 89 certMap := test.certs.AsMap() 90 91 for _, cert := range test.certs { 92 if cert.CAName != "" && certMap[cert.CAName] == nil { 93 t.Errorf("Certificate %q references nonexistent CA %q", cert.Name, cert.CAName) 94 } 95 } 96 }) 97 } 98 } 99 100 func TestMakeCertTree(t *testing.T) { 101 rootCert := &KubeadmCert{ 102 Name: "root", 103 } 104 leaf0 := &KubeadmCert{ 105 Name: "leaf0", 106 CAName: "root", 107 } 108 leaf1 := &KubeadmCert{ 109 Name: "leaf1", 110 CAName: "root", 111 } 112 selfSigned := &KubeadmCert{ 113 Name: "self-signed", 114 } 115 116 certMap := CertificateMap{ 117 "root": rootCert, 118 "leaf0": leaf0, 119 "leaf1": leaf1, 120 "self-signed": selfSigned, 121 } 122 123 orphanCertMap := CertificateMap{ 124 "leaf0": leaf0, 125 } 126 127 if _, err := orphanCertMap.CertTree(); err == nil { 128 t.Error("expected orphan cert map to error, but got nil") 129 } 130 131 certTree, err := certMap.CertTree() 132 t.Logf("cert tree: %v", certTree) 133 if err != nil { 134 t.Errorf("expected no error, but got %v", err) 135 } 136 137 if len(certTree) != 2 { 138 t.Errorf("Expected tree to have 2 roots, got %d", len(certTree)) 139 } 140 141 if len(certTree[rootCert]) != 2 { 142 t.Errorf("Expected root to have 2 leaves, got %d", len(certTree[rootCert])) 143 } 144 145 if _, ok := certTree[selfSigned]; !ok { 146 t.Error("Expected selfSigned to be present in tree, but missing") 147 } 148 } 149 150 func TestCreateCertificateChain(t *testing.T) { 151 dir, err := os.MkdirTemp("", t.Name()) 152 if err != nil { 153 t.Fatal(err) 154 } 155 defer os.RemoveAll(dir) 156 157 ic := &kubeadmapi.InitConfiguration{ 158 NodeRegistration: kubeadmapi.NodeRegistrationOptions{ 159 Name: "test-node", 160 }, 161 ClusterConfiguration: kubeadmapi.ClusterConfiguration{ 162 CertificatesDir: dir, 163 }, 164 } 165 166 caCfg := Certificates{ 167 { 168 config: pkiutil.CertConfig{}, 169 Name: "test-ca", 170 BaseName: "test-ca", 171 }, 172 { 173 config: pkiutil.CertConfig{ 174 Config: certutil.Config{ 175 AltNames: certutil.AltNames{ 176 DNSNames: []string{"test-domain.space"}, 177 }, 178 Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, 179 }, 180 }, 181 configMutators: []configMutatorsFunc{ 182 setCommonNameToNodeName(), 183 }, 184 CAName: "test-ca", 185 Name: "test-daughter", 186 BaseName: "test-daughter", 187 }, 188 } 189 190 certTree, err := caCfg.AsMap().CertTree() 191 if err != nil { 192 t.Fatalf("unexpected error getting tree: %v", err) 193 } 194 195 if err := certTree.CreateTree(ic); err != nil { 196 t.Fatal(err) 197 } 198 199 caCert, _ := parseCertAndKey(filepath.Join(dir, "test-ca"), t) 200 daughterCert, _ := parseCertAndKey(filepath.Join(dir, "test-daughter"), t) 201 202 pool := x509.NewCertPool() 203 pool.AddCert(caCert) 204 205 _, err = daughterCert.Verify(x509.VerifyOptions{ 206 DNSName: "test-domain.space", 207 Roots: pool, 208 KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, 209 }) 210 if err != nil { 211 t.Errorf("couldn't verify daughter cert: %v", err) 212 } 213 214 } 215 216 func parseCertAndKey(basePath string, t *testing.T) (*x509.Certificate, crypto.PrivateKey) { 217 certPair, err := tls.LoadX509KeyPair(basePath+".crt", basePath+".key") 218 if err != nil { 219 t.Fatalf("couldn't parse certificate and key: %v", err) 220 } 221 222 parsedCert, err := x509.ParseCertificate(certPair.Certificate[0]) 223 if err != nil { 224 t.Fatalf("couldn't parse certificate: %v", err) 225 } 226 227 return parsedCert, certPair.PrivateKey 228 } 229 230 func TestCreateKeyAndCSR(t *testing.T) { 231 dir, err := os.MkdirTemp("", t.Name()) 232 if err != nil { 233 t.Fatal(err) 234 } 235 defer os.RemoveAll(dir) 236 237 validKubeadmConfig := &kubeadmapi.InitConfiguration{ 238 NodeRegistration: kubeadmapi.NodeRegistrationOptions{ 239 Name: "test-node", 240 }, 241 ClusterConfiguration: kubeadmapi.ClusterConfiguration{ 242 CertificatesDir: dir, 243 }, 244 } 245 validKubeadmCert := &KubeadmCert{ 246 Name: "ca", 247 LongName: "self-signed Kubernetes CA to provision identities for other Kubernetes components", 248 BaseName: kubeadmconstants.CACertAndKeyBaseName, 249 config: pkiutil.CertConfig{ 250 Config: certutil.Config{ 251 CommonName: "kubernetes", 252 }, 253 }, 254 } 255 256 type args struct { 257 kubeadmConfig *kubeadmapi.InitConfiguration 258 cert *KubeadmCert 259 } 260 tests := []struct { 261 name string 262 args args 263 wantErr bool 264 createfile bool 265 }{ 266 { 267 name: "kubeadmConfig is nil", 268 args: args{ 269 kubeadmConfig: nil, 270 cert: validKubeadmCert, 271 }, 272 wantErr: true, 273 }, 274 { 275 name: "cert is nil", 276 args: args{ 277 kubeadmConfig: validKubeadmConfig, 278 cert: nil, 279 }, 280 wantErr: true, 281 }, 282 { 283 name: "key and CSR do not exist", 284 args: args{ 285 kubeadmConfig: validKubeadmConfig, 286 cert: validKubeadmCert, 287 }, 288 wantErr: false, 289 }, 290 { 291 name: "key or CSR already exist", 292 args: args{ 293 kubeadmConfig: validKubeadmConfig, 294 cert: validKubeadmCert, 295 }, 296 wantErr: true, 297 }, 298 } 299 for _, tt := range tests { 300 t.Run(tt.name, func(t *testing.T) { 301 if err := createKeyAndCSR(tt.args.kubeadmConfig, tt.args.cert); (err != nil) != tt.wantErr { 302 t.Errorf("createKeyAndCSR() error = %v, wantErr %v", err, tt.wantErr) 303 } 304 }) 305 } 306 } 307 308 func TestGetConfig(t *testing.T) { 309 var ( 310 now = time.Now() 311 backdate = kubeadmconstants.CertificateBackdate 312 ) 313 314 tests := []struct { 315 name string 316 cert *KubeadmCert 317 cfg *kubeadmapi.InitConfiguration 318 expectedConfig *pkiutil.CertConfig 319 }{ 320 { 321 name: "encryption algorithm is set", 322 cert: &KubeadmCert{ 323 creationTime: now, 324 }, 325 cfg: &kubeadmapi.InitConfiguration{ 326 ClusterConfiguration: kubeadmapi.ClusterConfiguration{ 327 EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSAP256, 328 }, 329 }, 330 expectedConfig: &pkiutil.CertConfig{ 331 Config: certutil.Config{ 332 NotBefore: now.Add(-backdate), 333 }, 334 EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSAP256, 335 }, 336 }, 337 { 338 name: "cert validity is set", 339 cert: &KubeadmCert{ 340 CAName: "some-ca", 341 creationTime: now, 342 }, 343 cfg: &kubeadmapi.InitConfiguration{ 344 ClusterConfiguration: kubeadmapi.ClusterConfiguration{ 345 CertificateValidityPeriod: &metav1.Duration{ 346 Duration: time.Hour * 1, 347 }, 348 }, 349 }, 350 expectedConfig: &pkiutil.CertConfig{ 351 Config: certutil.Config{ 352 NotBefore: now.Add(-backdate), 353 }, 354 NotAfter: now.Add(time.Hour * 1)}, 355 }, 356 { 357 name: "CA cert validity is set", 358 cert: &KubeadmCert{ 359 CAName: "", 360 creationTime: now, 361 }, 362 cfg: &kubeadmapi.InitConfiguration{ 363 ClusterConfiguration: kubeadmapi.ClusterConfiguration{ 364 CACertificateValidityPeriod: &metav1.Duration{ 365 Duration: time.Hour * 10, 366 }, 367 }, 368 }, 369 expectedConfig: &pkiutil.CertConfig{ 370 Config: certutil.Config{ 371 NotBefore: now.Add(-backdate), 372 }, 373 NotAfter: now.Add(time.Hour * 10), 374 }, 375 }, 376 } 377 for _, tt := range tests { 378 t.Run(tt.name, func(t *testing.T) { 379 actual, err := tt.cert.GetConfig(tt.cfg) 380 if err != nil { 381 t.Fatalf("unexpected error: %v", err) 382 } 383 if diff := cmp.Diff(actual, tt.expectedConfig); diff != "" { 384 t.Fatalf("GetConfig() returned diff (-want,+got):\n%s", diff) 385 } 386 }) 387 } 388 }