Using FreeIPA CA as an ACME Provider for Cert-Manager
I’m using FreeIPA for authentication services in my home lab. It’s extreme overkill for my situation, as I don’t have many users (mainly just me!) but alas I like overkill. :)
I am using FreeIPA’s DNS service to host some DNS subdomains for internal services. The way I have configured these subdomains is through DNS delegations, but since my IPA servers are not accessible from the internet, it breaks both the HTTP-01 and DNS-01 verification challenges from LetsEncypt’s.
Yesterday evening, I was playing around with TrueCommand and have it hosted on one of my IPA internal domains, but as I cannot use LetsEncrypt to issue a certificate for it, I decided to use the CA built into FreeIPA since it supports ACME as well.
As all the machines that will need to use the service are enrolled into IPA already, the CA certificate for IPA is also installed on those nodes, meaning any certificate issues by FreeIPA are automatically trusted.
To get this to work, I had to first enable ACME support from within FreeIPA:
[root@ipa-server ~]# ipa-acme-manage enable
FreeIPA’s ACME service supports both HTTP-01 and DNS-01 challenges, but I generally prefer DNS-01. For cert-manager to add the _acme-challenge DNS record to FreeIPA, we can use cert-manager’s RFC-2136 provider.
To do this, we must create a new TSIG key on our IPA server:
[root@ipa-server ~]# tsig-keygen -a hmac-sha512 acme-update >> /etc/named/ipa-ext.conf
[root@ipa-server ~]# systemctl restart named-pkcs11.service
Enable dynamic updates for the IPA DNS subdomain:
[root@ipa-server ~]# ipa dnszone-mod k8s.intahnet.co.uk --dynamic-update=True --update-policy='grant acme-update wildcard * ANY;'
Next, I had to modify my cert-manager installation slightly to include my own CA certificate bundle, which includes my IPA CA cert. To do this I had to first create the bundle, and then create a Kubernetes ConfigMap for it:
[mhamzahkhan@laptop ~]# cat /etc/ipa/ca.crt > ca-certificates.crt
[mhamzahkhan@laptop ~]# kubectl -n cert-manager create configmap ca-bundle --from-file ca-certificates.crt
Next, I had to modify the cert-manager deployment to make use of the ca-bundle. As I am using the cert-manager helm chart, this was quite easy. I added the following to my cert-manager helm values file:
---
volumes:
- name: ca-bundle
configMap:
name: ca-bundle
volumeMounts:
- name: ca-bundle
mountPath: /etc/ssl/certs/ca-certificates.crt
subPath: ca-certificates.crt
readOnly: false
Once this has been deployed, we can need to create a secret in Kubernetes for the TSIG key. Grab the TSIG key we generated earlier from your IPA server (/etc/named/ipa-ext.conf), and create a Kubernetes secret with it:
[mhamzahkhan@laptop ~]# kubectl -n cert-manager create secret generic ipa-tsig-secret --from-literal=tsig-secret-key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
Next, add a new ClusterIssuer for IPA’s ACME service:
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: ipa
namespace: cert-manager
spec:
acme:
email: admin@ipa.intahnet.co.uk
server: https://ipa-ca.ipa.intahnet.co.uk/acme/directory
privateKeySecretRef:
name: ipa-issuer-account-key
solvers:
- dns01:
rfc2136:
nameserver: 10.0.0.22
tsigKeyName: acme-update
tsigAlgorithm: HMACSHA512
tsigSecretSecretRef:
name: ipa-tsig-secret
key: tsig-secret-key
selector:
dnsZones:
- 'k8s.intahnet.co.uk'
Now you should be set to request certificates!
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: truecommand-certificate
namespace: default
spec:
commonName: 'truecommand.k8s.intahnet.co.uk'
dnsNames:
- truecommand.k8s.intahnet.co.uk
issuerRef:
name: ipa
kind: ClusterIssuer
privateKey:
algorithm: RSA
encoding: PKCS1
size: 4096
secretName: truecommand-tls
All working:
[mhamzahkhan@laptop ~]# kubectl get certificate
NAME READY SECRET AGE
truecommand-certificate True truecommand-tls 23s
[mhamzahkhan@laptop ~]# kubectl get secrets
NAME TYPE DATA AGE
truecommand-certificate-q8qkh kubernetes.io/tls 2 29s
It’s a very similar process to use ExternalDNS with FreeIPA as ExternalDNS also supports RFC2136. I have not set this up yet, but the process is described in this excellent blog post: How to set up Dynamic DNS on FreeIPA for your Kubernetes Cluster.