mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-30 21:30:16 +00:00 
			
		
		
		
	Merge pull request #111061 from pacoxu/key-encipherment-optional
modify the signing/approving controller to tolerate either set of usages for kubelet client and serving certificates
This commit is contained in:
		| @@ -50,16 +50,22 @@ var ( | ||||
| 	uriSANNotAllowedErr           = fmt.Errorf("URI subjectAltNames are not allowed") | ||||
| ) | ||||
|  | ||||
| var kubeletServingRequiredUsages = sets.NewString( | ||||
| 	string(UsageDigitalSignature), | ||||
| 	string(UsageKeyEncipherment), | ||||
| 	string(UsageServerAuth), | ||||
| var ( | ||||
| 	kubeletServingRequiredUsages = sets.NewString( | ||||
| 		string(UsageDigitalSignature), | ||||
| 		string(UsageKeyEncipherment), | ||||
| 		string(UsageServerAuth), | ||||
| 	) | ||||
| 	kubeletServingRequiredUsagesNoRSA = sets.NewString( | ||||
| 		string(UsageDigitalSignature), | ||||
| 		string(UsageServerAuth), | ||||
| 	) | ||||
| ) | ||||
|  | ||||
| func IsKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) bool { | ||||
| 	return ValidateKubeletServingCSR(req, usages) == nil | ||||
| func IsKubeletServingCSR(req *x509.CertificateRequest, usages sets.String, allowOmittingUsageKeyEncipherment bool) bool { | ||||
| 	return ValidateKubeletServingCSR(req, usages, allowOmittingUsageKeyEncipherment) == nil | ||||
| } | ||||
| func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) error { | ||||
| func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String, allowOmittingUsageKeyEncipherment bool) error { | ||||
| 	if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) { | ||||
| 		return organizationNotSystemNodesErr | ||||
| 	} | ||||
| @@ -76,8 +82,14 @@ func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) | ||||
| 		return uriSANNotAllowedErr | ||||
| 	} | ||||
|  | ||||
| 	if !kubeletServingRequiredUsages.Equal(usages) { | ||||
| 		return fmt.Errorf("usages did not match %v", kubeletServingRequiredUsages.List()) | ||||
| 	if allowOmittingUsageKeyEncipherment { | ||||
| 		if !kubeletServingRequiredUsages.Equal(usages) && !kubeletServingRequiredUsagesNoRSA.Equal(usages) { | ||||
| 			return fmt.Errorf("usages did not match %v", kubeletServingRequiredUsages.List()) | ||||
| 		} | ||||
| 	} else { | ||||
| 		if !kubeletServingRequiredUsages.Equal(usages) { | ||||
| 			return fmt.Errorf("usages did not match %v", kubeletServingRequiredUsages.List()) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if !strings.HasPrefix(req.Subject.CommonName, "system:node:") { | ||||
| @@ -87,16 +99,22 @@ func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| var kubeletClientRequiredUsages = sets.NewString( | ||||
| 	string(UsageDigitalSignature), | ||||
| 	string(UsageKeyEncipherment), | ||||
| 	string(UsageClientAuth), | ||||
| var ( | ||||
| 	kubeletClientRequiredUsagesNoRSA = sets.NewString( | ||||
| 		string(UsageDigitalSignature), | ||||
| 		string(UsageClientAuth), | ||||
| 	) | ||||
| 	kubeletClientRequiredUsages = sets.NewString( | ||||
| 		string(UsageDigitalSignature), | ||||
| 		string(UsageKeyEncipherment), | ||||
| 		string(UsageClientAuth), | ||||
| 	) | ||||
| ) | ||||
|  | ||||
| func IsKubeletClientCSR(req *x509.CertificateRequest, usages sets.String) bool { | ||||
| 	return ValidateKubeletClientCSR(req, usages) == nil | ||||
| func IsKubeletClientCSR(req *x509.CertificateRequest, usages sets.String, allowOmittingUsageKeyEncipherment bool) bool { | ||||
| 	return ValidateKubeletClientCSR(req, usages, allowOmittingUsageKeyEncipherment) == nil | ||||
| } | ||||
| func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages sets.String) error { | ||||
| func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages sets.String, allowOmittingUsageKeyEncipherment bool) error { | ||||
| 	if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) { | ||||
| 		return organizationNotSystemNodesErr | ||||
| 	} | ||||
| @@ -118,8 +136,14 @@ func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages sets.String) | ||||
| 		return commonNameNotSystemNode | ||||
| 	} | ||||
|  | ||||
| 	if !kubeletClientRequiredUsages.Equal(usages) { | ||||
| 		return fmt.Errorf("usages did not match %v", kubeletClientRequiredUsages.List()) | ||||
| 	if allowOmittingUsageKeyEncipherment { | ||||
| 		if !kubeletClientRequiredUsages.Equal(usages) && !kubeletClientRequiredUsagesNoRSA.Equal(usages) { | ||||
| 			return fmt.Errorf("usages did not match %v", kubeletClientRequiredUsages.List()) | ||||
| 		} | ||||
| 	} else { | ||||
| 		if !kubeletClientRequiredUsages.Equal(usages) { | ||||
| 			return fmt.Errorf("usages did not match %v", kubeletClientRequiredUsages.List()) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
|   | ||||
| @@ -56,27 +56,27 @@ func DefaultSignerNameFromSpec(obj *certificatesv1beta1.CertificateSigningReques | ||||
| 		// Set the signerName to 'legacy-unknown' as the CSR could not be | ||||
| 		// recognised. | ||||
| 		return certificatesv1beta1.LegacyUnknownSignerName | ||||
| 	case IsKubeletClientCSR(csr, obj.Usages): | ||||
| 	case IsKubeletClientCSR(csr, obj.Usages, false): | ||||
| 		return certificatesv1beta1.KubeAPIServerClientKubeletSignerName | ||||
| 	case IsKubeletServingCSR(csr, obj.Usages): | ||||
| 	case IsKubeletServingCSR(csr, obj.Usages, false): | ||||
| 		return certificatesv1beta1.KubeletServingSignerName | ||||
| 	default: | ||||
| 		return certificatesv1beta1.LegacyUnknownSignerName | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func IsKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) bool { | ||||
| 	return certificates.IsKubeletServingCSR(req, usagesToSet(usages)) | ||||
| func IsKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage, allowOmittingUsageKeyEncipherment bool) bool { | ||||
| 	return certificates.IsKubeletServingCSR(req, usagesToSet(usages), allowOmittingUsageKeyEncipherment) | ||||
| } | ||||
| func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) error { | ||||
| 	return certificates.ValidateKubeletServingCSR(req, usagesToSet(usages)) | ||||
| func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage, allowOmittingUsageKeyEncipherment bool) error { | ||||
| 	return certificates.ValidateKubeletServingCSR(req, usagesToSet(usages), allowOmittingUsageKeyEncipherment) | ||||
| } | ||||
|  | ||||
| func IsKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) bool { | ||||
| 	return certificates.IsKubeletClientCSR(req, usagesToSet(usages)) | ||||
| func IsKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage, allowOmittingUsageKeyEncipherment bool) bool { | ||||
| 	return certificates.IsKubeletClientCSR(req, usagesToSet(usages), allowOmittingUsageKeyEncipherment) | ||||
| } | ||||
| func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage) error { | ||||
| 	return certificates.ValidateKubeletClientCSR(req, usagesToSet(usages)) | ||||
| func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages []certificatesv1beta1.KeyUsage, allowOmittingUsageKeyEncipherment bool) error { | ||||
| 	return certificates.ValidateKubeletClientCSR(req, usagesToSet(usages), allowOmittingUsageKeyEncipherment) | ||||
| } | ||||
|  | ||||
| func usagesToSet(usages []certificatesv1beta1.KeyUsage) sets.String { | ||||
|   | ||||
| @@ -40,15 +40,28 @@ func TestIsKubeletServingCSR(t *testing.T) { | ||||
| 		return csr | ||||
| 	} | ||||
| 	tests := map[string]struct { | ||||
| 		req    *x509.CertificateRequest | ||||
| 		usages []capi.KeyUsage | ||||
| 		exp    bool | ||||
| 		req                               *x509.CertificateRequest | ||||
| 		usages                            []capi.KeyUsage | ||||
| 		allowOmittingUsageKeyEncipherment bool | ||||
| 		exp                               bool | ||||
| 	}{ | ||||
| 		"defaults for kubelet-serving": { | ||||
| 			req:    newCSR(kubeletServerPEMOptions), | ||||
| 			usages: kubeletServerUsages, | ||||
| 			exp:    true, | ||||
| 		}, | ||||
| 		"defaults without key encipherment for kubelet-serving if allow omitting key encipherment": { | ||||
| 			req:                               newCSR(kubeletServerPEMOptions), | ||||
| 			usages:                            kubeletServerUsagesNoRSA, | ||||
| 			allowOmittingUsageKeyEncipherment: true, | ||||
| 			exp:                               true, | ||||
| 		}, | ||||
| 		"defaults for kubelet-serving if allow omitting key encipherment": { | ||||
| 			req:                               newCSR(kubeletServerPEMOptions), | ||||
| 			usages:                            kubeletServerUsages, | ||||
| 			allowOmittingUsageKeyEncipherment: true, | ||||
| 			exp:                               true, | ||||
| 		}, | ||||
| 		"does not default to kube-apiserver-client-kubelet if org is not 'system:nodes'": { | ||||
| 			req:    newCSR(kubeletServerPEMOptions, pemOptions{org: "not-system:nodes"}), | ||||
| 			usages: kubeletServerUsages, | ||||
| @@ -69,6 +82,17 @@ func TestIsKubeletServingCSR(t *testing.T) { | ||||
| 			usages: kubeletServerUsages[1:], | ||||
| 			exp:    false, | ||||
| 		}, | ||||
| 		"does not default to kubelet-serving if it is missing an expected usage if allow omitting key encipherment": { | ||||
| 			req:                               newCSR(kubeletServerPEMOptions), | ||||
| 			usages:                            kubeletServerUsagesNoRSA[1:], | ||||
| 			allowOmittingUsageKeyEncipherment: true, | ||||
| 			exp:                               false, | ||||
| 		}, | ||||
| 		"does not default to kubelet-serving if it is missing an expected usage withou key encipherment": { | ||||
| 			req:    newCSR(kubeletServerPEMOptions), | ||||
| 			usages: kubeletServerUsagesNoRSA, | ||||
| 			exp:    false, | ||||
| 		}, | ||||
| 		"does not default to kubelet-serving if it does not specify any dnsNames or ipAddresses": { | ||||
| 			req:    newCSR(kubeletServerPEMOptions, pemOptions{ipAddresses: []net.IP{}, dnsNames: []string{}}), | ||||
| 			usages: kubeletServerUsages[1:], | ||||
| @@ -87,7 +111,7 @@ func TestIsKubeletServingCSR(t *testing.T) { | ||||
| 	} | ||||
| 	for name, test := range tests { | ||||
| 		t.Run(name, func(t *testing.T) { | ||||
| 			got := IsKubeletServingCSR(test.req, test.usages) | ||||
| 			got := IsKubeletServingCSR(test.req, test.usages, test.allowOmittingUsageKeyEncipherment) | ||||
| 			if test.exp != got { | ||||
| 				t.Errorf("unexpected IsKubeletClientCSR output: exp=%v, got=%v", test.exp, got) | ||||
| 			} | ||||
| @@ -105,9 +129,10 @@ func TestIsKubeletClientCSR(t *testing.T) { | ||||
| 		return csr | ||||
| 	} | ||||
| 	tests := map[string]struct { | ||||
| 		req    *x509.CertificateRequest | ||||
| 		usages []capi.KeyUsage | ||||
| 		exp    bool | ||||
| 		req                               *x509.CertificateRequest | ||||
| 		usages                            []capi.KeyUsage | ||||
| 		allowOmittingUsageKeyEncipherment bool | ||||
| 		exp                               bool | ||||
| 	}{ | ||||
| 		"defaults for kube-apiserver-client-kubelet": { | ||||
| 			req:    newCSR(kubeletClientPEMOptions), | ||||
| @@ -154,10 +179,28 @@ func TestIsKubeletClientCSR(t *testing.T) { | ||||
| 			usages: kubeletClientUsages[1:], | ||||
| 			exp:    false, | ||||
| 		}, | ||||
| 		"does not default to kube-apiserver-client-kubelet if it is missing an expected usage without key encipherment": { | ||||
| 			req:                               newCSR(kubeletClientPEMOptions), | ||||
| 			usages:                            kubeletClientUsagesNoRSA[1:], | ||||
| 			allowOmittingUsageKeyEncipherment: true, | ||||
| 			exp:                               false, | ||||
| 		}, | ||||
| 		"default to kube-apiserver-client-kubelet with key encipherment": { | ||||
| 			req:                               newCSR(kubeletClientPEMOptions), | ||||
| 			usages:                            kubeletClientUsages, | ||||
| 			allowOmittingUsageKeyEncipherment: true, | ||||
| 			exp:                               true, | ||||
| 		}, | ||||
| 		"default to kube-apiserver-client-kubelet without key encipherment": { | ||||
| 			req:                               newCSR(kubeletClientPEMOptions), | ||||
| 			usages:                            kubeletClientUsagesNoRSA, | ||||
| 			allowOmittingUsageKeyEncipherment: true, | ||||
| 			exp:                               true, | ||||
| 		}, | ||||
| 	} | ||||
| 	for name, test := range tests { | ||||
| 		t.Run(name, func(t *testing.T) { | ||||
| 			got := IsKubeletClientCSR(test.req, test.usages) | ||||
| 			got := IsKubeletClientCSR(test.req, test.usages, test.allowOmittingUsageKeyEncipherment) | ||||
| 			if test.exp != got { | ||||
| 				t.Errorf("unexpected IsKubeletClientCSR output: exp=%v, got=%v", test.exp, got) | ||||
| 			} | ||||
| @@ -171,6 +214,10 @@ var ( | ||||
| 		capi.UsageKeyEncipherment, | ||||
| 		capi.UsageClientAuth, | ||||
| 	} | ||||
| 	kubeletClientUsagesNoRSA = []capi.KeyUsage{ | ||||
| 		capi.UsageDigitalSignature, | ||||
| 		capi.UsageClientAuth, | ||||
| 	} | ||||
| 	kubeletClientPEMOptions = pemOptions{ | ||||
| 		cn:  "system:node:nodename", | ||||
| 		org: "system:nodes", | ||||
| @@ -181,6 +228,10 @@ var ( | ||||
| 		capi.UsageKeyEncipherment, | ||||
| 		capi.UsageServerAuth, | ||||
| 	} | ||||
| 	kubeletServerUsagesNoRSA = []capi.KeyUsage{ | ||||
| 		capi.UsageDigitalSignature, | ||||
| 		capi.UsageServerAuth, | ||||
| 	} | ||||
| 	kubeletServerPEMOptions = pemOptions{ | ||||
| 		cn:          "system:node:requester-name", | ||||
| 		org:         "system:nodes", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user