k8s.io/kubernetes@v1.29.3/pkg/kubelet/certificate/kubelet.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 "crypto/tls" 21 "crypto/x509" 22 "crypto/x509/pkix" 23 "fmt" 24 "math" 25 "net" 26 "sort" 27 "time" 28 29 certificates "k8s.io/api/certificates/v1" 30 v1 "k8s.io/api/core/v1" 31 "k8s.io/apimachinery/pkg/types" 32 clientset "k8s.io/client-go/kubernetes" 33 "k8s.io/client-go/util/certificate" 34 compbasemetrics "k8s.io/component-base/metrics" 35 "k8s.io/component-base/metrics/legacyregistry" 36 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" 37 "k8s.io/kubernetes/pkg/kubelet/metrics" 38 netutils "k8s.io/utils/net" 39 ) 40 41 // NewKubeletServerCertificateManager creates a certificate manager for the kubelet when retrieving a server certificate 42 // or returns an error. 43 func NewKubeletServerCertificateManager(kubeClient clientset.Interface, kubeCfg *kubeletconfig.KubeletConfiguration, nodeName types.NodeName, getAddresses func() []v1.NodeAddress, certDirectory string) (certificate.Manager, error) { 44 var clientsetFn certificate.ClientsetFunc 45 if kubeClient != nil { 46 clientsetFn = func(current *tls.Certificate) (clientset.Interface, error) { 47 return kubeClient, nil 48 } 49 } 50 certificateStore, err := certificate.NewFileStore( 51 "kubelet-server", 52 certDirectory, 53 certDirectory, 54 kubeCfg.TLSCertFile, 55 kubeCfg.TLSPrivateKeyFile) 56 if err != nil { 57 return nil, fmt.Errorf("failed to initialize server certificate store: %v", err) 58 } 59 var certificateRenewFailure = compbasemetrics.NewCounter( 60 &compbasemetrics.CounterOpts{ 61 Subsystem: metrics.KubeletSubsystem, 62 Name: "server_expiration_renew_errors", 63 Help: "Counter of certificate renewal errors.", 64 StabilityLevel: compbasemetrics.ALPHA, 65 }, 66 ) 67 legacyregistry.MustRegister(certificateRenewFailure) 68 69 certificateRotationAge := compbasemetrics.NewHistogram( 70 &compbasemetrics.HistogramOpts{ 71 Subsystem: metrics.KubeletSubsystem, 72 Name: "certificate_manager_server_rotation_seconds", 73 Help: "Histogram of the number of seconds the previous certificate lived before being rotated.", 74 Buckets: []float64{ 75 60, // 1 minute 76 3600, // 1 hour 77 14400, // 4 hours 78 86400, // 1 day 79 604800, // 1 week 80 2592000, // 1 month 81 7776000, // 3 months 82 15552000, // 6 months 83 31104000, // 1 year 84 124416000, // 4 years 85 }, 86 StabilityLevel: compbasemetrics.ALPHA, 87 }, 88 ) 89 legacyregistry.MustRegister(certificateRotationAge) 90 91 getTemplate := func() *x509.CertificateRequest { 92 hostnames, ips := addressesToHostnamesAndIPs(getAddresses()) 93 // don't return a template if we have no addresses to request for 94 if len(hostnames) == 0 && len(ips) == 0 { 95 return nil 96 } 97 return &x509.CertificateRequest{ 98 Subject: pkix.Name{ 99 CommonName: fmt.Sprintf("system:node:%s", nodeName), 100 Organization: []string{"system:nodes"}, 101 }, 102 DNSNames: hostnames, 103 IPAddresses: ips, 104 } 105 } 106 107 m, err := certificate.NewManager(&certificate.Config{ 108 ClientsetFn: clientsetFn, 109 GetTemplate: getTemplate, 110 SignerName: certificates.KubeletServingSignerName, 111 GetUsages: certificate.DefaultKubeletServingGetUsages, 112 CertificateStore: certificateStore, 113 CertificateRotation: certificateRotationAge, 114 CertificateRenewFailure: certificateRenewFailure, 115 }) 116 if err != nil { 117 return nil, fmt.Errorf("failed to initialize server certificate manager: %v", err) 118 } 119 legacyregistry.RawMustRegister(compbasemetrics.NewGaugeFunc( 120 &compbasemetrics.GaugeOpts{ 121 Subsystem: metrics.KubeletSubsystem, 122 Name: "certificate_manager_server_ttl_seconds", 123 Help: "Gauge of the shortest TTL (time-to-live) of " + 124 "the Kubelet's serving certificate. The value is in seconds " + 125 "until certificate expiry (negative if already expired). If " + 126 "serving certificate is invalid or unused, the value will " + 127 "be +INF.", 128 StabilityLevel: compbasemetrics.ALPHA, 129 }, 130 func() float64 { 131 if c := m.Current(); c != nil && c.Leaf != nil { 132 return math.Trunc(time.Until(c.Leaf.NotAfter).Seconds()) 133 } 134 return math.Inf(1) 135 }, 136 )) 137 return m, nil 138 } 139 140 func addressesToHostnamesAndIPs(addresses []v1.NodeAddress) (dnsNames []string, ips []net.IP) { 141 seenDNSNames := map[string]bool{} 142 seenIPs := map[string]bool{} 143 for _, address := range addresses { 144 if len(address.Address) == 0 { 145 continue 146 } 147 148 switch address.Type { 149 case v1.NodeHostName: 150 if ip := netutils.ParseIPSloppy(address.Address); ip != nil { 151 seenIPs[address.Address] = true 152 } else { 153 seenDNSNames[address.Address] = true 154 } 155 case v1.NodeExternalIP, v1.NodeInternalIP: 156 if ip := netutils.ParseIPSloppy(address.Address); ip != nil { 157 seenIPs[address.Address] = true 158 } 159 case v1.NodeExternalDNS, v1.NodeInternalDNS: 160 seenDNSNames[address.Address] = true 161 } 162 } 163 164 for dnsName := range seenDNSNames { 165 dnsNames = append(dnsNames, dnsName) 166 } 167 for ip := range seenIPs { 168 ips = append(ips, netutils.ParseIPSloppy(ip)) 169 } 170 171 // return in stable order 172 sort.Strings(dnsNames) 173 sort.Slice(ips, func(i, j int) bool { return ips[i].String() < ips[j].String() }) 174 175 return dnsNames, ips 176 } 177 178 // NewKubeletClientCertificateManager sets up a certificate manager without a 179 // client that can be used to sign new certificates (or rotate). If a CSR 180 // client is set later, it may begin rotating/renewing the client cert. 181 func NewKubeletClientCertificateManager( 182 certDirectory string, 183 nodeName types.NodeName, 184 bootstrapCertData []byte, 185 bootstrapKeyData []byte, 186 certFile string, 187 keyFile string, 188 clientsetFn certificate.ClientsetFunc, 189 ) (certificate.Manager, error) { 190 191 certificateStore, err := certificate.NewFileStore( 192 "kubelet-client", 193 certDirectory, 194 certDirectory, 195 certFile, 196 keyFile) 197 if err != nil { 198 return nil, fmt.Errorf("failed to initialize client certificate store: %v", err) 199 } 200 var certificateRenewFailure = compbasemetrics.NewCounter( 201 &compbasemetrics.CounterOpts{ 202 Namespace: metrics.KubeletSubsystem, 203 Subsystem: "certificate_manager", 204 Name: "client_expiration_renew_errors", 205 Help: "Counter of certificate renewal errors.", 206 StabilityLevel: compbasemetrics.ALPHA, 207 }, 208 ) 209 legacyregistry.Register(certificateRenewFailure) 210 211 m, err := certificate.NewManager(&certificate.Config{ 212 ClientsetFn: clientsetFn, 213 Template: &x509.CertificateRequest{ 214 Subject: pkix.Name{ 215 CommonName: fmt.Sprintf("system:node:%s", nodeName), 216 Organization: []string{"system:nodes"}, 217 }, 218 }, 219 SignerName: certificates.KubeAPIServerClientKubeletSignerName, 220 GetUsages: certificate.DefaultKubeletClientGetUsages, 221 // For backwards compatibility, the kubelet supports the ability to 222 // provide a higher privileged certificate as initial data that will 223 // then be rotated immediately. This code path is used by kubeadm on 224 // the masters. 225 BootstrapCertificatePEM: bootstrapCertData, 226 BootstrapKeyPEM: bootstrapKeyData, 227 228 CertificateStore: certificateStore, 229 CertificateRenewFailure: certificateRenewFailure, 230 }) 231 if err != nil { 232 return nil, fmt.Errorf("failed to initialize client certificate manager: %v", err) 233 } 234 235 return m, nil 236 }