From 4aadcdd89c5707197caf328996becdf5969a218a Mon Sep 17 00:00:00 2001 From: zhaoxiaohu Date: Fri, 23 Aug 2024 10:07:54 +0800 Subject: [PATCH 1/5] Fix kubelet panic when allocate resource for pod. Signed-off-by: zhaoxiaohu (cherry picked from commit cefe431cf425255161fda3f99e737cab8570b57e) --- ...panic-when-allocate-resource-for-pod.patch | 116 ++++++++++++++++++ kubernetes.spec | 9 +- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch diff --git a/0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch b/0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch new file mode 100644 index 0000000..29cea2a --- /dev/null +++ b/0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch @@ -0,0 +1,116 @@ +From 44140f192be2eea3a71b3b6372ef45e8535dd802 Mon Sep 17 00:00:00 2001 +From: zhaoxiaohu +Date: Thu, 22 Aug 2024 16:39:45 +0800 +Subject: [PATCH] Fix kubelet panic when allocate resource for pod. + +Reference: https://github.com/kubernetes/kubernetes/pull/119561/commits/d6b8a660b081916f3fae3319581ec2c49a2f5a05 + +Signed-off-by: zhaoxiaohu +Signed-off-by: payall4u +Signed-off-by: yuwang +--- + pkg/kubelet/cm/devicemanager/manager.go | 12 ++-- + pkg/kubelet/cm/devicemanager/manager_test.go | 60 ++++++++++++++++++++ + 2 files changed, 67 insertions(+), 5 deletions(-) + +diff --git a/pkg/kubelet/cm/devicemanager/manager.go b/pkg/kubelet/cm/devicemanager/manager.go +index 95cf058f..1370675b 100644 +--- a/pkg/kubelet/cm/devicemanager/manager.go ++++ b/pkg/kubelet/cm/devicemanager/manager.go +@@ -667,6 +667,13 @@ func (m *ManagerImpl) devicesToAllocate(podUID, contName, resource string, requi + // Create a closure to help with device allocation + // Returns 'true' once no more devices need to be allocated. + allocateRemainingFrom := func(devices sets.String) bool { ++ // When we call callGetPreferredAllocationIfAvailable below, we will release ++ // the lock and call the device plugin. If someone calls ListResource concurrently, ++ // device manager will recalculate the allocatedDevices map. Some entries with ++ // empty sets may be removed, so we reinit here. ++ if m.allocatedDevices[resource] == nil { ++ m.allocatedDevices[resource] = sets.NewString() ++ } + for device := range devices.Difference(allocated) { + m.allocatedDevices[resource].Insert(device) + allocated.Insert(device) +@@ -683,11 +690,6 @@ func (m *ManagerImpl) devicesToAllocate(podUID, contName, resource string, requi + return allocated, nil + } + +- // Needs to allocate additional devices. +- if m.allocatedDevices[resource] == nil { +- m.allocatedDevices[resource] = sets.NewString() +- } +- + // Gets Devices in use. + devicesInUse := m.allocatedDevices[resource] + // Gets Available devices. +diff --git a/pkg/kubelet/cm/devicemanager/manager_test.go b/pkg/kubelet/cm/devicemanager/manager_test.go +index 9034498c..354dee50 100644 +--- a/pkg/kubelet/cm/devicemanager/manager_test.go ++++ b/pkg/kubelet/cm/devicemanager/manager_test.go +@@ -1080,3 +1080,63 @@ func makeDevice(devOnNUMA checkpoint.DevicesPerNUMA, topology bool) map[string]p + } + return res + } ++ ++func TestDevicesToAllocateConflictWithUpdateAllocatedDevices(t *testing.T) { ++ podToAllocate := "podToAllocate" ++ containerToAllocate := "containerToAllocate" ++ podToRemove := "podToRemove" ++ containerToRemove := "containerToRemove" ++ deviceID := "deviceID" ++ resourceName := "domain1.com/resource" ++ ++ socket := filepath.Join(os.TempDir(), esocketName()) ++ devs := []*pluginapi.Device{ ++ {ID: deviceID, Health: pluginapi.Healthy}, ++ } ++ p, e := esetup(t, devs, socket, resourceName, func(n string, d []pluginapi.Device) {}) ++ ++ waitUpdateAllocatedDevicesChan := make(chan struct{}) ++ waitSetGetPreferredAllocChan := make(chan struct{}) ++ ++ p.SetGetPreferredAllocFunc(func(r *pluginapi.PreferredAllocationRequest, devs map[string]pluginapi.Device) (*pluginapi.PreferredAllocationResponse, error) { ++ waitSetGetPreferredAllocChan <- struct{}{} ++ <-waitUpdateAllocatedDevicesChan ++ return &pluginapi.PreferredAllocationResponse{ ++ ContainerResponses: []*pluginapi.ContainerPreferredAllocationResponse{ ++ { ++ DeviceIDs: []string{deviceID}, ++ }, ++ }, ++ }, nil ++ }) ++ ++ testManager := &ManagerImpl{ ++ endpoints: make(map[string]endpointInfo), ++ healthyDevices: make(map[string]sets.Set[string]), ++ unhealthyDevices: make(map[string]sets.Set[string]), ++ allocatedDevices: make(map[string]sets.Set[string]), ++ podDevices: newPodDevices(), ++ activePods: func() []*v1.Pod { return []*v1.Pod{} }, ++ sourcesReady: &sourcesReadyStub{}, ++ topologyAffinityStore: topologymanager.NewFakeManager(), ++ } ++ ++ testManager.endpoints[resourceName] = endpointInfo{ ++ e: e, ++ opts: &pluginapi.DevicePluginOptions{ ++ GetPreferredAllocationAvailable: true, ++ }, ++ } ++ testManager.healthyDevices[resourceName] = sets.NewString(deviceID) ++ testManager.podDevices.insert(podToRemove, containerToRemove, resourceName, nil, nil) ++ ++ go func() { ++ <-waitSetGetPreferredAllocChan ++ testManager.UpdateAllocatedDevices() ++ waitUpdateAllocatedDevicesChan <- struct{}{} ++ }() ++ ++ set, err := testManager.devicesToAllocate(podToAllocate, containerToAllocate, resourceName, 1, sets.NewString()) ++ assert.NoError(t, err) ++ assert.Equal(t, set, sets.NewString(deviceID)) ++} +-- +2.33.0 + diff --git a/kubernetes.spec b/kubernetes.spec index 1d048c4..7fd5a4c 100644 --- a/kubernetes.spec +++ b/kubernetes.spec @@ -3,7 +3,7 @@ Name: kubernetes Version: 1.20.2 -Release: 21 +Release: 22 Summary: Container cluster management License: ASL 2.0 URL: https://k8s.io/kubernetes @@ -40,6 +40,7 @@ Patch6012: 0013-Validate-etcd-paths.patch Patch6013: 0014-fix-node-address-validation.patch Patch6014: 0015-Add-ephemeralcontainer-to-imagepolicy-securityaccoun.patch Patch6015: 0016-Add-envFrom-to-serviceaccount-admission-plugin.patch +Patch6016: 0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch %description Container cluster management. @@ -271,6 +272,12 @@ getent passwd kube >/dev/null || useradd -r -g kube -d / -s /sbin/nologin \ %systemd_postun kubelet kube-proxy %changelog +* Thu Aug 22 2024 zhaoxiaohu - 1.20.2-22 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:fix kubelet panic when allocate resource for pod. #119561 + * Mon Apr 29 2024 liuxu - 1.20.2-21 - Type:bugfix - CVE:NA -- Gitee From edfb97b522fa957da1a4a24d8b899d54db05247c Mon Sep 17 00:00:00 2001 From: zhaoxiaohu Date: Fri, 23 Aug 2024 16:55:52 +0800 Subject: [PATCH 2/5] reduce configmap and secret watch of kubelet Signed-off-by: zhaoxiaohu (cherry picked from commit 8df5d228f79efbfced00e0b2249b44500fe646e2) --- ...onfigmap-and-secret-watch-of-kubelet.patch | 524 ++++++++++++++++++ kubernetes.spec | 6 +- 2 files changed, 529 insertions(+), 1 deletion(-) create mode 100644 0018-backport-reduce-configmap-and-secret-watch-of-kubelet.patch diff --git a/0018-backport-reduce-configmap-and-secret-watch-of-kubelet.patch b/0018-backport-reduce-configmap-and-secret-watch-of-kubelet.patch new file mode 100644 index 0000000..582f86e --- /dev/null +++ b/0018-backport-reduce-configmap-and-secret-watch-of-kubelet.patch @@ -0,0 +1,524 @@ +From 8e4558ba2469a92a215f43c027a4e8e082b38fe1 Mon Sep 17 00:00:00 2001 +From: zhaoxiaohu +Date: Fri, 23 Aug 2024 14:52:03 +0800 +Subject: [PATCH] reduce configmap and secret watch of kubelet + +Reference: https://github.com/kubernetes/kubernetes/pull/99393/commits/57a3b0abd678c66a9a04e553b6d6ae49671a4779 + +Signed-off-by: zhaoxiaohu +Signed-off-by: chenyw1990 +Signed-off-by: yuwang +--- + pkg/kubelet/configmap/configmap_manager.go | 4 +- + pkg/kubelet/kubelet.go | 4 +- + pkg/kubelet/secret/secret_manager.go | 4 +- + .../util/manager/watch_based_manager.go | 166 +++++++++++++++--- + .../util/manager/watch_based_manager_test.go | 101 +++++++++-- + .../integration/kubelet/watch_manager_test.go | 4 +- + 6 files changed, 245 insertions(+), 38 deletions(-) + +diff --git a/pkg/kubelet/configmap/configmap_manager.go b/pkg/kubelet/configmap/configmap_manager.go +index 2f525357..5466dd8b 100644 +--- a/pkg/kubelet/configmap/configmap_manager.go ++++ b/pkg/kubelet/configmap/configmap_manager.go +@@ -135,7 +135,7 @@ func NewCachingConfigMapManager(kubeClient clientset.Interface, getTTL manager.G + // - whenever a pod is created or updated, we start individual watches for all + // referenced objects that aren't referenced from other registered pods + // - every GetObject() returns a value from local cache propagated via watches +-func NewWatchingConfigMapManager(kubeClient clientset.Interface) Manager { ++func NewWatchingConfigMapManager(kubeClient clientset.Interface, resyncInterval time.Duration) Manager { + listConfigMap := func(namespace string, opts metav1.ListOptions) (runtime.Object, error) { + return kubeClient.CoreV1().ConfigMaps(namespace).List(context.TODO(), opts) + } +@@ -153,6 +153,6 @@ func NewWatchingConfigMapManager(kubeClient clientset.Interface) Manager { + } + gr := corev1.Resource("configmap") + return &configMapManager{ +- manager: manager.NewWatchBasedManager(listConfigMap, watchConfigMap, newConfigMap, isImmutable, gr, getConfigMapNames), ++ manager: manager.NewWatchBasedManager(listConfigMap, watchConfigMap, newConfigMap, isImmutable, gr, resyncInterval, getConfigMapNames), + } + } +diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go +index 9cb92eda..c6f9e6e5 100644 +--- a/pkg/kubelet/kubelet.go ++++ b/pkg/kubelet/kubelet.go +@@ -526,8 +526,8 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration, + var configMapManager configmap.Manager + switch kubeCfg.ConfigMapAndSecretChangeDetectionStrategy { + case kubeletconfiginternal.WatchChangeDetectionStrategy: +- secretManager = secret.NewWatchingSecretManager(kubeDeps.KubeClient) +- configMapManager = configmap.NewWatchingConfigMapManager(kubeDeps.KubeClient) ++ secretManager = secret.NewWatchingSecretManager(kubeDeps.KubeClient, klet.resyncInterval) ++ configMapManager = configmap.NewWatchingConfigMapManager(kubeDeps.KubeClient, klet.resyncInterval) + case kubeletconfiginternal.TTLCacheChangeDetectionStrategy: + secretManager = secret.NewCachingSecretManager( + kubeDeps.KubeClient, manager.GetObjectTTLFromNodeFunc(klet.GetNode)) +diff --git a/pkg/kubelet/secret/secret_manager.go b/pkg/kubelet/secret/secret_manager.go +index 3aa132a5..af17711e 100644 +--- a/pkg/kubelet/secret/secret_manager.go ++++ b/pkg/kubelet/secret/secret_manager.go +@@ -136,7 +136,7 @@ func NewCachingSecretManager(kubeClient clientset.Interface, getTTL manager.GetO + // - whenever a pod is created or updated, we start individual watches for all + // referenced objects that aren't referenced from other registered pods + // - every GetObject() returns a value from local cache propagated via watches +-func NewWatchingSecretManager(kubeClient clientset.Interface) Manager { ++func NewWatchingSecretManager(kubeClient clientset.Interface, resyncInterval time.Duration) Manager { + listSecret := func(namespace string, opts metav1.ListOptions) (runtime.Object, error) { + return kubeClient.CoreV1().Secrets(namespace).List(context.TODO(), opts) + } +@@ -154,6 +154,6 @@ func NewWatchingSecretManager(kubeClient clientset.Interface) Manager { + } + gr := corev1.Resource("secret") + return &secretManager{ +- manager: manager.NewWatchBasedManager(listSecret, watchSecret, newSecret, isImmutable, gr, getSecretNames), ++ manager: manager.NewWatchBasedManager(listSecret, watchSecret, newSecret, isImmutable, gr, resyncInterval, getSecretNames), + } + } +diff --git a/pkg/kubelet/util/manager/watch_based_manager.go b/pkg/kubelet/util/manager/watch_based_manager.go +index 4dfe1037..cee515ca 100644 +--- a/pkg/kubelet/util/manager/watch_based_manager.go ++++ b/pkg/kubelet/util/manager/watch_based_manager.go +@@ -31,6 +31,7 @@ import ( + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ++ "k8s.io/apimachinery/pkg/util/clock" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" +@@ -46,25 +47,108 @@ type isImmutableFunc func(runtime.Object) bool + // objectCacheItem is a single item stored in objectCache. + type objectCacheItem struct { + refCount int +- store cache.Store ++ store *cacheStore ++ reflector *cache.Reflector ++ + hasSynced func() (bool, error) + +- // lock is protecting from closing stopCh multiple times. +- lock sync.Mutex +- stopCh chan struct{} ++ // waitGroup is used to ensure that there won't be two concurrent calls to reflector.Run ++ waitGroup sync.WaitGroup ++ ++ // lock is to ensure the access and modify of lastAccessTime, stopped, and immutable are thread safety, ++ // and protecting from closing stopCh multiple times. ++ lock sync.Mutex ++ lastAccessTime time.Time ++ stopped bool ++ immutable bool ++ stopCh chan struct{} + } + + func (i *objectCacheItem) stop() bool { + i.lock.Lock() + defer i.lock.Unlock() +- select { +- case <-i.stopCh: +- // This means that channel is already closed. ++ return i.stopThreadUnsafe() ++} ++ ++func (i *objectCacheItem) stopThreadUnsafe() bool { ++ if i.stopped { + return false +- default: +- close(i.stopCh) +- return true + } ++ i.stopped = true ++ close(i.stopCh) ++ if !i.immutable { ++ i.store.unsetInitialized() ++ } ++ return true ++} ++ ++func (i *objectCacheItem) setLastAccessTime(time time.Time) { ++ i.lock.Lock() ++ defer i.lock.Unlock() ++ i.lastAccessTime = time ++} ++ ++func (i *objectCacheItem) setImmutable() { ++ i.lock.Lock() ++ defer i.lock.Unlock() ++ i.immutable = true ++} ++ ++func (i *objectCacheItem) stopIfIdle(now time.Time, maxIdleTime time.Duration) bool { ++ i.lock.Lock() ++ defer i.lock.Unlock() ++ if !i.stopped && now.After(i.lastAccessTime.Add(maxIdleTime)) { ++ return i.stopThreadUnsafe() ++ } ++ return false ++} ++ ++func (i *objectCacheItem) restartReflectorIfNeeded() { ++ i.lock.Lock() ++ defer i.lock.Unlock() ++ if i.immutable || !i.stopped { ++ return ++ } ++ i.stopCh = make(chan struct{}) ++ i.stopped = false ++ go i.startReflector() ++} ++ ++func (i *objectCacheItem) startReflector() { ++ i.waitGroup.Wait() ++ i.waitGroup.Add(1) ++ defer i.waitGroup.Done() ++ i.reflector.Run(i.stopCh) ++} ++ ++// cacheStore is in order to rewrite Replace function to mark initialized flag ++type cacheStore struct { ++ cache.Store ++ lock sync.Mutex ++ initialized bool ++} ++ ++func (c *cacheStore) Replace(list []interface{}, resourceVersion string) error { ++ c.lock.Lock() ++ defer c.lock.Unlock() ++ err := c.Store.Replace(list, resourceVersion) ++ if err != nil { ++ return err ++ } ++ c.initialized = true ++ return nil ++} ++ ++func (c *cacheStore) hasSynced() bool { ++ c.lock.Lock() ++ defer c.lock.Unlock() ++ return c.initialized ++} ++ ++func (c *cacheStore) unsetInitialized() { ++ c.lock.Lock() ++ defer c.lock.Unlock() ++ c.initialized = false + } + + // objectCache is a local cache of objects propagated via +@@ -75,35 +159,53 @@ type objectCache struct { + newObject newObjectFunc + isImmutable isImmutableFunc + groupResource schema.GroupResource ++ clock clock.Clock ++ maxIdleTime time.Duration + + lock sync.RWMutex + items map[objectKey]*objectCacheItem + } + ++const minIdleTime = 1 * time.Minute ++ + // NewObjectCache returns a new watch-based instance of Store interface. + func NewObjectCache( + listObject listObjectFunc, + watchObject watchObjectFunc, + newObject newObjectFunc, + isImmutable isImmutableFunc, +- groupResource schema.GroupResource) Store { +- return &objectCache{ ++ groupResource schema.GroupResource, ++ clock clock.Clock, ++ maxIdleTime time.Duration) Store { ++ ++ if maxIdleTime < minIdleTime { ++ maxIdleTime = minIdleTime ++ } ++ ++ store := &objectCache{ + listObject: listObject, + watchObject: watchObject, + newObject: newObject, + isImmutable: isImmutable, + groupResource: groupResource, ++ clock: clock, ++ maxIdleTime: maxIdleTime, + items: make(map[objectKey]*objectCacheItem), + } ++ ++ // TODO propagate stopCh from the higher level. ++ go wait.Until(store.startRecycleIdleWatch, time.Minute, wait.NeverStop) ++ return store + } + +-func (c *objectCache) newStore() cache.Store { ++func (c *objectCache) newStore() *cacheStore { + // TODO: We may consider created a dedicated store keeping just a single + // item, instead of using a generic store implementation for this purpose. + // However, simple benchmarks show that memory overhead in that case is + // decrease from ~600B to ~300B per object. So we are not optimizing it + // until we will see a good reason for that. +- return cache.NewStore(cache.MetaNamespaceKeyFunc) ++ store := cache.NewStore(cache.MetaNamespaceKeyFunc) ++ return &cacheStore{store, sync.Mutex{}, false} + } + + func (c *objectCache) newReflector(namespace, name string) *objectCacheItem { +@@ -124,14 +226,15 @@ func (c *objectCache) newReflector(namespace, name string) *objectCacheItem { + store, + 0, + ) +- stopCh := make(chan struct{}) +- go reflector.Run(stopCh) +- return &objectCacheItem{ ++ item := &objectCacheItem{ + refCount: 0, + store: store, +- hasSynced: func() (bool, error) { return reflector.LastSyncResourceVersion() != "", nil }, +- stopCh: stopCh, ++ reflector: reflector, ++ hasSynced: func() (bool, error) { return store.hasSynced(), nil }, ++ stopCh: make(chan struct{}), + } ++ go item.startReflector() ++ return item + } + + func (c *objectCache) AddReference(namespace, name string) { +@@ -186,10 +289,11 @@ func (c *objectCache) Get(namespace, name string) (runtime.Object, error) { + if !exists { + return nil, fmt.Errorf("object %q/%q not registered", namespace, name) + } ++ item.restartReflectorIfNeeded() + if err := wait.PollImmediate(10*time.Millisecond, time.Second, item.hasSynced); err != nil { + return nil, fmt.Errorf("failed to sync %s cache: %v", c.groupResource.String(), err) + } +- ++ item.setLastAccessTime(c.clock.Now()) + obj, exists, err := item.store.GetByKey(c.key(namespace, name)) + if err != nil { + return nil, err +@@ -209,6 +313,7 @@ func (c *objectCache) Get(namespace, name string) (runtime.Object, error) { + // - doing that would require significant refactoring to reflector + // we limit ourselves to just quickly stop the reflector here. + if utilfeature.DefaultFeatureGate.Enabled(features.ImmutableEphemeralVolumes) && c.isImmutable(object) { ++ item.setImmutable() + if item.stop() { + klog.V(4).Infof("Stopped watching for changes of %q/%q - object is immutable", namespace, name) + } +@@ -218,6 +323,17 @@ func (c *objectCache) Get(namespace, name string) (runtime.Object, error) { + return nil, fmt.Errorf("unexpected object type: %v", obj) + } + ++func (c *objectCache) startRecycleIdleWatch() { ++ c.lock.Lock() ++ defer c.lock.Unlock() ++ ++ for key, item := range c.items { ++ if item.stopIfIdle(c.clock.Now(), c.maxIdleTime) { ++ klog.V(4).InfoS("Not acquired for long time, Stopped watching for changes", "objectKey", key, "maxIdleTime", c.maxIdleTime) ++ } ++ } ++} ++ + // NewWatchBasedManager creates a manager that keeps a cache of all objects + // necessary for registered pods. + // It implements the following logic: +@@ -230,7 +346,15 @@ func NewWatchBasedManager( + newObject newObjectFunc, + isImmutable isImmutableFunc, + groupResource schema.GroupResource, ++ resyncInterval time.Duration, + getReferencedObjects func(*v1.Pod) sets.String) Manager { +- objectStore := NewObjectCache(listObject, watchObject, newObject, isImmutable, groupResource) ++ ++ // If a configmap/secret is used as a volume, the volumeManager will visit the objectCacheItem every resyncInterval cycle, ++ // We just want to stop the objectCacheItem referenced by environment variables, ++ // So, maxIdleTime is set to an integer multiple of resyncInterval, ++ // We currently set it to 5 times. ++ maxIdleTime := resyncInterval * 5 ++ ++ objectStore := NewObjectCache(listObject, watchObject, newObject, isImmutable, groupResource, clock.RealClock{}, maxIdleTime) + return NewCacheBasedManager(objectStore, getReferencedObjects) + } +diff --git a/pkg/kubelet/util/manager/watch_based_manager_test.go b/pkg/kubelet/util/manager/watch_based_manager_test.go +index 1f103404..17a8b01a 100644 +--- a/pkg/kubelet/util/manager/watch_based_manager_test.go ++++ b/pkg/kubelet/util/manager/watch_based_manager_test.go +@@ -28,6 +28,7 @@ import ( + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ++ "k8s.io/apimachinery/pkg/util/clock" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + +@@ -62,13 +63,15 @@ func isSecretImmutable(object runtime.Object) bool { + return false + } + +-func newSecretCache(fakeClient clientset.Interface) *objectCache { ++func newSecretCache(fakeClient clientset.Interface, fakeClock clock.Clock, maxIdleTime time.Duration) *objectCache { + return &objectCache{ + listObject: listSecret(fakeClient), + watchObject: watchSecret(fakeClient), + newObject: func() runtime.Object { return &v1.Secret{} }, + isImmutable: isSecretImmutable, + groupResource: corev1.Resource("secret"), ++ clock: fakeClock, ++ maxIdleTime: maxIdleTime, + items: make(map[objectKey]*objectCacheItem), + } + } +@@ -88,7 +91,8 @@ func TestSecretCache(t *testing.T) { + fakeWatch := watch.NewFake() + fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) + +- store := newSecretCache(fakeClient) ++ fakeClock := clock.NewFakeClock(time.Now()) ++ store := newSecretCache(fakeClient, fakeClock, time.Minute) + + store.AddReference("ns", "name") + _, err := store.Get("ns", "name") +@@ -157,7 +161,8 @@ func TestSecretCacheMultipleRegistrations(t *testing.T) { + fakeWatch := watch.NewFake() + fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) + +- store := newSecretCache(fakeClient) ++ fakeClock := clock.NewFakeClock(time.Now()) ++ store := newSecretCache(fakeClient, fakeClock, time.Minute) + + store.AddReference("ns", "name") + // This should trigger List and Watch actions eventually. +@@ -264,7 +269,8 @@ func TestImmutableSecretStopsTheReflector(t *testing.T) { + fakeWatch := watch.NewFake() + fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) + +- store := newSecretCache(fakeClient) ++ fakeClock := clock.NewFakeClock(time.Now()) ++ store := newSecretCache(fakeClient, fakeClock, time.Minute) + + key := objectKey{namespace: "ns", name: "name"} + itemExists := func() (bool, error) { +@@ -280,12 +286,7 @@ func TestImmutableSecretStopsTheReflector(t *testing.T) { + + item.lock.Lock() + defer item.lock.Unlock() +- select { +- case <-item.stopCh: +- return false +- default: +- return true +- } ++ return !item.stopped + } + + // AddReference should start reflector. +@@ -330,3 +331,83 @@ func TestImmutableSecretStopsTheReflector(t *testing.T) { + }) + } + } ++ ++func TestMaxIdleTimeStopsTheReflector(t *testing.T) { ++ secret := &v1.Secret{ ++ ObjectMeta: metav1.ObjectMeta{ ++ Name: "name", ++ Namespace: "ns", ++ ResourceVersion: "200", ++ }, ++ } ++ ++ fakeClient := &fake.Clientset{} ++ listReactor := func(a core.Action) (bool, runtime.Object, error) { ++ result := &v1.SecretList{ ++ ListMeta: metav1.ListMeta{ ++ ResourceVersion: "200", ++ }, ++ Items: []v1.Secret{*secret}, ++ } ++ ++ return true, result, nil ++ } ++ ++ fakeClient.AddReactor("list", "secrets", listReactor) ++ fakeWatch := watch.NewFake() ++ fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) ++ ++ fakeClock := clock.NewFakeClock(time.Now()) ++ store := newSecretCache(fakeClient, fakeClock, time.Minute) ++ ++ key := objectKey{namespace: "ns", name: "name"} ++ itemExists := func() (bool, error) { ++ store.lock.Lock() ++ defer store.lock.Unlock() ++ _, ok := store.items[key] ++ return ok, nil ++ } ++ ++ reflectorRunning := func() bool { ++ store.lock.Lock() ++ defer store.lock.Unlock() ++ item := store.items[key] ++ ++ item.lock.Lock() ++ defer item.lock.Unlock() ++ return !item.stopped ++ } ++ ++ // AddReference should start reflector. ++ store.AddReference("ns", "name") ++ if err := wait.Poll(10*time.Millisecond, 10*time.Second, itemExists); err != nil { ++ t.Errorf("item wasn't added to cache") ++ } ++ ++ obj, _ := store.Get("ns", "name") ++ assert.True(t, apiequality.Semantic.DeepEqual(secret, obj)) ++ ++ assert.True(t, reflectorRunning()) ++ ++ fakeClock.Step(90 * time.Second) ++ store.startRecycleIdleWatch() ++ ++ // Reflector should already be stopped for maxIdleTime exceeded. ++ assert.False(t, reflectorRunning()) ++ ++ obj, _ = store.Get("ns", "name") ++ assert.True(t, apiequality.Semantic.DeepEqual(secret, obj)) ++ // Reflector should reRun after get secret again. ++ assert.True(t, reflectorRunning()) ++ ++ fakeClock.Step(20 * time.Second) ++ _, _ = store.Get("ns", "name") ++ fakeClock.Step(20 * time.Second) ++ _, _ = store.Get("ns", "name") ++ fakeClock.Step(20 * time.Second) ++ _, _ = store.Get("ns", "name") ++ store.startRecycleIdleWatch() ++ ++ // Reflector should be running when the get function is called periodically. ++ assert.True(t, reflectorRunning()) ++} +diff --git a/test/integration/kubelet/watch_manager_test.go b/test/integration/kubelet/watch_manager_test.go +index a065bc26..07c934b5 100644 +--- a/test/integration/kubelet/watch_manager_test.go ++++ b/test/integration/kubelet/watch_manager_test.go +@@ -27,6 +27,7 @@ import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ++ "k8s.io/apimachinery/pkg/util/clock" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" +@@ -61,7 +62,8 @@ func TestWatchBasedManager(t *testing.T) { + // We want all watches to be up and running to stress test it. + // So don't treat any secret as immutable here. + isImmutable := func(_ runtime.Object) bool { return false } +- store := manager.NewObjectCache(listObj, watchObj, newObj, isImmutable, schema.GroupResource{Group: "v1", Resource: "secrets"}) ++ fakeClock := clock.NewFakeClock(time.Now()) ++ store := manager.NewObjectCache(listObj, watchObj, newObj, isImmutable, schema.GroupResource{Group: "v1", Resource: "secrets"}, fakeClock, time.Minute) + + // create 1000 secrets in parallel + t.Log(time.Now(), "creating 1000 secrets") +-- +2.33.0 + diff --git a/kubernetes.spec b/kubernetes.spec index 7fd5a4c..4686416 100644 --- a/kubernetes.spec +++ b/kubernetes.spec @@ -3,7 +3,7 @@ Name: kubernetes Version: 1.20.2 -Release: 22 +Release: 23 Summary: Container cluster management License: ASL 2.0 URL: https://k8s.io/kubernetes @@ -41,6 +41,7 @@ Patch6013: 0014-fix-node-address-validation.patch Patch6014: 0015-Add-ephemeralcontainer-to-imagepolicy-securityaccoun.patch Patch6015: 0016-Add-envFrom-to-serviceaccount-admission-plugin.patch Patch6016: 0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch +Patch6017: 0018-backport-reduce-configmap-and-secret-watch-of-kubelet.patch %description Container cluster management. @@ -272,6 +273,9 @@ getent passwd kube >/dev/null || useradd -r -g kube -d / -s /sbin/nologin \ %systemd_postun kubelet kube-proxy %changelog +* Fri Aug 23 2024 zhaoxiaohu - 1.20.2-23 +- DESC:reduce configmap and secret watch of kubelet + * Thu Aug 22 2024 zhaoxiaohu - 1.20.2-22 - Type:bugfix - CVE:NA -- Gitee From 591aac1ac145ac1abe3feeea7063a405d116667f Mon Sep 17 00:00:00 2001 From: zhaoxiaohu Date: Fri, 23 Aug 2024 16:59:03 +0800 Subject: [PATCH 3/5] Don't prematurely close reflectors in case of slow initialization in watch based manager Signed-off-by: zhaoxiaohu (cherry picked from commit ef0ef3875b779b8627f589e697ecddb563fb0fea) --- ...y-close-reflectors-in-case-of-slow-i.patch | 151 ++++++++++++++++++ kubernetes.spec | 6 +- 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 0019-backport-Don-t-prematurely-close-reflectors-in-case-of-slow-i.patch diff --git a/0019-backport-Don-t-prematurely-close-reflectors-in-case-of-slow-i.patch b/0019-backport-Don-t-prematurely-close-reflectors-in-case-of-slow-i.patch new file mode 100644 index 0000000..c1aa402 --- /dev/null +++ b/0019-backport-Don-t-prematurely-close-reflectors-in-case-of-slow-i.patch @@ -0,0 +1,151 @@ +From 033bafcda990c254a53794556ff5aa9d2fceb1f7 Mon Sep 17 00:00:00 2001 +From: zhaoxiaohu +Date: Fri, 23 Aug 2024 15:04:42 +0800 +Subject: [PATCH] Don't prematurely close reflectors in case of slow + initialization in watch based manager + +Reference: https://github.com/kubernetes/kubernetes/pull/104604/commits/515106b7957c4da6fdbbb7e474d19bad0fa3f57f + +Signed-off-by: zhaoxiaohu +Signed-off-by: wojtekt +Signed-off-by: yuwang +--- + .../util/manager/watch_based_manager.go | 11 ++- + .../util/manager/watch_based_manager_test.go | 91 +++++++++++++++++++ + 2 files changed, 100 insertions(+), 2 deletions(-) + +diff --git a/pkg/kubelet/util/manager/watch_based_manager.go b/pkg/kubelet/util/manager/watch_based_manager.go +index cee515ca..d4e7597b 100644 +--- a/pkg/kubelet/util/manager/watch_based_manager.go ++++ b/pkg/kubelet/util/manager/watch_based_manager.go +@@ -97,7 +97,11 @@ func (i *objectCacheItem) setImmutable() { + func (i *objectCacheItem) stopIfIdle(now time.Time, maxIdleTime time.Duration) bool { + i.lock.Lock() + defer i.lock.Unlock() +- if !i.stopped && now.After(i.lastAccessTime.Add(maxIdleTime)) { ++ // Ensure that we don't try to stop not yet initialized reflector. ++ // In case of overloaded kube-apiserver, if the list request is ++ // already being processed, all the work would lost and would have ++ // to be retried. ++ if !i.stopped && i.store.hasSynced() && now.After(i.lastAccessTime.Add(maxIdleTime)) { + return i.stopThreadUnsafe() + } + return false +@@ -289,11 +293,14 @@ func (c *objectCache) Get(namespace, name string) (runtime.Object, error) { + if !exists { + return nil, fmt.Errorf("object %q/%q not registered", namespace, name) + } ++ // Record last access time independently if it succeeded or not. ++ // This protects from premature (racy) reflector closure. ++ item.setLastAccessTime(c.clock.Now()) ++ + item.restartReflectorIfNeeded() + if err := wait.PollImmediate(10*time.Millisecond, time.Second, item.hasSynced); err != nil { + return nil, fmt.Errorf("failed to sync %s cache: %v", c.groupResource.String(), err) + } +- item.setLastAccessTime(c.clock.Now()) + obj, exists, err := item.store.GetByKey(c.key(namespace, name)) + if err != nil { + return nil, err +diff --git a/pkg/kubelet/util/manager/watch_based_manager_test.go b/pkg/kubelet/util/manager/watch_based_manager_test.go +index 17a8b01a..0fc9915c 100644 +--- a/pkg/kubelet/util/manager/watch_based_manager_test.go ++++ b/pkg/kubelet/util/manager/watch_based_manager_test.go +@@ -411,3 +411,94 @@ func TestMaxIdleTimeStopsTheReflector(t *testing.T) { + // Reflector should be running when the get function is called periodically. + assert.True(t, reflectorRunning()) + } ++ ++func TestReflectorNotStopedOnSlowInitialization(t *testing.T) { ++ secret := &v1.Secret{ ++ ObjectMeta: metav1.ObjectMeta{ ++ Name: "name", ++ Namespace: "ns", ++ ResourceVersion: "200", ++ }, ++ } ++ ++ fakeClock := clock.NewFakeClock(time.Now()) ++ ++ fakeClient := &fake.Clientset{} ++ listReactor := func(a core.Action) (bool, runtime.Object, error) { ++ <-fakeClock.After(120 * time.Second) ++ ++ result := &v1.SecretList{ ++ ListMeta: metav1.ListMeta{ ++ ResourceVersion: "200", ++ }, ++ Items: []v1.Secret{*secret}, ++ } ++ ++ return true, result, nil ++ } ++ ++ fakeClient.AddReactor("list", "secrets", listReactor) ++ fakeWatch := watch.NewFake() ++ fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) ++ store := newSecretCache(fakeClient, fakeClock, time.Minute) ++ ++ key := objectKey{namespace: "ns", name: "name"} ++ itemExists := func() (bool, error) { ++ store.lock.Lock() ++ defer store.lock.Unlock() ++ _, ok := store.items[key] ++ return ok, nil ++ } ++ ++ reflectorRunning := func() bool { ++ store.lock.Lock() ++ defer store.lock.Unlock() ++ item := store.items[key] ++ ++ item.lock.Lock() ++ defer item.lock.Unlock() ++ return !item.stopped ++ } ++ ++ reflectorInitialized := func() (bool, error) { ++ store.lock.Lock() ++ defer store.lock.Unlock() ++ item := store.items[key] ++ ++ item.lock.Lock() ++ defer item.lock.Unlock() ++ return item.store.hasSynced(), nil ++ } ++ ++ // AddReference should start reflector. ++ store.AddReference("ns", "name") ++ if err := wait.Poll(10*time.Millisecond, 10*time.Second, itemExists); err != nil { ++ t.Errorf("item wasn't added to cache") ++ } ++ ++ fakeClock.Step(90 * time.Second) ++ store.startRecycleIdleWatch() ++ ++ // Reflector didn't yet initialize, so it shouldn't be stopped. ++ // However, Get should still be failing. ++ assert.True(t, reflectorRunning()) ++ initialized, _ := reflectorInitialized() ++ assert.False(t, initialized) ++ _, err := store.Get("ns", "name") ++ if err == nil || !strings.Contains(err.Error(), "failed to sync") { ++ t.Errorf("Expected failed to sync error, got: %v", err) ++ } ++ ++ // Initialization should successfully finish. ++ fakeClock.Step(30 * time.Second) ++ if err := wait.Poll(10*time.Millisecond, time.Second, reflectorInitialized); err != nil { ++ t.Errorf("reflector didn't iniailize correctly") ++ } ++ ++ // recycling shouldn't stop the reflector because it was accessed within last minute. ++ store.startRecycleIdleWatch() ++ assert.True(t, reflectorRunning()) ++ ++ obj, _ := store.Get("ns", "name") ++ assert.True(t, apiequality.Semantic.DeepEqual(secret, obj)) ++} +-- +2.33.0 + diff --git a/kubernetes.spec b/kubernetes.spec index 4686416..678301a 100644 --- a/kubernetes.spec +++ b/kubernetes.spec @@ -3,7 +3,7 @@ Name: kubernetes Version: 1.20.2 -Release: 23 +Release: 24 Summary: Container cluster management License: ASL 2.0 URL: https://k8s.io/kubernetes @@ -42,6 +42,7 @@ Patch6014: 0015-Add-ephemeralcontainer-to-imagepolicy-securityaccoun.patch Patch6015: 0016-Add-envFrom-to-serviceaccount-admission-plugin.patch Patch6016: 0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch Patch6017: 0018-backport-reduce-configmap-and-secret-watch-of-kubelet.patch +Patch6018: 0019-backport-Don-t-prematurely-close-reflectors-in-case-of-slow-i.patch %description Container cluster management. @@ -273,6 +274,9 @@ getent passwd kube >/dev/null || useradd -r -g kube -d / -s /sbin/nologin \ %systemd_postun kubelet kube-proxy %changelog +* Fri Aug 23 2024 zhaoxiaohu - 1.20.2-24 +- DESC:Don't prematurely close reflectors in case of slow initialization in watch based manager + * Fri Aug 23 2024 zhaoxiaohu - 1.20.2-23 - DESC:reduce configmap and secret watch of kubelet -- Gitee From 9d3377302dc4899127a06907940e02661db863c1 Mon Sep 17 00:00:00 2001 From: zhaoxiaohu Date: Tue, 27 Aug 2024 16:14:52 +0800 Subject: [PATCH 4/5] Fix cpu share issues on systems with large amounts of cpu(cpucore>256) Signed-off-by: zhaoxiaohu (cherry picked from commit d8397b11a3e442a53a5747fcf1b0719840c1ba99) --- ...sues-on-systems-with-large-amounts-o.patch | 104 ++++++++++++++++++ kubernetes.spec | 9 +- 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 0020-backport-Fix-cpu-share-issues-on-systems-with-large-amounts-o.patch diff --git a/0020-backport-Fix-cpu-share-issues-on-systems-with-large-amounts-o.patch b/0020-backport-Fix-cpu-share-issues-on-systems-with-large-amounts-o.patch new file mode 100644 index 0000000..0175414 --- /dev/null +++ b/0020-backport-Fix-cpu-share-issues-on-systems-with-large-amounts-o.patch @@ -0,0 +1,104 @@ +From 051b66b82d8cc76eca0da38e44ae0e7dd391ba09 Mon Sep 17 00:00:00 2001 +From: zhaoxiaohu +Date: Tue, 27 Aug 2024 16:04:40 +0800 +Subject: [PATCH] Fix cpu share issues on systems with large amounts of cpu + +On systems where the calculated cpu shares results in a value above the +max value in linux, containers getting that value are unable to start. +This occur on systems with 300+ cpu cores, and where containers are +given such a value. + +This issue was fixed for the pod and qos control groups in the similar +cm.MilliCPUToShares that also has tests verifying the behavior. Since +this code already has an dependency on kubelet/cm, lets reuse that code +instead. + +Reference: https://github.com/kubernetes/kubernetes/pull/106570/commits/de0ece541c7fd885a32f1562342fe85535fc11f5 + +Signed-off-by: zhaoxiaohu +Signed-off-by: Odin Ugedal +Signed-off-by: yuwang@kuaishou.com +--- + pkg/kubelet/kuberuntime/helpers_linux.go | 17 ----------------- + pkg/kubelet/kuberuntime/helpers_unsupported.go | 5 ----- + .../kuberuntime/kuberuntime_container_linux.go | 7 ++++--- + 3 files changed, 4 insertions(+), 25 deletions(-) + +diff --git a/pkg/kubelet/kuberuntime/helpers_linux.go b/pkg/kubelet/kuberuntime/helpers_linux.go +index 204bc4e9..4257a014 100644 +--- a/pkg/kubelet/kuberuntime/helpers_linux.go ++++ b/pkg/kubelet/kuberuntime/helpers_linux.go +@@ -19,9 +19,6 @@ limitations under the License. + package kuberuntime + + const ( +- // Taken from lmctfy https://github.com/google/lmctfy/blob/master/lmctfy/controllers/cpu_controller.cc +- minShares = 2 +- sharesPerCPU = 1024 + milliCPUToCPU = 1000 + + // 100000 is equivalent to 100ms +@@ -29,20 +26,6 @@ const ( + minQuotaPeriod = 1000 + ) + +-// milliCPUToShares converts milliCPU to CPU shares +-func milliCPUToShares(milliCPU int64) int64 { +- if milliCPU == 0 { +- // Return 2 here to really match kernel default for zero milliCPU. +- return minShares +- } +- // Conceptually (milliCPU / milliCPUToCPU) * sharesPerCPU, but factored to improve rounding. +- shares := (milliCPU * sharesPerCPU) / milliCPUToCPU +- if shares < minShares { +- return minShares +- } +- return shares +-} +- + // milliCPUToQuota converts milliCPU to CFS quota and period values + func milliCPUToQuota(milliCPU int64, period int64) (quota int64) { + // CFS quota is measured in two values: +diff --git a/pkg/kubelet/kuberuntime/helpers_unsupported.go b/pkg/kubelet/kuberuntime/helpers_unsupported.go +index cc1e88a5..8f6da8f4 100644 +--- a/pkg/kubelet/kuberuntime/helpers_unsupported.go ++++ b/pkg/kubelet/kuberuntime/helpers_unsupported.go +@@ -17,8 +17,3 @@ limitations under the License. + */ + + package kuberuntime +- +-// milliCPUToShares converts milliCPU to CPU shares +-func milliCPUToShares(milliCPU int64) int64 { +- return 0 +-} +diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go +index d7c22c86..25fb68ad 100644 +--- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go ++++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go +@@ -28,6 +28,7 @@ import ( + "k8s.io/klog/v2" + v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" + kubefeatures "k8s.io/kubernetes/pkg/features" ++ "k8s.io/kubernetes/pkg/kubelet/cm" + kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" + "k8s.io/kubernetes/pkg/kubelet/qos" + ) +@@ -69,11 +70,11 @@ func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *v1.C + // API server does this for new containers, but we repeat this logic in Kubelet + // for containers running on existing Kubernetes clusters. + if cpuRequest.IsZero() && !cpuLimit.IsZero() { +- cpuShares = milliCPUToShares(cpuLimit.MilliValue()) ++ cpuShares = int64(cm.MilliCPUToShares(cpuLimit.MilliValue())) + } else { +- // if cpuRequest.Amount is nil, then milliCPUToShares will return the minimal number ++ // if cpuRequest.Amount is nil, then MilliCPUToShares will return the minimal number + // of CPU shares. +- cpuShares = milliCPUToShares(cpuRequest.MilliValue()) ++ cpuShares = int64(cm.MilliCPUToShares(cpuRequest.MilliValue())) + } + lc.Resources.CpuShares = cpuShares + if memoryLimit != 0 { +-- +2.33.0 + diff --git a/kubernetes.spec b/kubernetes.spec index 678301a..26e114c 100644 --- a/kubernetes.spec +++ b/kubernetes.spec @@ -3,7 +3,7 @@ Name: kubernetes Version: 1.20.2 -Release: 24 +Release: 25 Summary: Container cluster management License: ASL 2.0 URL: https://k8s.io/kubernetes @@ -43,6 +43,7 @@ Patch6015: 0016-Add-envFrom-to-serviceaccount-admission-plugin.patch Patch6016: 0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch Patch6017: 0018-backport-reduce-configmap-and-secret-watch-of-kubelet.patch Patch6018: 0019-backport-Don-t-prematurely-close-reflectors-in-case-of-slow-i.patch +Patch6019: 0020-backport-Fix-cpu-share-issues-on-systems-with-large-amounts-o.patch %description Container cluster management. @@ -274,6 +275,12 @@ getent passwd kube >/dev/null || useradd -r -g kube -d / -s /sbin/nologin \ %systemd_postun kubelet kube-proxy %changelog +* Tue Aug 27 2024 zhaoxiaohu - 1.20.2-25 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:Fix cpu share issues on systems with large amounts of cpu(when cpucore>256) + * Fri Aug 23 2024 zhaoxiaohu - 1.20.2-24 - DESC:Don't prematurely close reflectors in case of slow initialization in watch based manager -- Gitee From 6a1dd7ef6b4c6c402560ae43110ddbf331229be3 Mon Sep 17 00:00:00 2001 From: liuxu Date: Thu, 5 Dec 2024 11:06:32 +0800 Subject: [PATCH 5/5] fix CVE-2024-10220 Signed-off-by: liuxu (cherry picked from commit fe15e15e970f9f61901016b043d7ce6d79ba91aa) --- ...e-directory-must-be-max-1-level-deep.patch | 56 +++++++++++++++++++ kubernetes.spec | 9 ++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 0021-gitRepo-volume-directory-must-be-max-1-level-deep.patch diff --git a/0021-gitRepo-volume-directory-must-be-max-1-level-deep.patch b/0021-gitRepo-volume-directory-must-be-max-1-level-deep.patch new file mode 100644 index 0000000..1712bd3 --- /dev/null +++ b/0021-gitRepo-volume-directory-must-be-max-1-level-deep.patch @@ -0,0 +1,56 @@ +From c7846fd24c16266a3bfd86315171f5b4d5f0c9c9 Mon Sep 17 00:00:00 2001 +From: Imre Rad +Date: Thu, 25 Apr 2024 14:21:51 +0000 +Subject: [PATCH] gitRepo volume: directory must be max 1 level deep + +More details on Hackerone #2266560 +--- + pkg/volume/git_repo/git_repo.go | 6 ++++++ + pkg/volume/git_repo/git_repo_test.go | 14 ++++++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/pkg/volume/git_repo/git_repo.go b/pkg/volume/git_repo/git_repo.go +index 995018d9007..b3827b92ad0 100644 +--- a/pkg/volume/git_repo/git_repo.go ++++ b/pkg/volume/git_repo/git_repo.go +@@ -261,6 +261,12 @@ func validateVolume(src *v1.GitRepoVolumeSource) error { + if err := validateNonFlagArgument(src.Directory, "directory"); err != nil { + return err + } ++ if (src.Revision != "") && (src.Directory != "") { ++ cleanedDir := filepath.Clean(src.Directory) ++ if strings.Contains(cleanedDir, "/") || (strings.Contains(cleanedDir, "\\")) { ++ return fmt.Errorf("%q is not a valid directory, it must not contain a directory separator", src.Directory) ++ } ++ } + return nil + } + +diff --git a/pkg/volume/git_repo/git_repo_test.go b/pkg/volume/git_repo/git_repo_test.go +index 5b1461be892..650f765cc48 100644 +--- a/pkg/volume/git_repo/git_repo_test.go ++++ b/pkg/volume/git_repo/git_repo_test.go +@@ -267,6 +267,20 @@ func TestPlugin(t *testing.T) { + }, + isExpectedFailure: true, + }, ++ { ++ name: "invalid-revision-directory-combo", ++ vol: &v1.Volume{ ++ Name: "vol1", ++ VolumeSource: v1.VolumeSource{ ++ GitRepo: &v1.GitRepoVolumeSource{ ++ Repository: gitURL, ++ Revision: "main", ++ Directory: "foo/bar", ++ }, ++ }, ++ }, ++ isExpectedFailure: true, ++ }, + } + + for _, scenario := range scenarios { +-- +2.34.1 + diff --git a/kubernetes.spec b/kubernetes.spec index 26e114c..e097591 100644 --- a/kubernetes.spec +++ b/kubernetes.spec @@ -3,7 +3,7 @@ Name: kubernetes Version: 1.20.2 -Release: 25 +Release: 26 Summary: Container cluster management License: ASL 2.0 URL: https://k8s.io/kubernetes @@ -44,6 +44,7 @@ Patch6016: 0017-backport-Fix-kubelet-panic-when-allocate-resource-for-pod.patch Patch6017: 0018-backport-reduce-configmap-and-secret-watch-of-kubelet.patch Patch6018: 0019-backport-Don-t-prematurely-close-reflectors-in-case-of-slow-i.patch Patch6019: 0020-backport-Fix-cpu-share-issues-on-systems-with-large-amounts-o.patch +Patch6020: 0021-gitRepo-volume-directory-must-be-max-1-level-deep.patch %description Container cluster management. @@ -275,6 +276,12 @@ getent passwd kube >/dev/null || useradd -r -g kube -d / -s /sbin/nologin \ %systemd_postun kubelet kube-proxy %changelog +* Thu Dec 05 2024 liuxu - 1.20.2-26 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:fix CVE-2024-10220 + * Tue Aug 27 2024 zhaoxiaohu - 1.20.2-25 - Type:bugfix - CVE:NA -- Gitee