Home

Admission Controllers

Every time a request goes through the kubectl cli...

  • the req. goes to api server
  • the req. gets authenticated, usually through certificates
    • kubectl uses the kubeconfig file, which has certs configured
  • the req is authorized
    • does the user have the permission to do what they are trying to? RBAC check, usually
  • the api serve runs mutation admissionControllers
    • these change the reques
  • the api serve runs validation admissionControllers
    • these allow/deny events from happening
  • info is persistent in the etcd database

NOTE: built-in admission controllers run in an order. The order makes logical sense in order of applicability.

Kubectl gui
Authentication
Authorization
Do The Work
Mutation AdmissionControllers
Kubectl gets user authN from $HOME/.kube/config
AUTHZNOTE
VDC

Permissions can be more granular than user RBAC with Admission Controllers

Some use-cases for different authz checks:

  • review pod config for specifics
    • non-public images allowed
    • block the tag latest
    • block running containers as root user
    • assure metadata has specific labels present

AdmissionControllers to the rescue.
AdmissionControllers "live" in the apiserver, and are bits of code.
AdmissionControllers can change requests and do its own work during a request.

Why We Need Admission Controllers

Per the K8s Docs on this subject, with seemingly strong language,
"...a Kubernetes API server that is not properly configured with the right set of admission controllers is an incomplete server..."

AdmissionControllers Validate and Mutate

Mutations first.
Validations second.
Also, these controllers can perform side-effects on non-request objects.

During the request lifecycle, if either of the controllers reject the request due to some missed mutation or validation, the entire request is rejected with an err.

Some Built-In AdmissionControllers

  • AlwaysPullImages
    • ensures images are pulled prior to pod creation
  • DefaultStorageClass
    • observes creation of pvcs and sets a default storage class if none is present
  • EventRateLimit
    • sets limit on requests to prevent the api server from flooding with req
  • NamespaceExists, deprecated
    • rejects requests to namespaces that don't exist
  • NamespaceAutoProvision
    • not automatically enabled, and deprecated but present and allowed to be enabled
    • when a new namespace is referenced, but the namespaces is not created first, the namespace is created on-the-fly
  • NamespaceLifecycle
    • replaces the above 2 admission controllers
    • default namespace cant be deleted
    • auto-rejects object handling in namespaces that are not present
  • ...there are a more

View All Enabled Admission Controllers

kube-apiserver -h | grep enable-admission-plugins

Add An Admission Controller

Edit the kube-apiserver.service file.

# add this flag, here with example vals
--enable-admission-plugins=NodeRestriction,NamespaceAutoProvision

Custom External Admission Controllers with Webhooks

Webhooks can be configured to point to a server. The server can be hosted within or outside of the kubernetes cluster. The server has custom admission services.
After the built-in admission controllers finish, the webhook admission functions het hit & applied.
A json blob, a "admission review object", gets passed to the admission webhook server, and returns a result object depending on the success/failure of the hook applied. The result has a allowed: <bool> key value, indicating its success or failure.

Build & Deploy a webhook server

Heres an example of a webhook.
This can be any api server. The only requirement is for the api to

  • accept the payload from k8s, from the admission controllers
    • this should permit or reject a request
  • return a response to k8s that k8s is expecting
    • the response should contain the "allowed" key

With a server developed,

  • run it as a container+pod+deployment
  • put it in the same k8s cluster
  • give it a service

Config the cluster to reach out to the service with a new config:

apiVersion: admissionregistration.k8s.io/v1
# kind: MutatingWebhookConfiguration
kind: ValidatingWebhookConfiguration
metadata:
  name: "pod-policy.example.com"
webhooks:
# list of webhooks
- name: "pod-policy.example.com"
  # where the location of the server is
  # when external, a url to the server
  clientConfig:
    # url: for-externally-hosted-servers
    service:
      # quotes are expected
      namespace: "webhook-namespace-name"
      # the SERVICE, not the pod
      name: "webhook-service"
    # Webhook server has to be over tls, and use certs
    caBundle: "certstringhere"
  # WHEN to call the server
  # here, on pod creation
  rules:
  - apiGroups: [""]
    apiVersions: ["v1"]
    operations: ["CREATE"]
    resources: ["pods"]
    scope: "Namespaced"

Things To Do

  • Create a namespace, webhook-space
  • Create a secret, with a provided crt + key && base64 encrypt the values prior to inserting into secret values
  • create a provided deployment of webhook pods
    • see below deployment file example
  • create a provided service for the webhook deployment
    • see below service file example
  • apply a webhook config
    • see below webhook config file example
  • deploy some carious pods that work differently with the webhook

Demo Config files

A Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webhook-server
  namespace: webhook-demo
  labels:
    app: webhook-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webhook-server
  template:
    metadata:
      labels:
        app: webhook-server
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1234
      containers:
      - name: server
        image: stackrox/admission-controller-webhook-demo:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8443
          name: webhook-api
        volumeMounts:
        - name: webhook-tls-certs
          mountPath: /run/secrets/tls
          readOnly: true
      volumes:
      - name: webhook-tls-certs
        secret:
          secretName: webhook-server-tls

