Kubernetes
Python client
CSR approval
Kubernetes automation
Kubernetes security

Approve a CSR in Kuberentes Using the Python client

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Approving Kubernetes CertificateSigningRequest objects from Python is straightforward, but it should never be treated as a blind automation step. A single approval can grant a principal valid cluster credentials, so your code must enforce policy before writing approval status. The safest pattern is read, validate, approve, and audit every decision.

Understand the CSR Lifecycle in Kubernetes

A CSR usually moves through four stages:

  1. request creation by a user, node, or controller
  2. review by an approval process
  3. approval or denial status update
  4. certificate issuance by a signer

Your Python script controls stage two and stage three. It does not mint the certificate directly, but it can authorize issuance. That is why policy checks must happen before status updates.

A practical review should inspect:

  • 'spec.signerName'
  • 'spec.usages'
  • requester identity fields such as username and groups
  • existing status.conditions

If you skip these checks and approve all pending requests, you are effectively bypassing cluster authentication policy.

Configure the Python Client

Install the Kubernetes client package and load credentials from your environment.

bash
pip install kubernetes
python
1from kubernetes import client, config
2
3config.load_kube_config()
4api = client.CertificatesV1Api()

Inside a cluster workload, replace load_kube_config with load_incluster_config and bind a tightly scoped service account.

Read and Evaluate a CSR Before Approval

Start by reading the target object and skipping already-reviewed requests.

python
1from kubernetes import client
2
3
4def read_csr(api: client.CertificatesV1Api, name: str):
5    return api.read_certificate_signing_request(name)
6
7
8def is_terminal(csr) -> bool:
9    for cond in csr.status.conditions or []:
10        if cond.type in ("Approved", "Denied"):
11            return True
12    return False

Now apply explicit policy. Keep allow-lists in code or configuration so reviews are deterministic.

python
1ALLOWED_SIGNERS = {
2    "kubernetes.io/kube-apiserver-client",
3    "kubernetes.io/kubelet-serving",
4}
5
6ALLOWED_USAGES = {
7    "digital signature",
8    "key encipherment",
9    "client auth",
10}
11
12
13def policy_allows(csr) -> bool:
14    signer = csr.spec.signer_name
15    usages = set(csr.spec.usages or [])
16    groups = set(csr.spec.groups or [])
17
18    if signer not in ALLOWED_SIGNERS:
19        return False
20    if not usages.issubset(ALLOWED_USAGES):
21        return False
22    if "system:authenticated" not in groups:
23        return False
24
25    return True

This policy is intentionally simple. Real implementations typically add namespace, subject, or naming constraints.

Approve Using the Approval Subresource

Use the dedicated approval endpoint rather than a generic object update.

python
1from kubernetes import client
2
3
4def approve_csr(api: client.CertificatesV1Api, name: str) -> None:
5    body = {
6        "status": {
7            "conditions": [
8                {
9                    "type": "Approved",
10                    "status": "True",
11                    "reason": "PolicyApproved",
12                    "message": "Approved by automated policy controller"
13                }
14            ]
15        }
16    }
17
18    api.replace_certificate_signing_request_approval(name=name, body=body)

A small end-to-end flow:

python
1from kubernetes import client, config
2
3
4def process(name: str):
5    config.load_kube_config()
6    api = client.CertificatesV1Api()
7
8    csr = read_csr(api, name)
9
10    if is_terminal(csr):
11        print(f"skip terminal: {name}")
12        return
13
14    if not policy_allows(csr):
15        raise PermissionError(f"policy rejected csr: {name}")
16
17    approve_csr(api, name)
18    print(f"approved: {name}")
19
20
21if __name__ == "__main__":
22    process("example-csr")

This structure is easy to test and supports idempotent retries.

Add Audit and Operational Safeguards

Security-sensitive automation needs observability. At minimum, log:

  • CSR name
  • requester username
  • signer name
  • allow or deny result
  • policy reason

Add metrics for approvals, denials, and failures, then alert when unknown signer names appear. In incident review, these records explain why a credential was authorized.

RBAC should also be minimal. Your automation usually needs get, list, and watch on CSRs plus update or patch on certificatesigningrequests/approval. Avoid cluster-admin for this workflow.

Example role:

yaml
1apiVersion: rbac.authorization.k8s.io/v1
2kind: ClusterRole
3metadata:
4  name: csr-approver
5rules:
6  - apiGroups: ["certificates.k8s.io"]
7    resources: ["certificatesigningrequests"]
8    verbs: ["get", "list", "watch"]
9  - apiGroups: ["certificates.k8s.io"]
10    resources: ["certificatesigningrequests/approval"]
11    verbs: ["update", "patch"]

Pair this role with one dedicated service account and no extra permissions.

Common Pitfalls

Approving every pending request is the biggest mistake. It turns an approval system into automatic credential issuance.

Another frequent issue is mutating the wrong resource path. Use the approval subresource so status changes follow the Kubernetes review model.

Teams also forget terminal-state checks. Repeatedly writing approval conditions creates noisy audit history and unnecessary API traffic.

Missing audit logs is another problem. If an incident occurs, you need evidence of what policy decision was made and why.

Finally, over-privileged RBAC for automation expands blast radius if credentials are compromised.

Summary

  • CSR approval automation should enforce explicit policy, not approve by default.
  • Validate signer, usages, and requester metadata before any status update.
  • Use replace_certificate_signing_request_approval for approval writes.
  • Make the workflow idempotent by skipping already terminal requests.
  • Add strong audit logging and least-privilege RBAC for production safety.

Course illustration
Course illustration

All Rights Reserved.