Skip to content

Commit ac1118b

Browse files
committed
Whitelist for kubeadm extra args
1 parent cb6947e commit ac1118b

15 files changed

+410
-60
lines changed

cmd/minikube/cmd/start.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"encoding/json"
2121
"fmt"
2222
"io/ioutil"
23+
. "k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm"
2324
"net"
2425
"os"
2526
"os/exec"
@@ -79,7 +80,6 @@ const (
7980
apiServerPort = "apiserver-port"
8081
dnsDomain = "dns-domain"
8182
serviceCIDR = "service-cluster-ip-range"
82-
podSubnet = "pod-network-cidr"
8383
imageRepository = "image-repository"
8484
imageMirrorCountry = "image-mirror-country"
8585
mountString = "mount-string"
@@ -132,7 +132,6 @@ func init() {
132132
startCmd.Flags().IPSliceVar(&apiServerIPs, "apiserver-ips", nil, "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine")
133133
startCmd.Flags().String(dnsDomain, constants.ClusterDNSDomain, "The cluster dns domain name used in the kubernetes cluster")
134134
startCmd.Flags().String(serviceCIDR, pkgutil.DefaultServiceCIDR, "The CIDR to be used for service cluster IPs.")
135-
startCmd.Flags().String(podSubnet, "", "Specify range of IP addresses for the pod network. If set, the control plane will automatically allocate CIDRs for every node.")
136135
startCmd.Flags().StringSliceVar(&insecureRegistry, "insecure-registry", nil, "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.")
137136
startCmd.Flags().StringSliceVar(&registryMirror, "registry-mirror", nil, "Registry mirrors to pass to the Docker daemon")
138137
startCmd.Flags().String(imageRepository, "", "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \"auto\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers")
@@ -148,7 +147,8 @@ func init() {
148147
startCmd.Flags().Var(&extraOptions, "extra-config",
149148
`A set of key=value pairs that describe configuration that may be passed to different components.
150149
The key should be '.' separated, and the first part before the dot is the component to apply the configuration to.
151-
Valid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler.`)
150+
Valid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler
151+
Valid kubeadm parameters: `+fmt.Sprintf("%s, %s", strings.Join(KubeadmExtraArgsWhitelist[KubeadmCmdParam], ", "), strings.Join(KubeadmExtraArgsWhitelist[KubeadmConfigParam], ",")))
152152
startCmd.Flags().String(uuid, "", "Provide VM UUID to restore MAC address (only supported with Hyperkit driver).")
153153
startCmd.Flags().String(vpnkitSock, "", "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock.")
154154
startCmd.Flags().StringSlice(vsockPorts, []string{}, "List of guest VSock ports that should be exposed as sockets on the host (Only supported on with hyperkit now).")
@@ -337,6 +337,14 @@ func validateConfig() {
337337
if viper.GetBool(hidden) && viper.GetString(vmDriver) != "kvm2" {
338338
exit.Usage("Sorry, the --hidden feature is currently only supported with --vm-driver=kvm2")
339339
}
340+
341+
// check that kubeadm extra args contain only whitelisted parameters
342+
for param := range extraOptions.AsMap().Get(Kubeadm) {
343+
if !pkgutil.ContainsString(KubeadmExtraArgsWhitelist[KubeadmCmdParam], param, nil) &&
344+
!pkgutil.ContainsString(KubeadmExtraArgsWhitelist[KubeadmConfigParam], param, nil) {
345+
exit.Usage("Sorry, the kubeadm.%s parameter is currently not supported by --extra-config", param)
346+
}
347+
}
340348
}
341349

342350
// doCacheBinaries caches Kubernetes binaries in the foreground
@@ -457,7 +465,6 @@ func generateConfig(cmd *cobra.Command, k8sVersion string) (cfg.Config, error) {
457465
CRISocket: viper.GetString(criSocket),
458466
NetworkPlugin: selectedNetworkPlugin,
459467
ServiceCIDR: viper.GetString(serviceCIDR),
460-
PodSubnet: viper.GetString(podSubnet),
461468
ImageRepository: repository,
462469
ExtraOptions: extraOptions,
463470
ShouldLoadCachedImages: viper.GetBool(cacheImages),

pkg/minikube/bootstrapper/kubeadm/kubeadm.go

+65-8
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,34 @@ import (
4343
"k8s.io/minikube/pkg/util"
4444
)
4545

46+
// enum to differentiate kubeadm command line parameters from kubeadm config file parameters (see the
47+
// KubeadmExtraArgsWhitelist variable below for more info)
48+
const (
49+
KubeadmCmdParam = iota
50+
KubeadmConfigParam = iota
51+
)
52+
53+
// whitelist of supported kubeadm params that can be supplied to kubeadm through minikube's ExtraArgs parameter. The list
54+
// is split into two parts - params that can be supplied as flags on the command line and params that have to be
55+
// inserted into the kubeadm config file. This is because of a kubeadm constraint which allows only certain params
56+
// to be provided from the command line when the --config parameter is specified
57+
var KubeadmExtraArgsWhitelist = map[int][]string{
58+
KubeadmCmdParam: {
59+
"ignore-preflight-errors",
60+
"dry-run",
61+
"kubeconfig",
62+
"kubeconfig-dir",
63+
"node-name",
64+
"cri-socket",
65+
"experimental-upload-certs",
66+
"certificate-key",
67+
"rootfs",
68+
},
69+
KubeadmConfigParam: {
70+
"pod-network-cidr",
71+
},
72+
}
73+
4674
// SkipPreflights are preflight checks we always skip.
4775
var SkipPreflights = []string{
4876
// We use --ignore-preflight-errors=DirAvailable since we have our own custom addons
@@ -163,18 +191,29 @@ func (k *Bootstrapper) LogCommands(o bootstrapper.LogOptions) map[string]string
163191
}
164192
}
165193

194+
// createFlagsFromExtraArgs converts kubeadm extra args into flags to be supplied from the commad linne
195+
func createFlagsFromExtraArgs(extraOptions util.ExtraOptionSlice) string {
196+
kubeadmExtraOpts := extraOptions.AsMap().Get(Kubeadm)
197+
198+
// kubeadm allows only a small set of parameters to be supplied from the command line when the --config param
199+
// is specified, here we remove those that are not allowed
200+
for opt := range kubeadmExtraOpts {
201+
if !util.ContainsString(KubeadmExtraArgsWhitelist[KubeadmCmdParam], opt, nil) == true {
202+
// kubeadmExtraOpts is a copy so safe to delete
203+
delete(kubeadmExtraOpts, opt)
204+
}
205+
}
206+
return convertToFlags(kubeadmExtraOpts)
207+
}
208+
166209
// StartCluster starts the cluster
167210
func (k *Bootstrapper) StartCluster(k8s config.KubernetesConfig) error {
168211
version, err := ParseKubernetesVersion(k8s.KubernetesVersion)
169212
if err != nil {
170213
return errors.Wrap(err, "parsing kubernetes version")
171214
}
172215

173-
extraOpts, err := ExtraConfigForComponent(Kubeadm, k8s.ExtraOptions, version)
174-
if err != nil {
175-
return errors.Wrap(err, "generating extra configuration for kubelet")
176-
}
177-
extraFlags := convertToFlags(extraOpts)
216+
extraFlags := createFlagsFromExtraArgs(k8s.ExtraOptions)
178217

179218
r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime})
180219
if err != nil {
@@ -474,6 +513,25 @@ sudo systemctl start kubelet
474513
return nil
475514
}
476515

516+
// createExtraComponentConfig generates a map of component to extra args for all of the components except kubeadm
517+
func createExtraComponentConfig(extraOptions util.ExtraOptionSlice, version semver.Version, componentFeatureArgs string) ([]ComponentExtraArgs, error) {
518+
extraArgsSlice, err := NewComponentExtraArgs(extraOptions, version, componentFeatureArgs)
519+
if err != nil {
520+
return nil, err
521+
}
522+
523+
// kubeadm extra args should not be included in the kubeadm config in the extra args section (instead, they must
524+
// be inserted explicitly in the appropriate places or supplied from the command line); here we remove all of the
525+
// kubeadm extra args from the slice
526+
for i, extraArgs := range extraArgsSlice {
527+
if extraArgs.Component == Kubeadm {
528+
extraArgsSlice = append(extraArgsSlice[:i], extraArgsSlice[i+1:]...)
529+
break
530+
}
531+
}
532+
return extraArgsSlice, nil
533+
}
534+
477535
func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, error) {
478536
version, err := ParseKubernetesVersion(k8s.KubernetesVersion)
479537
if err != nil {
@@ -486,8 +544,7 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, er
486544
return "", errors.Wrap(err, "parses feature gate config for kubeadm and component")
487545
}
488546

489-
// generates a map of component to extra args for apiserver, controller-manager, and scheduler
490-
extraComponentConfig, err := NewComponentExtraArgs(k8s.ExtraOptions, version, componentFeatureArgs)
547+
extraComponentConfig, err := createExtraComponentConfig(k8s.ExtraOptions, version, componentFeatureArgs)
491548
if err != nil {
492549
return "", errors.Wrap(err, "generating extra component config for kubeadm")
493550
}
@@ -515,7 +572,7 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, er
515572
}{
516573
CertDir: util.DefaultCertPath,
517574
ServiceCIDR: util.DefaultServiceCIDR,
518-
PodSubnet: k8s.PodSubnet,
575+
PodSubnet: k8s.ExtraOptions.Get("pod-network-cidr", Kubeadm),
519576
AdvertiseAddress: k8s.NodeIP,
520577
APIServerPort: nodePort,
521578
KubernetesVersion: k8s.KubernetesVersion,

pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,24 @@ func TestGenerateConfig(t *testing.T) {
153153
Key: "scheduler-name",
154154
Value: "mini-scheduler",
155155
},
156+
util.ExtraOption{
157+
Component: Kubeadm,
158+
Key: "ignore-preflight-errors",
159+
Value: "true",
160+
},
161+
util.ExtraOption{
162+
Component: Kubeadm,
163+
Key: "dry-run",
164+
Value: "true",
165+
},
166+
}
167+
168+
extraOptsPodCidr := util.ExtraOptionSlice{
169+
util.ExtraOption{
170+
Component: Kubeadm,
171+
Key: "pod-network-cidr",
172+
Value: "192.168.32.0/20",
173+
},
156174
}
157175

158176
// Test version policy: Last 4 major releases (slightly looser than our general policy)
@@ -177,7 +195,7 @@ func TestGenerateConfig(t *testing.T) {
177195
{"crio-options-gates", "crio", false, config.KubernetesConfig{ExtraOptions: extraOpts, FeatureGates: "a=b"}},
178196
{"unknown-component", "docker", true, config.KubernetesConfig{ExtraOptions: util.ExtraOptionSlice{util.ExtraOption{Component: "not-a-real-component", Key: "killswitch", Value: "true"}}}},
179197
{"containerd-api-port", "containerd", false, config.KubernetesConfig{NodePort: 12345}},
180-
{"containerd-pod-network-cidr", "containerd", false, config.KubernetesConfig{PodSubnet: "192.168.32.0/20"}},
198+
{"containerd-pod-network-cidr", "containerd", false, config.KubernetesConfig{ExtraOptions: extraOptsPodCidr}},
181199
{"image-repository", "docker", false, config.KubernetesConfig{ImageRepository: "test/repo"}},
182200
}
183201
for vname, version := range versions {

pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__default.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ dns:
2828
etcd:
2929
local:
3030
dataDir: /data/minikube
31-
kubernetesVersion: v1.14.0
31+
kubernetesVersion: v1.14.1
3232
networking:
3333
dnsDomain: cluster.local
3434
podSubnet: ""

pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__default.yaml

-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ controllerManager:
2626
extraArgs:
2727
feature-gates: "a=b"
2828
kube-api-burst: "32"
29-
kubeadm:
30-
extraArgs:
31-
feature-gates: "a=b"
3229
scheduler:
3330
extraArgs:
3431
feature-gates: "a=b"

pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__new.yaml

-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ controllerManager:
2626
extraArgs:
2727
feature-gates: "a=b"
2828
kube-api-burst: "32"
29-
kubeadm:
30-
extraArgs:
31-
feature-gates: "a=b"
3229
scheduler:
3330
extraArgs:
3431
feature-gates: "a=b"

pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__obsolete.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ apiServerExtraArgs:
2020
controllerManagerExtraArgs:
2121
feature-gates: "a=b"
2222
kube-api-burst: "32"
23-
kubeadmExtraArgs:
24-
feature-gates: "a=b"
2523
schedulerExtraArgs:
2624
feature-gates: "a=b"
2725
scheduler-name: "mini-scheduler"

pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__old.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ apiServerExtraArgs:
2424
controllerManagerExtraArgs:
2525
feature-gates: "a=b"
2626
kube-api-burst: "32"
27-
kubeadmExtraArgs:
28-
feature-gates: "a=b"
2927
schedulerExtraArgs:
3028
feature-gates: "a=b"
3129
scheduler-name: "mini-scheduler"

pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__recent.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ apiServerExtraArgs:
2424
controllerManagerExtraArgs:
2525
feature-gates: "a=b"
2626
kube-api-burst: "32"
27-
kubeadmExtraArgs:
28-
feature-gates: "a=b"
2927
schedulerExtraArgs:
3028
feature-gates: "a=b"
3129
scheduler-name: "mini-scheduler"

pkg/minikube/config/types.go

-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ type KubernetesConfig struct {
7171
NetworkPlugin string
7272
FeatureGates string
7373
ServiceCIDR string
74-
PodSubnet string
7574
ImageRepository string
7675
ExtraOptions util.ExtraOptionSlice
7776

pkg/util/extra_options.go

+35
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ func (e *ExtraOption) String() string {
3535
// ExtraOptionSlice is a slice of ExtraOption
3636
type ExtraOptionSlice []ExtraOption
3737

38+
// ComponentExtraOptionMap maps components to their extra opts, which is a map of keys to values
39+
type ComponentExtraOptionMap map[string]map[string]string
40+
3841
// Set parses the string value into a slice
3942
func (es *ExtraOptionSlice) Set(value string) error {
4043
// The component is the value before the first dot.
@@ -68,7 +71,39 @@ func (es *ExtraOptionSlice) String() string {
6871
return strings.Join(s, " ")
6972
}
7073

74+
// Get finds and returns the value of an argument with the specified key and component (optional) or an empty string
75+
// if not found. If component contains more than one value, the value for the first component found is returned. If
76+
// component is not specified, all of the components are used.
77+
func (es *ExtraOptionSlice) Get(key string, component ...string) string {
78+
for _, opt := range *es {
79+
if component == nil || ContainsString(component, opt.Component, nil) {
80+
if opt.Key == key {
81+
return opt.Value
82+
}
83+
}
84+
}
85+
return ""
86+
}
87+
88+
// AsMap converts the slice to a map of components to a map of keys and values.
89+
func (es *ExtraOptionSlice) AsMap() ComponentExtraOptionMap {
90+
ret := ComponentExtraOptionMap{}
91+
for _, opt := range *es {
92+
if _, ok := ret[opt.Component]; !ok {
93+
ret[opt.Component] = map[string]string{opt.Key: opt.Value}
94+
} else {
95+
ret[opt.Component][opt.Key] = opt.Value
96+
}
97+
}
98+
return ret
99+
}
100+
71101
// Type returns the type
72102
func (es *ExtraOptionSlice) Type() string {
73103
return "ExtraOption"
74104
}
105+
106+
// Get returns the extra option map of keys to values for the specified componet
107+
func (cm ComponentExtraOptionMap) Get(component string) map[string]string {
108+
return cm[component]
109+
}

pkg/util/extra_options_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,57 @@ func TestValidFlags(t *testing.T) {
7878
}
7979
}
8080
}
81+
82+
func TestGet(t *testing.T) {
83+
extraOptions := ExtraOptionSlice{
84+
ExtraOption{Component: "c1", Key: "bar", Value: "c1-bar"},
85+
ExtraOption{Component: "c1", Key: "bar-baz", Value: "c1-bar-baz"},
86+
ExtraOption{Component: "c2", Key: "bar", Value: "c2-bar"},
87+
ExtraOption{Component: "c3", Key: "bar", Value: "c3-bar"},
88+
}
89+
90+
for _, tc := range []struct {
91+
searchKey string
92+
searchComponent []string
93+
expRes string
94+
values ExtraOptionSlice
95+
}{
96+
{"nonexistent", nil, "", extraOptions},
97+
{"nonexistent", []string{"c1"}, "", extraOptions},
98+
{"bar", []string{"c2"}, "c2-bar", extraOptions},
99+
{"bar", []string{"c2", "c3"}, "c2-bar", extraOptions},
100+
{"bar", nil, "c1-bar", extraOptions},
101+
} {
102+
if res := tc.values.Get(tc.searchKey, tc.searchComponent...); res != tc.expRes {
103+
t.Errorf("Unexpected value. Expected %s, got %s", tc.expRes, res)
104+
}
105+
}
106+
}
107+
108+
func TestAsMap(t *testing.T) {
109+
extraOptions := ExtraOptionSlice{
110+
ExtraOption{Component: "c1", Key: "bar", Value: "c1-bar"},
111+
ExtraOption{Component: "c1", Key: "bar-baz", Value: "c1-bar-baz"},
112+
ExtraOption{Component: "c2", Key: "bar", Value: "c2-bar"},
113+
ExtraOption{Component: "c3", Key: "bar", Value: "c3-bar"},
114+
}
115+
116+
expectedRes := ComponentExtraOptionMap{
117+
"c1": {
118+
"bar": "c1-bar",
119+
"bar-baz": "c1-bar-baz",
120+
},
121+
"c2": {
122+
"bar": "c2-bar",
123+
},
124+
"c3": {
125+
"bar": "c3-bar",
126+
},
127+
}
128+
129+
res := extraOptions.AsMap()
130+
131+
if !reflect.DeepEqual(expectedRes, res) {
132+
t.Errorf("Unexpected value. Expected %s, got %s", expectedRes, res)
133+
}
134+
}

0 commit comments

Comments
 (0)