Getting wildcard SSL certificate in Kubernetes with cert-manager

Amet Umerov
Jun 17, 2019 · 5 min read

In this article, I’ll shortly describe how to get an SSL certificate with HTTP01 validation and a wildcard certificate with DNS01 validation on AWS example.

Image for post
Image for post
https://letsencrypt.org/

So we already have some ingress and HELM for our k8s cluster, and we want to get some certs for domain dummy.example.com.

Let’s install cert-manager using HELM:

helm install --namespace kube-system -n cert-manager stable/cert-manager

If you prefer to use the latest chart version for cert-manager you can follow the instructions here.

For issuing some certificates we need to have at least one Issuer or ClusterIssuer. The difference between them that Issuer works only inside one namespace, unlike ClusterIssuer which works globally for the cluster.

Let’s create ClusterIssuer:

cat <<EOF | kubectl create -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: le-clusterissuer
namespace: kube-system
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: devops@example.com
privateKeySecretRef:
name: le-clusterissuer
http01: {}
EOF

What’s there:

  • le-clusteissuer — ClusterIssuer name

After creating ClusterIssuer we can check the status:

kubectl describe clusterissuer le-clusterissuer -n kube-system | egrep "Status|Message"
Status:
Message: The ACME account was registered with the ACME server
Status: True

So now we have ClusterIssuer, and we can create new certificates.

Certificate for dummy.example.com

Let’s create one (you should set up DNS record to your load balancer before doing that):

cat <<EOF | kubectl create -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: dummy-stage
namespace: stage
spec:
secretName: cert-stage-dummy
dnsNames:
- dummy.example.com
acme:
config:
- http01:
ingressClass: nginx
domains:
- dummy.example.com
issuerRef:
name: le-clusterissuer
kind: ClusterIssuer
EOF

So here we created the certificate for the domain dummy.example.com:

  • with name dummy-stage

Check the certificate status:

kubectl describe certificates dummy-stage -n stage
...
Message: Certificate issued successfully
Reason: CertIssued
Status: True
Type: Ready
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CreateOrder 38s cert-manager Created new ACME order, attempting validation...
Normal DomainVerified 5s cert-manager Domain "dummy.example.com" verified with "http-01" validation
Normal IssueCert 5s cert-manager Issuing certificate...
Normal CertObtained 3s cert-manager Obtained certificate from ACME server
Normal CertIssued 3s cert-manager Certificate issued successfully

Certificate issued successfully and now we can use it in our test ingress:

cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-stage
namespace: stage
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- dummy.example.com
secretName: cert-stage-dummy
rules:
- host: dummy.example.com
http:
paths:
- backend:
serviceName: somesvc
servicePort: 80
EOF

We set up here:

  • host dummy.example.com

Remember, we saved secret to the stage namespace, so ingress should be also created in the same namespace.

Checking it:

openssl s_client -servername dummy.example.com -connect dummy.example.com:443
CONNECTED(00000006)
...
---
Certificate chain
0 s:/CN=dummy.example.com
i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/CN=dummy.example.com
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
...
Image for post
Image for post
It works

Wildcard certificate *.example.com

For using wildcard certificates issuing we need to set up a new Issuer with DNS01 validation. Here is a list of available DNS01 providers.

In our example we use AWS, so let’s start with it.

Create a policy in AWS for Route53 access:

cat << EOF > json/letsencrypt-wildcard.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:GetChange",
"Resource": "arn:aws:route53:::change/*"
},
{
"Effect": "Allow",
"Action": "route53:ChangeResourceRecordSets",
"Resource": "arn:aws:route53:::hostedzone/*"
},
{
"Effect": "Allow",
"Action": "route53:ListHostedZonesByName",
"Resource": "*"
}
]
}
EOF
aws iam create-policy --policy-name letsencrypt-wildcard --policy-document file://json/letsencrypt-wildcard.json

Create user and get CLI credentials AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY:

LE_POLICY_ARN=$(aws iam list-policies --output json --query 'Policies[*].[PolicyName,Arn]' --output text | grep letsencrypt-wildcard | awk '{print $2}')aws iam create-group --group-name letsencrypt-wildcard
aws iam attach-group-policy --policy-arn ${LE_POLICY_ARN} --group-name letsencrypt-wildcard
aws iam create-user --user-name letsencrypt-wildcard
aws iam add-user-to-group --user-name letsencrypt-wildcard --group-name letsencrypt-wildcard
aws iam create-access-key --user-name letsencrypt-wildcard

After getting AWS credentials we can create secret with AWS_SECRET_ACCESS_KEY:

AWS_ACCESS_KEY_ID=your-access-id
AWS_SECRET_ACCESS_KEY=your-access-secret
echo ${AWS_SECRET_ACCESS_KEY} > password.txt
kubectl create secret generic aws-route53-creds --from-file=password.txt -n stage
rm -f password.txt

Create an Issuer with DNS01 verification and credentials (replace AWS_ACCESS_KEY_ID to the valid value):

cat <<EOF | kubectl create -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
name: le-wildcard-issuer
namespace: stage
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: devops@example.com
privateKeySecretRef:
name: le-wildcard-issuer
dns01:
providers:
- name: aws-dns
route53:
region: eu-west-1
accessKeyID: AWS_ACCESS_KEY_ID
secretAccessKeySecretRef:
name: aws-route53-creds
key: password.txt
EOF

Creating a wildcard SSL certificate *.example.com:

cat <<EOF | kubectl create -f -
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: wildcard-stage
namespace: stage
spec:
secretName: cert-stage-wildcard
issuerRef:
name: le-wildcard-issuer
kind: Issuer
commonName: '*.example.com'
dnsNames:
- example.com
- '*.example.com'
acme:
config:
- dns01:
provider: aws-dns
domains:
- example.com
- '*.example.com'
EOF

Let’s test this certificate:

openssl s_client -servername wildcard.example.com -connect wildcard.example.com:443
...
Certificate chain
0 s:/CN=*.example.com
i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/CN=*.example.com
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
...

That works too.

Could we share certificates between namespaces?

Sure, (example for copying cert-stage-wildcard secret from stage namespace to preprod namespace):

kubectl get secret cert-stage-wildcard -n stage --export -o yaml | \
kubectl apply -n preprod -f -

Or you can use Kubernetes-replicator for that:

See ya!

Image for post
Image for post

Subscribe to FAUN topics and get your weekly curated email of the must-read tech stories, news, and tutorials 🗞️

Follow us on Twitter 🐦 and Facebook 👥 and Instagram 📷 and join our Facebook and Linkedin Groups 💬

Image for post
Image for post

If this post was helpful, please click the clap 👏 button below a few times to show your support for the author! ⬇

FAUN

The Must-Read Publication for Creative Developers & DevOps Enthusiasts

Sign up for FAUN

By FAUN

Medium’s largest and most followed independent DevOps publication. Join thousands of aspiring developers and DevOps enthusiasts Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Amet Umerov

Written by

DevOps Engineer — https://www.linkedin.com/in/amet13/

FAUN

FAUN

The Must-Read Publication for Creative Developers & DevOps Enthusiasts. Medium’s largest DevOps publication.

Amet Umerov

Written by

DevOps Engineer — https://www.linkedin.com/in/amet13/

FAUN

FAUN

The Must-Read Publication for Creative Developers & DevOps Enthusiasts. Medium’s largest DevOps publication.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store