enable kubelet server to dynamically load tls certificate files
This commit is contained in:
		@@ -17,6 +17,7 @@ limitations under the License.
 | 
				
			|||||||
package certificate
 | 
					package certificate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"crypto/x509"
 | 
						"crypto/x509"
 | 
				
			||||||
	"crypto/x509/pkix"
 | 
						"crypto/x509/pkix"
 | 
				
			||||||
@@ -24,15 +25,18 @@ import (
 | 
				
			|||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
						"sync/atomic"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	certificates "k8s.io/api/certificates/v1"
 | 
						certificates "k8s.io/api/certificates/v1"
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
 | 
						"k8s.io/apiserver/pkg/server/dynamiccertificates"
 | 
				
			||||||
	clientset "k8s.io/client-go/kubernetes"
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
	"k8s.io/client-go/util/certificate"
 | 
						"k8s.io/client-go/util/certificate"
 | 
				
			||||||
	compbasemetrics "k8s.io/component-base/metrics"
 | 
						compbasemetrics "k8s.io/component-base/metrics"
 | 
				
			||||||
	"k8s.io/component-base/metrics/legacyregistry"
 | 
						"k8s.io/component-base/metrics/legacyregistry"
 | 
				
			||||||
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
	kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
 | 
						kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/metrics"
 | 
						"k8s.io/kubernetes/pkg/kubelet/metrics"
 | 
				
			||||||
	netutils "k8s.io/utils/net"
 | 
						netutils "k8s.io/utils/net"
 | 
				
			||||||
@@ -234,3 +238,66 @@ func NewKubeletClientCertificateManager(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return m, nil
 | 
						return m, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewKubeletServerCertificateDynamicFileManager creates a certificate manager based on reading and watching certificate and key files.
 | 
				
			||||||
 | 
					// The returned struct implements certificate.Manager interface, enabling using it like other CertificateManager in this package.
 | 
				
			||||||
 | 
					// But the struct doesn't communicate with API server to perform certificate request at all.
 | 
				
			||||||
 | 
					func NewKubeletServerCertificateDynamicFileManager(certFile, keyFile string) (certificate.Manager, error) {
 | 
				
			||||||
 | 
						c, err := dynamiccertificates.NewDynamicServingContentFromFiles("kubelet-server-cert-files", certFile, keyFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("unable to set up dynamic certificate manager for kubelet server cert files: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m := &kubeletServerCertificateDynamicFileManager{
 | 
				
			||||||
 | 
							dynamicCertificateContent: c,
 | 
				
			||||||
 | 
							certFile:                  certFile,
 | 
				
			||||||
 | 
							keyFile:                   keyFile,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.Enqueue()
 | 
				
			||||||
 | 
						c.AddListener(m)
 | 
				
			||||||
 | 
						return m, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// kubeletServerCertificateDynamicFileManager uses a dynamic CertKeyContentProvider based on cert and key files.
 | 
				
			||||||
 | 
					type kubeletServerCertificateDynamicFileManager struct {
 | 
				
			||||||
 | 
						cancelFn                  context.CancelFunc
 | 
				
			||||||
 | 
						certFile                  string
 | 
				
			||||||
 | 
						keyFile                   string
 | 
				
			||||||
 | 
						dynamicCertificateContent *dynamiccertificates.DynamicCertKeyPairContent
 | 
				
			||||||
 | 
						currentTLSCertificate     atomic.Pointer[tls.Certificate]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Enqueue implements the functions to be notified when the serving cert content changes.
 | 
				
			||||||
 | 
					func (m *kubeletServerCertificateDynamicFileManager) Enqueue() {
 | 
				
			||||||
 | 
						certContent, keyContent := m.dynamicCertificateContent.CurrentCertKeyContent()
 | 
				
			||||||
 | 
						cert, err := tls.X509KeyPair(certContent, keyContent)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							klog.ErrorS(err, "invalid certificate and key pair from file", "certFile", m.certFile, "keyFile", m.keyFile)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.currentTLSCertificate.Store(&cert)
 | 
				
			||||||
 | 
						klog.V(4).InfoS("loaded certificate and key pair in kubelet server certificate manager", "certFile", m.certFile, "keyFile", m.keyFile)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Current returns the last valid certificate key pair loaded from files.
 | 
				
			||||||
 | 
					func (m *kubeletServerCertificateDynamicFileManager) Current() *tls.Certificate {
 | 
				
			||||||
 | 
						return m.currentTLSCertificate.Load()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Start starts watching the certificate and key files
 | 
				
			||||||
 | 
					func (m *kubeletServerCertificateDynamicFileManager) Start() {
 | 
				
			||||||
 | 
						var ctx context.Context
 | 
				
			||||||
 | 
						ctx, m.cancelFn = context.WithCancel(context.Background())
 | 
				
			||||||
 | 
						go m.dynamicCertificateContent.Run(ctx, 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stop stops watching the certificate and key files
 | 
				
			||||||
 | 
					func (m *kubeletServerCertificateDynamicFileManager) Stop() {
 | 
				
			||||||
 | 
						if m.cancelFn != nil {
 | 
				
			||||||
 | 
							m.cancelFn()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ServerHealthy always returns true since the file manager doesn't communicate with any server
 | 
				
			||||||
 | 
					func (m *kubeletServerCertificateDynamicFileManager) ServerHealthy() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -774,11 +774,21 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	klet.imageManager = imageManager
 | 
						klet.imageManager = imageManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if kubeCfg.ServerTLSBootstrap && kubeDeps.TLSOptions != nil && utilfeature.DefaultFeatureGate.Enabled(features.RotateKubeletServerCertificate) {
 | 
						if kubeDeps.TLSOptions != nil {
 | 
				
			||||||
 | 
							if kubeCfg.ServerTLSBootstrap && utilfeature.DefaultFeatureGate.Enabled(features.RotateKubeletServerCertificate) {
 | 
				
			||||||
			klet.serverCertificateManager, err = kubeletcertificate.NewKubeletServerCertificateManager(klet.kubeClient, kubeCfg, klet.nodeName, klet.getLastObservedNodeAddresses, certDirectory)
 | 
								klet.serverCertificateManager, err = kubeletcertificate.NewKubeletServerCertificateManager(klet.kubeClient, kubeCfg, klet.nodeName, klet.getLastObservedNodeAddresses, certDirectory)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("failed to initialize certificate manager: %v", err)
 | 
									return nil, fmt.Errorf("failed to initialize certificate manager: %w", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} else if kubeDeps.TLSOptions.CertFile != "" && kubeDeps.TLSOptions.KeyFile != "" && utilfeature.DefaultFeatureGate.Enabled(features.ReloadKubeletServerCertificateFile) {
 | 
				
			||||||
 | 
								klet.serverCertificateManager, err = kubeletcertificate.NewKubeletServerCertificateDynamicFileManager(kubeDeps.TLSOptions.CertFile, kubeDeps.TLSOptions.KeyFile)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, fmt.Errorf("failed to initialize file based certificate manager: %w", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if klet.serverCertificateManager != nil {
 | 
				
			||||||
			kubeDeps.TLSOptions.Config.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
 | 
								kubeDeps.TLSOptions.Config.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
 | 
				
			||||||
				cert := klet.serverCertificateManager.Current()
 | 
									cert := klet.serverCertificateManager.Current()
 | 
				
			||||||
				if cert == nil {
 | 
									if cert == nil {
 | 
				
			||||||
@@ -787,6 +797,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
 | 
				
			|||||||
				return cert, nil
 | 
									return cert, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if kubeDeps.ProbeManager != nil {
 | 
						if kubeDeps.ProbeManager != nil {
 | 
				
			||||||
		klet.probeManager = kubeDeps.ProbeManager
 | 
							klet.probeManager = kubeDeps.ProbeManager
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user