Skip to content

Webhook server allow to intercept Deployments formed by simple HTTPS POST requests to the kube-APIServer. Then they will transform/modify these objects so that they respect certain rules defined beforehand and finally validate or invalidate their deployment.

Notifications You must be signed in to change notification settings

Yulypso/K8S-Webhooks

Repository files navigation

K8S-Webhooks

Author

Linkedin: Thierry Khamphousone


Architecture Architecture Architecture


Setup

$ git clone https://github.com/Yulypso/K8S-Webhooks.git
$ cd K8S-Webhooks


Start Webhooks

(Namespace: webhookserver-ns)

Generate CA certificate/CA private key Generate server certificate/server private key signed by CA private key

$ ./generate-keys.sh

Add label on the server deployment node

# replace <node> by the desired node
$ kubectl label nodes <node> webhook=true

Generate Kubernetes objects for webhooks within webhookserver-ns namespace

$ ./generate-K8S-objects.sh

Start test pod

(Namespace: admissionwebhook-ns)

Generate pod within admissionwebhook-ns namespace which calls the created webhook

$ kubectl apply -f ./TestDeployments/pod-1.yml


Reset

Reset K8S webhook server.

$ ./reset.sh

Clean certificates

$ ./reset.sh certificates

Build and Push to Docker Hub (for development uses)

$ ./reset.sh docker


Certificates

Verify CSR content

$ openssl req -text -noout -verify -in Certificates/webhookservertls.csr

Verify CERT content

$ openssl x509 -text -noout -in Certificates/webhookservertls.cert


Questions and Answers

What happens if you have both a MutatingWebhook and a ValidatingWebhook, which is applied first during a deployment?

"Admission webhooks are HTTP callbacks that receive admission requests and do something with them. You can define two types of admission webhooks, validating admission webhook and mutating admission webhook. Mutating admission webhooks are invoked first, and can modify objects sent to the API server to enforce custom defaults. After all object modifications are complete, and after the incoming object is validated by the API server, validating admission webhooks are invoked and can reject requests to enforce custom policies."

What happens when you have two MutatingWebhooks, which of the two is run first? How is it going ?

The metadata.name field is used to define the order of application of MutatingWebhooks and ValidatingWebhooks. Within the prototype, a-mutatingwebhook is applied before b-mutatingwebhook

Therefore, we can have in this order of execution, a MutatingWebhook [a] which can do [0-n] operations and have a second MutatingWebhook [b] which can also do [0-n] operations.

Should we specify runAsUser, runAsGroup, fsGroup field in addition to the field runAsNonRoot: true ?

It is not recommanded because some images such as the jenkins/jenkins official server image runs as group:user == jenkins:jenkins. That is why we should not specify any of those fields in order to make sure the server will work properly in this case.



JSONPath

Filter must be written with: []

  • OK

$.spec.containers[?(@[name] == 'node-app0')]

  • KO

$.spec.containers[?(@.name == 'node-app0')]

Keys containing '.' must be surrounded by []

  • OK

$.metadata.annotations.[artemis.site]

  • KO

$.metadata.annotations.artemis.site


Architecture


Default DSL Config (common to all deployed pods)

{
    "add":[
        {
            "path":"$.spec.securityContext.runAsNonRoot",
            "value":true
        }
    ],
    "remove":[
        {
            "path":"$.spec.volumes[*].hostPath"
        },
        {
            "path":"$.spec.securityContext.runAsNonRoot",
            "value":false
        }
    ],
    "mandatorydata":[
        {
            "path":"$.spec.securityContext.runAsNonRoot",
            "value":true
        }
    ],
    "forbiddendata":[
        {
            "path":"$.spec.volumes[*].hostPath"
        },
        {
            "path": "$.spec.securityContext.runAsUser",
            "value": 0
        },
        {
            "path":"$.spec.securityContext.runAsNonRoot",
            "value":false
        }
    ]
}


HTTP Requests (import from Postman directory)

PATCH: DSL Config (example)

> Method: PUT  
> Endpoint: localhost:31000/namespace/<NAMESPACE>  
> Body: 
{
    "add": [
        {
            "path": "$.spec.containers[*].securityContext.allowPrivilegeEscalation",
            "value": false
        },

        {
            "path": "$.metadata.annotations.[co.elastic.logs/multiline.pattern]",
            "value": "^\d{4}-\d{2}-\d{2}"
        },
        {
            "path": "$.spec.containers",
            "value": {
                "env": [
                    {
                        "name": "ELASTOMCAT_HOST",
                        "value": "http://srvelasprod.technique.artemis:9200"
                    },
                    {
                        "name": "ELASTOMCAT_USERNAME",
                        "value": "UpXo3on-wowrT8g"
                    },
                    {
                        "name": "ELASTOMACT_USERPWD",
                        "value": "Artemis2019****"
                    }
                ],
                "image": "tomcat:8.0-alpine",
                "imagePullPolicy": "Always",
                "name": "tomcatelas--1185509365",
                "ports": [
                    {
                        "containerPort": 8080,
                        "name": "http-ext",
                        "protocol": "TCP"
                    }
                ]
            }
        }
    ],
    "replace": [
        {
            "path": "$.spec.containers[?(@[name] == 'node-app0')]",
            "value": {
                "env": [
                    {
                        "name": "elas_host",
                        "value": "http://srvelasprod.technique.artemis:9200"
                    },
                    {
                        "name": "username",
                        "value": "anonymous"
                    },
                    {
                        "name": "password",
                        "value": "na"
                    }
                ],
                "image": "tomcat:8.5-alpine",
                "imagePullPolicy": "Always",
                "name": "containerexistant",
                "ports": [
                    {
                        "containerPort": 8080,
                        "name": "http-ext",
                        "protocol": "TCP"
                    }
                ]
            }
        }
    ],
    "remove": [
        {
            "path": "$.spec.securityContext.runAsUser"
        }
    ],
    "mandatorydata": [
        {
            "path": "$.metadata.labels.a4c_nodeid"
        },
        {
            "path": "$.metadata.annotations.[artemis.site]",
            "value": "prod"
        }
    ],
    "forbiddendata": [
        {
            "path": "$.spec.securityContext.runAsUser",
            "value": 0
        }
    ]
}

RESET: DSL Config to the initial default.json (empty config => {})

> Method: DELETE
> Endpoint: localhost:31000/reset  
> Body:   

CLEAR: DSL Config by namespace

> Method: DELETE 
> Endpoint: localhost:31000/namespace/<NAMESPACE>  
> Body:   

About

Webhook server allow to intercept Deployments formed by simple HTTPS POST requests to the kube-APIServer. Then they will transform/modify these objects so that they respect certain rules defined beforehand and finally validate or invalidate their deployment.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published