k8s.io/kubernetes@v1.29.3/pkg/kubelet/clustertrustbundle/clustertrustbundle_manager.go (about) 1 /* 2 Copyright 2023 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 clustertrustbundle abstracts access to ClusterTrustBundles so that 18 // projected volumes can use them. 19 package clustertrustbundle 20 21 import ( 22 "encoding/pem" 23 "fmt" 24 "math/rand" 25 "time" 26 27 certificatesv1alpha1 "k8s.io/api/certificates/v1alpha1" 28 k8serrors "k8s.io/apimachinery/pkg/api/errors" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 lrucache "k8s.io/apimachinery/pkg/util/cache" 31 "k8s.io/apimachinery/pkg/util/sets" 32 certinformersv1alpha1 "k8s.io/client-go/informers/certificates/v1alpha1" 33 certlistersv1alpha1 "k8s.io/client-go/listers/certificates/v1alpha1" 34 "k8s.io/client-go/tools/cache" 35 "k8s.io/klog/v2" 36 ) 37 38 const ( 39 maxLabelSelectorLength = 100 * 1024 40 ) 41 42 // Manager abstracts over the ability to get trust anchors. 43 type Manager interface { 44 GetTrustAnchorsByName(name string, allowMissing bool) ([]byte, error) 45 GetTrustAnchorsBySigner(signerName string, labelSelector *metav1.LabelSelector, allowMissing bool) ([]byte, error) 46 } 47 48 // InformerManager is the "real" manager. It uses informers to track 49 // ClusterTrustBundle objects. 50 type InformerManager struct { 51 ctbInformer cache.SharedIndexInformer 52 ctbLister certlistersv1alpha1.ClusterTrustBundleLister 53 54 normalizationCache *lrucache.LRUExpireCache 55 cacheTTL time.Duration 56 } 57 58 var _ Manager = (*InformerManager)(nil) 59 60 // NewInformerManager returns an initialized InformerManager. 61 func NewInformerManager(bundles certinformersv1alpha1.ClusterTrustBundleInformer, cacheSize int, cacheTTL time.Duration) (*InformerManager, error) { 62 // We need to call Informer() before calling start on the shared informer 63 // factory, or the informer won't be registered to be started. 64 m := &InformerManager{ 65 ctbInformer: bundles.Informer(), 66 ctbLister: bundles.Lister(), 67 normalizationCache: lrucache.NewLRUExpireCache(cacheSize), 68 cacheTTL: cacheTTL, 69 } 70 71 // Have the informer bust cache entries when it sees updates that could 72 // apply to them. 73 _, err := m.ctbInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ 74 AddFunc: func(obj any) { 75 ctb, ok := obj.(*certificatesv1alpha1.ClusterTrustBundle) 76 if !ok { 77 return 78 } 79 klog.InfoS("Dropping all cache entries for signer", "signerName", ctb.Spec.SignerName) 80 m.dropCacheFor(ctb) 81 }, 82 UpdateFunc: func(old, new any) { 83 ctb, ok := new.(*certificatesv1alpha1.ClusterTrustBundle) 84 if !ok { 85 return 86 } 87 klog.InfoS("Dropping cache for ClusterTrustBundle", "signerName", ctb.Spec.SignerName) 88 m.dropCacheFor(new.(*certificatesv1alpha1.ClusterTrustBundle)) 89 }, 90 DeleteFunc: func(obj any) { 91 ctb, ok := obj.(*certificatesv1alpha1.ClusterTrustBundle) 92 if !ok { 93 tombstone, ok := obj.(cache.DeletedFinalStateUnknown) 94 if !ok { 95 return 96 } 97 ctb, ok = tombstone.Obj.(*certificatesv1alpha1.ClusterTrustBundle) 98 if !ok { 99 return 100 } 101 } 102 klog.InfoS("Dropping cache for ClusterTrustBundle", "signerName", ctb.Spec.SignerName) 103 m.dropCacheFor(ctb) 104 }, 105 }) 106 if err != nil { 107 return nil, fmt.Errorf("while registering event handler on informer: %w", err) 108 } 109 110 return m, nil 111 } 112 113 func (m *InformerManager) dropCacheFor(ctb *certificatesv1alpha1.ClusterTrustBundle) { 114 if ctb.Spec.SignerName != "" { 115 m.normalizationCache.RemoveAll(func(key any) bool { 116 return key.(cacheKeyType).signerName == ctb.Spec.SignerName 117 }) 118 } else { 119 m.normalizationCache.RemoveAll(func(key any) bool { 120 return key.(cacheKeyType).ctbName == ctb.ObjectMeta.Name 121 }) 122 } 123 } 124 125 // GetTrustAnchorsByName returns normalized and deduplicated trust anchors from 126 // a single named ClusterTrustBundle. 127 func (m *InformerManager) GetTrustAnchorsByName(name string, allowMissing bool) ([]byte, error) { 128 if !m.ctbInformer.HasSynced() { 129 return nil, fmt.Errorf("ClusterTrustBundle informer has not yet synced") 130 } 131 132 cacheKey := cacheKeyType{ctbName: name} 133 134 if cachedAnchors, ok := m.normalizationCache.Get(cacheKey); ok { 135 return cachedAnchors.([]byte), nil 136 } 137 138 ctb, err := m.ctbLister.Get(name) 139 if k8serrors.IsNotFound(err) && allowMissing { 140 return []byte{}, nil 141 } 142 if err != nil { 143 return nil, fmt.Errorf("while getting ClusterTrustBundle: %w", err) 144 } 145 146 pemTrustAnchors, err := m.normalizeTrustAnchors([]*certificatesv1alpha1.ClusterTrustBundle{ctb}) 147 if err != nil { 148 return nil, fmt.Errorf("while normalizing trust anchors: %w", err) 149 } 150 151 m.normalizationCache.Add(cacheKey, pemTrustAnchors, m.cacheTTL) 152 153 return pemTrustAnchors, nil 154 } 155 156 // GetTrustAnchorsBySigner returns normalized and deduplicated trust anchors 157 // from a set of selected ClusterTrustBundles. 158 func (m *InformerManager) GetTrustAnchorsBySigner(signerName string, labelSelector *metav1.LabelSelector, allowMissing bool) ([]byte, error) { 159 if !m.ctbInformer.HasSynced() { 160 return nil, fmt.Errorf("ClusterTrustBundle informer has not yet synced") 161 } 162 163 // Note that this function treats nil as "match nothing", and non-nil but 164 // empty as "match everything". 165 selector, err := metav1.LabelSelectorAsSelector(labelSelector) 166 if err != nil { 167 return nil, fmt.Errorf("while parsing label selector: %w", err) 168 } 169 170 cacheKey := cacheKeyType{signerName: signerName, labelSelector: selector.String()} 171 172 if lsLen := len(cacheKey.labelSelector); lsLen > maxLabelSelectorLength { 173 return nil, fmt.Errorf("label selector length (%d) is larger than %d", lsLen, maxLabelSelectorLength) 174 } 175 176 if cachedAnchors, ok := m.normalizationCache.Get(cacheKey); ok { 177 return cachedAnchors.([]byte), nil 178 } 179 180 rawCTBList, err := m.ctbLister.List(selector) 181 if err != nil { 182 return nil, fmt.Errorf("while listing ClusterTrustBundles matching label selector %v: %w", labelSelector, err) 183 } 184 185 ctbList := []*certificatesv1alpha1.ClusterTrustBundle{} 186 for _, ctb := range rawCTBList { 187 if ctb.Spec.SignerName == signerName { 188 ctbList = append(ctbList, ctb) 189 } 190 } 191 192 if len(ctbList) == 0 { 193 if allowMissing { 194 return []byte{}, nil 195 } 196 return nil, fmt.Errorf("combination of signerName and labelSelector matched zero ClusterTrustBundles") 197 } 198 199 pemTrustAnchors, err := m.normalizeTrustAnchors(ctbList) 200 if err != nil { 201 return nil, fmt.Errorf("while normalizing trust anchors: %w", err) 202 } 203 204 m.normalizationCache.Add(cacheKey, pemTrustAnchors, m.cacheTTL) 205 206 return pemTrustAnchors, nil 207 } 208 209 func (m *InformerManager) normalizeTrustAnchors(ctbList []*certificatesv1alpha1.ClusterTrustBundle) ([]byte, error) { 210 // Deduplicate trust anchors from all ClusterTrustBundles. 211 trustAnchorSet := sets.Set[string]{} 212 for _, ctb := range ctbList { 213 rest := []byte(ctb.Spec.TrustBundle) 214 var b *pem.Block 215 for { 216 b, rest = pem.Decode(rest) 217 if b == nil { 218 break 219 } 220 trustAnchorSet = trustAnchorSet.Insert(string(b.Bytes)) 221 } 222 } 223 224 // Give the list a stable ordering that changes each time Kubelet restarts. 225 trustAnchorList := sets.List(trustAnchorSet) 226 rand.Shuffle(len(trustAnchorList), func(i, j int) { 227 trustAnchorList[i], trustAnchorList[j] = trustAnchorList[j], trustAnchorList[i] 228 }) 229 230 pemTrustAnchors := []byte{} 231 for _, ta := range trustAnchorList { 232 b := &pem.Block{ 233 Type: "CERTIFICATE", 234 Bytes: []byte(ta), 235 } 236 pemTrustAnchors = append(pemTrustAnchors, pem.EncodeToMemory(b)...) 237 } 238 239 return pemTrustAnchors, nil 240 } 241 242 type cacheKeyType struct { 243 ctbName string 244 signerName string 245 labelSelector string 246 } 247 248 // NoopManager always returns an error, for use in static kubelet mode. 249 type NoopManager struct{} 250 251 var _ Manager = (*NoopManager)(nil) 252 253 // GetTrustAnchorsByName implements Manager. 254 func (m *NoopManager) GetTrustAnchorsByName(name string, allowMissing bool) ([]byte, error) { 255 return nil, fmt.Errorf("ClusterTrustBundle projection is not supported in static kubelet mode") 256 } 257 258 // GetTrustAnchorsBySigner implements Manager. 259 func (m *NoopManager) GetTrustAnchorsBySigner(signerName string, labelSelector *metav1.LabelSelector, allowMissing bool) ([]byte, error) { 260 return nil, fmt.Errorf("ClusterTrustBundle projection is not supported in static kubelet mode") 261 }