From c1e34bc725010cfab2157545d4c0f8bc851db966 Mon Sep 17 00:00:00 2001 From: Michael Taufen Date: Fri, 9 Feb 2018 09:50:04 -0800 Subject: [PATCH] Secure Kubelet's componentconfig defaults while maintaining CLI compatibility This updates the Kubelet's componentconfig defaults, while applying the legacy defaults to values from options.NewKubeletConfiguration(). This keeps defaults the same for the command line and improves the security of defaults when you load config from a file. See: https://github.com/kubernetes/kubernetes/issues/53618 See: https://github.com/kubernetes/kubernetes/pull/53833#discussion_r166669931 --- .../kubeadm/validation/validation_test.go | 4 ++-- cmd/kubelet/app/options/BUILD | 1 + cmd/kubelet/app/options/options.go | 22 ++++++++++++++++++- cmd/kubelet/app/server.go | 6 ++--- .../apis/kubeletconfig/fuzzer/fuzzer.go | 1 - .../apis/kubeletconfig/helpers_test.go | 1 - pkg/kubelet/apis/kubeletconfig/types.go | 2 -- .../apis/kubeletconfig/v1alpha1/defaults.go | 12 +++------- .../apis/kubeletconfig/v1alpha1/types.go | 6 ++--- .../v1alpha1/zz_generated.conversion.go | 14 ++---------- .../v1alpha1/zz_generated.deepcopy.go | 18 --------------- pkg/kubemark/hollow_kubelet.go | 2 +- 12 files changed, 35 insertions(+), 54 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 897121a68fe..13293a2f1ba 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -587,7 +587,7 @@ func TestValidateKubeletConfiguration(t *testing.T) { OOMScoreAdj: utilpointer.Int32Ptr(-999), PodsPerCore: 100, Port: 65535, - ReadOnlyPort: utilpointer.Int32Ptr(0), + ReadOnlyPort: 0, RegistryBurst: 10, RegistryPullQPS: utilpointer.Int32Ptr(5), HairpinMode: "promiscuous-bridge", @@ -617,7 +617,7 @@ func TestValidateKubeletConfiguration(t *testing.T) { OOMScoreAdj: utilpointer.Int32Ptr(-1001), PodsPerCore: -10, Port: 0, - ReadOnlyPort: utilpointer.Int32Ptr(-10), + ReadOnlyPort: -10, RegistryBurst: -10, RegistryPullQPS: utilpointer.Int32Ptr(-10), }, diff --git a/cmd/kubelet/app/options/BUILD b/cmd/kubelet/app/options/BUILD index 8ba49aaa0c6..b33a0fd97d0 100644 --- a/cmd/kubelet/app/options/BUILD +++ b/cmd/kubelet/app/options/BUILD @@ -61,6 +61,7 @@ go_library( "//pkg/kubelet/apis/kubeletconfig/validation:go_default_library", "//pkg/kubelet/config:go_default_library", "//pkg/kubelet/types:go_default_library", + "//pkg/master/ports:go_default_library", "//pkg/util/taints:go_default_library", "//pkg/version/verflag:go_default_library", "//vendor/github.com/golang/glog:go_default_library", diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index ce663dbb7ea..4faec0a1bf5 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -39,6 +39,7 @@ import ( kubeletconfigvalidation "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/validation" "k8s.io/kubernetes/pkg/kubelet/config" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" + "k8s.io/kubernetes/pkg/master/ports" utiltaints "k8s.io/kubernetes/pkg/util/taints" ) @@ -65,6 +66,9 @@ type KubeletFlags struct { // run those in addition to the pods specified by the local manifest, and exit. RunOnce bool + // enableServer enables the Kubelet's server + EnableServer bool + // HostnameOverride is the hostname used to identify the kubelet instead // of the actual hostname. HostnameOverride string @@ -214,6 +218,7 @@ func NewKubeletFlags() *KubeletFlags { } return &KubeletFlags{ + EnableServer: true, ContainerRuntimeOptions: *NewContainerRuntimeOptions(), CertDirectory: "/var/lib/kubelet/pki", RootDirectory: v1alpha1.DefaultRootDir, @@ -264,9 +269,24 @@ func NewKubeletConfiguration() (*kubeletconfig.KubeletConfiguration, error) { if err := scheme.Convert(versioned, config, nil); err != nil { return nil, err } + applyLegacyDefaults(config) return config, nil } +// applyLegacyDefaults applies legacy default values to the KubeletConfiguration in order to +// preserve the command line API. This is used to construct the baseline default KubeletConfiguration +// before the first round of flag parsing. +func applyLegacyDefaults(kc *kubeletconfig.KubeletConfiguration) { + // --anonymous-auth + kc.Authentication.Anonymous.Enabled = true + // --authentication-token-webhook + kc.Authentication.Webhook.Enabled = false + // --authorization-mode + kc.Authorization.Mode = kubeletconfig.KubeletAuthorizationModeAlwaysAllow + // --read-only-port + kc.ReadOnlyPort = ports.KubeletReadOnlyPort +} + // KubeletServer encapsulates all of the parameters necessary for starting up // a kubelet. These can either be set via command line or directly. type KubeletServer struct { @@ -322,6 +342,7 @@ func (f *KubeletFlags) AddFlags(fs *pflag.FlagSet) { fs.Float64Var(&f.ChaosChance, "chaos-chance", f.ChaosChance, "If > 0.0, introduce random client errors and latency. Intended for testing.") fs.BoolVar(&f.RunOnce, "runonce", f.RunOnce, "If true, exit after spawning pods from local manifests or remote urls. Exclusive with --enable-server") + fs.BoolVar(&f.EnableServer, "enable-server", f.EnableServer, "Enable the Kubelet's server") fs.StringVar(&f.HostnameOverride, "hostname-override", f.HostnameOverride, "If non-empty, will use this string as identification instead of the actual hostname.") @@ -407,7 +428,6 @@ func AddKubeletConfigFlags(fs *pflag.FlagSet, c *kubeletconfig.KubeletConfigurat fs.DurationVar(&c.HTTPCheckFrequency.Duration, "http-check-frequency", c.HTTPCheckFrequency.Duration, "Duration between checking http for new data") fs.StringVar(&c.ManifestURL, "manifest-url", c.ManifestURL, "URL for accessing the container manifest") fs.Var(flag.NewColonSeparatedMultimapStringString(&c.ManifestURLHeader), "manifest-url-header", "Comma-separated list of HTTP headers to use when accessing the manifest URL. Multiple headers with the same name will be added in the same order provided. This flag can be repeatedly invoked. For example: `--manifest-url-header 'a:hello,b:again,c:world' --manifest-url-header 'b:beautiful'`") - fs.BoolVar(&c.EnableServer, "enable-server", c.EnableServer, "Enable the Kubelet's server") fs.Var(componentconfig.IPVar{Val: &c.Address}, "address", "The IP address for the Kubelet to serve on (set to `0.0.0.0` for all IPv4 interfaces and `::` for all IPv6 interfaces)") fs.Int32Var(&c.Port, "port", c.Port, "The port for the Kubelet to serve on.") fs.Int32Var(&c.ReadOnlyPort, "read-only-port", c.ReadOnlyPort, "The read-only port for the Kubelet to serve on with no authentication/authorization (set to 0 to disable)") diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index af473275e2c..21347dba652 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -930,18 +930,18 @@ func RunKubelet(kubeFlags *options.KubeletFlags, kubeCfg *kubeletconfiginternal. } glog.Infof("Started kubelet as runonce") } else { - startKubelet(k, podCfg, kubeCfg, kubeDeps) + startKubelet(k, podCfg, kubeCfg, kubeDeps, kubeFlags.EnableServer) glog.Infof("Started kubelet") } return nil } -func startKubelet(k kubelet.Bootstrap, podCfg *config.PodConfig, kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeDeps *kubelet.Dependencies) { +func startKubelet(k kubelet.Bootstrap, podCfg *config.PodConfig, kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeDeps *kubelet.Dependencies, enableServer bool) { // start the kubelet go wait.Until(func() { k.Run(podCfg.Updates()) }, 0, wait.NeverStop) // start the kubelet server - if kubeCfg.EnableServer { + if enableServer { go wait.Until(func() { k.ListenAndServe(net.ParseIP(kubeCfg.Address), uint(kubeCfg.Port), kubeDeps.TLSOptions, kubeDeps.Auth, kubeCfg.EnableDebuggingHandlers, kubeCfg.EnableContentionProfiling) }, 0, wait.NeverStop) diff --git a/pkg/kubelet/apis/kubeletconfig/fuzzer/fuzzer.go b/pkg/kubelet/apis/kubeletconfig/fuzzer/fuzzer.go index 82d9e51e7f4..b0d57b79ae8 100644 --- a/pkg/kubelet/apis/kubeletconfig/fuzzer/fuzzer.go +++ b/pkg/kubelet/apis/kubeletconfig/fuzzer/fuzzer.go @@ -50,7 +50,6 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { obj.EventRecordQPS = 5 obj.EnableControllerAttachDetach = true obj.EnableDebuggingHandlers = true - obj.EnableServer = true obj.FileCheckFrequency = metav1.Duration{Duration: 20 * time.Second} obj.HealthzBindAddress = "127.0.0.1" obj.HealthzPort = 10248 diff --git a/pkg/kubelet/apis/kubeletconfig/helpers_test.go b/pkg/kubelet/apis/kubeletconfig/helpers_test.go index b0a8757fd3d..06839367ad6 100644 --- a/pkg/kubelet/apis/kubeletconfig/helpers_test.go +++ b/pkg/kubelet/apis/kubeletconfig/helpers_test.go @@ -157,7 +157,6 @@ var ( "EnableContentionProfiling", "EnableControllerAttachDetach", "EnableDebuggingHandlers", - "EnableServer", "EnforceNodeAllocatable[*]", "EventBurst", "EventRecordQPS", diff --git a/pkg/kubelet/apis/kubeletconfig/types.go b/pkg/kubelet/apis/kubeletconfig/types.go index 25805431abf..d48a4c3634b 100644 --- a/pkg/kubelet/apis/kubeletconfig/types.go +++ b/pkg/kubelet/apis/kubeletconfig/types.go @@ -66,8 +66,6 @@ type KubeletConfiguration struct { // manifestURLHeader is the HTTP header to use when accessing the manifest // URL, with the key separated from the value with a ':', as in 'key:value' ManifestURLHeader map[string][]string - // enableServer enables the Kubelet's server - EnableServer bool // address is the IP address for the Kubelet to serve on (set to 0.0.0.0 // for all interfaces) Address string diff --git a/pkg/kubelet/apis/kubeletconfig/v1alpha1/defaults.go b/pkg/kubelet/apis/kubeletconfig/v1alpha1/defaults.go index ada91214d5c..67c1d80c6b5 100644 --- a/pkg/kubelet/apis/kubeletconfig/v1alpha1/defaults.go +++ b/pkg/kubelet/apis/kubeletconfig/v1alpha1/defaults.go @@ -50,16 +50,16 @@ func SetDefaults_KubeletConfiguration(obj *KubeletConfiguration) { obj.ConfigTrialDuration = &metav1.Duration{Duration: 10 * time.Minute} } if obj.Authentication.Anonymous.Enabled == nil { - obj.Authentication.Anonymous.Enabled = boolVar(true) + obj.Authentication.Anonymous.Enabled = boolVar(false) } if obj.Authentication.Webhook.Enabled == nil { - obj.Authentication.Webhook.Enabled = boolVar(false) + obj.Authentication.Webhook.Enabled = boolVar(true) } if obj.Authentication.Webhook.CacheTTL == zeroDuration { obj.Authentication.Webhook.CacheTTL = metav1.Duration{Duration: 2 * time.Minute} } if obj.Authorization.Mode == "" { - obj.Authorization.Mode = KubeletAuthorizationModeAlwaysAllow + obj.Authorization.Mode = KubeletAuthorizationModeWebhook } if obj.Authorization.Webhook.CacheAuthorizedTTL == zeroDuration { obj.Authorization.Webhook.CacheAuthorizedTTL = metav1.Duration{Duration: 5 * time.Minute} @@ -92,9 +92,6 @@ func SetDefaults_KubeletConfiguration(obj *KubeletConfiguration) { if obj.EnableDebuggingHandlers == nil { obj.EnableDebuggingHandlers = boolVar(true) } - if obj.EnableServer == nil { - obj.EnableServer = boolVar(true) - } if obj.FileCheckFrequency == zeroDuration { obj.FileCheckFrequency = metav1.Duration{Duration: 20 * time.Second} } @@ -145,9 +142,6 @@ func SetDefaults_KubeletConfiguration(obj *KubeletConfiguration) { if obj.Port == 0 { obj.Port = ports.KubeletPort } - if obj.ReadOnlyPort == nil { - obj.ReadOnlyPort = utilpointer.Int32Ptr(ports.KubeletReadOnlyPort) - } if obj.RegistryBurst == 0 { obj.RegistryBurst = 10 } diff --git a/pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go b/pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go index 4b50e51b1f0..7280fa9d493 100644 --- a/pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go +++ b/pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go @@ -66,16 +66,14 @@ type KubeletConfiguration struct { // manifestURLHeader is the HTTP header to use when accessing the manifest // URL, with the key separated from the value with a ':', as in 'key:value' ManifestURLHeader map[string][]string `json:"manifestURLHeader"` - // enableServer enables the Kubelet's server - EnableServer *bool `json:"enableServer"` // address is the IP address for the Kubelet to serve on (set to 0.0.0.0 // for all interfaces) Address string `json:"address"` // port is the port for the Kubelet to serve on. Port int32 `json:"port"` // readOnlyPort is the read-only port for the Kubelet to serve on with - // no authentication/authorization (set to 0 to disable) - ReadOnlyPort *int32 `json:"readOnlyPort"` + // no authentication/authorization. Disabled (set to 0) by default. + ReadOnlyPort int32 `json:"readOnlyPort"` // tlsCertFile is the file containing x509 Certificate for HTTPS. (CA cert, // if any, concatenated after server cert). If tlsCertFile and // tlsPrivateKeyFile are not provided, a self-signed certificate diff --git a/pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.conversion.go b/pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.conversion.go index 823e08e1c8b..a316df4b488 100644 --- a/pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.conversion.go +++ b/pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.conversion.go @@ -150,14 +150,9 @@ func autoConvert_v1alpha1_KubeletConfiguration_To_kubeletconfig_KubeletConfigura out.HTTPCheckFrequency = in.HTTPCheckFrequency out.ManifestURL = in.ManifestURL out.ManifestURLHeader = *(*map[string][]string)(unsafe.Pointer(&in.ManifestURLHeader)) - if err := v1.Convert_Pointer_bool_To_bool(&in.EnableServer, &out.EnableServer, s); err != nil { - return err - } out.Address = in.Address out.Port = in.Port - if err := v1.Convert_Pointer_int32_To_int32(&in.ReadOnlyPort, &out.ReadOnlyPort, s); err != nil { - return err - } + out.ReadOnlyPort = in.ReadOnlyPort out.TLSCertFile = in.TLSCertFile out.TLSPrivateKeyFile = in.TLSPrivateKeyFile out.TLSCipherSuites = *(*[]string)(unsafe.Pointer(&in.TLSCipherSuites)) @@ -275,14 +270,9 @@ func autoConvert_kubeletconfig_KubeletConfiguration_To_v1alpha1_KubeletConfigura out.HTTPCheckFrequency = in.HTTPCheckFrequency out.ManifestURL = in.ManifestURL out.ManifestURLHeader = *(*map[string][]string)(unsafe.Pointer(&in.ManifestURLHeader)) - if err := v1.Convert_bool_To_Pointer_bool(&in.EnableServer, &out.EnableServer, s); err != nil { - return err - } out.Address = in.Address out.Port = in.Port - if err := v1.Convert_int32_To_Pointer_int32(&in.ReadOnlyPort, &out.ReadOnlyPort, s); err != nil { - return err - } + out.ReadOnlyPort = in.ReadOnlyPort out.TLSCertFile = in.TLSCertFile out.TLSPrivateKeyFile = in.TLSPrivateKeyFile out.TLSCipherSuites = *(*[]string)(unsafe.Pointer(&in.TLSCipherSuites)) diff --git a/pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.deepcopy.go b/pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.deepcopy.go index 6a44b4c1b19..7fa533668f8 100644 --- a/pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.deepcopy.go @@ -114,24 +114,6 @@ func (in *KubeletConfiguration) DeepCopyInto(out *KubeletConfiguration) { } } } - if in.EnableServer != nil { - in, out := &in.EnableServer, &out.EnableServer - if *in == nil { - *out = nil - } else { - *out = new(bool) - **out = **in - } - } - if in.ReadOnlyPort != nil { - in, out := &in.ReadOnlyPort, &out.ReadOnlyPort - if *in == nil { - *out = nil - } else { - *out = new(int32) - **out = **in - } - } if in.TLSCipherSuites != nil { in, out := &in.TLSCipherSuites, &out.TLSCipherSuites *out = make([]string, len(*in)) diff --git a/pkg/kubemark/hollow_kubelet.go b/pkg/kubemark/hollow_kubelet.go index f087ebfea3e..3517055421c 100644 --- a/pkg/kubemark/hollow_kubelet.go +++ b/pkg/kubemark/hollow_kubelet.go @@ -110,6 +110,7 @@ func GetHollowKubeletConfig( // Flags struct f := options.NewKubeletFlags() + f.EnableServer = true f.RootDirectory = testRootDir f.HostnameOverride = nodeName f.MinimumGCAge = metav1.Duration{Duration: 1 * time.Minute} @@ -144,7 +145,6 @@ func GetHollowKubeletConfig( c.CPUCFSQuota = true c.EnableControllerAttachDetach = false c.EnableDebuggingHandlers = true - c.EnableServer = true c.CgroupsPerQOS = false // hairpin-veth is used to allow hairpin packets. Note that this deviates from // what the "real" kubelet currently does, because there's no way to