From 5fcf75db409bf035104114e8d998e33ccf8bd31c Mon Sep 17 00:00:00 2001 From: Jiaqi Luo <6218999+jiaqiluo@users.noreply.github.com> Date: Tue, 1 Nov 2022 22:05:35 -0700 Subject: [PATCH] add the support for PodSecurity on cluster at least v1.23 --- cluster/cluster.go | 2 +- cluster/defaults.go | 124 +++++++++++++++++++++++++++++++++++- cluster/hosts.go | 144 ++++++++++++++++++++++++------------------ cluster/plan.go | 43 +++++++------ cluster/reconcile.go | 18 +++++- cluster/state.go | 2 +- cluster/validation.go | 32 +++++++++- cmd/up.go | 4 ++ go.mod | 1 + go.sum | 2 + types/rke_types.go | 6 +- 11 files changed, 290 insertions(+), 88 deletions(-) diff --git a/cluster/cluster.go b/cluster/cluster.go index ec9c1a49..fec37cf2 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "net" "os" "reflect" @@ -31,6 +30,7 @@ import ( "gopkg.in/yaml.v2" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" apiserverv1 "k8s.io/apiserver/pkg/apis/apiserver/v1" diff --git a/cluster/defaults.go b/cluster/defaults.go index 1b15d33c..f5e7a553 100644 --- a/cluster/defaults.go +++ b/cluster/defaults.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" "strings" "github.com/blang/semver" @@ -25,6 +24,9 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" apiserverv1 "k8s.io/apiserver/pkg/apis/apiserver/v1" auditv1 "k8s.io/apiserver/pkg/apis/audit/v1" + eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" + admissionapiv1 "k8s.io/pod-security-admission/admission/api/v1" + admissionapiv1beta1 "k8s.io/pod-security-admission/admission/api/v1beta1" ) const ( @@ -118,6 +120,9 @@ const ( DefaultKubeAPIArgAdmissionControlConfigFileValue = "/etc/kubernetes/admission.yaml" EventRateLimitPluginName = "EventRateLimit" + PodSecurityPluginName = "PodSecurity" + PodSecurityPrivileged = "privileged" + PodSecurityRestricted = "restricted" KubeAPIArgAuditLogPath = "audit-log-path" KubeAPIArgAuditLogMaxAge = "audit-log-maxage" @@ -382,6 +387,9 @@ func (c *Cluster) setClusterServicesDefaults() { c.Services.KubeAPI.EventRateLimit.Configuration == nil { c.Services.KubeAPI.EventRateLimit.Configuration = newDefaultEventRateLimitConfig() } + if len(c.Services.KubeAPI.PodSecurityConfiguration) == 0 { + c.Services.KubeAPI.PodSecurityConfiguration = PodSecurityPrivileged + } } enableKubeAPIAuditLog, err := checkVersionNeedsKubeAPIAuditLog(c.Version) @@ -500,6 +508,120 @@ func newDefaultAdmissionConfiguration() (*apiserverv1.AdmissionConfiguration, er return admissionConfiguration, nil } +func newDefaultPodSecurityPluginConfigurationRestricted(Version string) (apiserverv1.AdmissionPluginConfiguration, error) { + plugin := apiserverv1.AdmissionPluginConfiguration{ + Name: PodSecurityPluginName, + Configuration: &runtime.Unknown{ + ContentType: "application/json", + }, + } + parsedVersion, err := getClusterVersion(Version) + if err != nil { + return plugin, err + } + var cBytes []byte + if parsedRangeAtLeast125(parsedVersion) { + configuration := admissionapiv1.PodSecurityConfiguration{ + TypeMeta: v1.TypeMeta{ + Kind: "PodSecurityConfiguration", + APIVersion: admissionapiv1.SchemeGroupVersion.String(), + }, + Defaults: admissionapiv1.PodSecurityDefaults{ + Enforce: "restricted", + EnforceVersion: "latest", + Audit: "restricted", + AuditVersion: "latest", + Warn: "restricted", + WarnVersion: "latest", + }, + Exemptions: admissionapiv1.PodSecurityExemptions{ + Usernames: nil, + Namespaces: []string{"ingress-nginx", "kube-system"}, + RuntimeClasses: nil, + }, + } + cBytes, err = json.Marshal(configuration) + if err != nil { + return plugin, fmt.Errorf("error marshalling podSecurity config: %v", err) + } + } + if parsedRange123(parsedVersion) || parsedRange124(parsedVersion) { + configuration := admissionapiv1beta1.PodSecurityConfiguration{ + TypeMeta: v1.TypeMeta{ + Kind: "PodSecurityConfiguration", + APIVersion: admissionapiv1beta1.SchemeGroupVersion.String(), + }, + Defaults: admissionapiv1beta1.PodSecurityDefaults{ + Enforce: "restricted", + EnforceVersion: "latest", + Audit: "restricted", + AuditVersion: "latest", + Warn: "restricted", + WarnVersion: "latest", + }, + Exemptions: admissionapiv1beta1.PodSecurityExemptions{ + Usernames: nil, + Namespaces: []string{"ingress-nginx", "kube-system"}, + RuntimeClasses: nil, + }, + } + cBytes, err = json.Marshal(configuration) + if err != nil { + return plugin, fmt.Errorf("error marshalling podSecurity config: %v", err) + } + } + plugin.Configuration.Raw = cBytes + return plugin, nil +} + +func newDefaultPodSecurityPluginConfigurationPrivileged(Version string) (apiserverv1.AdmissionPluginConfiguration, error) { + plugin := apiserverv1.AdmissionPluginConfiguration{ + Name: PodSecurityPluginName, + Configuration: &runtime.Unknown{ + ContentType: "application/json", + }, + } + parsedVersion, err := getClusterVersion(Version) + if err != nil { + return plugin, err + } + var cBytes []byte + if parsedRangeAtLeast125(parsedVersion) { + configuration := admissionapiv1.PodSecurityConfiguration{ + TypeMeta: v1.TypeMeta{ + Kind: "PodSecurityConfiguration", + APIVersion: admissionapiv1.SchemeGroupVersion.String(), + }, + Defaults: admissionapiv1.PodSecurityDefaults{ + Enforce: "privileged", + EnforceVersion: "latest", + }, + } + cBytes, err = json.Marshal(configuration) + if err != nil { + return plugin, fmt.Errorf("error marshalling podSecurity config: %v", err) + } + } + if parsedRange123(parsedVersion) || parsedRange124(parsedVersion) { + configuration := admissionapiv1beta1.PodSecurityConfiguration{ + TypeMeta: v1.TypeMeta{ + Kind: "PodSecurityConfiguration", + APIVersion: admissionapiv1beta1.SchemeGroupVersion.String(), + }, + Defaults: admissionapiv1beta1.PodSecurityDefaults{ + Enforce: "privileged", + EnforceVersion: "latest", + }, + } + cBytes, err = json.Marshal(configuration) + if err != nil { + return plugin, fmt.Errorf("error marshalling podSecurity config: %v", err) + } + } + plugin.Configuration.Raw = cBytes + return plugin, nil +} + func (c *Cluster) setClusterImageDefaults() error { var privRegURL string diff --git a/cluster/hosts.go b/cluster/hosts.go index ac8fbe96..5f713156 100644 --- a/cluster/hosts.go +++ b/cluster/hosts.go @@ -148,67 +148,78 @@ func (c *Cluster) CalculateMaxUnavailable() (int, int, error) { return maxUnavailableWorker, maxUnavailableControl, nil } +// getConsolidatedAdmissionConfiguration returns a consolidated admission configuration; +// for individual plugin configuration, the one under KubeAPI.AdmissionConfiguration takes precedence over the one under KubeAPI. func (c *Cluster) getConsolidatedAdmissionConfiguration() (*apiserverv1.AdmissionConfiguration, error) { - var err error - var admissionConfig *apiserverv1.AdmissionConfiguration - - if c.Services.KubeAPI.EventRateLimit == nil || - !c.Services.KubeAPI.EventRateLimit.Enabled { - return c.Services.KubeAPI.AdmissionConfiguration, nil + admissionConfig, err := newDefaultAdmissionConfiguration() + if err != nil { + logrus.Errorf("error getting default admission configuration: %v", err) + return nil, err } + if c.Services.KubeAPI.AdmissionConfiguration != nil { + copy(admissionConfig.Plugins, c.Services.KubeAPI.AdmissionConfiguration.Plugins) + } + // EventRateLimit + ertConfig, err := c.getEventRateLimitPluginConfiguration() + if err != nil { + return nil, err + } + _ = setPluginConfiguration(admissionConfig, ertConfig) - logrus.Debugf("EventRateLimit is enabled") - found := false + // PodSecurity + psConfig, err := c.getPodSecurityAdmissionPluginConfiguration() + if err != nil { + return nil, err + } + _ = setPluginConfiguration(admissionConfig, psConfig) + + return admissionConfig, nil +} + +func (c *Cluster) getEventRateLimitPluginConfiguration() (apiserverv1.AdmissionPluginConfiguration, error) { + // the configuration under KubeAPI.AdmissionConfiguration takes precedence over the one under KubeAPI.EventRateLimit if c.Services.KubeAPI.AdmissionConfiguration != nil { plugins := c.Services.KubeAPI.AdmissionConfiguration.Plugins for _, plugin := range plugins { if plugin.Name == EventRateLimitPluginName { - found = true - break + logrus.Debug("using the EventRateLimit configuration under the admission configuration") + return plugin, nil } } } - if found { - logrus.Debugf("EventRateLimit Plugin configuration found in admission config") - if c.Services.KubeAPI.EventRateLimit.Configuration != nil { - logrus.Warnf("conflicting EventRateLimit configuration found, using the one from Admission Configuration") - return c.Services.KubeAPI.AdmissionConfiguration, nil - } + if c.Services.KubeAPI.EventRateLimit != nil && + c.Services.KubeAPI.EventRateLimit.Enabled && + c.Services.KubeAPI.EventRateLimit.Configuration != nil { + logrus.Debug("using the user-specified EventRateLimit configuration") + return getEventRateLimitPluginFromConfig(c.Services.KubeAPI.EventRateLimit.Configuration) } + logrus.Debug("using the default EventRateLimit configuration") + return newDefaultEventRateLimitPlugin() +} - logrus.Debugf("EventRateLimit Plugin configuration not found in admission config") - if c.Services.KubeAPI.AdmissionConfiguration == nil { - logrus.Debugf("no user specified admission configuration found") - admissionConfig, err = newDefaultAdmissionConfiguration() - if err != nil { - logrus.Errorf("error getting default admission configuration: %v", err) - return nil, err +func (c *Cluster) getPodSecurityAdmissionPluginConfiguration() (apiserverv1.AdmissionPluginConfiguration, error) { + // the configuration under KubeAPI.AdmissionConfiguration takes precedence over + // the one under KubeAPI.PodSecurityConfiguration + if c.Services.KubeAPI.AdmissionConfiguration != nil { + plugins := c.Services.KubeAPI.AdmissionConfiguration.Plugins + for _, plugin := range plugins { + logrus.Debug("using the PodSecurity configuration under the admission configuration") + if plugin.Name == PodSecurityPluginName { + return plugin, nil + } } - } else { - admissionConfig, err = newDefaultAdmissionConfiguration() - if err != nil { - logrus.Errorf("error getting default admission configuration: %v", err) - return nil, err - } - copy(admissionConfig.Plugins, c.Services.KubeAPI.AdmissionConfiguration.Plugins) } - if c.Services.KubeAPI.EventRateLimit.Configuration != nil { - logrus.Debugf("user specified EventRateLimit configuration found") - p, err := getEventRateLimitPluginFromConfig(c.Services.KubeAPI.EventRateLimit.Configuration) - if err != nil { - logrus.Errorf("error getting eventratelimit plugin from config: %v", err) - } - admissionConfig.Plugins = append(admissionConfig.Plugins, p) - } else { - logrus.Debugf("using default EventRateLimit configuration") - p, err := newDefaultEventRateLimitPlugin() - if err != nil { - logrus.Errorf("error getting default eventratelimit plugin: %v", err) - } - admissionConfig.Plugins = append(admissionConfig.Plugins, p) + level := c.Services.KubeAPI.PodSecurityConfiguration + logrus.Debugf("using the PodSecurity configuration [%s]", level) + switch level { + case PodSecurityPrivileged: + return newDefaultPodSecurityPluginConfigurationPrivileged(c.Version) + case PodSecurityRestricted: + return newDefaultPodSecurityPluginConfigurationRestricted(c.Version) + default: + logrus.Debugf("invalid PodSecurity configuration [%s], using the default [privileged] configuration", level) + return newDefaultPodSecurityPluginConfigurationPrivileged(c.Version) } - - return admissionConfig, nil } func (c *Cluster) SetUpHosts(ctx context.Context, flags ExternalFlags) error { @@ -270,21 +281,19 @@ func (c *Cluster) SetUpHosts(ctx context.Context, flags ExternalFlags) error { } if _, ok := c.Services.KubeAPI.ExtraArgs[KubeAPIArgAdmissionControlConfigFile]; !ok { - if c.Services.KubeAPI.EventRateLimit != nil && c.Services.KubeAPI.EventRateLimit.Enabled { - controlPlaneHosts := hosts.GetUniqueHostList(nil, c.ControlPlaneHosts, nil) - ac, err := c.getConsolidatedAdmissionConfiguration() - if err != nil { - return fmt.Errorf("error getting consolidated admission configuration: %v", err) - } - bytes, err := yaml.Marshal(ac) - if err != nil { - return err - } - if err := deployFile(ctx, controlPlaneHosts, c.SystemImages.Alpine, c.PrivateRegistriesMap, DefaultKubeAPIArgAdmissionControlConfigFileValue, string(bytes), c.Version); err != nil { - return err - } - log.Infof(ctx, "[%s] Successfully deployed admission control config to Cluster control nodes", DefaultKubeAPIArgAdmissionControlConfigFileValue) + controlPlaneHosts := hosts.GetUniqueHostList(nil, c.ControlPlaneHosts, nil) + ac, err := c.getConsolidatedAdmissionConfiguration() + if err != nil { + return fmt.Errorf("error getting consolidated admission configuration: %v", err) } + bytes, err := yaml.Marshal(ac) + if err != nil { + return err + } + if err := deployFile(ctx, controlPlaneHosts, c.SystemImages.Alpine, c.PrivateRegistriesMap, DefaultKubeAPIArgAdmissionControlConfigFileValue, string(bytes), c.Version); err != nil { + return err + } + log.Infof(ctx, "[%s] Successfully deployed admission control config to Cluster control nodes", DefaultKubeAPIArgAdmissionControlConfigFileValue) } if _, ok := c.Services.KubeAPI.ExtraArgs[KubeAPIArgAuditPolicyFile]; !ok { @@ -332,3 +341,18 @@ func removeFromRKENodes(nodeToRemove v3.RKEConfigNode, nodeList []v3.RKEConfigNo } return l } + +// setPluginConfiguration either adds the plugin configuration or replaces the existing one in the admission configuration +func setPluginConfiguration(admissionConfig *apiserverv1.AdmissionConfiguration, pluginConfig apiserverv1.AdmissionPluginConfiguration) error { + if admissionConfig == nil { + return fmt.Errorf("admission configuarion does not exist") + } + for i, plugin := range admissionConfig.Plugins { + if plugin.Name == pluginConfig.Name { + admissionConfig.Plugins[i] = pluginConfig + return nil + } + } + admissionConfig.Plugins = append(admissionConfig.Plugins, pluginConfig) + return nil +} diff --git a/cluster/plan.go b/cluster/plan.go index e630d4f1..9bae1381 100644 --- a/cluster/plan.go +++ b/cluster/plan.go @@ -66,8 +66,11 @@ const ( var ( admissionControlOptionNames = []string{"enable-admission-plugins", "admission-control"} + parsedRangeAtLeast123 = semver.MustParseRange(">= 1.23.0-rancher0") parsedRangeAtLeast124 = semver.MustParseRange(">= 1.24.0-rancher0") parsedRangeAtLeast125 = semver.MustParseRange(">= 1.25.0-rancher0") + parsedRange123 = semver.MustParseRange(">=1.23.0-rancher0 <=1.23.99-rancher-0") + parsedRange124 = semver.MustParseRange(">=1.24.0-rancher0 <=1.24.99-rancher-0") ) func GetServiceOptionData(data map[string]interface{}) map[string]*v3.KubernetesServicesOptions { @@ -174,24 +177,25 @@ func (c *Cluster) BuildKubeAPIProcess(host *hosts.Host, serviceOptions v3.Kubern Command := c.getRKEToolsEntryPoint(host.OS(), "kube-apiserver") CommandArgs := map[string]string{ - "client-ca-file": pki.GetCertPath(pki.CACertName), - "cloud-provider": c.CloudProvider.Name, - "etcd-cafile": etcdCAClientCert, - "etcd-certfile": etcdClientCert, - "etcd-keyfile": etcdClientKey, - "etcd-prefix": etcdPathPrefix, - "etcd-servers": etcdConnectionString, - "kubelet-client-certificate": pki.GetCertPath(pki.KubeAPICertName), - "kubelet-client-key": pki.GetKeyPath(pki.KubeAPICertName), - "proxy-client-cert-file": pki.GetCertPath(pki.APIProxyClientCertName), - "proxy-client-key-file": pki.GetKeyPath(pki.APIProxyClientCertName), - "requestheader-allowed-names": pki.APIProxyClientCertName, - "requestheader-client-ca-file": pki.GetCertPath(pki.RequestHeaderCACertName), - "service-account-key-file": pki.GetKeyPath(pki.ServiceAccountTokenKeyName), - "service-cluster-ip-range": c.Services.KubeAPI.ServiceClusterIPRange, - "service-node-port-range": c.Services.KubeAPI.ServiceNodePortRange, - "tls-cert-file": pki.GetCertPath(pki.KubeAPICertName), - "tls-private-key-file": pki.GetKeyPath(pki.KubeAPICertName), + "admission-control-config-file": DefaultKubeAPIArgAdmissionControlConfigFileValue, + "client-ca-file": pki.GetCertPath(pki.CACertName), + "cloud-provider": c.CloudProvider.Name, + "etcd-cafile": etcdCAClientCert, + "etcd-certfile": etcdClientCert, + "etcd-keyfile": etcdClientKey, + "etcd-prefix": etcdPathPrefix, + "etcd-servers": etcdConnectionString, + "kubelet-client-certificate": pki.GetCertPath(pki.KubeAPICertName), + "kubelet-client-key": pki.GetKeyPath(pki.KubeAPICertName), + "proxy-client-cert-file": pki.GetCertPath(pki.APIProxyClientCertName), + "proxy-client-key-file": pki.GetKeyPath(pki.APIProxyClientCertName), + "requestheader-allowed-names": pki.APIProxyClientCertName, + "requestheader-client-ca-file": pki.GetCertPath(pki.RequestHeaderCACertName), + "service-account-key-file": pki.GetKeyPath(pki.ServiceAccountTokenKeyName), + "service-cluster-ip-range": c.Services.KubeAPI.ServiceClusterIPRange, + "service-node-port-range": c.Services.KubeAPI.ServiceNodePortRange, + "tls-cert-file": pki.GetCertPath(pki.KubeAPICertName), + "tls-private-key-file": pki.GetKeyPath(pki.KubeAPICertName), } CommandArrayArgs := make(map[string][]string, len(c.Services.KubeAPI.ExtraArgsArray)) @@ -256,7 +260,6 @@ func (c *Cluster) BuildKubeAPIProcess(host *hosts.Host, serviceOptions v3.Kubern } if c.Services.KubeAPI.EventRateLimit != nil && c.Services.KubeAPI.EventRateLimit.Enabled { - CommandArgs[KubeAPIArgAdmissionControlConfigFile] = DefaultKubeAPIArgAdmissionControlConfigFileValue CommandArgs[admissionControlOptionName] = CommandArgs[admissionControlOptionName] + ",EventRateLimit" } @@ -705,7 +708,7 @@ func (c *Cluster) BuildKubeProxyProcess(host *hosts.Host, serviceOptions v3.Kube services.SidekickContainerName, } - //TODO: we should reevaluate if any of the bind mounts here should be using read-only mode + // TODO: we should reevaluate if any of the bind mounts here should be using read-only mode var Binds []string if host.IsWindows() { // compatible with Windows Binds = []string{ diff --git a/cluster/reconcile.go b/cluster/reconcile.go index c15fa2bb..cac36895 100644 --- a/cluster/reconcile.go +++ b/cluster/reconcile.go @@ -3,6 +3,7 @@ package cluster import ( "context" "fmt" + "reflect" "time" "github.com/rancher/rke/docker" @@ -455,7 +456,7 @@ func syncTaints(ctx context.Context, currentCluster, kubeCluster *Cluster) { } } -//getHostsTaintsMap return the taint set with unique key & effect for each host +// getHostsTaintsMap return the taint set with unique key & effect for each host func getHostsTaintsMap(list []*hosts.Host) map[string]map[string]string { rtn := make(map[string]map[string]string) for _, item := range list { @@ -488,3 +489,18 @@ func getTaintKey(taint v3.RKETaint) string { func getTaintValue(taint v3.RKETaint) string { return fmt.Sprintf("%s=%s:%s", taint.Key, taint.Value, taint.Effect) } + +// RestartKubeAPIServerWhenConfigChanges restarts the kube-apiserver container on the control plan nodes +// when changes are detected on the to-be-applied kube-api configuration. This is needed to handle the case +// where changes happen on the generated admission-control-config-file but not on the kube-apiserver container +func RestartKubeAPIServerWhenConfigChanges(ctx context.Context, kubeCluster, currentCluster *Cluster) error { + if currentCluster == nil { + return nil + } + if !reflect.DeepEqual(currentCluster.Services.KubeAPI, kubeCluster.Services.KubeAPI) { + for _, host := range kubeCluster.ControlPlaneHosts { + return services.RestartKubeAPI(ctx, host) + } + } + return nil +} diff --git a/cluster/state.go b/cluster/state.go index 593e1af2..7e5c82f6 100644 --- a/cluster/state.go +++ b/cluster/state.go @@ -210,7 +210,7 @@ func (s *FullState) WriteStateFile(ctx context.Context, statePath string) error return fmt.Errorf("Failed to Marshal state object: %v", err) } logrus.Tracef("Writing state file: %s", stateFile) - if err := ioutil.WriteFile(statePath, stateFile, 0600); err != nil { + if err := os.WriteFile(statePath, stateFile, 0600); err != nil { return fmt.Errorf("Failed to write state file: %v", err) } log.Infof(ctx, "Successfully Deployed state file at [%s]", statePath) diff --git a/cluster/validation.go b/cluster/validation.go index bc3634d1..ebbe54c5 100644 --- a/cluster/validation.go +++ b/cluster/validation.go @@ -55,7 +55,11 @@ func (c *Cluster) ValidateCluster(ctx context.Context) error { } // validate enabling Pod Security Policy - if err := validatePSP(c); err != nil { + if err := validatePodSecurityPolicy(c); err != nil { + return err + } + // validate enabling Pod Security + if err := validatePodSecurity(c); err != nil { return err } @@ -655,12 +659,13 @@ func validateCRIDockerdOption(c *Cluster) error { return nil } -func validatePSP(c *Cluster) error { +func validatePodSecurityPolicy(c *Cluster) error { parsedVersion, err := getClusterVersion(c.Version) if err != nil { logrus.Warnf("Failed to parse semver range for validating Pod Security Policy") return err } + logrus.Debugf("Checking PodSecurityPolicy for cluster version [%s]", c.Version) if c.Services.KubeAPI.PodSecurityPolicy { if c.Authorization.Mode != services.RBACAuthorizationMode { return errors.New("PodSecurityPolicy can't be enabled with RBAC support disabled") @@ -672,6 +677,29 @@ func validatePSP(c *Cluster) error { return nil } +func validatePodSecurity(c *Cluster) error { + parsedVersion, err := getClusterVersion(c.Version) + if err != nil { + logrus.Warnf("Failed to parse semver range for validating Pod Security") + return err + } + logrus.Debugf("Checking PodSecurity for cluster version [%s]", c.Version) + level := c.Services.KubeAPI.PodSecurityConfiguration + if len(level) != 0 { + if c.Authorization.Mode != services.RBACAuthorizationMode { + return errors.New("PodSecurity can't be enabled with RBAC support disabled") + } + if !parsedRangeAtLeast123(parsedVersion) { + return errors.New("cluster version must be at least v1.23 to use PodSecurity in RKE") + } + if level != PodSecurityPrivileged && level != PodSecurityRestricted { + return fmt.Errorf("invalid pod_security_configuration [%s]. Supported values: [%s, %s]", + level, PodSecurityPrivileged, PodSecurityRestricted) + } + } + return nil +} + func getClusterVersion(version string) (semver.Version, error) { var parsedVersion semver.Version if len(version) <= 1 || !strings.HasPrefix(version, "v") { diff --git a/cmd/up.go b/cmd/up.go index 55910d00..989bfb5f 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -200,6 +200,10 @@ func ClusterUp(ctx context.Context, dialersOptions hosts.DialersOptions, flags c return APIURL, caCrt, clientCert, clientKey, nil, err } + if err := cluster.RestartKubeAPIServerWhenConfigChanges(ctx, kubeCluster, currentCluster); err != nil { + return APIURL, caCrt, clientCert, clientKey, nil, err + } + if err := kubeCluster.PrePullK8sImages(ctx); err != nil { return APIURL, caCrt, clientCert, clientKey, nil, err } diff --git a/go.mod b/go.mod index 84b4175d..b3d7a139 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 k8s.io/kubectl v0.25.3 k8s.io/kubernetes v1.13.0 + k8s.io/pod-security-admission v0.25.3 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index f1e629f8..01d35605 100644 --- a/go.sum +++ b/go.sum @@ -1806,6 +1806,8 @@ k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM= +k8s.io/pod-security-admission v0.25.3 h1:2HnXWKUIDSez2sWtvxeGgGVUFvYnJJHutL4AI1MIuwk= +k8s.io/pod-security-admission v0.25.3/go.mod h1:xSaLkcMPD6cGKrZ//ZUrCNs0BewZzQdOEcC9LuXBGR4= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= diff --git a/types/rke_types.go b/types/rke_types.go index 18ab716e..1f61d251 100644 --- a/types/rke_types.go +++ b/types/rke_types.go @@ -295,6 +295,8 @@ type KubeAPIService struct { ServiceNodePortRange string `yaml:"service_node_port_range" json:"serviceNodePortRange,omitempty" norman:"default=30000-32767"` // Enabled/Disable PodSecurityPolicy PodSecurityPolicy bool `yaml:"pod_security_policy" json:"podSecurityPolicy,omitempty"` + // setting the default configuration for PodSecurityAdmission + PodSecurityConfiguration string `yaml:"pod_security_configuration" json:"podSecurityConfiguration,omitempty" norman:"default=privileged"` // Enable/Disable AlwaysPullImages admissions plugin AlwaysPullImages bool `yaml:"always_pull_images" json:"alwaysPullImages,omitempty"` // Secrets encryption provider config @@ -920,14 +922,14 @@ type GlobalAwsOpts struct { // Security group for each ELB this security group will be used instead. ElbSecurityGroup string `json:"elb-security-group" yaml:"elb-security-group" ini:"ElbSecurityGroup,omitempty"` - // During the instantiation of an new AWS cloud provider, the detected region + // During the instantiation of a new AWS cloud provider, the detected region // is validated against a known set of regions. // // In a non-standard, AWS like environment (e.g. Eucalyptus), this check may // be undesirable. Setting this to true will disable the check and provide // a warning that the check was skipped. Please note that this is an // experimental feature and work-in-progress for the moment. If you find - // yourself in an non-AWS cloud and open an issue, please indicate that in the + // yourself in a non-AWS cloud and open an issue, please indicate that in the // issue body. DisableStrictZoneCheck bool `json:"disable-strict-zone-check" yaml:"disable-strict-zone-check" ini:"DisableStrictZoneCheck,omitempty"` }