mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 10:19:50 +00:00
kubelet: config: add userNamespaces.idsPerPod
IDsPerPod is the mapping length of subids for UserNS. The length must be multiple of 65536. Default: 65536 Implements kubernetes/enhancements PR 5020 (addendum to KEP-127) Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
parent
07a275437f
commit
1592bfa4a8
@ -261,6 +261,7 @@ API rule violation: names_match,k8s.io/kube-proxy/config/v1alpha1,KubeProxyConfi
|
|||||||
API rule violation: names_match,k8s.io/kubelet/config/v1beta1,KubeletConfiguration,IPTablesDropBit
|
API rule violation: names_match,k8s.io/kubelet/config/v1beta1,KubeletConfiguration,IPTablesDropBit
|
||||||
API rule violation: names_match,k8s.io/kubelet/config/v1beta1,KubeletConfiguration,IPTablesMasqueradeBit
|
API rule violation: names_match,k8s.io/kubelet/config/v1beta1,KubeletConfiguration,IPTablesMasqueradeBit
|
||||||
API rule violation: names_match,k8s.io/kubelet/config/v1beta1,KubeletConfiguration,ResolverConfig
|
API rule violation: names_match,k8s.io/kubelet/config/v1beta1,KubeletConfiguration,ResolverConfig
|
||||||
|
API rule violation: names_match,k8s.io/kubelet/config/v1beta1,UserNamespaces,IDsPerPod
|
||||||
API rule violation: names_match,k8s.io/metrics/pkg/apis/custom_metrics/v1beta1,MetricValue,WindowSeconds
|
API rule violation: names_match,k8s.io/metrics/pkg/apis/custom_metrics/v1beta1,MetricValue,WindowSeconds
|
||||||
API rule violation: names_match,k8s.io/metrics/pkg/apis/external_metrics/v1beta1,ExternalMetricValue,WindowSeconds
|
API rule violation: names_match,k8s.io/metrics/pkg/apis/external_metrics/v1beta1,ExternalMetricValue,WindowSeconds
|
||||||
API rule violation: streaming_list_type_proto_tags,k8s.io/apimachinery/pkg/apis/meta/v1beta1,PartialObjectMetadataList,Items
|
API rule violation: streaming_list_type_proto_tags,k8s.io/apimachinery/pkg/apis/meta/v1beta1,PartialObjectMetadataList,Items
|
||||||
|
29
pkg/generated/openapi/zz_generated.openapi.go
generated
29
pkg/generated/openapi/zz_generated.openapi.go
generated
@ -1280,6 +1280,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
|||||||
"k8s.io/kubelet/config/v1beta1.MemorySwapConfiguration": schema_k8sio_kubelet_config_v1beta1_MemorySwapConfiguration(ref),
|
"k8s.io/kubelet/config/v1beta1.MemorySwapConfiguration": schema_k8sio_kubelet_config_v1beta1_MemorySwapConfiguration(ref),
|
||||||
"k8s.io/kubelet/config/v1beta1.SerializedNodeConfigSource": schema_k8sio_kubelet_config_v1beta1_SerializedNodeConfigSource(ref),
|
"k8s.io/kubelet/config/v1beta1.SerializedNodeConfigSource": schema_k8sio_kubelet_config_v1beta1_SerializedNodeConfigSource(ref),
|
||||||
"k8s.io/kubelet/config/v1beta1.ShutdownGracePeriodByPodPriority": schema_k8sio_kubelet_config_v1beta1_ShutdownGracePeriodByPodPriority(ref),
|
"k8s.io/kubelet/config/v1beta1.ShutdownGracePeriodByPodPriority": schema_k8sio_kubelet_config_v1beta1_ShutdownGracePeriodByPodPriority(ref),
|
||||||
|
"k8s.io/kubelet/config/v1beta1.UserNamespaces": schema_k8sio_kubelet_config_v1beta1_UserNamespaces(ref),
|
||||||
"k8s.io/kubernetes/pkg/apis/abac/v1beta1.Policy": schema_pkg_apis_abac_v1beta1_Policy(ref),
|
"k8s.io/kubernetes/pkg/apis/abac/v1beta1.Policy": schema_pkg_apis_abac_v1beta1_Policy(ref),
|
||||||
"k8s.io/kubernetes/pkg/apis/abac/v1beta1.PolicySpec": schema_pkg_apis_abac_v1beta1_PolicySpec(ref),
|
"k8s.io/kubernetes/pkg/apis/abac/v1beta1.PolicySpec": schema_pkg_apis_abac_v1beta1_PolicySpec(ref),
|
||||||
"k8s.io/metrics/pkg/apis/custom_metrics/v1beta1.MetricListOptions": schema_pkg_apis_custom_metrics_v1beta1_MetricListOptions(ref),
|
"k8s.io/metrics/pkg/apis/custom_metrics/v1beta1.MetricListOptions": schema_pkg_apis_custom_metrics_v1beta1_MetricListOptions(ref),
|
||||||
@ -66201,12 +66202,18 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen
|
|||||||
Format: "",
|
Format: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"userNamespaces": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Description: "UserNamespaces contains User Namespace configurations.",
|
||||||
|
Ref: ref("k8s.io/kubelet/config/v1beta1.UserNamespaces"),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Required: []string{"containerRuntimeEndpoint"},
|
Required: []string{"containerRuntimeEndpoint"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Dependencies: []string{
|
Dependencies: []string{
|
||||||
"k8s.io/api/core/v1.Taint", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "k8s.io/component-base/logs/api/v1.LoggingConfiguration", "k8s.io/component-base/tracing/api/v1.TracingConfiguration", "k8s.io/kubelet/config/v1beta1.CrashLoopBackOffConfig", "k8s.io/kubelet/config/v1beta1.KubeletAuthentication", "k8s.io/kubelet/config/v1beta1.KubeletAuthorization", "k8s.io/kubelet/config/v1beta1.MemoryReservation", "k8s.io/kubelet/config/v1beta1.MemorySwapConfiguration", "k8s.io/kubelet/config/v1beta1.ShutdownGracePeriodByPodPriority"},
|
"k8s.io/api/core/v1.Taint", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "k8s.io/component-base/logs/api/v1.LoggingConfiguration", "k8s.io/component-base/tracing/api/v1.TracingConfiguration", "k8s.io/kubelet/config/v1beta1.CrashLoopBackOffConfig", "k8s.io/kubelet/config/v1beta1.KubeletAuthentication", "k8s.io/kubelet/config/v1beta1.KubeletAuthorization", "k8s.io/kubelet/config/v1beta1.MemoryReservation", "k8s.io/kubelet/config/v1beta1.MemorySwapConfiguration", "k8s.io/kubelet/config/v1beta1.ShutdownGracePeriodByPodPriority", "k8s.io/kubelet/config/v1beta1.UserNamespaces"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66403,6 +66410,26 @@ func schema_k8sio_kubelet_config_v1beta1_ShutdownGracePeriodByPodPriority(ref co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func schema_k8sio_kubelet_config_v1beta1_UserNamespaces(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||||
|
return common.OpenAPIDefinition{
|
||||||
|
Schema: spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Description: "UserNamespaces contains User Namespace configurations.",
|
||||||
|
Type: []string{"object"},
|
||||||
|
Properties: map[string]spec.Schema{
|
||||||
|
"idsPerPod": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Description: "IDsPerPod is the mapping length of UIDs and GIDs. The length must be a multiple of 65536, and must be less than 1<<32. On non-linux such as windows, only null / absent is allowed.\n\nChanging the value may require recreating all containers on the node.\n\nDefault: 65536",
|
||||||
|
Type: []string{"integer"},
|
||||||
|
Format: "int64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func schema_pkg_apis_abac_v1beta1_Policy(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
func schema_pkg_apis_abac_v1beta1_Policy(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||||
return common.OpenAPIDefinition{
|
return common.OpenAPIDefinition{
|
||||||
Schema: spec.Schema{
|
Schema: spec.Schema{
|
||||||
|
@ -306,5 +306,6 @@ var (
|
|||||||
"LocalStorageCapacityIsolation",
|
"LocalStorageCapacityIsolation",
|
||||||
"FailCgroupV1",
|
"FailCgroupV1",
|
||||||
"CrashLoopBackOff.MaxContainerRestartPeriod",
|
"CrashLoopBackOff.MaxContainerRestartPeriod",
|
||||||
|
"UserNamespaces.IDsPerPod",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -541,6 +541,11 @@ type KubeletConfiguration struct {
|
|||||||
// +featureGate=KubeletCrashLoopBackoffMax
|
// +featureGate=KubeletCrashLoopBackoffMax
|
||||||
// +optional
|
// +optional
|
||||||
CrashLoopBackOff CrashLoopBackOffConfig
|
CrashLoopBackOff CrashLoopBackOffConfig
|
||||||
|
|
||||||
|
// UserNamespaces contains User Namespace configurations.
|
||||||
|
// +featureGate=UserNamespaceSupport
|
||||||
|
// +optional
|
||||||
|
UserNamespaces *UserNamespaces
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubeletAuthorizationMode denotes the authorization mode for the kubelet
|
// KubeletAuthorizationMode denotes the authorization mode for the kubelet
|
||||||
@ -878,3 +883,17 @@ type ImagePullSecret struct {
|
|||||||
// content of the secret specified by the UID/Namespace/Name coordinates.
|
// content of the secret specified by the UID/Namespace/Name coordinates.
|
||||||
CredentialHash string
|
CredentialHash string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserNamespaces contains User Namespace configurations.
|
||||||
|
type UserNamespaces struct {
|
||||||
|
// IDsPerPod is the mapping length of UIDs and GIDs.
|
||||||
|
// The length must be a multiple of 65536, and must be less than 1<<32.
|
||||||
|
// On non-linux such as windows, only null / absent is allowed.
|
||||||
|
//
|
||||||
|
// Changing the value may require recreating all containers on the node.
|
||||||
|
//
|
||||||
|
// Default: 65536
|
||||||
|
// +featureGate=UserNamespaceSupport
|
||||||
|
// +optional
|
||||||
|
IDsPerPod *int64
|
||||||
|
}
|
||||||
|
@ -185,6 +185,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.AddGeneratedConversionFunc((*configv1beta1.UserNamespaces)(nil), (*config.UserNamespaces)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return Convert_v1beta1_UserNamespaces_To_config_UserNamespaces(a.(*configv1beta1.UserNamespaces), b.(*config.UserNamespaces), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddGeneratedConversionFunc((*config.UserNamespaces)(nil), (*configv1beta1.UserNamespaces)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return Convert_config_UserNamespaces_To_v1beta1_UserNamespaces(a.(*config.UserNamespaces), b.(*configv1beta1.UserNamespaces), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := s.AddConversionFunc((*config.CredentialProvider)(nil), (*configv1beta1.CredentialProvider)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
if err := s.AddConversionFunc((*config.CredentialProvider)(nil), (*configv1beta1.CredentialProvider)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
return Convert_config_CredentialProvider_To_v1beta1_CredentialProvider(a.(*config.CredentialProvider), b.(*configv1beta1.CredentialProvider), scope)
|
return Convert_config_CredentialProvider_To_v1beta1_CredentialProvider(a.(*config.CredentialProvider), b.(*configv1beta1.CredentialProvider), scope)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@ -584,6 +594,7 @@ func autoConvert_v1beta1_KubeletConfiguration_To_config_KubeletConfiguration(in
|
|||||||
if err := v1.Convert_Pointer_bool_To_bool(&in.FailCgroupV1, &out.FailCgroupV1, s); err != nil {
|
if err := v1.Convert_Pointer_bool_To_bool(&in.FailCgroupV1, &out.FailCgroupV1, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
out.UserNamespaces = (*config.UserNamespaces)(unsafe.Pointer(in.UserNamespaces))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,6 +800,7 @@ func autoConvert_config_KubeletConfiguration_To_v1beta1_KubeletConfiguration(in
|
|||||||
if err := Convert_config_CrashLoopBackOffConfig_To_v1beta1_CrashLoopBackOffConfig(&in.CrashLoopBackOff, &out.CrashLoopBackOff, s); err != nil {
|
if err := Convert_config_CrashLoopBackOffConfig_To_v1beta1_CrashLoopBackOffConfig(&in.CrashLoopBackOff, &out.CrashLoopBackOff, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
out.UserNamespaces = (*configv1beta1.UserNamespaces)(unsafe.Pointer(in.UserNamespaces))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,3 +960,23 @@ func autoConvert_config_ShutdownGracePeriodByPodPriority_To_v1beta1_ShutdownGrac
|
|||||||
func Convert_config_ShutdownGracePeriodByPodPriority_To_v1beta1_ShutdownGracePeriodByPodPriority(in *config.ShutdownGracePeriodByPodPriority, out *configv1beta1.ShutdownGracePeriodByPodPriority, s conversion.Scope) error {
|
func Convert_config_ShutdownGracePeriodByPodPriority_To_v1beta1_ShutdownGracePeriodByPodPriority(in *config.ShutdownGracePeriodByPodPriority, out *configv1beta1.ShutdownGracePeriodByPodPriority, s conversion.Scope) error {
|
||||||
return autoConvert_config_ShutdownGracePeriodByPodPriority_To_v1beta1_ShutdownGracePeriodByPodPriority(in, out, s)
|
return autoConvert_config_ShutdownGracePeriodByPodPriority_To_v1beta1_ShutdownGracePeriodByPodPriority(in, out, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func autoConvert_v1beta1_UserNamespaces_To_config_UserNamespaces(in *configv1beta1.UserNamespaces, out *config.UserNamespaces, s conversion.Scope) error {
|
||||||
|
out.IDsPerPod = (*int64)(unsafe.Pointer(in.IDsPerPod))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_v1beta1_UserNamespaces_To_config_UserNamespaces is an autogenerated conversion function.
|
||||||
|
func Convert_v1beta1_UserNamespaces_To_config_UserNamespaces(in *configv1beta1.UserNamespaces, out *config.UserNamespaces, s conversion.Scope) error {
|
||||||
|
return autoConvert_v1beta1_UserNamespaces_To_config_UserNamespaces(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoConvert_config_UserNamespaces_To_v1beta1_UserNamespaces(in *config.UserNamespaces, out *configv1beta1.UserNamespaces, s conversion.Scope) error {
|
||||||
|
out.IDsPerPod = (*int64)(unsafe.Pointer(in.IDsPerPod))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_config_UserNamespaces_To_v1beta1_UserNamespaces is an autogenerated conversion function.
|
||||||
|
func Convert_config_UserNamespaces_To_v1beta1_UserNamespaces(in *config.UserNamespaces, out *configv1beta1.UserNamespaces, s conversion.Scope) error {
|
||||||
|
return autoConvert_config_UserNamespaces_To_v1beta1_UserNamespaces(in, out, s)
|
||||||
|
}
|
||||||
|
@ -21,12 +21,15 @@ package validation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
libcontainercgroups "github.com/opencontainers/cgroups"
|
libcontainercgroups "github.com/opencontainers/cgroups"
|
||||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||||
"k8s.io/utils/ptr"
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const userNsUnitLength = 65536
|
||||||
|
|
||||||
// validateKubeletOSConfiguration validates os specific kubelet configuration and returns an error if it is invalid.
|
// validateKubeletOSConfiguration validates os specific kubelet configuration and returns an error if it is invalid.
|
||||||
func validateKubeletOSConfiguration(kc *kubeletconfig.KubeletConfiguration) error {
|
func validateKubeletOSConfiguration(kc *kubeletconfig.KubeletConfiguration) error {
|
||||||
isCgroup1 := !libcontainercgroups.IsCgroup2UnifiedMode()
|
isCgroup1 := !libcontainercgroups.IsCgroup2UnifiedMode()
|
||||||
@ -38,5 +41,20 @@ func validateKubeletOSConfiguration(kc *kubeletconfig.KubeletConfiguration) erro
|
|||||||
return fmt.Errorf("invalid configuration: singleProcessOOMKill must not be explicitly set to false when using cgroup v1")
|
return fmt.Errorf("invalid configuration: singleProcessOOMKill must not be explicitly set to false when using cgroup v1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userNs := kc.UserNamespaces; userNs != nil {
|
||||||
|
if idsPerPod := userNs.IDsPerPod; idsPerPod != nil {
|
||||||
|
if *idsPerPod < userNsUnitLength {
|
||||||
|
return fmt.Errorf("invalid configuration: userNamespaces.idsPerPod must not be less than %d", userNsUnitLength)
|
||||||
|
}
|
||||||
|
if *idsPerPod%userNsUnitLength != 0 {
|
||||||
|
return fmt.Errorf("invalid configuration: userNamespaces.idsPerPod must be a multiple of %d", userNsUnitLength)
|
||||||
|
}
|
||||||
|
if *idsPerPod > math.MaxUint32 {
|
||||||
|
// int64() is needed for 32-bit targets
|
||||||
|
return fmt.Errorf("invalid configuration: userNamespaces.idsPerPod must not be more than %d", int64(math.MaxUint32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -31,5 +31,9 @@ func validateKubeletOSConfiguration(kc *kubeletconfig.KubeletConfiguration) erro
|
|||||||
return fmt.Errorf("invalid configuration: singleProcessOOMKill is only supported on linux")
|
return fmt.Errorf("invalid configuration: singleProcessOOMKill is only supported on linux")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if kc.UserNamespaces != nil {
|
||||||
|
return fmt.Errorf("invalid configuration: userNamespaces is only supported on linux")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -46,5 +46,9 @@ func validateKubeletOSConfiguration(kc *kubeletconfig.KubeletConfiguration) erro
|
|||||||
klog.Warningf(message, "EnforceNodeAllocatable", "--enforce-node-allocatable", kc.EnforceNodeAllocatable)
|
klog.Warningf(message, "EnforceNodeAllocatable", "--enforce-node-allocatable", kc.EnforceNodeAllocatable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if kc.UserNamespaces != nil {
|
||||||
|
return fmt.Errorf("invalid configuration: userNamespaces is not supported on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
26
pkg/kubelet/apis/config/zz_generated.deepcopy.go
generated
26
pkg/kubelet/apis/config/zz_generated.deepcopy.go
generated
@ -459,6 +459,11 @@ func (in *KubeletConfiguration) DeepCopyInto(out *KubeletConfiguration) {
|
|||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
in.CrashLoopBackOff.DeepCopyInto(&out.CrashLoopBackOff)
|
in.CrashLoopBackOff.DeepCopyInto(&out.CrashLoopBackOff)
|
||||||
|
if in.UserNamespaces != nil {
|
||||||
|
in, out := &in.UserNamespaces, &out.UserNamespaces
|
||||||
|
*out = new(UserNamespaces)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,3 +647,24 @@ func (in *ShutdownGracePeriodByPodPriority) DeepCopy() *ShutdownGracePeriodByPod
|
|||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *UserNamespaces) DeepCopyInto(out *UserNamespaces) {
|
||||||
|
*out = *in
|
||||||
|
if in.IDsPerPod != nil {
|
||||||
|
in, out := &in.IDsPerPod, &out.IDsPerPod
|
||||||
|
*out = new(int64)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserNamespaces.
|
||||||
|
func (in *UserNamespaces) DeepCopy() *UserNamespaces {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(UserNamespaces)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
@ -30,4 +30,5 @@ const (
|
|||||||
KubeletPluginsDirSELinuxLabel = "system_u:object_r:container_file_t:s0"
|
KubeletPluginsDirSELinuxLabel = "system_u:object_r:container_file_t:s0"
|
||||||
KubeletContainersSharedSELinuxLabel = "system_u:object_r:container_file_t:s0"
|
KubeletContainersSharedSELinuxLabel = "system_u:object_r:container_file_t:s0"
|
||||||
DefaultKubeletCheckpointsDirName = "checkpoints"
|
DefaultKubeletCheckpointsDirName = "checkpoints"
|
||||||
|
DefaultKubeletUserNamespacesIDsPerPod = 65536
|
||||||
)
|
)
|
||||||
|
@ -140,6 +140,20 @@ func (kl *Kubelet) GetMaxPods() int {
|
|||||||
return kl.maxPods
|
return kl.maxPods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kl *Kubelet) GetUserNamespacesIDsPerPod() uint32 {
|
||||||
|
userNs := kl.kubeletConfiguration.UserNamespaces
|
||||||
|
if userNs == nil {
|
||||||
|
return config.DefaultKubeletUserNamespacesIDsPerPod
|
||||||
|
}
|
||||||
|
idsPerPod := userNs.IDsPerPod
|
||||||
|
if idsPerPod == nil || *idsPerPod == 0 {
|
||||||
|
return config.DefaultKubeletUserNamespacesIDsPerPod
|
||||||
|
}
|
||||||
|
// The value is already validated to be <= MaxUint32,
|
||||||
|
// so we can safely drop the upper bits.
|
||||||
|
return uint32(*idsPerPod)
|
||||||
|
}
|
||||||
|
|
||||||
// getPodDir returns the full path to the per-pod directory for the pod with
|
// getPodDir returns the full path to the per-pod directory for the pod with
|
||||||
// the given UID.
|
// the given UID.
|
||||||
func (kl *Kubelet) getPodDir(podUID types.UID) string {
|
func (kl *Kubelet) getPodDir(podUID types.UID) string {
|
||||||
|
@ -26,4 +26,5 @@ type userNsPodsManager interface {
|
|||||||
ListPodsFromDisk() ([]types.UID, error)
|
ListPodsFromDisk() ([]types.UID, error)
|
||||||
GetKubeletMappings() (uint32, uint32, error)
|
GetKubeletMappings() (uint32, uint32, error)
|
||||||
GetMaxPods() int
|
GetMaxPods() int
|
||||||
|
GetUserNamespacesIDsPerPod() uint32
|
||||||
}
|
}
|
||||||
|
@ -39,12 +39,14 @@ import (
|
|||||||
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// length for the user namespace to create (65536).
|
const (
|
||||||
const userNsLength = (1 << 16)
|
// Create a new map when we removed enough pods to avoid memory leaks
|
||||||
|
// since Go maps never free memory.
|
||||||
|
mapReInitializeThreshold = 1000
|
||||||
|
|
||||||
// Create a new map when we removed enough pods to avoid memory leaks
|
// userNsUnitLength is the unit length of UserNS
|
||||||
// since Go maps never free memory.
|
userNsUnitLength = 65536
|
||||||
const mapReInitializeThreshold = 1000
|
)
|
||||||
|
|
||||||
type UsernsManager struct {
|
type UsernsManager struct {
|
||||||
used *allocator.AllocationBitmap
|
used *allocator.AllocationBitmap
|
||||||
@ -54,6 +56,8 @@ type UsernsManager struct {
|
|||||||
off int
|
off int
|
||||||
len int
|
len int
|
||||||
|
|
||||||
|
userNsLength uint32
|
||||||
|
|
||||||
kl userNsPodsManager
|
kl userNsPodsManager
|
||||||
// This protects all members except for kl.anager
|
// This protects all members except for kl.anager
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
@ -130,6 +134,11 @@ func MakeUserNsManager(kl userNsPodsManager) (*UsernsManager, error) {
|
|||||||
return nil, fmt.Errorf("kubelet mappings: %w", err)
|
return nil, fmt.Errorf("kubelet mappings: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userNsLength := kl.GetUserNamespacesIDsPerPod()
|
||||||
|
|
||||||
|
if userNsLength%userNsUnitLength != 0 {
|
||||||
|
return nil, fmt.Errorf("kubelet user namespace length %v is not a multiple of %d", userNsLength, userNsUnitLength)
|
||||||
|
}
|
||||||
if kubeletMappingID%userNsLength != 0 {
|
if kubeletMappingID%userNsLength != 0 {
|
||||||
return nil, fmt.Errorf("kubelet user assigned ID %v is not a multiple of %v", kubeletMappingID, userNsLength)
|
return nil, fmt.Errorf("kubelet user assigned ID %v is not a multiple of %v", kubeletMappingID, userNsLength)
|
||||||
}
|
}
|
||||||
@ -152,6 +161,7 @@ func MakeUserNsManager(kl userNsPodsManager) (*UsernsManager, error) {
|
|||||||
kl: kl,
|
kl: kl,
|
||||||
off: off,
|
off: off,
|
||||||
len: len,
|
len: len,
|
||||||
|
userNsLength: userNsLength,
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not bother reading the list of pods if user namespaces are not enabled.
|
// do not bother reading the list of pods if user namespaces are not enabled.
|
||||||
@ -196,7 +206,7 @@ func (m *UsernsManager) recordPodMappings(pod types.UID) error {
|
|||||||
|
|
||||||
// isSet checks if the specified index is already set.
|
// isSet checks if the specified index is already set.
|
||||||
func (m *UsernsManager) isSet(v uint32) bool {
|
func (m *UsernsManager) isSet(v uint32) bool {
|
||||||
index := int(v/userNsLength) - m.off
|
index := int(v/m.userNsLength) - m.off
|
||||||
if index < 0 || index >= m.len {
|
if index < 0 || index >= m.len {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -217,24 +227,24 @@ func (m *UsernsManager) allocateOne(pod types.UID) (firstID uint32, length uint3
|
|||||||
|
|
||||||
klog.V(5).InfoS("new pod user namespace allocation", "podUID", pod)
|
klog.V(5).InfoS("new pod user namespace allocation", "podUID", pod)
|
||||||
|
|
||||||
firstID = uint32((firstZero + m.off) * userNsLength)
|
firstID = uint32((firstZero + m.off)) * m.userNsLength
|
||||||
m.usedBy[pod] = firstID
|
m.usedBy[pod] = firstID
|
||||||
return firstID, userNsLength, nil
|
return firstID, m.userNsLength, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// record stores the user namespace [from; from+length] to the specified pod.
|
// record stores the user namespace [from; from+length] to the specified pod.
|
||||||
func (m *UsernsManager) record(pod types.UID, from, length uint32) (err error) {
|
func (m *UsernsManager) record(pod types.UID, from, length uint32) (err error) {
|
||||||
if length != userNsLength {
|
if length != m.userNsLength {
|
||||||
return fmt.Errorf("wrong user namespace length %v", length)
|
return fmt.Errorf("wrong user namespace length %v", length)
|
||||||
}
|
}
|
||||||
if from%userNsLength != 0 {
|
if from%m.userNsLength != 0 {
|
||||||
return fmt.Errorf("wrong user namespace offset specified %v", from)
|
return fmt.Errorf("wrong user namespace offset specified %v", from)
|
||||||
}
|
}
|
||||||
prevFrom, found := m.usedBy[pod]
|
prevFrom, found := m.usedBy[pod]
|
||||||
if found && prevFrom != from {
|
if found && prevFrom != from {
|
||||||
return fmt.Errorf("different user namespace range already used by pod %q", pod)
|
return fmt.Errorf("different user namespace range already used by pod %q", pod)
|
||||||
}
|
}
|
||||||
index := int(from/userNsLength) - m.off
|
index := int(from/m.userNsLength) - m.off
|
||||||
if index < 0 || index >= m.len {
|
if index < 0 || index >= m.len {
|
||||||
return fmt.Errorf("id %v is out of range", from)
|
return fmt.Errorf("id %v is out of range", from)
|
||||||
}
|
}
|
||||||
@ -302,7 +312,7 @@ func (m *UsernsManager) releaseWithLock(pod types.UID) {
|
|||||||
m.usedBy = n
|
m.usedBy = n
|
||||||
m.removed = 0
|
m.removed = 0
|
||||||
}
|
}
|
||||||
_ = m.used.Release(int(v/userNsLength) - m.off)
|
_ = m.used.Release(int(v/m.userNsLength) - m.off)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UsernsManager) parseUserNsFileAndRecord(pod types.UID, content []byte) (userNs userNamespace, err error) {
|
func (m *UsernsManager) parseUserNsFileAndRecord(pod types.UID, content []byte) (userNs userNamespace, err error) {
|
||||||
|
@ -38,10 +38,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
testUserNsLength = uint32(65536)
|
||||||
// skip the first block
|
// skip the first block
|
||||||
minimumMappingUID = userNsLength
|
minimumMappingUID = testUserNsLength
|
||||||
// allocate enough space for 2000 user namespaces
|
// allocate enough space for 2000 user namespaces
|
||||||
mappingLen = userNsLength * 2000
|
mappingLen = testUserNsLength * 2000
|
||||||
testMaxPods = 110
|
testMaxPods = 110
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,6 +53,7 @@ type testUserNsPodsManager struct {
|
|||||||
maxPods int
|
maxPods int
|
||||||
mappingFirstID uint32
|
mappingFirstID uint32
|
||||||
mappingLen uint32
|
mappingLen uint32
|
||||||
|
userNsLength uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *testUserNsPodsManager) GetPodDir(podUID types.UID) string {
|
func (m *testUserNsPodsManager) GetPodDir(podUID types.UID) string {
|
||||||
@ -90,6 +92,13 @@ func (m *testUserNsPodsManager) GetMaxPods() int {
|
|||||||
return testMaxPods
|
return testMaxPods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *testUserNsPodsManager) GetUserNamespacesIDsPerPod() uint32 {
|
||||||
|
if m.userNsLength != 0 {
|
||||||
|
return m.userNsLength
|
||||||
|
}
|
||||||
|
return testUserNsLength
|
||||||
|
}
|
||||||
|
|
||||||
func TestUserNsManagerAllocate(t *testing.T) {
|
func TestUserNsManagerAllocate(t *testing.T) {
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.UserNamespacesSupport, true)
|
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.UserNamespacesSupport, true)
|
||||||
|
|
||||||
@ -99,7 +108,7 @@ func TestUserNsManagerAllocate(t *testing.T) {
|
|||||||
|
|
||||||
allocated, length, err := m.allocateOne("one")
|
allocated, length, err := m.allocateOne("one")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, userNsLength, int(length), "m.isSet(%d).length=%v", allocated, length)
|
assert.Equal(t, testUserNsLength, length, "m.isSet(%d).length=%v", allocated, length)
|
||||||
assert.True(t, m.isSet(allocated), "m.isSet(%d)", allocated)
|
assert.True(t, m.isSet(allocated), "m.isSet(%d)", allocated)
|
||||||
|
|
||||||
allocated2, length2, err := m.allocateOne("two")
|
allocated2, length2, err := m.allocateOne("two")
|
||||||
@ -122,11 +131,11 @@ func TestUserNsManagerAllocate(t *testing.T) {
|
|||||||
var allocs []uint32
|
var allocs []uint32
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
allocated, length, err = m.allocateOne(types.UID(fmt.Sprintf("%d", i)))
|
allocated, length, err = m.allocateOne(types.UID(fmt.Sprintf("%d", i)))
|
||||||
assert.Equal(t, userNsLength, int(length), "length is not the expected. iter: %v", i)
|
assert.Equal(t, testUserNsLength, length, "length is not the expected. iter: %v", i)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.GreaterOrEqual(t, allocated, uint32(minimumMappingUID))
|
assert.GreaterOrEqual(t, allocated, uint32(minimumMappingUID))
|
||||||
// The last ID of the userns range (allocated+userNsLength) should be within bounds.
|
// The last ID of the userns range (allocated+userNsLength) should be within bounds.
|
||||||
assert.LessOrEqual(t, allocated, uint32(minimumMappingUID+mappingLen-userNsLength))
|
assert.LessOrEqual(t, allocated, uint32(minimumMappingUID+mappingLen-testUserNsLength))
|
||||||
allocs = append(allocs, allocated)
|
allocs = append(allocs, allocated)
|
||||||
}
|
}
|
||||||
for i, v := range allocs {
|
for i, v := range allocs {
|
||||||
@ -134,7 +143,7 @@ func TestUserNsManagerAllocate(t *testing.T) {
|
|||||||
m.Release(types.UID(fmt.Sprintf("%d", i)))
|
m.Release(types.UID(fmt.Sprintf("%d", i)))
|
||||||
assert.False(t, m.isSet(v), "m.isSet(%d) should be false", v)
|
assert.False(t, m.isSet(v), "m.isSet(%d) should be false", v)
|
||||||
|
|
||||||
err = m.record(types.UID(fmt.Sprintf("%d", i)), v, userNsLength)
|
err = m.record(types.UID(fmt.Sprintf("%d", i)), v, testUserNsLength)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
m.Release(types.UID(fmt.Sprintf("%d", i)))
|
m.Release(types.UID(fmt.Sprintf("%d", i)))
|
||||||
assert.False(t, m.isSet(v), "m.isSet(%d) should be false", v)
|
assert.False(t, m.isSet(v), "m.isSet(%d) should be false", v)
|
||||||
|
@ -924,6 +924,11 @@ type KubeletConfiguration struct {
|
|||||||
// Default: false
|
// Default: false
|
||||||
// +optional
|
// +optional
|
||||||
FailCgroupV1 *bool `json:"failCgroupV1,omitempty"`
|
FailCgroupV1 *bool `json:"failCgroupV1,omitempty"`
|
||||||
|
|
||||||
|
// UserNamespaces contains User Namespace configurations.
|
||||||
|
// +featureGate=UserNamespaceSupport
|
||||||
|
// +optional
|
||||||
|
UserNamespaces *UserNamespaces `json:"userNamespaces,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type KubeletAuthorizationMode string
|
type KubeletAuthorizationMode string
|
||||||
@ -1119,3 +1124,17 @@ type ExecEnvVar struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserNamespaces contains User Namespace configurations.
|
||||||
|
type UserNamespaces struct {
|
||||||
|
// IDsPerPod is the mapping length of UIDs and GIDs.
|
||||||
|
// The length must be a multiple of 65536, and must be less than 1<<32.
|
||||||
|
// On non-linux such as windows, only null / absent is allowed.
|
||||||
|
//
|
||||||
|
// Changing the value may require recreating all containers on the node.
|
||||||
|
//
|
||||||
|
// Default: 65536
|
||||||
|
// +featureGate=UserNamespaceSupport
|
||||||
|
// +optional
|
||||||
|
IDsPerPod *int64 `json:"idsPerPod,omitempty"`
|
||||||
|
}
|
||||||
|
@ -527,6 +527,11 @@ func (in *KubeletConfiguration) DeepCopyInto(out *KubeletConfiguration) {
|
|||||||
*out = new(bool)
|
*out = new(bool)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
|
if in.UserNamespaces != nil {
|
||||||
|
in, out := &in.UserNamespaces, &out.UserNamespaces
|
||||||
|
*out = new(UserNamespaces)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,3 +689,24 @@ func (in *ShutdownGracePeriodByPodPriority) DeepCopy() *ShutdownGracePeriodByPod
|
|||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *UserNamespaces) DeepCopyInto(out *UserNamespaces) {
|
||||||
|
*out = *in
|
||||||
|
if in.IDsPerPod != nil {
|
||||||
|
in, out := &in.IDsPerPod, &out.IDsPerPod
|
||||||
|
*out = new(int64)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserNamespaces.
|
||||||
|
func (in *UserNamespaces) DeepCopy() *UserNamespaces {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(UserNamespaces)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user