k8s.io/kubernetes@v1.29.3/pkg/apis/certificates/helpers.go (about) 1 /* 2 Copyright 2016 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 certificates 18 19 import ( 20 "crypto/x509" 21 "encoding/pem" 22 "errors" 23 "fmt" 24 "reflect" 25 "strings" 26 27 "k8s.io/apimachinery/pkg/util/sets" 28 ) 29 30 // ParseCSR extracts the CSR from the bytes and decodes it. 31 func ParseCSR(pemBytes []byte) (*x509.CertificateRequest, error) { 32 block, _ := pem.Decode(pemBytes) 33 if block == nil || block.Type != "CERTIFICATE REQUEST" { 34 return nil, errors.New("PEM block type must be CERTIFICATE REQUEST") 35 } 36 csr, err := x509.ParseCertificateRequest(block.Bytes) 37 if err != nil { 38 return nil, err 39 } 40 return csr, nil 41 } 42 43 var ( 44 organizationNotSystemNodesErr = fmt.Errorf("subject organization is not system:nodes") 45 commonNameNotSystemNode = fmt.Errorf("subject common name does not begin with system:node:") 46 dnsOrIPSANRequiredErr = fmt.Errorf("DNS or IP subjectAltName is required") 47 dnsSANNotAllowedErr = fmt.Errorf("DNS subjectAltNames are not allowed") 48 emailSANNotAllowedErr = fmt.Errorf("Email subjectAltNames are not allowed") 49 ipSANNotAllowedErr = fmt.Errorf("IP subjectAltNames are not allowed") 50 uriSANNotAllowedErr = fmt.Errorf("URI subjectAltNames are not allowed") 51 ) 52 53 var ( 54 kubeletServingRequiredUsages = sets.NewString( 55 string(UsageDigitalSignature), 56 string(UsageKeyEncipherment), 57 string(UsageServerAuth), 58 ) 59 kubeletServingRequiredUsagesNoRSA = sets.NewString( 60 string(UsageDigitalSignature), 61 string(UsageServerAuth), 62 ) 63 ) 64 65 func IsKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) bool { 66 return ValidateKubeletServingCSR(req, usages) == nil 67 } 68 func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) error { 69 if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) { 70 return organizationNotSystemNodesErr 71 } 72 73 // at least one of dnsNames or ipAddresses must be specified 74 if len(req.DNSNames) == 0 && len(req.IPAddresses) == 0 { 75 return dnsOrIPSANRequiredErr 76 } 77 78 if len(req.EmailAddresses) > 0 { 79 return emailSANNotAllowedErr 80 } 81 if len(req.URIs) > 0 { 82 return uriSANNotAllowedErr 83 } 84 85 if !kubeletServingRequiredUsages.Equal(usages) && !kubeletServingRequiredUsagesNoRSA.Equal(usages) { 86 return fmt.Errorf("usages did not match %v", kubeletServingRequiredUsages.List()) 87 } 88 89 if !strings.HasPrefix(req.Subject.CommonName, "system:node:") { 90 return commonNameNotSystemNode 91 } 92 93 return nil 94 } 95 96 var ( 97 kubeletClientRequiredUsagesNoRSA = sets.NewString( 98 string(UsageDigitalSignature), 99 string(UsageClientAuth), 100 ) 101 kubeletClientRequiredUsages = sets.NewString( 102 string(UsageDigitalSignature), 103 string(UsageKeyEncipherment), 104 string(UsageClientAuth), 105 ) 106 ) 107 108 func IsKubeletClientCSR(req *x509.CertificateRequest, usages sets.String) bool { 109 return ValidateKubeletClientCSR(req, usages) == nil 110 } 111 func ValidateKubeletClientCSR(req *x509.CertificateRequest, usages sets.String) error { 112 if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) { 113 return organizationNotSystemNodesErr 114 } 115 116 if len(req.DNSNames) > 0 { 117 return dnsSANNotAllowedErr 118 } 119 if len(req.EmailAddresses) > 0 { 120 return emailSANNotAllowedErr 121 } 122 if len(req.IPAddresses) > 0 { 123 return ipSANNotAllowedErr 124 } 125 if len(req.URIs) > 0 { 126 return uriSANNotAllowedErr 127 } 128 129 if !strings.HasPrefix(req.Subject.CommonName, "system:node:") { 130 return commonNameNotSystemNode 131 } 132 133 if !kubeletClientRequiredUsages.Equal(usages) && !kubeletClientRequiredUsagesNoRSA.Equal(usages) { 134 return fmt.Errorf("usages did not match %v", kubeletClientRequiredUsages.List()) 135 } 136 137 return nil 138 }