A Service

apiVersion: v1
kind: Service
metadata:
  name: webhook-server
  namespace: webhook-demo
spec:
  selector:
    app: webhook-server
  ports:
    - port: 443
      targetPort: webhook-api

Webhook Config

piVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: demo-webhook
webhooks:
  - name: webhook-server.webhook-demo.svc
    sideEffects: None
    admissionReviewVersions: ["v1"]
    clientConfig:
      service:
        name: webhook-server
        namespace: webhook-demo
        path: "/mutate"
      caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURQekNDQWllZ0F3SUJBZ0lVSmZnS1RxcUExM2FOM3c0WkJQY29TT2phdWJnd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0x6RXRNQ3NHQTFVRUF3d2tRV1J0YVhOemFXOXVJRU52Ym5SeWIyeHNaWElnVjJWaWFHOXZheUJFWlcxdgpJRU5CTUI0WERUSXlNRGd4TWpFeE1USXpPVm9YRFRJeU1Ea3hNVEV4TVRJek9Wb3dMekV0TUNzR0ExVUVBd3drClFXUnRhWE56YVc5dUlFTnZiblJ5YjJ4c1pYSWdWMlZpYUc5dmF5QkVaVzF2SUVOQk1JSUJJakFOQmdrcWhraUcKOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXlIcjhkdVJJTml5M1ZQZlRIR1hFckNQWW9JUG5Pd2N6UjM2Vwpma0pINjIxczZlRGU0VUNMbW5jTUptbGRSclZGdmxjMitGMC9ZOU5nc2tqbDN0MVEwVXI4M3g5Q2hmUEZiTjNoCi9IaDdsTjNNQjZSYWdhL3ExS3AvclY2Zndvcm1MYTV3a0txSFFFUUVTUFMwRjZYSDJaU24xemo0RHcvWHc4SHUKcmpidDhPejdCaFBibG10YUYrWWxyR2N5UkxSTk83Mml6RVMrNDBBRklSZmlRYUNXNzd5N21HZ3NhUU1zU2RJaQp3U3BId3pza0JpZURTNFNqaGJ4NFpZOTVUOHNMTUJqek1oaVo3ZlhKQ0Q0RWdLZXV5VUxrL0xCQ1grTi8vcHFYCkJDNFJBWjlaY1JyOVFITC8wb2RIQlY1MVN0eTJVeG1EaERIS1BRSUkydzB4UE5KWEZ3SURBUUFCbzFNd1VUQWQKQmdOVkhRNEVGZ1FVaDBidjFPWk5PRWUzaS92ZGtsQVhscFNjV2V3d0h3WURWUjBqQkJnd0ZvQVVoMGJ2MU9aTgpPRWUzaS92ZGtsQVhscFNjV2V3d0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DCkFRRUFFb1FZSTJWSXd5bEFnSUZDK1NTcGxNeGZVK0RrT2hLSjk5NTltNTV6d2h6SjB1dHpRMExIZ0dhZndlUDIKeXUvQW16eW1ETWFHdjI3MHBoajhYNms0Zit2VTRWbzN5cnJDWFM3NjY0ajRiRVllREozMFJYNE1SVDZxZmgrMgpmK0tKVlg5VEk3MWJvL2tSVnh0RzQ2U3ZtVjdEMVM2cTZWNkMzTWt2SWpqVUhsYjNPekt6UERIMHBRTHdJVFViCmVZTzdZckZET3U1eWlhUTJyUVhGOWlJWlBzcXdFOHhzNHR1YURPRGFZNWlMbmpoSE96b3Q4OENNM25GOGlYSFgKL1JqdjRudTVLM0MvR0FaUmJBVWRyekRGOGR5YVc0bXQ2TjNuT3RNOFV2d056RFo1SlV4MkNuL3FPcXJwa1dURQp0QitOZkRlUWVsQVFhVFVmbjB1dG16T2pwZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    rules:
      - operations: [ "CREATE" ]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]
# A pod with a conflicting securityContext setting: it has to run as a non-root
# user, but we explicitly request a user id of 0 (root).
# Without the webhook, the pod could be created, but would be unable to launch
# due to an unenforceable security context leading to it being stuck in a
# 'CreateContainerConfigError' status. With the webhook, the creation of
# the pod is outright rejected.
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-conflict
  labels:
    app: pod-with-conflict
spec:
  restartPolicy: OnFailure
  securityContext:
    runAsNonRoot: true
    runAsUser: 0
  containers:
    - name: busybox
      image: busybox
      command: ["sh", "-c", "echo I am running as user $(id -u)"]
# A pod with a securityContext explicitly allowing it to run as root.
# The effect of deploying this with and without the webhook is the same. The
# explicit setting however prevents the webhook from applying more secure
# defaults.
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-override
  labels:
    app: pod-with-override
spec:
  restartPolicy: OnFailure
  securityContext:
    runAsNonRoot: false
  containers:
    - name: busybox
      image: busybox
      command: ["sh", "-c", "echo I am running as user $(id -u)"]


kk logs pod-with-override
I am running as user 0
Tags: