step-ca
step-ca
step-ca
in a Docker containerstep-ca
step-ca
step-ca
in a Docker containerIn this example, we'll configure Kubernetes cert-manager to get a certificate from an internal ACME server, using cert-manager's ACME issuer.
step-ca
ACME instance using the steps in our ACME server tutorial.This example uses the ACME dns-01
challenge type, with Google Cloud DNS.
We'll create a service account on Google Cloud that cert-manager will use to solve DNS challenges.
For other DNS providers, or other ACME challenge types, you'll need to change the challenge solver settings below.
For this tutorial, I created a Google Compute Engine VM running a kind cluster. I'm using kind for testing, but pretty much any Kubernetes cluster will do.
$ kind cluster create
Let's install Kubernetes cert-manager and patch it so that it will trust your internal ACME CA.
First, install cert-manager:
$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.0.4/cert-manager.yaml
Next, create a ConfigMap
that contains your ACME server's CA certificate.
To find your certificate's PEM file, select your CA in the Google Cloud CAS Console, and view your CA certificate under the Actions menu.
Create a file called internal-ca.yaml
, replacing the certificate shown here with your own:
apiVersion: v1
data:
internal-ca.pem: |
-----BEGIN CERTIFICATE-----
[REPLACE with your CA certificate]
-----END CERTIFICATE-----
kind: ConfigMap
metadata:
name: ca-pemstore
namespace: cert-manager
resourceVersion: "9978"
selfLink: /api/v1/namespaces/cert-manager/configmaps/ca-pemstore
---
Apply it:
$ kubectl apply -f internal-ca.yaml
To inject this ConfigMap
into cert-manager, we need to patch the cert-manager
Deployment to add the CA certificate as a container volume mount.
Create a file called cm-ca-patch.yaml
:
spec:
template:
spec:
containers:
- args:
- --v=2
- --cluster-resource-namespace=$(POD_NAMESPACE)
- --leader-election-namespace=kube-system
name: cert-manager
volumeMounts:
- name: ca-pemstore
mountPath: /etc/ssl/certs/internal-ca.pem
subPath: internal-ca.pem
readOnly: false
resources: {}
volumes:
- name: ca-pemstore
configMap:
# Provide the name of the ConfigMap containing the files you want
# to add to the container
name: ca-pemstore
Apply the patch:
$ kubectl patch deployment cert-manager -n cert-manager --patch "$(cat cm-ca-patch.yaml)"
Cert-manager is now configured to trust your ACME CA.
We're going to have cert-manager solve dns-01
ACME challenges.
So, it will need to be able to manage DNS entries.
Let's create a Google Cloud Platform service account with the roles/dns.admin
role. Replace the PROJECT_ID
here with your own:
$ export PROJECT_ID=step-cas-test
$ gcloud iam service-accounts create dns01-solver \
--project $PROJECT_ID --display-name "dns01-solver"
$ gcloud projects add-iam-policy-binding $PROJECT_ID \
--member serviceAccount:dns01-solver@$PROJECT_ID.iam.gserviceaccount.com \
--role roles/dns.admin
Now import the service account's credentials as a Kubernetes secret:
$ gcloud iam service-accounts keys create key.json \
--iam-account dns01-solver@$PROJECT_ID.iam.gserviceaccount.com
$ kubectl create secret generic clouddns-dns01-solver-svc-acct \
--from-file=key.json
Finally, let's create an cert-manager Issuer to perform dns-01
ACME challenges. Make a new file called acme-issuer.yaml
:
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: acme-issuer
spec:
acme:
email: carl@smallstep.com
server: https://ca.smallstep.internal/acme/acme/directory
privateKeySecretRef:
name: acme-issuer-account-key
solvers:
- dns01:
cloudDNS:
# Your Google Cloud Platform project ID:
project: step-cas-test
# Your Google CloudDNS zone name we will use for DNS01 challenges:
hostedZoneName: step-cas-internal
serviceAccountSecretRef:
name: clouddns-dns01-solver-svc-acct
key: key.json
Replace the values for email
, server
URL, project
and hostedZoneName
with your own. Your Smallstep ACME endpoint will always take the form of https://[your CA hostname]/acme/acme/directory
.
Apply it:
$ kubectl apply -f acme-issuer.yaml
You now have an automated ACME certificate manager running inside your Kubernetes cluster.
Let's get a test certificate from our ACME CA, using a Certificate object. Create a file called tls-certificate.yaml
:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: k8s-internal
namespace: default
spec:
secretName: k8s-internal-tls
issuerRef:
name: acme-issuer
dnsNames:
- k8s.smallstep.internal
Replace the dnsNames
value with a DNS name that's inside your zone.
Apply it:
$ kubectl apply -f tls-certificate.yaml
You can check the status with kubectl get certificaterequest
or kubectl describe certificate
:
$ kubectl get certificaterequest
NAME READY AGE
k8s-internal-nzbnm True 7s
$ kubectl describe certificate k8s-internal
Name: k8s-internal
Namespace: default
...
Kind: Certificate
Metadata:
Creation Timestamp: 2020-11-03T23:06:46Z
...
Spec:
Dns Names:
k8s.smallstep.internal
Issuer Ref:
Name: acme-issuer
Secret Name: k8s-internal-tls
Status:
Conditions:
Last Transition Time: 2020-11-03T23:11:01Z
Message: Certificate is up to date and has not expired
Reason: Ready
Status: True
Type: Ready
Not After: 2020-11-04T23:11:01Z
Not Before: 2020-11-03T23:11:01Z
Renewal Time: 2020-11-04T15:11:01Z
Revision: 1
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 10m cert-manager Issuing certificate as Secret does not exist
Normal Generated 10m cert-manager Stored new private key in temporary Secret resource "k8s-internal-g79jq"
Normal Requested 10m cert-manager Created new CertificateRequest resource "k8s-internal-nzbnm"
Normal Issuing 9m33s cert-manager The certificate has been successfully issued
As you can see, cert-manager will automatically renew the certificate when approximately 2/3 of its lifetime has elapsed.
That's it! You now have automated, short-lived certificates for your Kubernetes cluster. There are many use cases for X.509 certificates issued through cert-manager.
Unsubscribe anytime. See our privacy policy.
© 2023 Smallstep Labs, Inc. All rights reserved.