When I setup labs and POC environments, I often need or want to test with a functioning SSL environment. These playbooks are evolving to help me manage a Self Signed Root CA and Intermediate SSL CA environment.
You can create multiple CA environments, which are trees in the tls subdirectory, and protected by .gitignore.
When signing certificates / CSRs, you have the opportunity to create:
- server certs
- client certs
- combo server+client certs (OneView needed this)
- openssl installed on local system
- MacOS Mojave 10.14.3 with Homebrew openssl: stable 1.0.2q
- CentOS7 Linux release 7.6.1810 (Core) with OpenSSL 1.0.2k-fips
tls
└── domain.tld
├── root-ca Root CA information
│ ├── certs
│ │ └── ca.crt.pem CA public certificate
│ ├── crl
│ │ └── ca.crl.pem List of revoked keys (publish)
│ ├── csr
│ ├── index.txt Registered certificates
│ ├── index.txt.attr
│ ├── newcerts Copy of each certificate signed
│ │ └── 1000.pem
│ ├── openssl.cnf CA configuration
│ ├── private
│ │ └── ca.key.pem CA private key (keep safe)
│ └── serial Next serial number
├── ssl-ca
│ ├── certs
│ │ ├── ca.crt.pem CA public certificate
│ │ └── chain.crt.pem Int+Root CA public certificate (distribute)
│ ├── crl
│ │ └── ca.crl.pem List of revoked keys (publish)
│ ├── crlnumber Next CRL number
│ ├── csr
│ │ └── ca.csr
│ ├── index.txt Registered certificates
│ ├── index.txt.attr
│ ├── newcerts Copy of each certificate signed
│ ├── openssl.cnf CA configuration
│ ├── private
│ │ └── ca.key.pem CA private key (keep safe)
│ └── serial Next serial number
└── store
└── host.domain.tld
└── YYYY-MM-DD
├── openssl_csr_san.cnf Host configuration
├── host.domain.tld.crt.pem Host certificate
├── host.domain.tld.csr Host signing request
├── host.domain.tld.key.pem Host key
└── host.domain.tld.keycrt.pem Host key+certificate combo
Create ansible vault protected variables for the secrets to the Root CA key and the Intermediate/SSL CA key
Note, the ansible.cfg in the directory assumes your ansible vault password is in .vaultpass
$ ansible-vault encrypt_string --name root_passphrase 'my-super-secret'
root_passphrase: !vault |
$ANSIBLE_VAULT;1.1;AES256
62303038373766323261323532333765656433303332346263666237666464623934323430326633
3636373032613131323566383331336332353563373561640a343239346634343361616136326235
35646639666166333066666432613065636135396630646231623930343339643639636465633732
3632383333613666360a633865373366353638373062343836386266313131373035643932646337
6339
Encryption successful
---
all:
hosts:
localhost:
vars:
root_passphrase: !vault |
$ANSIBLE_VAULT;1.1;AES256
62303038373766323261323532333765656433303332346263666237666464623934323430326633
3636373032613131323566383331336332353563373561640a343239346634343361616136326235
35646639666166333066666432613065636135396630646231623930343339643639636465633732
3632383333613666360a633865373366353638373062343836386266313131373035643932646337
6339
intermediate_passphrase: !vault |
$ANSIBLE_VAULT;1.1;AES256
33323035396661376265313762306636666161376237353938333966653630313465316562366332
6339383035626234373733363362663932323538656461650a643432306139346130613061383836
65376138373663363234323034363936306334356635613832376534303964333230303536326361
6162346138336266380a656165393935366330383864623132313361373032653665393033663566
63313165356165643334646261373935376538386539333033626630616564663734
$ ansible-playbook create-ca.yml
Enter domain [domain.tld]:
Enter CA common name (Private CA will be appended automatically) [My Company]:
Enter the CA organization [My Company]:
Enter the CA organizational unit [Infrastructure Services]:
Enter the CA country (2 letters) [US]:
Enter the CA state [State]:
Enter the CA locality [City]:
Enter the CA duration in days [3650]:
Enter the CRL CA duration in days [30]:
Enter the CA email [[email protected]]:
PLAY [localhost] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************************************************
ok: [localhost]
TASK [set_fact] ************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Create directory structure] ******************************************************************************************************************************************************************************
changed: [localhost] => (item=['root-ca', 'certs'])
changed: [localhost] => (item=['root-ca', 'crl'])
changed: [localhost] => (item=['root-ca', 'newcerts'])
changed: [localhost] => (item=['root-ca', 'csr'])
changed: [localhost] => (item=['ssl-ca', 'certs'])
changed: [localhost] => (item=['ssl-ca', 'crl'])
changed: [localhost] => (item=['ssl-ca', 'newcerts'])
changed: [localhost] => (item=['ssl-ca', 'csr'])
TASK [Secure private directory] ********************************************************************************************************************************************************************************
changed: [localhost] => (item=root-ca)
changed: [localhost] => (item=ssl-ca)
TASK [Create store directory] **********************************************************************************************************************************************************************************
changed: [localhost]
TASK [Init registered certifiates] *****************************************************************************************************************************************************************************
changed: [localhost] => (item=['root-ca', 'index.txt'])
changed: [localhost] => (item=['root-ca', 'index.txt.attr'])
changed: [localhost] => (item=['ssl-ca', 'index.txt'])
changed: [localhost] => (item=['ssl-ca', 'index.txt.attr'])
TASK [Set serial for Root CA] **********************************************************************************************************************************************************************************
changed: [localhost]
TASK [Set serial for Intermediate CA] **************************************************************************************************************************************************************************
changed: [localhost]
TASK [Set CRL for Intermediate CA] *****************************************************************************************************************************************************************************
changed: [localhost]
TASK [Install Root CA openssl.cnf from template] ***************************************************************************************************************************************************************
changed: [localhost]
TASK [Install Intermediate CA openssl.cnf from template] *******************************************************************************************************************************************************
changed: [localhost]
TASK [Create Root CA private key] ******************************************************************************************************************************************************************************
changed: [localhost]
TASK [Create self-signed Root CA] ******************************************************************************************************************************************************************************
changed: [localhost]
TASK [Create private key and CSR for the Intermediate CA] ******************************************************************************************************************************************************
changed: [localhost]
TASK [Create the intermediate certificate] *********************************************************************************************************************************************************************
changed: [localhost]
TASK [Create the certificate chain] ****************************************************************************************************************************************************************************
changed: [localhost]
PLAY RECAP *****************************************************************************************************************************************************************************************************
localhost : ok=16 changed=14 unreachable=0 failed=0
$ ansible-playbook create-sign-cert.yml
Enter CA domain [domain.tld]:
Enter the certificate organization [My Company]:
Enter the certificate organizational unit [Infrastructure Services]:
Enter the certificate country [US]:
Enter the certificate state [State]:
Enter the certificate locality [City]:
Enter the certificate duration in days [3650]:
Enter server names (FQDN list separated by commas) [vip.domain.tld,www.domain.tld]:
Enter IPs (list separated by commas) [192.168.100.100]:
Server cert (true/false) [True]:
Client cert (true/false) [False]:
PLAY [localhost] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************************************************
ok: [localhost]
TASK [set_fact] ************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [set_fact] ************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [set_fact] ************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Create directory structure] ******************************************************************************************************************************************************************************
changed: [localhost]
TASK [Install Intermediate CA openssl.cnf from template] *******************************************************************************************************************************************************
changed: [localhost]
TASK [Create private key and CSR for the server] ***************************************************************************************************************************************************************
changed: [localhost]
TASK [Create the server certificate] ***************************************************************************************************************************************************************************
changed: [localhost]
TASK [Create the client certificate] ***************************************************************************************************************************************************************************
skipping: [localhost]
TASK [Create the server+client certificate] ********************************************************************************************************************************************************************
skipping: [localhost]
TASK [Create combined certificate] *****************************************************************************************************************************************************************************
changed: [localhost]
PLAY RECAP *****************************************************************************************************************************************************************************************************
localhost : ok=9 changed=5 unreachable=0 failed=0
The playbook first examines the CSR to see if you want to sign it. Exit with a 'Ctrl-C' + 'A' 'Enter/Return' to continue
$ ansible-playbook sign-csr.yml
Enter CA domain [domain.tld]:
Enter the CSR filename with path [server.csr]:
Enter the certificate duration in days [3650]:
Server cert (true/false) [True]:
Client cert (true/false) [False]:
PLAY [localhost] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Set TLS root] ********************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Verify CSR exists] ***************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Get text from CSR] ***************************************************************************************************************************************************************************************
changed: [localhost]
TASK [Confirm signing of CSR] **********************************************************************************************************************************************************************************
[Confirm signing of CSR]
Certificate Request:
Data:
Version: 0 (0x0)
Subject: CN=server, OU=Infrastructure Services, O=My Company, L=City, ST=State, C=US
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:cd:1f:b6:a2:d9:1e:2b:0e:3e:98:40:b3:74:ee:
2f:5b:a0:0f:e4:0c:ab:fb:49:33:1e:42:c8:a9:c5:
0a:0a:01:32:48:46:45:83:29:9a:72:a5:b4:a8:ef:
74:cb:c7:16:cf:fb:43:f2:e8:3f:a4:3a:00:89:fc:
52:d5:29:e2:a1:e6:63:41:37:b2:5e:42:98:37:3d:
2e:67:ae:7a:24:cc:f1:1b:13:82:e9:62:41:6f:d1:
af:8f:f9:db:3e:e3:36:b5:ce:08:ed:94:98:4a:ac:
98:94:48:47:92:64:21:ce:08:bb:79:c0:ac:98:1c:
82:63:e5:aa:2e:27:c2:12:97:46:6e:e6:61:34:66:
41:cd:ca:35:d2:0f:15:4f:e4:fc:0f:bf:03:d5:f9:
96:66:dc:d3:a9:71:da:57:16:f5:fd:0f:f1:05:4f:
30:07:e2:0e:19:2c:f3:0d:a3:76:f2:dd:82:f8:34:
26:bf:91:e4:7f:2b:13:c9:e2:5d:a6:95:df:19:55:
d6:7a:65:86:ff:df:b6:1c:07:45:3b:99:5c:7a:07:
3c:c5:b0:7c:ad:1a:2b:b0:40:9b:35:ea:aa:7e:96:
8a:32:b7:a8:2f:cc:ec:2e:5e:d1:8d:00:5c:5a:00:
ea:e0:a9:2d:eb:a1:ad:40:0f:c3:2f:bc:a8:01:a0:
de:2d
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:server, IP Address:10.10.10.10, IP Address:FE80:0:0:0:FFFF:FFFF:FFFF:FFFF, IP Address:2607:FFFF:0:1:FFFF:0:0:1
Signature Algorithm: sha256WithRSAEncryption
5b:42:80:2e:7f:88:4e:1f:fe:5c:9b:f8:88:6a:e0:a9:21:46:
d1:4f:b9:f2:a4:4d:fd:99:3d:af:a3:9a:da:e1:5d:af:ec:0e:
87:0c:e0:a6:c0:19:eb:64:46:9c:ab:7b:50:4d:3f:3e:f6:76:
10:04:94:4e:f5:0e:74:a9:03:8b:fc:dc:dd:b3:72:5e:e6:87:
93:84:4c:47:89:21:aa:08:8e:c1:c1:3e:94:08:e7:55:4c:ee:
33:66:96:03:f0:d2:88:61:99:1d:a2:d7:f3:2a:ce:aa:e7:72:
08:03:5a:e2:46:a4:aa:ba:ef:07:df:00:53:b0:9f:63:34:1c:
98:f0:d8:44:74:65:36:19:8c:0d:43:40:c0:56:3e:ed:ca:32:
83:27:08:a3:75:5a:8e:6e:a2:8e:da:43:e4:45:74:3a:d5:ef:
4e:43:1a:f8:86:c3:55:2f:b8:71:f1:5e:f2:84:ac:30:54:ec:
7e:eb:8a:da:2d:52:94:aa:fb:f3:0a:5a:1b:60:e8:e5:4f:dd:
ec:a9:fb:06:c3:a5:d7:39:77:e0:21:1e:b5:1d:e8:f2:e3:ad:
0e:a8:c7:1c:e3:c1:de:ba:01:32:e1:46:e3:36:aa:1d:e6:d2:
da:f1:61:11:7c:09:60:41:5c:ee:6b:15:74:13:56:2f:68:a1:
6e:96:1f:bc
Please press 'Ctrl-C' 'A' to abort
Please press Enter to continue
:
ok: [localhost]
TASK [Get subject from CSR] ************************************************************************************************************************************************************************************
changed: [localhost]
TASK [Extract CN from CSR subject] *****************************************************************************************************************************************************************************
ok: [localhost]
TASK [Set Host root] *******************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Create directory structure] ******************************************************************************************************************************************************************************
changed: [localhost]
TASK [Copy CSR to TLS directory] *******************************************************************************************************************************************************************************
changed: [localhost]
TASK [Create the server certificate] ***************************************************************************************************************************************************************************
changed: [localhost]
TASK [Create the client certificate] ***************************************************************************************************************************************************************************
skipping: [localhost]
TASK [Create the server+client certificate] ********************************************************************************************************************************************************************
skipping: [localhost]
PLAY RECAP *****************************************************************************************************************************************************************************************************
localhost : ok=11 changed=5 unreachable=0 failed=0
- Automate webserver configuration for certificate revocation lists
- Handle the certificate revocation process
- Handle certificate renewals