DEV Community

M. Hamzah Khan
M. Hamzah Khan

Posted on • Originally published at hamzahkhan.com on

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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;'

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

Info

If the machine you are using is enrolled in the IPA domain, you could also just use /etc/pki/tls/certs/ca-bundle.crt, which is actually what I did since it contains all the other CA certificates that cert-manager may need (for example the ISRG Root X1 CA certificate, which is needed so cert-manager can properly access the LetsEncrypt ACME servers).

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

Enter fullscreen mode Exit fullscreen mode

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"

Enter fullscreen mode Exit fullscreen mode

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'

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)