How-to guide / Package task guides
Configure cert-manager issuers
Kix separates the cert-manager controller from issuer configuration. Install
cert-manager, then add one cluster-issuer instance for each ClusterIssuer.
The ClusterIssuer takes the instance’s name, and config.spec is its spec
in cert-manager’s API.
Put every cluster-issuer instance in cert-manager’s namespace. cert-manager
reads a ClusterIssuer’s Secrets only from its own namespace, so the issuer’s
credentials must be there, and a Kix Secret serves only the namespace it is
declared in. Instance names are unique within a namespace, so two issuers
cannot render the same ClusterIssuer name. An instance in another namespace
fails the build and names the namespace to move it to.
Configure an ACME issuer
Section titled “Configure an ACME issuer”For public certificates, give the ACME server, the account email, and a solver. This example uses Cloudflare DNS-01:
109 instances.cert-manager.cert-manager = {110 package = packages.cert-manager;111 };112
113 # A Secret created outside Kix that holds the Cloudflare API token114 # under the key `api-token`.115 instances.cert-manager.cloudflare-api-token = {116 package = packages.secret-ref;117 config.keys = [ "api-token" ];118 };119
120 instances.cert-manager.letsencrypt = {121 package = packages.cluster-issuer;122 deps.issuerCredentials = ref.cert-manager.cloudflare-api-token;123 config.spec.acme = {124 server = "https://acme-v02.api.letsencrypt.org/directory";125126 solvers = [127 {128 dns01.cloudflare.apiTokenSecretRef.key = "api-token";129 selector.dnsZones = [ "example.com" ];130 }131 ];132 };133 };The cloudflare-api-token instance declares a Secret created outside Kix that
holds the token under the key api-token. Use
a Kix-managed Secret,
a SOPS-backed Secret,
or an externally managed Secret,
in the cert-manager namespace.
deps.issuerCredentials wires that Secret to the issuer. A Kix Secret
dependency is named for what it holds; issuerCredentials holds every key
this issuer’s Secret key selectors name. Leave name out of each selector,
such as apiTokenSecretRef, and set only key. Kix fills in the Secret’s
name and fails the build when the Secret does not declare that key, so the
ClusterIssuer cannot reach the cluster pointing at a Secret or key that is
not there. To name a Secret that Kix should not check, write
name = kix.untrackedRef "<secret>".
An issuer that needs several credentials, such as an ACME external account
binding and a DNS token, keeps them as several keys of its one
issuerCredentials Secret; each selector picks its own key.
Kix sets acme.privateKeySecretRef.name to <instance>-acme-account-key
unless the spec sets it. cert-manager creates that Secret when it registers
the ACME account. Restrict selector.dnsZones to the zones the token may
update.
The spec is checked against the ClusterIssuer CRD that the cert-manager
package vendors. A field the CRD does not define, a missing required field
such as acme.server, a value of the wrong type (such as solvers written
without list brackets), a spec with no issuer type or two, and a solver that
sets both http01 and dns01 or two DNS providers each fail the build with
the spec path and the accepted values. A field set to null is left out.
external-dns reads its Cloudflare token from a Secret in its own namespace. Give that Secret the same key, so one source feeds both.
Add a staging issuer
Section titled “Add a staging issuer”Every cluster-issuer instance answers the clusterIssuer dependency that
TLS-capable packages read, so a second issuer in the same namespace makes that
dependency ambiguous. Give the staging issuer an empty aliases list:
137 instances.cert-manager.letsencrypt-staging = {138 package = packages.cluster-issuer;139 # An empty list replaces the default alias `clusterIssuer`, so140 # only the production issuer answers it.141 aliases = [ ];142 deps.issuerCredentials = ref.cert-manager.cloudflare-api-token;143 config.spec.acme = {144 server = "https://acme-staging-v02.api.letsencrypt.org/directory";145146 solvers = [147 {148 dns01.cloudflare.apiTokenSecretRef.key = "api-token";149 selector.dnsZones = [ "example.com" ];150 }151 ];152 };153 };Instance aliases replace the package’s default alias, so the staging issuer
answers no alias. A package that needs it names it with
deps.clusterIssuer = ref.cert-manager.letsencrypt-staging.
Configure a self-signed issuer
Section titled “Configure a self-signed issuer”For a local cluster, use a self-signed issuer:
53 instances.cert-manager.selfsigned = {54 package = packages.cluster-issuer;55 config.spec.selfSigned = { };56 };A self-signed issuer tests certificate creation and TLS wiring without an ACME account or DNS provider. Clients will not trust its certificates unless you install the generated trust material, so do not use this configuration for a public service.
Use cert-manager installed outside Kix
Section titled “Use cert-manager installed outside Kix”When cert-manager and its ClusterIssuer come from Helm or a platform add-on,
Kix does not own them and the cluster-issuer package cannot be used: it
needs the cert-manager package’s CRD handle, schema, and namespace. Declare
the existing issuer with kix.mkImport and the clusterIssuer alias instead:
157 instances.external-tls.cluster-issuer = {158 package = kix.mkImport {159 out.withCert =160 args:161 args162 // {163 annotations = (args.annotations or { }) // {164 "cert-manager.io/cluster-issuer" = "letsencrypt";165 };166 };167 };168 aliases = [ "clusterIssuer" ];169 };Kix then checks nothing about the issuer beyond the name the import gives it.
Because cluster-issuer sets meta.scope = "cluster" and dependency
resolution prefers a managed instance over an import, a cluster-issuer
instance that answers clusterIssuer anywhere in the cluster takes precedence
over the import, so use one or the other. The example cluster has both, which
is why its consumer of the import sets deps.clusterIssuer explicitly.
HTTP-01 solvers
Section titled “HTTP-01 solvers”The spec accepts an http01 solver. cert-manager creates the HTTP-01 solver
pods at runtime in each Certificate’s namespace, and they listen on TCP 8089.
When the cluster enforces network policy, kix.expose admits the ingress
controller to those pods for each Ingress it builds with TLS from this issuer.
A Certificate you create another way needs that traffic admitted too, for
example with a platform intent as in
Declare platform intents for non-Kix-managed pods.
References Kix does not track
Section titled “References Kix does not track”Kix fills and checks only Secret key selectors and the ACME account key.
ca.secretName, venafi.*.credentialsRef.name,
vault.auth.kubernetes.serviceAccountRef.name, the HTTP-01 solver’s Ingress,
IngressClass, and Gateway references, and anything inside a webhook solver’s
config pass through as written. Kix does not check that those
objects exist.
Check and deploy
Section titled “Check and deploy”Evaluate the cluster before applying it:
❱ kix check doc-how-tos Deploy cert-manager, its CRDs, and the ClusterIssuers:
❱ kix deploy doc-how-tos A deploy waits for each ClusterIssuer, and each Certificate built from
certManager.out.crds.Certificate, to report Ready. An ACME ClusterIssuer
reports Ready once cert-manager has registered its account. Kix does not
wait for the Certificates that cert-manager creates from Ingress annotations.
ACME registration and DNS-01 issuance of the Certificates Kix waits for can
take longer than the default readiness timeout of 180 seconds; raise it with
--timeout on clusters that issue through ACME:
❱ kix deploy doc-how-tos --timeout 15m After deployment, inspect issuer readiness:
❱ kubectl get clusterissuers❱ kubectl describe clusterissuer letsencrypt For ACME failures, check the credential key, the zone restriction, DNS delegation, and the cert-manager controller logs.
See Add TLS through a cluster issuer to use the issuer from an application package.