Manages TLS certificates in Kubernetes clusters using custom resources. Currently, it supports certificate authorities using the Automatic Certificate Management Environment (ACME) like Let's Encrypt.
In a multi-cluster environment like Gardener, using existing open source projects for certificate management like cert-manager becomes cumbersome. With this project the separation of concerns between multiple clusters is realized more easily. The cert-controller-manager runs in a secured cluster where the issuer secrets are stored. At the same time it watches an untrusted source cluster and can provide certificates for it. The cert-controller-manager relies on DNS challenges for validating the domain names of the certificates. For this purpose it creates DNSEntry custom resources (in a possible separate dns cluster) to be handled by the compagnion dns-controller-manager from external-dns-management.
Before you can obtain certificates from a certificate authority (CA), you need to set up an issuer.
The issuer is specified in the default
cluster, while the certificates are specified in the source
cluster.
The issuer custom resource contains the configuration and registration data for your account at the CA.
Currently, the cert-controller-manager
only supports certificate authorities via the
Automatic Certificate Management Environment (ACME) protocol like Let's Encrypt.
Two modes are supported:
- auto registration
- using an existing account
Auto registration is mainly used for development and test environments. You only need to provide the server URL and an email address. The registration process is done automatically for you by creating a private key and performing the registration at the CA. Optionally you can provide the target secret with the privateKeySecretRef section.
For example see examples/20-issuer-staging.yaml:
apiVersion: cert.gardener.cloud/v1alpha1
kind: Issuer
metadata:
name: issuer-staging
namespace: default
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: some.user@mydomain.com
autoRegistration: true
# with 'autoRegistration: true' a new account will be created if the secretRef is not existing
privateKeySecretRef:
name: issuer-staging-secret
namespace: default
If you already have an existing account at the certificate authority, you need to specify email address and reference the private key from a secret.
apiVersion: v1
kind: Secret
metadata:
name: my-issuer-secret
namespace: default
type: Opaque
data:
privateKey: LS0tLS1...
apiVersion: cert.gardener.cloud/v1alpha1
kind: Issuer
metadata:
name: my-issuer
namespace: default
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: my.account@mydomain.com
privateKeySecretRef:
name: my-issuer-secret
namespace: default
In both cases, the state of an issuer resource can be checked on the default
cluster with
▶ kubectl get issuer
NAME SERVER EMAIL STATUS TYPE AGE
issuer-staging https://acme-staging-v02.api.letsencrypt.org/directory some.user@mydomain.com Ready acme 8s
To obtain a certificate for a domain, you specify a certificate custom resource on the source
cluster.
You can specify the issuer explicitly by reference. If there is no issuer reference, the default issuer is
used (provided as command line option). You must either specify the commonName
and further optional dnsNames
or
you can also start with a certificate signing request (CSR).
For domain validation, the cert-controller-manager
only supports DNS challenges. For this purpose it relies
on the dns-controller-manager
from the external-dns-management
project.
If any domain name (commonName
or any item from dnsNames
) needs to be validated, it create a custom resource
DNSEntry
in the dns
cluster.
When the certificate authority sees the temporary DNS record, the certificate is stored in a secret finally.
The name of the secret can be specified explicitly with secretName
and will be stored in the same namespace as the
certificate on the source
cluster.
The certificate is checked for renewal periodically. The renewal is performed automatically and the secret is updated. Default values for periodical check is daily, the certificate is renewed if its validity expires within 60 days.
For example see examples/30-cert-simple.yaml:
apiVersion: cert.gardener.cloud/v1alpha1
kind: Certificate
metadata:
name: cert-simple
namespace: default
spec:
commonName: cert1.mydomain.com
dnsNames:
- cert1-foo.mydomain.com
- cert1-bar.mydomain.com
# if issuer is not specified, the default issuer is used
issuerRef:
name: issuer-staging
You can provide a complete CSR in PEM format (and encoded as Base64).
For example see examples/30-cert-csr.yaml:
apiVersion: cert.gardener.cloud/v1alpha1
kind: Certificate
metadata:
name: cert-csr
namespace: default
spec:
csr: LS0tLS1CRUd...
issuerRef:
name: issuer-staging
Add the annotation cert.gardener.cloud/purpose: managed
to the Ingress resource.
The cert-controller-manager
will then automatically request a certificate for all domains given by the hosts in the
tls
section of the Ingress spec.
For compatibility with the Gardener Cert-Broker, you can
alternatively use the label garden.sapcloud.io/purpose: managed-cert
for the same outcome.
See also examples/40-ingress-echoheaders.yaml:
-
Create the Ingress Resource (optional)
In order to request a certificate for a domain managed by
cert-controller-manager
an Ingress is required. In case you don’t already have one, take the following as an example:apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: vuejs-ingress spec: tls: # Gardener managed default domain. # Must not exceed 64 characters. - hosts: - test.ingress.<GARDENER-CLUSTER>.<GARDENER-PROJECT>.shoot.example.com # Certificate and private key reside in this secret. secretName: testsecret-tls rules: - host: test.ingress.<GARDENER-CLUSTER>.<GARDENER-PROJECT>.shoot.example.com http: paths: - backend: serviceName: vuejs-svc servicePort: 8080
-
Annotate the Ingress Resource
The annotation
cert.gardener.cloud/purpose: managed
instructscert-controller-manager
to handle certificate issuance for the domains found in labeled Ingress.apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: tls-example-ingress annotations: # Let Gardener manage certificates for this Ingress. cert.gardener.cloud/purpose: managed ...
-
Check status
A
certificate
custom resource is created in the same namespace of thesource
cluster. You can either check the status of this certificate resource withkubectl get cert
or you can check the events for the ingress withkubectl get events
The certificate is stored in the secret as specified in the Ingress resource.
If you have a service of type LoadBalancer
, you can use the annotation cert.gardener.cloud/secretname
together
with the annotation dns.gardener.cloud/dnsnames
from the dns-controller-manager
to trigger automatic creation of a certificate.
apiVersion: v1
kind: Service
metadata:
annotations:
cert.gardener.cloud/secretname: test-service-secret
dns.gardener.cloud/dnsnames: test-service.demo.mydomain.com
dns.gardener.cloud/ttl: "600"
name: test-service
namespace: default
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
type: LoadBalancer
-
Run dns-controller-manager with:
./dns-controller-manager --controllers=azure-dns --identifier=myOwnerId --disable-namespace-restriction
-
Ensure provider and its secret, e.g.
kubectl apply -f azure-secret.yaml kubectl apply -f azure-provider.yaml
-
check with
▶ kubectl get dnspr NAME TYPE STATUS AGE azure-playground azure-dns Ready 28m
-
-
Create test namespace
kubectl create ns test
-
Run cert-controller-manager
./cert-controller-manager
-
Register user
some.user@mydomain.com
at let's encryptkubectl apply -f examples/20-issuer-staging.yaml
-
check with
▶ kubectl get issuer NAME SERVER EMAIL STATUS TYPE AGE issuer-staging https://acme-staging-v02.api.letsencrypt.org/directory some.user@mydomain.com Ready acme 8s
-
-
Request a certificate for
cert1.martin.test6227.ml
kubectl apply -f examples/30-cert-simple.yaml
If this certificate has been already registered for the same issuer before, it will be returned immediately from the ACME server. Otherwise a DNS challenge is started using a temporary DNSEntry to be set by
dns-controller-manager
-
check with
▶ kubectl get cert -o wide NAME COMMON NAME ISSUER STATUS EXPIRATION_DATE DNS_NAMES AGE cert-simple cert1.mydomain.com issuer-staging Ready 2019-11-10T09:48:17Z [cert1.my-domain.com] 34s
-
The cert-controller-manager communicated with up to four different clusters:
- default
used for managing issuers and lease management.
The path to the kubeconfig is specified with command line option
--kubeconfig
. - source
used for watching resources ingresses, services and certificates
The path to the kubeconfig is specified with command line option
--source
. If option is omitted, the default cluster is used for source. - dns
used to write temporary DNSEntries for DNS challenges
The path to the kubeconfig is specified with command line option
--dns
.
If option is omitted, the default cluster is used for dns. - target
used for storing generated certificates
The path to the kubeconfig is specified with command line option
--target
.
If option is omitted, the source cluster is also used for target.
The complete list of options is:
Usage:
cert-controller-manager [flags]
Flags:
--cascade-delete default for all controller "cascade-delete" options
--cert-class string default for all controller "cert-class" options
--cert-target-class string default for all controller "cert-target-class" options
-c, --controllers string comma separated list of controllers to start (<name>,source,target,all) (default "all")
--cpuprofile string set file for cpu profiling
--default-issuer string default for all controller "default-issuer" options
--default-issuer-domain-ranges string default for all controller "default-issuer-domain-ranges" options
--disable-namespace-restriction disable access restriction for namespace local access only
--dns string cluster for writing challenge DNS entries
--dns-class string default for all controller "dns-class" options
--dns-namespace string default for all controller "dns-namespace" options
--dns-owner-id string default for all controller "dns-owner-id" options
--dns.disable-deploy-crds disable deployment of required crds for cluster dns
--dns.id string id for cluster dns
--grace-period duration inactivity grace period for detecting end of cleanup for shutdown
-h, --help help for cert-controller-manager
--ingress-cert.cert-class string Identifier used to differentiate responsible controllers for entries
--ingress-cert.cert-target-class string Identifier used to differentiate responsible dns controllers for target entries
--ingress-cert.default.pool.resync-period duration Period for resynchronization of pool default of controller ingress-cert (default: 2m0s)
--ingress-cert.default.pool.size int Worker pool size for pool default of controller ingress-cert (default: 2)
--ingress-cert.target-name-prefix string name prefix in target namespace for cross cluster generation
--ingress-cert.target-namespace string target namespace for cross cluster generation
--ingress-cert.targets.pool.size int Worker pool size for pool targets of controller ingress-cert (default: 2)
--issuer-namespace string default for all controller "issuer-namespace" options
--issuer.cascade-delete If true, certificate secrets are deleted if dependent resources (certificate, ingress) are deleted
--issuer.cert-class string Identifier used to differentiate responsible controllers for entries
--issuer.default-issuer string name of default issuer (from default cluster)
--issuer.default-issuer-domain-ranges string domain range restrictions when using default issuer separated by comma
--issuer.default.pool.resync-period duration Period for resynchronization of pool default of controller issuer (default: 24h0m0s)
--issuer.default.pool.size int Worker pool size for pool default of controller issuer (default: 2)
--issuer.dns-class string class for creating challenge DNSEntries (in DNS cluster)
--issuer.dns-namespace string namespace for creating challenge DNSEntries (in DNS cluster)
--issuer.dns-owner-id string ownerId for creating challenge DNSEntries
--issuer.issuer-namespace string namespace to lookup issuers on default cluster
--issuer.issuers.pool.size int Worker pool size for pool issuers of controller issuer (default: 1)
--issuer.precheck-additional-wait duration additional wait time after DNS propagation check
--issuer.precheck-nameservers string DNS nameservers used for checking DNS propagation. If explicity set empty, it is tried to read them from /etc/resolv.conf
--issuer.renewal-window duration certificate is renewed if its validity period is shorter
--issuer.secrets.pool.size int Worker pool size for pool secrets of controller issuer (default: 1)
--kubeconfig string default cluster access
--kubeconfig.disable-deploy-crds disable deployment of required crds for cluster default
--kubeconfig.id string id for cluster default
-D, --log-level string logrus log level
--name string name used for controller manager
--namespace string namespace for lease
-n, --namespace-local-access-only enable access restriction for namespace local access only (deprecated)
--omit-lease omit lease for development
--plugin-dir string directory containing go plugins
--pool.resync-period duration default for all controller "pool.resync-period" options
--pool.size int default for all controller "pool.size" options
--precheck-additional-wait duration default for all controller "precheck-additional-wait" options
--precheck-nameservers string default for all controller "precheck-nameservers" options
--renewal-window duration default for all controller "renewal-window" options
--server-port-http int HTTP server port (serving /healthz, /metrics, ...)
--service-cert.cert-class string Identifier used to differentiate responsible controllers for entries
--service-cert.cert-target-class string Identifier used to differentiate responsible dns controllers for target entries
--service-cert.default.pool.resync-period duration Period for resynchronization of pool default of controller service-cert (default: 2m0s)
--service-cert.default.pool.size int Worker pool size for pool default of controller service-cert (default: 2)
--service-cert.target-name-prefix string name prefix in target namespace for cross cluster generation
--service-cert.target-namespace string target namespace for cross cluster generation
--service-cert.targets.pool.size int Worker pool size for pool targets of controller service-cert (default: 2)
--source string source cluster to watch for ingresses and services
--source.disable-deploy-crds disable deployment of required crds for cluster source
--source.id string id for cluster source
--target string target cluster for certificates
--target-name-prefix string default for all controller "target-name-prefix" options
--target-namespace string default for all controller "target-namespace" options
--target.disable-deploy-crds disable deployment of required crds for cluster target
--target.id string id for cluster target
For development it is recommended to use the issuer-staging