k8s.io/kubernetes@v1.29.3/pkg/apis/certificates/v1beta1/defaults_test.go (about) 1 /* 2 Copyright 2020 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 v1beta1 18 19 import ( 20 "crypto/ed25519" 21 "crypto/rand" 22 "crypto/x509" 23 "crypto/x509/pkix" 24 "encoding/pem" 25 "net" 26 "net/url" 27 "reflect" 28 "testing" 29 30 capi "k8s.io/api/certificates/v1beta1" 31 ) 32 33 func TestIsKubeletServingCSR(t *testing.T) { 34 newCSR := func(base pemOptions, overlays ...pemOptions) *x509.CertificateRequest { 35 b := csrWithOpts(base, overlays...) 36 csr, err := ParseCSR(b) 37 if err != nil { 38 t.Fatal(err) 39 } 40 return csr 41 } 42 tests := map[string]struct { 43 req *x509.CertificateRequest 44 usages []capi.KeyUsage 45 exp bool 46 }{ 47 "defaults for kubelet-serving": { 48 req: newCSR(kubeletServerPEMOptions), 49 usages: kubeletServerUsages, 50 exp: true, 51 }, 52 "defaults without key encipherment for kubelet-serving": { 53 req: newCSR(kubeletServerPEMOptions), 54 usages: kubeletServerUsagesNoRSA, 55 exp: true, 56 }, 57 "does not default to kube-apiserver-client-kubelet if org is not 'system:nodes'": { 58 req: newCSR(kubeletServerPEMOptions, pemOptions{org: "not-system:nodes"}), 59 usages: kubeletServerUsages, 60 exp: false, 61 }, 62 "does not default to kubelet-serving if CN does not have system:node: prefix": { 63 req: newCSR(kubeletServerPEMOptions, pemOptions{cn: "notprefixed"}), 64 usages: kubeletServerUsages, 65 exp: false, 66 }, 67 "does not default to kubelet-serving if it has an unexpected usage": { 68 req: newCSR(kubeletServerPEMOptions), 69 usages: append(kubeletServerUsages, capi.UsageClientAuth), 70 exp: false, 71 }, 72 "does not default to kubelet-serving if it is missing an expected usage": { 73 req: newCSR(kubeletServerPEMOptions), 74 usages: kubeletServerUsages[1:], 75 exp: false, 76 }, 77 "does not default to kubelet-serving if it does not specify any dnsNames or ipAddresses": { 78 req: newCSR(kubeletServerPEMOptions, pemOptions{ipAddresses: []net.IP{}, dnsNames: []string{}}), 79 usages: kubeletServerUsages[1:], 80 exp: false, 81 }, 82 "does not default to kubelet-serving if it specifies a URI SAN": { 83 req: newCSR(kubeletServerPEMOptions, pemOptions{uris: []string{"http://something"}}), 84 usages: kubeletServerUsages, 85 exp: false, 86 }, 87 "does not default to kubelet-serving if it specifies an emailAddress SAN": { 88 req: newCSR(kubeletServerPEMOptions, pemOptions{emailAddresses: []string{"something"}}), 89 usages: kubeletServerUsages, 90 exp: false, 91 }, 92 } 93 for name, test := range tests { 94 t.Run(name, func(t *testing.T) { 95 got := IsKubeletServingCSR(test.req, test.usages) 96 if test.exp != got { 97 t.Errorf("unexpected IsKubeletClientCSR output: exp=%v, got=%v", test.exp, got) 98 } 99 }) 100 } 101 } 102 103 func TestIsKubeletClientCSR(t *testing.T) { 104 newCSR := func(base pemOptions, overlays ...pemOptions) *x509.CertificateRequest { 105 b := csrWithOpts(base, overlays...) 106 csr, err := ParseCSR(b) 107 if err != nil { 108 t.Fatal(err) 109 } 110 return csr 111 } 112 tests := map[string]struct { 113 req *x509.CertificateRequest 114 usages []capi.KeyUsage 115 exp bool 116 }{ 117 "defaults for kube-apiserver-client-kubelet": { 118 req: newCSR(kubeletClientPEMOptions), 119 usages: kubeletClientUsages, 120 exp: true, 121 }, 122 "does not default to kube-apiserver-client-kubelet if org is not 'system:nodes'": { 123 req: newCSR(kubeletClientPEMOptions, pemOptions{org: "not-system:nodes"}), 124 usages: kubeletClientUsages, 125 exp: false, 126 }, 127 "does not default to kube-apiserver-client-kubelet if a dnsName is set": { 128 req: newCSR(kubeletClientPEMOptions, pemOptions{dnsNames: []string{"something"}}), 129 usages: kubeletClientUsages, 130 exp: false, 131 }, 132 "does not default to kube-apiserver-client-kubelet if an emailAddress is set": { 133 req: newCSR(kubeletClientPEMOptions, pemOptions{emailAddresses: []string{"something"}}), 134 usages: kubeletClientUsages, 135 exp: false, 136 }, 137 "does not default to kube-apiserver-client-kubelet if a uri SAN is set": { 138 req: newCSR(kubeletClientPEMOptions, pemOptions{uris: []string{"http://something"}}), 139 usages: kubeletClientUsages, 140 exp: false, 141 }, 142 "does not default to kube-apiserver-client-kubelet if an ipAddress is set": { 143 req: newCSR(kubeletClientPEMOptions, pemOptions{ipAddresses: []net.IP{{0, 0, 0, 0}}}), 144 usages: kubeletClientUsages, 145 exp: false, 146 }, 147 "does not default to kube-apiserver-client-kubelet if CN does not have 'system:node:' prefix": { 148 req: newCSR(kubeletClientPEMOptions, pemOptions{cn: "not-prefixed"}), 149 usages: kubeletClientUsages, 150 exp: false, 151 }, 152 "does not default to kube-apiserver-client-kubelet if it has an unexpected usage": { 153 req: newCSR(kubeletClientPEMOptions), 154 usages: append(kubeletClientUsages, capi.UsageServerAuth), 155 exp: false, 156 }, 157 "does not default to kube-apiserver-client-kubelet if it is missing an expected usage": { 158 req: newCSR(kubeletClientPEMOptions), 159 usages: kubeletClientUsages[1:], 160 exp: false, 161 }, 162 "does not default to kube-apiserver-client-kubelet if it is missing an expected usage without key encipherment": { 163 req: newCSR(kubeletClientPEMOptions), 164 usages: kubeletClientUsagesNoRSA[1:], 165 exp: false, 166 }, 167 "default to kube-apiserver-client-kubelet without key encipherment": { 168 req: newCSR(kubeletClientPEMOptions), 169 usages: kubeletClientUsagesNoRSA, 170 exp: true, 171 }, 172 } 173 for name, test := range tests { 174 t.Run(name, func(t *testing.T) { 175 got := IsKubeletClientCSR(test.req, test.usages) 176 if test.exp != got { 177 t.Errorf("unexpected IsKubeletClientCSR output: exp=%v, got=%v", test.exp, got) 178 } 179 }) 180 } 181 } 182 183 var ( 184 kubeletClientUsages = []capi.KeyUsage{ 185 capi.UsageDigitalSignature, 186 capi.UsageKeyEncipherment, 187 capi.UsageClientAuth, 188 } 189 kubeletClientUsagesNoRSA = []capi.KeyUsage{ 190 capi.UsageDigitalSignature, 191 capi.UsageClientAuth, 192 } 193 kubeletClientPEMOptions = pemOptions{ 194 cn: "system:node:nodename", 195 org: "system:nodes", 196 } 197 198 kubeletServerUsages = []capi.KeyUsage{ 199 capi.UsageDigitalSignature, 200 capi.UsageKeyEncipherment, 201 capi.UsageServerAuth, 202 } 203 kubeletServerUsagesNoRSA = []capi.KeyUsage{ 204 capi.UsageDigitalSignature, 205 capi.UsageServerAuth, 206 } 207 kubeletServerPEMOptions = pemOptions{ 208 cn: "system:node:requester-name", 209 org: "system:nodes", 210 dnsNames: []string{"node-server-name"}, 211 ipAddresses: []net.IP{{0, 0, 0, 0}}, 212 } 213 ) 214 215 func TestSetDefaults_CertificateSigningRequestSpec(t *testing.T) { 216 strPtr := func(s string) *string { return &s } 217 tests := map[string]struct { 218 csr capi.CertificateSigningRequestSpec 219 expectedSignerName string 220 expectedUsages []capi.KeyUsage 221 }{ 222 "defaults to legacy-unknown if request is not a CSR": { 223 csr: capi.CertificateSigningRequestSpec{ 224 Request: []byte("invalid data"), 225 Usages: kubeletServerUsages, 226 }, 227 expectedSignerName: capi.LegacyUnknownSignerName, 228 }, 229 "does not default signerName if signerName is already set": { 230 csr: capi.CertificateSigningRequestSpec{ 231 Request: csrWithOpts(kubeletServerPEMOptions), 232 Usages: kubeletServerUsages, 233 SignerName: strPtr("example.com/not-kubelet-serving"), 234 }, 235 expectedSignerName: "example.com/not-kubelet-serving", 236 }, 237 "defaults usages if not set": { 238 csr: capi.CertificateSigningRequestSpec{ 239 Request: csrWithOpts(kubeletServerPEMOptions), 240 SignerName: strPtr("example.com/test"), 241 }, 242 expectedSignerName: "example.com/test", 243 expectedUsages: []capi.KeyUsage{capi.UsageDigitalSignature, capi.UsageKeyEncipherment}, 244 }, 245 } 246 for name, test := range tests { 247 t.Run(name, func(t *testing.T) { 248 // create a deepcopy to be sure we don't modify anything in-place 249 csrSpec := test.csr.DeepCopy() 250 SetDefaults_CertificateSigningRequestSpec(csrSpec) 251 if *csrSpec.SignerName != test.expectedSignerName { 252 t.Errorf("expected signerName to be defaulted to %q but it is %q", test.expectedSignerName, *csrSpec.SignerName) 253 } 254 255 // only check expectedUsages if it is non-nil 256 if test.expectedUsages != nil { 257 if !reflect.DeepEqual(test.expectedUsages, csrSpec.Usages) { 258 t.Errorf("expected usages to be defaulted to %v but it is %v", test.expectedUsages, csrSpec.Usages) 259 } 260 } 261 }) 262 } 263 } 264 265 func TestSetDefaults_CertificateSigningRequestSpec_KubeletServing(t *testing.T) { 266 tests := map[string]struct { 267 csr capi.CertificateSigningRequestSpec 268 expectedSignerName string 269 }{ 270 "defaults for kubelet-serving": { 271 csr: capi.CertificateSigningRequestSpec{ 272 Request: csrWithOpts(kubeletServerPEMOptions), 273 Usages: kubeletServerUsages, 274 Username: kubeletServerPEMOptions.cn, 275 }, 276 expectedSignerName: capi.KubeletServingSignerName, 277 }, 278 "does not default to kube-apiserver-client-kubelet if org is not 'system:nodes'": { 279 csr: capi.CertificateSigningRequestSpec{ 280 Request: csrWithOpts(kubeletServerPEMOptions, pemOptions{org: "not-system:nodes"}), 281 Usages: kubeletServerUsages, 282 Username: "system:node:not-requester-name", 283 }, 284 expectedSignerName: capi.LegacyUnknownSignerName, 285 }, 286 "does not default to kubelet-serving if CN does not have system:node: prefix": { 287 csr: capi.CertificateSigningRequestSpec{ 288 Request: csrWithOpts(kubeletServerPEMOptions, pemOptions{cn: "notprefixed"}), 289 Usages: kubeletServerUsages, 290 Username: "notprefixed", 291 }, 292 expectedSignerName: capi.LegacyUnknownSignerName, 293 }, 294 "does not default to kubelet-serving if it has an unexpected usage": { 295 csr: capi.CertificateSigningRequestSpec{ 296 Request: csrWithOpts(kubeletServerPEMOptions), 297 Usages: append(kubeletServerUsages, capi.UsageClientAuth), 298 Username: kubeletServerPEMOptions.cn, 299 }, 300 expectedSignerName: capi.LegacyUnknownSignerName, 301 }, 302 "does not default to kubelet-serving if it is missing an expected usage": { 303 csr: capi.CertificateSigningRequestSpec{ 304 Request: csrWithOpts(kubeletServerPEMOptions), 305 // Remove the first usage in 'kubeletServerUsages' 306 Usages: kubeletServerUsages[1:], 307 Username: kubeletServerPEMOptions.cn, 308 }, 309 expectedSignerName: capi.LegacyUnknownSignerName, 310 }, 311 "does not default to kubelet-serving if it does not specify any dnsNames or ipAddresses": { 312 csr: capi.CertificateSigningRequestSpec{ 313 Request: csrWithOpts(kubeletServerPEMOptions, pemOptions{ipAddresses: []net.IP{}, dnsNames: []string{}}), 314 Usages: kubeletServerUsages, 315 Username: kubeletServerPEMOptions.cn, 316 }, 317 expectedSignerName: capi.LegacyUnknownSignerName, 318 }, 319 "does not default to kubelet-serving if it specifies a URI SAN": { 320 csr: capi.CertificateSigningRequestSpec{ 321 Request: csrWithOpts(kubeletServerPEMOptions, pemOptions{uris: []string{"http://something"}}), 322 Usages: kubeletServerUsages, 323 Username: kubeletServerPEMOptions.cn, 324 }, 325 expectedSignerName: capi.LegacyUnknownSignerName, 326 }, 327 "does not default to kubelet-serving if it specifies an emailAddress SAN": { 328 csr: capi.CertificateSigningRequestSpec{ 329 Request: csrWithOpts(kubeletServerPEMOptions, pemOptions{emailAddresses: []string{"something"}}), 330 Usages: kubeletServerUsages, 331 Username: kubeletServerPEMOptions.cn, 332 }, 333 expectedSignerName: capi.LegacyUnknownSignerName, 334 }, 335 } 336 for name, test := range tests { 337 t.Run(name, func(t *testing.T) { 338 // create a deepcopy to be sure we don't modify anything in-place 339 csrSpec := test.csr.DeepCopy() 340 SetDefaults_CertificateSigningRequestSpec(csrSpec) 341 if *csrSpec.SignerName != test.expectedSignerName { 342 t.Errorf("expected signerName to be defaulted to %q but it is %q", test.expectedSignerName, *csrSpec.SignerName) 343 } 344 }) 345 } 346 } 347 348 func TestSetDefaults_CertificateSigningRequestSpec_KubeletClient(t *testing.T) { 349 tests := map[string]struct { 350 csr capi.CertificateSigningRequestSpec 351 expectedSignerName string 352 }{ 353 "defaults for kube-apiserver-client-kubelet": { 354 csr: capi.CertificateSigningRequestSpec{ 355 Request: csrWithOpts(kubeletClientPEMOptions), 356 Usages: kubeletClientUsages, 357 }, 358 expectedSignerName: capi.KubeAPIServerClientKubeletSignerName, 359 }, 360 "does not default to kube-apiserver-client-kubelet if org is not 'system:nodes'": { 361 csr: capi.CertificateSigningRequestSpec{ 362 Request: csrWithOpts(kubeletClientPEMOptions, pemOptions{org: "not-system:nodes"}), 363 Usages: kubeletClientUsages, 364 }, 365 expectedSignerName: capi.LegacyUnknownSignerName, 366 }, 367 "does not default to kube-apiserver-client-kubelet if a dnsName is set": { 368 csr: capi.CertificateSigningRequestSpec{ 369 Request: csrWithOpts(kubeletClientPEMOptions, pemOptions{dnsNames: []string{"something"}}), 370 Usages: kubeletClientUsages, 371 }, 372 expectedSignerName: capi.LegacyUnknownSignerName, 373 }, 374 "does not default to kube-apiserver-client-kubelet if an emailAddress is set": { 375 csr: capi.CertificateSigningRequestSpec{ 376 Request: csrWithOpts(kubeletClientPEMOptions, pemOptions{emailAddresses: []string{"something"}}), 377 Usages: kubeletClientUsages, 378 }, 379 expectedSignerName: capi.LegacyUnknownSignerName, 380 }, 381 "does not default to kube-apiserver-client-kubelet if a uri SAN is set": { 382 csr: capi.CertificateSigningRequestSpec{ 383 Request: csrWithOpts(kubeletClientPEMOptions, pemOptions{uris: []string{"http://something"}}), 384 Usages: kubeletClientUsages, 385 }, 386 expectedSignerName: capi.LegacyUnknownSignerName, 387 }, 388 "does not default to kube-apiserver-client-kubelet if an ipAddress is set": { 389 csr: capi.CertificateSigningRequestSpec{ 390 Request: csrWithOpts(kubeletClientPEMOptions, pemOptions{ipAddresses: []net.IP{{0, 0, 0, 0}}}), 391 Usages: kubeletClientUsages, 392 }, 393 expectedSignerName: capi.LegacyUnknownSignerName, 394 }, 395 "does not default to kube-apiserver-client-kubelet if CN does not have 'system:node:' prefix": { 396 csr: capi.CertificateSigningRequestSpec{ 397 Request: csrWithOpts(kubeletClientPEMOptions, pemOptions{cn: "not-prefixed"}), 398 Usages: kubeletClientUsages, 399 }, 400 expectedSignerName: capi.LegacyUnknownSignerName, 401 }, 402 "does not default to kube-apiserver-client-kubelet if it has an unexpected usage": { 403 csr: capi.CertificateSigningRequestSpec{ 404 Request: csrWithOpts(kubeletClientPEMOptions), 405 Usages: append(kubeletClientUsages, capi.UsageServerAuth), 406 }, 407 expectedSignerName: capi.LegacyUnknownSignerName, 408 }, 409 "does not default to kube-apiserver-client-kubelet if it is missing an expected usage": { 410 csr: capi.CertificateSigningRequestSpec{ 411 Request: csrWithOpts(kubeletClientPEMOptions), 412 // Remove the first usage in 'kubeletClientUsages' 413 Usages: kubeletClientUsages[1:], 414 }, 415 expectedSignerName: capi.LegacyUnknownSignerName, 416 }, 417 } 418 for name, test := range tests { 419 t.Run(name, func(t *testing.T) { 420 // create a deepcopy to be sure we don't modify anything in-place 421 csrSpec := test.csr.DeepCopy() 422 SetDefaults_CertificateSigningRequestSpec(csrSpec) 423 if *csrSpec.SignerName != test.expectedSignerName { 424 t.Errorf("expected signerName to be defaulted to %q but it is %q", test.expectedSignerName, *csrSpec.SignerName) 425 } 426 }) 427 } 428 } 429 430 type pemOptions struct { 431 cn string 432 org string 433 ipAddresses []net.IP 434 dnsNames []string 435 emailAddresses []string 436 uris []string 437 } 438 439 // overlayPEMOptions overlays one set of pemOptions on top of another to allow 440 // for easily overriding a single field in the options 441 func overlayPEMOptions(opts ...pemOptions) pemOptions { 442 if len(opts) == 0 { 443 return pemOptions{} 444 } 445 base := opts[0] 446 for _, opt := range opts[1:] { 447 if opt.cn != "" { 448 base.cn = opt.cn 449 } 450 if opt.org != "" { 451 base.org = opt.org 452 } 453 if opt.ipAddresses != nil { 454 base.ipAddresses = opt.ipAddresses 455 } 456 if opt.dnsNames != nil { 457 base.dnsNames = opt.dnsNames 458 } 459 if opt.emailAddresses != nil { 460 base.emailAddresses = opt.emailAddresses 461 } 462 if opt.uris != nil { 463 base.uris = opt.uris 464 } 465 } 466 return base 467 } 468 469 func csrWithOpts(base pemOptions, overlays ...pemOptions) []byte { 470 opts := overlayPEMOptions(append([]pemOptions{base}, overlays...)...) 471 uris := make([]*url.URL, len(opts.uris)) 472 for i, s := range opts.uris { 473 u, err := url.ParseRequestURI(s) 474 if err != nil { 475 panic(err) 476 } 477 uris[i] = u 478 } 479 template := &x509.CertificateRequest{ 480 Subject: pkix.Name{ 481 CommonName: opts.cn, 482 Organization: []string{opts.org}, 483 }, 484 IPAddresses: opts.ipAddresses, 485 DNSNames: opts.dnsNames, 486 EmailAddresses: opts.emailAddresses, 487 URIs: uris, 488 } 489 490 _, key, err := ed25519.GenerateKey(rand.Reader) 491 if err != nil { 492 panic(err) 493 } 494 495 csrDER, err := x509.CreateCertificateRequest(rand.Reader, template, key) 496 if err != nil { 497 panic(err) 498 } 499 500 csrPemBlock := &pem.Block{ 501 Type: "CERTIFICATE REQUEST", 502 Bytes: csrDER, 503 } 504 505 p := pem.EncodeToMemory(csrPemBlock) 506 if p == nil { 507 panic("invalid pem block") 508 } 509 510 return p 511 }