Skip to content

Commit

Permalink
UPSTREAM: <carry>: prevent the kubecontrollermanager service-ca from …
Browse files Browse the repository at this point in the history
…getting less secure
  • Loading branch information
deads2k authored and damemi committed Aug 27, 2021
1 parent 8da1750 commit 980d3c5
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 0 deletions.
Expand Up @@ -11,6 +11,7 @@ import (
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/dns"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/features"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/image"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/kubecontrollermanager"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/network"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/oauth"
"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation/project"
Expand All @@ -36,6 +37,9 @@ var AllCustomResourceValidators = []string{
rolebindingrestriction.PluginName,
network.PluginName,

// the kubecontrollermanager operator resource has to exist in order to run deployments to deploy admission webhooks.
kubecontrollermanager.PluginName,

// this one is special because we don't work without it.
securitycontextconstraints.DefaultingPluginName,
}
Expand All @@ -51,6 +55,7 @@ func RegisterCustomResourceValidation(plugins *admission.Plugins) {
project.Register(plugins)
config.Register(plugins)
scheduler.Register(plugins)
kubecontrollermanager.Register(plugins)

// This plugin validates the quota.openshift.io/v1 ClusterResourceQuota resources.
// NOTE: This is only allowed because it is required to get a running control plane operator.
Expand Down
@@ -0,0 +1,113 @@
package kubecontrollermanager

import (
"fmt"
"io"

operatorv1 "github.com/openshift/api/operator/v1"

"k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/admission"

"k8s.io/kubernetes/openshift-kube-apiserver/admission/customresourcevalidation"
)

const PluginName = "operator.openshift.io/ValidateKubeControllerManager"

// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return customresourcevalidation.NewValidator(
map[schema.GroupResource]bool{
operatorv1.Resource("kubecontrollermanagers"): true,
},
map[schema.GroupVersionKind]customresourcevalidation.ObjectValidator{
operatorv1.GroupVersion.WithKind("KubeControllerManager"): kubeControllerManagerV1{},
})
})
}

func toKubeControllerManager(uncastObj runtime.Object) (*operatorv1.KubeControllerManager, field.ErrorList) {
if uncastObj == nil {
return nil, nil
}

allErrs := field.ErrorList{}

obj, ok := uncastObj.(*operatorv1.KubeControllerManager)
if !ok {
return nil, append(allErrs,
field.NotSupported(field.NewPath("kind"), fmt.Sprintf("%T", uncastObj), []string{"KubeControllerManager"}),
field.NotSupported(field.NewPath("apiVersion"), fmt.Sprintf("%T", uncastObj), []string{"operator.openshift.io/v1"}))
}

return obj, nil
}

type kubeControllerManagerV1 struct {
}

func validateKubeControllerManagerSpecCreate(spec operatorv1.KubeControllerManagerSpec) field.ErrorList {
allErrs := field.ErrorList{}

// on create, we allow anything
return allErrs
}

func validateKubeControllerManagerSpecUpdate(spec, oldSpec operatorv1.KubeControllerManagerSpec) field.ErrorList {
allErrs := field.ErrorList{}

// on update, fail if we go from secure to insecure
if oldSpec.UseMoreSecureServiceCA && !spec.UseMoreSecureServiceCA {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec.useMoreSecureServiceCA"), "once enabled, the more secure service-ca.crt cannot be disabled"))
}

return allErrs
}

func (kubeControllerManagerV1) ValidateCreate(uncastObj runtime.Object) field.ErrorList {
obj, allErrs := toKubeControllerManager(uncastObj)
if len(allErrs) > 0 {
return allErrs
}

allErrs = append(allErrs, validation.ValidateObjectMeta(&obj.ObjectMeta, false, customresourcevalidation.RequireNameCluster, field.NewPath("metadata"))...)
allErrs = append(allErrs, validateKubeControllerManagerSpecCreate(obj.Spec)...)

return allErrs
}

func (kubeControllerManagerV1) ValidateUpdate(uncastObj runtime.Object, uncastOldObj runtime.Object) field.ErrorList {
obj, allErrs := toKubeControllerManager(uncastObj)
if len(allErrs) > 0 {
return allErrs
}
oldObj, allErrs := toKubeControllerManager(uncastOldObj)
if len(allErrs) > 0 {
return allErrs
}

allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))...)
allErrs = append(allErrs, validateKubeControllerManagerSpecUpdate(obj.Spec, oldObj.Spec)...)

return allErrs
}

func (kubeControllerManagerV1) ValidateStatusUpdate(uncastObj runtime.Object, uncastOldObj runtime.Object) field.ErrorList {
obj, errs := toKubeControllerManager(uncastObj)
if len(errs) > 0 {
return errs
}
oldObj, errs := toKubeControllerManager(uncastOldObj)
if len(errs) > 0 {
return errs
}

// TODO validate the obj. remember that status validation should *never* fail on spec validation errors.
errs = append(errs, validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))...)

return errs
}

0 comments on commit 980d3c5

Please sign in to comment.