Post

How to Integrate Authorization Webhook with API Server

How to Integrate Authorization Webhook with API Server

Kubernetes clusters have built-in authorization mechanisms like RBAC. These work for many cases, but sometimes you need more control. You want to implement your own custom authorization rules.

This is where authorization webhooks come in. You write your own authorization service, and Kubernetes API Server calls it to make decisions. This gives you full control over who can access what in your cluster.

But after you write the webhook, you need to configure API Server to use it. That’s what this post is about.

I’ll show you how to configure Kubernetes API Server to use your authorization webhook.

What is Authorization Webhook?

An authorization webhook is an HTTP service that API Server calls before making authorization decisions. When a user tries to access a resource, API Server sends a request to the webhook asking: “Can this user do this action?” The webhook returns Allow or Deny.

This gives you full control over authorization logic. You can implement custom rules that RBAC alone cannot handle.

Implementation

We mounted webhook configuration files directly to master nodes.

Step 1: Create Directory and Files on Master Nodes

First, we create the directory on each master node:

1
2
3
4
5
6
7
8
# SSH into master node
ssh master-node-01

# Create directory for webhook files
sudo mkdir -p /etc/kubernetes/webhook

# Verify directory exists
ls -la /etc/kubernetes/webhook

Now we create two files in this directory.

Create webhook-config.yaml

Create the configuration file:

1
2
3
sudo vi /etc/kubernetes/webhook/webhook-config.yaml
# or
sudo nano /etc/kubernetes/webhook/webhook-config.yaml

Then write this content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Config
clusters:
- cluster:
    server: https://kube-authz-webhook.auth-system/authorize
    certificate-authority: /etc/kubernetes/webhook/webhook-ca.crt
  name: kube-authz-webhook
contexts:
- context:
    cluster: kube-authz-webhook
    user: webhook-client
  name: webhook-context
current-context: webhook-context
users:
- name: webhook-client
  user: {}

Save the file.

Place webhook-ca.crt

Copy the CA certificate file to the directory:

1
2
3
4
5
# Copy your CA certificate file to this location
sudo cp /path/to/your/webhook-ca.crt /etc/kubernetes/webhook/webhook-ca.crt

# Verify files are in place
ls -la /etc/kubernetes/webhook/

You should see both files:

1
2
-rw-r--r--  1 root root    webhook-ca.crt
-rw-r--r--  1 root root    webhook-config.yaml

Step 2: Edit API Server Manifest

API Server runs as a static pod. We need to edit its manifest file on each master node.

When you edit files in /etc/kubernetes/manifests/, kubelet automatically restarts the pod. API Server will restart in few seconds. Make sure you test on non-production cluster first!

Find API Server Manifest

On most Kubernetes clusters, API Server manifests are in /etc/kubernetes/manifests/ directory:

1
2
3
# Check manifest file location
ls -la /etc/kubernetes/manifests/
# You should see: kube-apiserver.yaml

Edit the Manifest File

1
2
3
4
# Open the file in editor
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
# or
sudo nano /etc/kubernetes/manifests/kube-apiserver.yaml

Add Volume Mount

Find the volumeMounts section in the manifest. Add our webhook volume:

1
2
3
4
5
volumeMounts:
  - name: webhook-volume
    mountPath: /etc/kubernetes/webhook
    readOnly: true
  # ... existing volumeMounts

Add Volume Definition

Find the volumes section. Add our webhook volume:

1
2
3
4
5
6
volumes:
  - name: webhook-volume
    hostPath:
      path: /etc/kubernetes/webhook
      type: Directory
  # ... existing volumes

Add Authorization Flags

Find the spec.containers[0].command section. Add these flags:

1
2
3
4
5
6
7
8
9
10
spec:
  containers:
  - name: kube-apiserver
    command:
    - kube-apiserver
    # ... existing flags ...
    - --authorization-mode=Node,RBAC,Webhook
    - --authorization-webhook-config-file=/etc/kubernetes/webhook/webhook-config.yaml
    - --authorization-webhook-cache-authorized-ttl=120s
    - --authorization-webhook-cache-unauthorized-ttl=30s

