feat: emitting events for policies violations

This commit is contained in:
Dario Tranchitella
2021-06-15 21:42:39 +02:00
parent e7b33bda26
commit accd9ca038
16 changed files with 153 additions and 106 deletions
+4 -3
View File
@@ -6,6 +6,7 @@ package webhook
import (
"context"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
@@ -13,7 +14,7 @@ import (
type Func func(ctx context.Context, req admission.Request) admission.Response
type Handler interface {
OnCreate(client client.Client, decoder *admission.Decoder) Func
OnDelete(client client.Client, decoder *admission.Decoder) Func
OnUpdate(client client.Client, decoder *admission.Decoder) Func
OnCreate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) Func
OnDelete(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) Func
OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) Func
}
+6 -3
View File
@@ -9,6 +9,7 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -45,7 +46,7 @@ func Handler() capsulewebhook.Handler {
return &handler{}
}
func (r *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnCreate(c client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
var pod = &corev1.Pod{}
@@ -77,6 +78,8 @@ func (r *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
usedPullPolicy := string(container.ImagePullPolicy)
if !policy.IsPolicySupported(usedPullPolicy) {
recorder.Eventf(&tnt, corev1.EventTypeWarning, "PullPolicy", "Pod %s/%s pull policy %s is not allowed", req.Namespace, req.Name, usedPullPolicy)
return admission.Denied(NewImagePullPolicyForbidden(usedPullPolicy, container.Name, policy.AllowedPullPolicies()).Error())
}
}
@@ -85,13 +88,13 @@ func (r *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
}
}
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnUpdate(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
+19 -6
View File
@@ -8,12 +8,15 @@ import (
"fmt"
"net/http"
"regexp"
"strings"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -53,29 +56,29 @@ func Handler(configuration configuration.Configuration) capsulewebhook.Handler {
return &handler{configuration: configuration}
}
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
ingress, err := r.ingressFromRequest(req, decoder)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
return r.validateIngress(ctx, client, ingress)
return r.validateIngress(ctx, client, ingress, recorder)
}
}
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
ingress, err := r.ingressFromRequest(req, decoder)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
return r.validateIngress(ctx, client, ingress)
return r.validateIngress(ctx, client, ingress, recorder)
}
}
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
@@ -173,7 +176,7 @@ func (r *handler) validateHostnames(tenant v1alpha1.Tenant, hostnames []string)
return nil
}
func (r *handler) validateIngress(ctx context.Context, c client.Client, ingress Ingress) admission.Response {
func (r *handler) validateIngress(ctx context.Context, c client.Client, ingress Ingress, recorder record.EventRecorder) admission.Response {
tenantList := &v1alpha1.TenantList{}
if err := c.List(ctx, tenantList, client.MatchingFieldsSelector{
Selector: fields.OneTermEqualSelector(".status.namespaces", ingress.Namespace()),
@@ -187,14 +190,24 @@ func (r *handler) validateIngress(ctx context.Context, c client.Client, ingress
tenant := tenantList.Items[0]
if err := r.validateClass(tenant, ingress.IngressClass()); err != nil {
if ic := ingress.IngressClass(); ic != nil {
recorder.Eventf(&tenant, corev1.EventTypeWarning, "InvalidIngressClass", "Ingress %s/%s class %s is forbidden for the current Tenant", ingress.Namespace(), ingress.Name(), *ic)
} else {
recorder.Eventf(&tenant, corev1.EventTypeWarning, "MissingIngressClass", "Ingress %s/%s is missing required class for the current Tenant", ingress.Namespace(), ingress.Name())
}
return admission.Errored(http.StatusBadRequest, err)
}
if err := r.validateHostnames(tenant, ingress.Hostnames()); err != nil {
recorder.Eventf(&tenant, corev1.EventTypeWarning, "InvalidHostname", "Ingress %s/%s hostnames %s is forbidden for the current Tenant", ingress.Namespace(), ingress.Name(), strings.Join(ingress.Hostnames(), ","))
return admission.Errored(http.StatusBadRequest, err)
}
if err := r.validateCollision(ctx, c, ingress); err != nil {
recorder.Eventf(&tenant, corev1.EventTypeWarning, "HostnameCollision", "Ingress %s/%s hostnames collision for %s ", ingress.Namespace(), ingress.Name(), strings.Join(ingress.Hostnames(), ","))
return admission.Errored(http.StatusBadRequest, err)
}
+6 -9
View File
@@ -42,16 +42,13 @@ func (w *webhook) GetPath() string {
}
type handler struct {
recorder record.EventRecorder
}
func Handler(recorder record.EventRecorder) capsulewebhook.Handler {
return &handler{
recorder: recorder,
}
func Handler() capsulewebhook.Handler {
return &handler{}
}
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
ns := &corev1.Namespace{}
if err := decoder.Decode(req, ns); err != nil {
@@ -65,7 +62,7 @@ func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) cap
return admission.Errored(http.StatusBadRequest, err)
}
if tnt.IsFull() {
r.recorder.Eventf(tnt, corev1.EventTypeWarning, "Error", "the Namespace quota has been exceeded, Namespace %s cannot been attached", ns.GetName())
recorder.Eventf(tnt, corev1.EventTypeWarning, "NamespaceQuota", "Namespace %s cannot be attached, quota exceeded", ns.GetName())
return admission.Denied(NewNamespaceQuotaExceededError().Error())
}
@@ -75,13 +72,13 @@ func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) cap
}
}
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
+29 -16
View File
@@ -7,8 +7,10 @@ import (
"context"
"net/http"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -45,30 +47,44 @@ func Handler() capsulewebhook.Handler {
return &handler{}
}
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (r *handler) generic(ctx context.Context, req admission.Request, client client.Client, _ *admission.Decoder) (bool, error) {
func (r *handler) generic(ctx context.Context, req admission.Request, client client.Client, _ *admission.Decoder) (*v1alpha1.Tenant, error) {
var err error
np := &networkingv1.NetworkPolicy{}
err = client.Get(ctx, types.NamespacedName{Namespace: req.AdmissionRequest.Namespace, Name: req.AdmissionRequest.Name}, np)
if err != nil {
return false, err
return nil, err
}
return r.isCapsuleNetworkPolicy(np), nil
tnt := &v1alpha1.Tenant{}
l, _ := v1alpha1.GetTypeLabel(&v1alpha1.Tenant{})
if v, ok := np.GetLabels()[l]; ok {
if err = client.Get(ctx, types.NamespacedName{Name: v}, tnt); err != nil {
return nil, err
}
return tnt, nil
}
return nil, nil
}
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
// nolint:dupl
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
ok, err := r.generic(ctx, req, client, decoder)
tnt, err := r.generic(ctx, req, client, decoder)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}
if ok {
if tnt != nil {
recorder.Eventf(tnt, corev1.EventTypeWarning, "NetworkPolicyDeletion", "NetworkPolicy %s/%s cannot be deleted", req.Namespace, req.Name)
return admission.Denied("Capsule Network Policies cannot be deleted: please, reach out to the system administrators")
}
@@ -76,19 +92,16 @@ func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder) cap
}
}
func (r *handler) isCapsuleNetworkPolicy(np *networkingv1.NetworkPolicy) (ok bool) {
l, _ := v1alpha1.GetTypeLabel(&v1alpha1.Tenant{})
_, ok = np.GetLabels()[l]
return
}
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
// nolint:dupl
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
ok, err := r.generic(ctx, req, client, decoder)
tnt, err := r.generic(ctx, req, client, decoder)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}
if ok {
if tnt != nil {
recorder.Eventf(tnt, corev1.EventTypeWarning, "NetworkPolicyUpdate", "NetworkPolicy %s/%s cannot be updated", req.Namespace, req.Name)
return admission.Denied("Capsule Network Policies cannot be updated: please, reach out to the system administrators")
}
+14 -14
View File
@@ -48,18 +48,16 @@ func (w *webhook) GetPath() string {
}
type handler struct {
cfg configuration.Configuration
recorder record.EventRecorder
cfg configuration.Configuration
}
func Handler(cfg configuration.Configuration, recorder record.EventRecorder) capsulewebhook.Handler {
func Handler(cfg configuration.Configuration) capsulewebhook.Handler {
return &handler{
cfg: cfg,
recorder: recorder,
cfg: cfg,
}
}
func (h *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnCreate(clt client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
ns := &corev1.Namespace{}
if err := decoder.Decode(req, ns); err != nil {
@@ -78,10 +76,12 @@ func (h *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsul
}
// Tenant owner must adhere to user that asked for NS creation
if !h.isTenantOwner(tnt.Spec.Owner, req.UserInfo) {
recorder.Eventf(tnt, corev1.EventTypeWarning, "NonOwnedTenant", "Namespace %s cannot be assigned to the current Tenant", ns.GetName())
return admission.Denied("Cannot assign the desired namespace to a non-owned Tenant")
}
// Patching the response
return h.patchResponseForOwnerRef(tnt, ns)
return h.patchResponseForOwnerRef(tnt, ns, recorder)
}
// If we forceTenantPrefix -> find Tenant from NS name
@@ -117,13 +117,13 @@ func (h *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsul
}
if len(tenants) == 1 {
return h.patchResponseForOwnerRef(&tenants[0], ns)
return h.patchResponseForOwnerRef(&tenants[0], ns, recorder)
}
if h.cfg.ForceTenantPrefix() {
for _, tnt := range tenants {
if strings.HasPrefix(ns.GetName(), fmt.Sprintf("%s-", tnt.GetName())) {
return h.patchResponseForOwnerRef(tnt.DeepCopy(), ns)
return h.patchResponseForOwnerRef(tnt.DeepCopy(), ns, recorder)
}
}
admission.Denied("The Namespace prefix used doesn't match any available Tenant")
@@ -132,31 +132,31 @@ func (h *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsul
return admission.Denied("Unable to assign namespace to tenant. Please use " + ln + " label when creating a namespace")
}
}
func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Denied("Capsule user cannot update a Namespace")
}
}
func (h *handler) patchResponseForOwnerRef(tenant *capsulev1alpha1.Tenant, ns *corev1.Namespace) admission.Response {
func (h *handler) patchResponseForOwnerRef(tenant *capsulev1alpha1.Tenant, ns *corev1.Namespace, recorder record.EventRecorder) admission.Response {
scheme := runtime.NewScheme()
_ = capsulev1alpha1.AddToScheme(scheme)
_ = corev1.AddToScheme(scheme)
o, _ := json.Marshal(ns.DeepCopy())
if err := controllerutil.SetControllerReference(tenant, ns, scheme); err != nil {
h.recorder.Eventf(tenant, corev1.EventTypeWarning, "Error", "namespace %s cannot be assigned to the desired Tenant", ns.GetName())
recorder.Eventf(tenant, corev1.EventTypeWarning, "Error", "Namespace %s cannot be assigned to the desired Tenant", ns.GetName())
return admission.Errored(http.StatusInternalServerError, err)
}
h.recorder.Eventf(tenant, corev1.EventTypeNormal, "NamespaceCreationWebhook", "namespace %s has been assigned to the desired Tenant", ns.GetName())
recorder.Eventf(tenant, corev1.EventTypeNormal, "NamespaceCreationWebhook", "Namespace %s has been assigned to the desired Tenant", ns.GetName())
c, _ := json.Marshal(ns)
return admission.PatchResponseFromRaw(o, c)
+8 -5
View File
@@ -7,8 +7,9 @@ import (
"context"
"net/http"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -46,9 +47,9 @@ func Handler() capsulewebhook.Handler {
return &handler{}
}
func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
var pod = &v1.Pod{}
var pod = &corev1.Pod{}
if err := decoder.Decode(req, pod); err != nil {
return admission.Errored(http.StatusBadRequest, err)
@@ -78,6 +79,8 @@ func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
// We don't have to force Pod to specify a Priority Class
return admission.Allowed("")
case !allowed.ExactMatch(priorityClassName) && !allowed.RegexMatch(priorityClassName):
recorder.Eventf(&tntList.Items[0], corev1.EventTypeWarning, "PriorityClass", "Pod %s/%s is using Priority Class %s not allowed for the current Tenant", pod.Namespace, pod.Name, priorityClassName)
return admission.Errored(http.StatusBadRequest, NewPodPriorityClassForbidden(priorityClassName, *allowed))
default:
return admission.Allowed("")
@@ -85,13 +88,13 @@ func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
}
}
func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnUpdate(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
+10 -5
View File
@@ -7,8 +7,9 @@ import (
"context"
"net/http"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -45,10 +46,10 @@ func Handler() capsulewebhook.Handler {
return &handler{}
}
func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
var valid, matched bool
pvc := &v1.PersistentVolumeClaim{}
pvc := &corev1.PersistentVolumeClaim{}
if err := decoder.Decode(req, pvc); err != nil {
return admission.Errored(http.StatusBadRequest, err)
@@ -72,6 +73,8 @@ func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
}
if pvc.Spec.StorageClassName == nil {
recorder.Eventf(&tnt, corev1.EventTypeWarning, "StorageClassInvalid", "PersistentVolumeClaim %s/%s is missing StorageClass", req.Namespace, req.Name)
return admission.Errored(http.StatusBadRequest, NewStorageClassNotValid(*tntList.Items[0].Spec.StorageClasses))
}
@@ -79,19 +82,21 @@ func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
valid = tnt.Spec.StorageClasses.ExactMatch(sc)
matched = tnt.Spec.StorageClasses.RegexMatch(sc)
if !valid && !matched {
recorder.Eventf(&tnt, corev1.EventTypeWarning, "StorageClassForbidden", "PersistentVolumeClaim %s/%s StorageClass %s is forbidden for the current Tenant", req.Namespace, req.Name, sc)
return admission.Errored(http.StatusBadRequest, NewStorageClassForbidden(*pvc.Spec.StorageClassName, *tnt.Spec.StorageClasses))
}
return admission.Allowed("")
}
}
func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnUpdate(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
+8 -5
View File
@@ -7,8 +7,9 @@ import (
"context"
"net/http"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -46,9 +47,9 @@ func Handler() capsulewebhook.Handler {
return &handler{}
}
func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
pod := &v1.Pod{}
pod := &corev1.Pod{}
if err := decoder.Decode(req, pod); err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
@@ -72,6 +73,8 @@ func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
valid = tnt.Spec.ContainerRegistries.ExactMatch(registry.Registry())
matched = tnt.Spec.ContainerRegistries.RegexMatch(registry.Registry())
if !valid && !matched {
recorder.Eventf(&tnt, corev1.EventTypeWarning, "ContainerRegistry", "Pod %s/%s is using a forbidden registry %s", req.Namespace, req.Name, registry.Registry())
return admission.Errored(http.StatusBadRequest, NewContainerRegistryForbidden(container.Image, *tnt.Spec.ContainerRegistries))
}
}
@@ -81,13 +84,13 @@ func (h *handler) OnCreate(c client.Client, decoder *admission.Decoder) capsulew
}
}
func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnUpdate(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
+12 -7
View File
@@ -8,6 +8,7 @@ import (
"io/ioutil"
admissionv1 "k8s.io/api/admission/v1"
"k8s.io/client-go/tools/record"
controllerruntime "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -21,11 +22,14 @@ func Register(manager controllerruntime.Manager, webhookList ...Webhook) error {
return nil
}
recorder := manager.GetEventRecorderFor("tenant-webhook")
server := manager.GetWebhookServer()
for _, wh := range webhookList {
server.Register(wh.GetPath(), &webhook.Admission{
Handler: &handlerRouter{
handler: wh.GetHandler(),
recorder: recorder,
handler: wh.GetHandler(),
},
})
}
@@ -33,19 +37,20 @@ func Register(manager controllerruntime.Manager, webhookList ...Webhook) error {
}
type handlerRouter struct {
handler Handler
client client.Client
decoder *admission.Decoder
handler Handler
client client.Client
decoder *admission.Decoder
recorder record.EventRecorder
}
func (r *handlerRouter) Handle(ctx context.Context, req admission.Request) admission.Response {
switch req.Operation {
case admissionv1.Create:
return r.handler.OnCreate(r.client, r.decoder)(ctx, req)
return r.handler.OnCreate(r.client, r.decoder, r.recorder)(ctx, req)
case admissionv1.Update:
return r.handler.OnUpdate(r.client, r.decoder)(ctx, req)
return r.handler.OnUpdate(r.client, r.decoder, r.recorder)(ctx, req)
case admissionv1.Delete:
return r.handler.OnDelete(r.client, r.decoder)(ctx, req)
return r.handler.OnDelete(r.client, r.decoder, r.recorder)(ctx, req)
default:
return admission.Allowed("")
}
+12 -6
View File
@@ -7,9 +7,11 @@ import (
"context"
"net"
"net/http"
"strings"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -49,7 +51,7 @@ func Handler() capsulewebhook.Handler {
return &handler{}
}
func (r *handler) handleService(ctx context.Context, clt client.Client, decoder *admission.Decoder, req admission.Request) admission.Response {
func (r *handler) handleService(ctx context.Context, clt client.Client, decoder *admission.Decoder, req admission.Request, recorder record.EventRecorder) admission.Response {
svc := &corev1.Service{}
if err := decoder.Decode(req, svc); err != nil {
return admission.Errored(http.StatusBadRequest, err)
@@ -67,6 +69,8 @@ func (r *handler) handleService(ctx context.Context, clt client.Client, decoder
tnt := tntList.Items[0]
if svc.Spec.Type == corev1.ServiceTypeNodePort && tnt.GetAnnotations()[enableNodePortsAnnotation] == "false" {
recorder.Eventf(&tnt, corev1.EventTypeWarning, "NodePort", "Service %s/%s cannot be type of NodePort", req.Namespace, req.Name)
return admission.Errored(http.StatusBadRequest, NewNodePortDisabledError())
}
@@ -84,22 +88,24 @@ func (r *handler) handleService(ctx context.Context, clt client.Client, decoder
}
}
recorder.Eventf(&tnt, corev1.EventTypeWarning, "NodePort", "Service %s/%s external IPs %s are not in the expected range for the current Tenant", req.Namespace, req.Name, strings.Join(svc.Spec.ExternalIPs, ","))
return admission.Errored(http.StatusBadRequest, NewExternalServiceIPForbidden(tnt.Spec.ExternalServiceIPs.Allowed))
}
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnCreate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return r.handleService(ctx, client, decoder, req)
return r.handleService(ctx, client, decoder, req, recorder)
}
}
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return r.handleService(ctx, client, decoder, req)
return r.handleService(ctx, client, decoder, req, recorder)
}
}
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
+4 -3
View File
@@ -9,6 +9,7 @@ import (
"regexp"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -153,7 +154,7 @@ func (h *handler) validateTenantByRegex(tenant *v1alpha1.Tenant) error {
return nil
}
func (h *handler) OnCreate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnCreate(client client.Client, decoder *admission.Decoder, _ record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
if err := h.validateTenant(ctx, req, client, decoder); err != nil {
return admission.Denied(err.Error())
@@ -162,13 +163,13 @@ func (h *handler) OnCreate(client client.Client, decoder *admission.Decoder) cap
}
}
func (h *handler) OnDelete(client.Client, *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(context.Context, admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder, _ record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
if err := h.validateTenant(ctx, req, client, decoder); err != nil {
return admission.Denied(err.Error())
+5 -7
View File
@@ -46,17 +46,15 @@ func (w *webhook) GetPath() string {
type handler struct {
configuration configuration.Configuration
recorder record.EventRecorder
}
func Handler(configuration configuration.Configuration, recorder record.EventRecorder) capsulewebhook.Handler {
func Handler(configuration configuration.Configuration) capsulewebhook.Handler {
return &handler{
configuration: configuration,
recorder: recorder,
}
}
func (r *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnCreate(clt client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
ns := &corev1.Namespace{}
if err := decoder.Decode(req, ns); err != nil {
@@ -79,7 +77,7 @@ func (r *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsul
return admission.Errored(http.StatusBadRequest, err)
}
if e := fmt.Sprintf("%s-%s", tnt.GetName(), ns.GetName()); !strings.HasPrefix(ns.GetName(), fmt.Sprintf("%s-", tnt.GetName())) {
r.recorder.Eventf(tnt, corev1.EventTypeWarning, "TenantPrefix", "Namespace %s does not match the expected prefix", ns.GetName())
recorder.Eventf(tnt, corev1.EventTypeWarning, "TenantPrefix", "Namespace %s does not match the expected Tenant prefix", ns.GetName())
return admission.Denied("The namespace doesn't match the tenant prefix, expected " + e)
}
@@ -88,13 +86,13 @@ func (r *handler) OnCreate(clt client.Client, decoder *admission.Decoder) capsul
}
}
func (r *handler) OnDelete(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
}
func (r *handler) OnUpdate(client client.Client, decoder *admission.Decoder) capsulewebhook.Func {
func (r *handler) OnUpdate(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
return admission.Allowed("")
}
+7 -6
View File
@@ -6,6 +6,7 @@ package utils
import (
"context"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -43,30 +44,30 @@ func (h handler) isCapsuleUser(req admission.Request) bool {
return false
}
func (h *handler) OnCreate(client client.Client, decoder *admission.Decoder) webhook.Func {
func (h *handler) OnCreate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) webhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
if !h.isCapsuleUser(req) {
return admission.Allowed("")
}
return h.handler.OnCreate(client, decoder)(ctx, req)
return h.handler.OnCreate(client, decoder, recorder)(ctx, req)
}
}
func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder) webhook.Func {
func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) webhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
if !h.isCapsuleUser(req) {
return admission.Allowed("")
}
return h.handler.OnDelete(client, decoder)(ctx, req)
return h.handler.OnDelete(client, decoder, recorder)(ctx, req)
}
}
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder) webhook.Func {
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) webhook.Func {
return func(ctx context.Context, req admission.Request) admission.Response {
if !h.isCapsuleUser(req) {
return admission.Allowed("")
}
return h.handler.OnUpdate(client, decoder)(ctx, req)
return h.handler.OnUpdate(client, decoder, recorder)(ctx, req)
}
}