cert-manager Codebase Walkthrough
Welcome to the cert-manager codebase walkthrough. This guide helps experienced Go developers navigate the internals of cert-manager, a Kubernetes controller that automates X.509 certificate management. We’ll cover execution entry points, core abstractions, the certificate issuance lifecycle, and common patterns used throughout the codebase.
1. Where Execution Starts
cert-manager consists of four main binaries, each with its own entry point in the cmd/ directory:
Key Entry Points
-
cert-manager controller: The main controller binary that runs all certificate lifecycle controllers.
- File:
cmd/controller/main.go - Function:
main()calls intoapp.NewServerCommand()which configures the controller-manager, registers all controllers, and starts the leader election loop. - Initialization: Registers controller constructors via
controllerpkg.Knownmap, sets up informer factories for cert-manager CRDs and core Kubernetes resources, then starts the selected controllers.
- File:
-
cert-manager webhook: The admission webhook server for validation and defaulting.
- File:
cmd/webhook/main.go - Function:
main()sets up the webhook server with TLS, registers validation and mutation handlers for all cert-manager API types. - Initialization: Configures serving certificates (can bootstrap its own TLS via a self-signed CA), registers conversion webhooks for API version migration.
- File:
-
cert-manager cainjector: The CA bundle injection controller.
- File:
cmd/cainjector/main.go - Function:
main()starts a controller that watches annotated resources and injects CA certificates from referenced Secrets.
- File:
-
ACME solver: A temporary HTTP server for ACME HTTP-01 challenge validation.
- File:
cmd/acmesolver/main.go - Function:
main()starts a minimal HTTP server that responds to/.well-known/acme-challenge/<token>requests with the computed challenge key.
- File:
Startup Process Overview
Each binary follows the standard Kubernetes controller-manager pattern:
- Parse command-line flags and build a configuration object.
- Create a Kubernetes client and informer factories.
- Register controllers and start informer caches.
- Begin leader election (for the main controller) and start reconciliation loops.
The controller binary is the most complex, as it registers and manages dozens of individual controllers that each handle a specific aspect of certificate management.
2. Core Abstractions
cert-manager’s codebase is built around Kubernetes custom resources and the controller-runtime pattern. Understanding these abstractions is essential for navigating the code.
Key Types and Interfaces
- Certificate: Declared in
pkg/apis/certmanager/v1/types_certificate.go. The primary user-facing resource that defines desired certificate properties (DNS names, duration, issuer reference, private key algorithm). - CertificateRequest: Declared in
pkg/apis/certmanager/v1/types_certificate_request.go. Represents a one-shot CSR submission to an issuer. Immutable after creation. - Issuer / ClusterIssuer: Declared in
pkg/apis/certmanager/v1/types_issuer.go. Configuration resources that define how to connect to a certificate authority. - Order: Declared in
pkg/apis/acme/v1/types_order.go. ACME-specific resource tracking a certificate order with an ACME server. - Challenge: Declared in
pkg/apis/acme/v1/types_challenge.go. ACME-specific resource for individual domain validation challenges. - Issuer Interface: Defined in
pkg/issuer/issuer.go. The core interface that all issuer implementations must satisfy, with aSign(ctx, CertificateRequest) ([]byte, error)method.
Component Diagram
graph TD
subgraph API Types
CERT[Certificate]
CR[CertificateRequest]
ISS[Issuer / ClusterIssuer]
ORD[Order]
CH[Challenge]
end
subgraph Controllers
CT[certificates-trigger]
CK[certificates-key-manager]
CI[certificates-issuing]
CRR[certificates-readiness]
CRI[certificaterequests-issuer-*]
OC[orders controller]
CC[challenges controller]
IS[ingress-shim]
end
subgraph Issuer Implementations
ACME[ACME Issuer]
CA[CA Issuer]
SS[SelfSigned Issuer]
VLT[Vault Issuer]
VEN[Venafi Issuer]
end
CERT --> CT
CT --> CR
CK --> CERT
CI --> CR
CRR --> CERT
CR --> CRI
CRI --> ACME
CRI --> CA
CRI --> SS
CRI --> VLT
CRI --> VEN
ACME --> ORD
ORD --> OC
OC --> CH
CH --> CC
IS -->|Creates| CERT
ISS -->|Configures| CRI
This diagram shows how the API types flow through the controller hierarchy. Certificate resources trigger CertificateRequests, which are processed by issuer-specific controllers. For ACME, Orders and Challenges add additional layers.
3. Request/Operation Lifecycle
Let’s trace the most common operation: creating a Certificate and obtaining a signed certificate from Let’s Encrypt via ACME.
Step-by-Step Flow
-
User creates a Certificate resource:
apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: my-cert spec: secretName: my-cert-tls issuerRef: name: letsencrypt-prod kind: ClusterIssuer dnsNames: - example.com -
certificates-key-manager detects the new Certificate, generates an RSA/ECDSA private key, and stores it in the target Secret (
my-cert-tls).- File:
pkg/controller/certificates/keymanager/keymanager.go
- File:
-
certificates-trigger evaluates the Certificate and determines issuance is needed (no existing certificate or certificate approaching expiry). It creates a CertificateRequest containing the CSR signed with the private key.
- File:
pkg/controller/certificates/trigger/trigger.go
- File:
-
certificaterequests-issuer-acme controller picks up the CertificateRequest, reads the ClusterIssuer configuration, and creates an Order resource with the ACME server.
- File:
pkg/controller/certificaterequests/acme/acme.go
- File:
-
orders controller reconciles the Order, communicating with the ACME server to determine required challenges. It creates Challenge resources for each domain.
- File:
pkg/controller/acmeorders/controller.go
- File:
-
challenges controller solves each Challenge. For HTTP-01, it creates a temporary Pod (running
acmesolver), a Service, and an Ingress to serve the challenge token. For DNS-01, it creates a TXT record via the configured DNS provider.- File:
pkg/controller/acmechallenges/controller.go
- File:
-
ACME server validates the challenge by checking the HTTP endpoint or DNS record.
-
orders controller detects all challenges are valid, finalizes the Order with the ACME server, and receives the signed certificate chain.
-
certificaterequests-issuer-acme updates the CertificateRequest with the signed certificate.
-
certificates-issuing watches for completed CertificateRequests, updates the target Secret with the signed certificate and CA chain, and marks the Certificate as Ready.
- File:
pkg/controller/certificates/issuing/issuing.go
- File:
-
certificates-readiness continuously monitors the certificate in the Secret and updates the Certificate’s status conditions, including the
NotAftertime and renewal time.- File:
pkg/controller/certificates/readiness/readiness.go
- File:
4. Reading Order
For a developer new to the cert-manager codebase, here is a recommended path through the code:
-
Start with API types:
- File:
pkg/apis/certmanager/v1/types_certificate.go - Why: Understand the Certificate spec and status fields. This is the primary resource users interact with.
- File:
-
Certificate trigger controller:
- File:
pkg/controller/certificates/trigger/trigger.go - Why: See how cert-manager decides when to issue or renew a certificate. This is the entry point for the issuance workflow.
- File:
-
CertificateRequest processing:
- File:
pkg/controller/certificaterequests/acme/acme.go - Why: Understand how CertificateRequests are handled for the most common issuer type (ACME).
- File:
-
ACME Order and Challenge flow:
- Files:
pkg/controller/acmeorders/controller.go,pkg/controller/acmechallenges/controller.go - Why: Trace the ACME-specific flow from order creation through challenge solving.
- Files:
-
Certificate issuing controller:
- File:
pkg/controller/certificates/issuing/issuing.go - Why: See how the signed certificate is stored in the Kubernetes Secret and the Certificate status is finalized.
- File:
-
Ingress shim:
- File:
pkg/controller/ingress-shim/controller.go - Why: Understand how cert-manager integrates with Kubernetes Ingress resources for automatic certificate provisioning.
- File:
-
Issuer implementations:
- Directory:
pkg/issuer/ - Why: Explore the different CA backends (ACME, CA, SelfSigned, Vault, Venafi) and how they implement the issuer interface.
- Directory:
-
Webhook validation:
- Directory:
internal/webhook/ - Why: Understand how cert-manager validates and defaults resources before they are persisted.
- Directory:
5. Common Patterns
cert-manager employs several recurring design patterns that are important to recognize:
-
Controller Registration: Controllers are registered via a
Knownmap in the controller package. Each controller provides a constructor function and metadata (informer requirements, feature gates). This allows controllers to be selectively enabled/disabled via CLI flags. -
Status Conditions: cert-manager uses Kubernetes-style status conditions extensively. The
Readycondition on Certificate and CertificateRequest resources is the primary signal for completion. Controllers set, update, and watch conditions to coordinate lifecycle phases. -
Annotation-Based Coordination: Controllers communicate through annotations on shared resources. For example, the
cert-manager.io/certificate-revisionannotation tracks the issuance revision, allowing the issuing controller to detect stale CertificateRequests. -
Informer-Based Watches: All controllers use SharedInformerFactories to watch resources efficiently. The controller binary starts informer factories for both cert-manager CRDs (
cmclient) and core Kubernetes types (kubeclient), sharing caches across controllers. -
External Issuer Pattern: cert-manager defines a clear interface for external issuers. Third-party projects implement their own CertificateRequest controllers that watch for CertificateRequests referencing their issuer type, sign them, and update the status. This pattern enables projects like
google-cas-issuerandaws-privateca-issuerto exist independently. -
Feature Gates: cert-manager uses feature gates (
pkg/feature/) to control experimental or optional functionality. Controllers check feature gates at startup to decide whether to register themselves. -
ACME DNS Provider Plugins: DNS-01 challenge solvers are implemented via a webhook interface (
pkg/acme/webhook/), allowing third-party DNS providers to be added without modifying the core codebase.
Design Trade-offs
- Kubernetes-Native vs. Performance: cert-manager stores all state in the Kubernetes API (etcd), which provides durability and observability but introduces API server load for high certificate volume deployments.
- CRD Granularity: Using separate CRDs for each lifecycle phase (Certificate, CertificateRequest, Order, Challenge) provides visibility but means a single certificate issuance can create 4+ resources.
- Leader Election: Only one controller pod is active at a time via leader election, trading horizontal scalability for simplicity in state management.
By recognizing these patterns and trade-offs, you will be well-equipped to navigate, debug, and contribute to the cert-manager codebase effectively.