Let me explain these flags:

  • --authorization-mode=Node,RBAC,Webhook - We use three authorization modes. API Server checks in this order: Node, RBAC, then Webhook. If Node or RBAC makes a decision, webhook is not called.
  • --authorization-webhook-config-file - Path to our webhook configuration file
  • --authorization-webhook-cache-authorized-ttl=120s - Cache allowed decisions for 120 seconds
  • --authorization-webhook-cache-unauthorized-ttl=30s - Cache denied decisions for 30 seconds

Cache TTL values help reduce load on the webhook service. Use shorter TTL for production if you need faster policy updates.

Save the file. Kubelet will automatically restart API Server with new config.

Verify Changes

Wait a few seconds for API Server to restart:

1
2
3
4
5
# Check if API Server pod is running
kubectl get pods -n kube-system | grep apiserver

# Check if volume is mounted
kubectl describe pod kube-apiserver-master-0 -n kube-system | grep webhook

Step 3: Repeat on All Master Nodes

Important: You need to do Steps 1-3 on ALL master nodes in your cluster. API Server runs on each master, and all need the same configuration.

1
2
3
4
5
6
7
8
# List all master nodes
kubectl get nodes -l node-role.kubernetes.io/master

# SSH and apply changes to each master
for master in master-01 master-02 master-03; do
  ssh $master
  # Repeat the steps above
done

How It Works

When a user makes a request to API Server:

  1. API Server authenticates the user first
  2. Then it checks authorization in this order:
    • Node authorization
    • RBAC authorization
    • Webhook authorization (only if Node and RBAC don’t make a decision)
  3. If webhook is called, it returns Allow or Deny
  4. API Server caches the result for the TTL duration
  5. API Server makes final decision

This chain allows you to use webhook for custom logic while keeping standard RBAC for common cases.

Webhook Service

Our webhook service runs as a Kubernetes Deployment. API Server calls it via Service URL.

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
  name: kube-authz-webhook
  namespace: auth-system
spec:
  selector:
    app: kube-authz-webhook
  ports:
  - port: 443
    targetPort: 8443
    protocol: TCP

Request and Response Format

When API Server calls our webhook, it sends this request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  "kind": "SubjectAccessReview",
  "apiVersion": "authorization.k8s.io/v1",
  "spec": {
    "resourceAttributes": {
      "namespace": "default",
      "verb": "get",
      "group": "",
      "resource": "pods"
    },
    "user": "system:serviceaccount:default:myuser",
    "group": ["system:authenticated"]
  },
  "status": {
    "allowed": false
  }
}

And our webhook returns:

1
2
3
4
5
6
7
8
{
  "apiVersion": "authorization.k8s.io/v1",
  "kind": "SubjectAccessReview",
  "status": {
    "allowed": true,
    "reason": "User has permission based on fine-grained rules"
  }
}

Testing the Webhook

To test if our webhook works:

1
2
3
4
5
6
7
8
# Test if a user can get pods
kubectl auth can-i get pods --as=system:serviceaccount:default:myuser

# Check webhook logs
kubectl logs -n auth-system deployment/kube-authz-webhook

# Check API Server logs to see webhook calls
kubectl logs -n kube-system kube-apiserver-master-0

You should see logs showing webhook requests and responses.

Troubleshooting

If something goes wrong:

1
2
3
4
5
6
7
8
9
10
11
# Check API Server logs for errors
kubectl logs -n kube-system kube-apiserver-master-0 | grep -i webhook

# Verify manifest file is correct
sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml

# Check if files exist on master node
ls -la /etc/kubernetes/webhook/

# Restore backup if needed
sudo cp /etc/kubernetes/manifests/kube-apiserver.yaml.bak /etc/kubernetes/manifests/kube-apiserver.yaml

Common issue ❯ API Server fails to start if webhook config file has wrong path or missing CA certificate. Always verify files exist before adding flags.

Resources