Creating a certificate authority in Ansible
Posted on Thu 26 March 2020 by Matt Williams in ansible
As part of my continuing work on Cluster in the Cloud, I had a situation where I needed to create a TLS certificate for a non-public host. These days for anything internet-facing with a DNS name I would normally just use Let's Encrypt but this was an internal host on a cloud platform.
It's possible to use ACME I suppose to create a local CA for issuing certificates or use something like DogTag but I only needed to create a single host certificate for a single service and so I wanted a very simple solution. Luckily Ansible comes with support for generating OpenSSL certificates.
There were two main steps that needed to be performed:
- Create a root certificate authority (CA) certificate. This must be self-signed as no-one else is going to sign it for me.
- Create a host certificate, signed by the CA root certificate.
The first step has been supported for a while in Ansible using openssl_certificate and its selfsigned provider. The second step however only became easy to do in Ansible 2.7 (release October 2018) when the ownca provider was added.
We start by creating a key, a certificate signing request (CSR) and then self-signing it using the selfsigned provider:
- name: create CA key
openssl_privatekey:
path: /root/CA_key.pem
register: ca_key
- name: create the CA CSR
openssl_csr:
path: /root/CA.csr
privatekey_path: "{{ ca_key.filename }}"
common_name: "my-ca"
register: ca_csr
- name: sign the CA CSR
openssl_certificate:
path: /root/CA.crt
csr_path: "{{ ca_csr.filename }}"
privatekey_path: "{{ ca_key.filename }}"
provider: selfsigned
register: ca_crt
This has created the CA root certificate (the path to which is now stored in ca_crt.filename) and the key which can be used with it to sign other certificates (at ca_key.filename). We can then go ahead and generate a key and CSR for the host:
- name: create host CSR signing key
openssl_privatekey:
path: /root/example_com_host_key.pem
register: example_com_key
- name: create the CSR for the LDAP server
openssl_csr:
path: /root/example_com.csr
privatekey_path: "{{ example_com_key.filename }}"
common_name: example.com
subject_alt_name: 'DNS:www.example.com'
register: example_com_csr
Finally, we can now sign that CSR with the CA certificate and key using the ownca provider:
- name: sign the CSR for the LDAP server
openssl_certificate:
path: /root/example_com.crt
csr_path: "{{ example_com_csr.filename }}"
provider: ownca
ownca_path: "{{ ca_crt.filename }}"
ownca_privatekey_path: "{{ ca_key.filename }}"
register: example_com_crt
The CA certificate can then be placed on the other hosts in my system and be used to authenticate that they are indeed talking to example.com.
In my case I also then had to convert this into a PKCS #12 file for use with NSS and the 389 Directory Server. To use this feature, you must make sure that pyOpenSSL is installed:
- name: Generate PKCS #12 file
openssl_pkcs12:
action: export
path: /root/example_com.p12
friendly_name: example.com
privatekey_path: "{{ example_com_key.filename }}"
certificate_path: "{{ example_com_crt.filename }}"
other_certificates: "{{ example_com_crt.ca_cert }}"
state: present
register: example_com_crt_p12