Skip to content

Commit

Permalink
Move mutator validation to the validating webhook (#1153)
Browse files Browse the repository at this point in the history
Signed-off-by: Max Smythe <smythe@google.com>
  • Loading branch information
maxsmythe committed Mar 1, 2021
1 parent 0fad0d2 commit 6b17c17
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 65 deletions.
63 changes: 0 additions & 63 deletions pkg/webhook/mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,18 @@ package webhook
import (
"context"
"errors"
"fmt"
"net/http"
"time"

"github.com/open-policy-agent/cert-controller/pkg/rotator"
opa "github.com/open-policy-agent/frameworks/constraint/pkg/client"
"github.com/open-policy-agent/gatekeeper/apis"
mutationsv1alpha1 "github.com/open-policy-agent/gatekeeper/apis/mutations/v1alpha1"
"github.com/open-policy-agent/gatekeeper/pkg/controller/config/process"
"github.com/open-policy-agent/gatekeeper/pkg/mutation"
"github.com/open-policy-agent/gatekeeper/pkg/util"
admissionv1 "k8s.io/api/admission/v1"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
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/types"
Expand Down Expand Up @@ -117,19 +114,6 @@ func (h *mutationHandler) Handle(ctx context.Context, req admission.Request) adm
return admission.ValidationResponse(true, "Mutating only on create or update")
}

if userErr, err := h.validateGatekeeperResources(ctx, req); err != nil {
vResp := admission.ValidationResponse(false, err.Error())
if vResp.Result == nil {
vResp.Result = &metav1.Status{}
}
if userErr {
vResp.Result.Code = http.StatusUnprocessableEntity
} else {
vResp.Result.Code = http.StatusInternalServerError
}
return vResp
}

if h.isGatekeeperResource(ctx, req) {
return admission.ValidationResponse(true, "Not mutating gatekeeper resources")
}
Expand Down Expand Up @@ -230,50 +214,3 @@ func AppendMutationWebhookIfEnabled(webhooks []rotator.WebhookInfo) []rotator.We
}
return webhooks
}

// validateGatekeeperResources returns whether an issue is user error (vs internal) and any errors
// validating internal resources
func (h *mutationHandler) validateGatekeeperResources(ctx context.Context, req admission.Request) (bool, error) {
if req.AdmissionRequest.Kind.Group == mutationsGroup && req.AdmissionRequest.Kind.Kind == "AssignMetadata" {
return h.validateAssignMetadata(ctx, req)
}
if req.AdmissionRequest.Kind.Group == mutationsGroup && req.AdmissionRequest.Kind.Kind == "Assign" {
return h.validateAssign(ctx, req)
}
return false, nil
}

func (h *mutationHandler) validateAssignMetadata(ctx context.Context, req admission.Request) (bool, error) {
obj, _, err := deserializer.Decode(req.AdmissionRequest.Object.Raw, nil, &mutationsv1alpha1.AssignMetadata{})
if err != nil {
return false, err
}
assignMetadata, ok := obj.(*mutationsv1alpha1.AssignMetadata)
if !ok {
return false, fmt.Errorf("Deserialized object is not of type AssignMetadata")
}
err = mutation.IsValidAssignMetadata(assignMetadata)
if err != nil {
return true, err
}

return false, nil
}

func (h *mutationHandler) validateAssign(ctx context.Context, req admission.Request) (bool, error) {
obj, _, err := deserializer.Decode(req.AdmissionRequest.Object.Raw, nil, &mutationsv1alpha1.Assign{})
if err != nil {
return false, err
}
assign, ok := obj.(*mutationsv1alpha1.Assign)
if !ok {
return false, fmt.Errorf("Deserialized object is not of type Assign")
}

err = mutation.IsValidAssign(assign)
if err != nil {
return true, err
}

return false, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ spec:
}
for _, tt := range tc {
t.Run(tt.Name, func(t *testing.T) {
handler := mutationHandler{webhookHandler: webhookHandler{}}
handler := validationHandler{webhookHandler: webhookHandler{}}
b, err := yaml.YAMLToJSON([]byte(tt.AssignMeta))
if err != nil {
t.Fatalf("Error parsing yaml: %s", err)
Expand Down Expand Up @@ -270,7 +270,7 @@ spec:
}
for _, tt := range tc {
t.Run(tt.Name, func(t *testing.T) {
handler := mutationHandler{webhookHandler: webhookHandler{}}
handler := validationHandler{webhookHandler: webhookHandler{}}
b, err := yaml.YAMLToJSON([]byte(tt.Assign))
if err != nil {
t.Fatalf("Error parsing yaml: %s", err)
Expand Down
40 changes: 40 additions & 0 deletions pkg/webhook/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/open-policy-agent/frameworks/constraint/pkg/core/templates"
rtypes "github.com/open-policy-agent/frameworks/constraint/pkg/types"
"github.com/open-policy-agent/gatekeeper/apis"
mutationsv1alpha1 "github.com/open-policy-agent/gatekeeper/apis/mutations/v1alpha1"
"github.com/open-policy-agent/gatekeeper/pkg/controller/config/process"
"github.com/open-policy-agent/gatekeeper/pkg/keys"
"github.com/open-policy-agent/gatekeeper/pkg/logging"
Expand Down Expand Up @@ -278,6 +279,10 @@ func (h *validationHandler) validateGatekeeperResources(ctx context.Context, req
if err := h.validateConfigResource(ctx, req); err != nil {
return true, err
}
case req.AdmissionRequest.Kind.Group == mutationsGroup && req.AdmissionRequest.Kind.Kind == "AssignMetadata":
return h.validateAssignMetadata(ctx, req)
case req.AdmissionRequest.Kind.Group == mutationsGroup && req.AdmissionRequest.Kind.Kind == "Assign":
return h.validateAssign(ctx, req)
}

return false, nil
Expand Down Expand Up @@ -332,6 +337,41 @@ func (h *validationHandler) validateConfigResource(ctx context.Context, req admi
return nil
}

func (h *validationHandler) validateAssignMetadata(ctx context.Context, req admission.Request) (bool, error) {
obj, _, err := deserializer.Decode(req.AdmissionRequest.Object.Raw, nil, &mutationsv1alpha1.AssignMetadata{})
if err != nil {
return false, err
}
assignMetadata, ok := obj.(*mutationsv1alpha1.AssignMetadata)
if !ok {
return false, fmt.Errorf("Deserialized object is not of type AssignMetadata")
}
err = mutation.IsValidAssignMetadata(assignMetadata)
if err != nil {
return true, err
}

return false, nil
}

func (h *validationHandler) validateAssign(ctx context.Context, req admission.Request) (bool, error) {
obj, _, err := deserializer.Decode(req.AdmissionRequest.Object.Raw, nil, &mutationsv1alpha1.Assign{})
if err != nil {
return false, err
}
assign, ok := obj.(*mutationsv1alpha1.Assign)
if !ok {
return false, fmt.Errorf("Deserialized object is not of type Assign")
}

err = mutation.IsValidAssign(assign)
if err != nil {
return true, err
}

return false, nil
}

// traceSwitch returns true if a request should be traced
func (h *validationHandler) reviewRequest(ctx context.Context, req admission.Request) (*rtypes.Responses, error) {
// if we have a maximum number of concurrent serving goroutines, try to acquire
Expand Down

0 comments on commit 6b17c17

Please sign in to comment.