Create CA and server certificates

Change-Id: I21d40ba822b059ebf1cefb3d4557403f780596f6
This commit is contained in:
Jonathan Rosser 2021-04-21 14:38:47 +00:00
parent 8b8d70c8ee
commit e5bbd440a7
13 changed files with 564 additions and 19 deletions

View File

@ -11,4 +11,132 @@
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# limitations under the License.
# CA certificates to create
# Setting this variable will disable searching for other vars containing authorities
# pki_authorities: []
# Variable name pattern to search ansible vars for other authority definitions
pki_search_authorities_pattern: "pki_authorities_"
# Example variables defining a certificate authorities
# pki_authorities_roots:
# - name: "SnakeRoot"
# provider: selfsigned
# email_address: "pki@snakeoil.com"
# basic_constraints: "CA:TRUE"
# cn: "Snake Oil Corp Root CA"
# country_name: "GB"
# state_or_province_name: "England"
# organization_name: "Snake Oil Corporation"
# organizational_unit_name: "IT Security"
# key_usage:
# - digitalSignature
# - cRLSign
# - keyCertSign
# not_after: "+3650d"
#pki_authorities_intermediates:
# - name: "SnakeRootIntermediate"
# email_address: "pki@snakeoil.com"
# provider: ownca
# cn: "Snake Oil Corp Openstack Infrastructure Intermediate CA"
# country_name: "GB"
# state_or_province_name: "England"
# organization_name: "Snake Oil Corporation"
# organizational_unit_name: "IT Security"
# key_usage:
# - digitalSignature
# - cRLSign
# - keyCertSign
# not_after: "+365d"
# signed_by: "SnakeRoot"
# example variable of CA to install
# pki_install_ca:
# # CA created but the PKI role
# - name: SnakeRoot
#
# # user provided CA copied from the deploy host (src), to the target (filename)
# - src: /opt/my-ca/MyRoot.crt
# filename: /etc/ssl/certs/MyRoot.crt
#
pki_install_ca: []
# set this to the name of a CA to regenerate, or to 'true' to regenerate all
pki_regen_ca: ''
# locations of system trust stores to install CA certs to
pki_trust_store_location:
apt: /usr/local/share/ca-certificates/
dnf: /etc/pki/ca-trust/source/anchors/
# Server certificates to create
# Setting this variable will disable searching for other vars containing certificates
# pki_certificates: []
# Variable name pattern to search ansible vars for other certificate definitions
pki_search_certificates_pattern: "pki_certificates_"
# Example variable defining a server certificate
# pki_certificates_default:
# - name: "SnakeWeb"
# provider: ownca
# cn: "www.snakeoil.com"
# san: "DNS:www.snakeoil.com,DNS:snakeoil.com"
# - name: "SnakeMail"
# signed_by: "SnakeRootIntermediate"
# provider: ownca
# cn: "imap.snakeoil.com"
# signed_by: "SnakeRootIntermediate"
# Example variable defining a server certificate from ansible host variables
# pki_certificates_default:
# - name: "myservice_{{ ansible_facts['hostname'] }}"
# cn: "{{ ansible_facts['hostname'] }}"
# provider: ownca
# san: "{{ 'DNS:' ~ ansible_facts['hostname'] ~ ',DNS:' ~ ansible_facts['fqdn'] ~ ',IP:' ~ ansible_facts['default_ipv4'] }}"
# signed_by: "SnakeRootIntermediate"
# set this to the name of the certificate to regenerate, or to 'true' to regenerate all
pki_regen_cert: ''
# host where the generated PKI files are kept
pki_setup_host: localhost
# base directory for the CA and server certificates
pki_dir: "/etc/pki"
# subdirectories to be created for holding CA certs/keys/csr
pki_ca_dirs: "{{ _pki_ca_dirs }}"
# subdirectories to be created for holding server certs/keys/csr
pki_cert_dirs: "{{ _pki_cert_dirs }}"
# certificates to install
pki_install_certificates: []
# Example variable for installation of server certificates with optional user supplied cert override
# pki_install_certificates:
# # server certificate
# - src: "{{ user_ssl_cert | default(pki_dir ~ '/certs/certs/myservice_' ~ ansible_facts['hostname'] ~ '.crt') }}"
# dest: "{{ myservice_ssl_cert }}"
# owner: "root"
# group: "root"
# mode: "0644"
# #private key
# - src: "{{ myservice_user_ssl_key | default(pki_dir ~ 'certs/keys/myservice_' ~ ansible_facts['hostname'] ~ '.key.pem') }}"
# dest: "{{ myservice_ssl_key }}"
# owner: "myservice"
# group: "myservice"
# mode: "0600"
# # intermediate CA
# - src: "{{ myservice_user_ssl_ca_cert | default(pki_dir ~ '/roots/SnakeRootIntermediate/certs/SnakeRootIntermediate.crt' }}"
# dest: "{{ myservice_ssl_ca_cert }}"
# owner: "myservice"
# group: "myservice"
# mode: "0644"
# method used to create the certificates
pki_method: standalone

28
handlers/main.yml Normal file
View File

@ -0,0 +1,28 @@
# Copyright 2021, BBC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# when the created CA certificates are changed
- name: ca cert changed
meta: noop
when: false
# when the created server certificates are changed
- name: cert changed
meta: noop
when: false
# when a certificate is installed onto a server
- name: cert installed
meta: noop
when: false

View File

@ -13,17 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Gather variables for each operating system
include_vars: "{{ item }}"
with_first_found:
- "{{ ansible_distribution | lower }}-{{ ansible_distribution_version | lower }}.yml"
- "{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version | lower }}.yml"
- "{{ ansible_os_family | lower }}-{{ ansible_distribution_major_version | lower }}.yml"
- "{{ ansible_distribution | lower }}.yml"
- "{{ ansible_os_family | lower }}-{{ ansible_distribution_version.split('.')[0] }}.yml"
- "{{ ansible_os_family | lower }}.yml"
tags:
- always
- debug: Test task
msg: "Hello World"
- include_tasks: main_ca.yml
- include_tasks: main_ca_install.yml
- include_tasks: main_certs.yml

39
tasks/main_ca.yml Normal file
View File

@ -0,0 +1,39 @@
---
# Copyright 2021, BBC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Include {{ pki_method }} CA variables
include_vars:
file: "{{ pki_method ~ '_ca.yml' }}"
- name: Create PKI directories
file:
state: directory
path: "{{ item.path }}"
owner: "{{ item.owner | default(omit) }}"
mode: "{{ item.mode | default(omit) }}"
with_items:
- "{{ pki_ca_dirs }}"
delegate_to: "{{ pki_setup_host }}"
when: pki_create_ca | bool
- name: Create certificate authorities
include_tasks: "{{ pki_method }}/create_ca.yml"
loop: "{{ pki_ca_defs }}"
loop_control:
loop_var: ca
vars:
ca_dir: "{{ pki_dir }}/roots/{{ ca.name }}"
ca_cert_prefix: "{{ ca_dir ~ '/certs/' ~ ca.name }}"
when: pki_create_ca | bool

View File

@ -12,3 +12,11 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Include {{ pki_method }} CA variables
include_vars:
file: "{{ pki_method ~ '_ca.yml' }}"
- name: Install certificate authorities
include_tasks: "{{ pki_method }}/install_ca.yml"
when: pki_install_ca | length > 0

53
tasks/main_certs.yml Normal file
View File

@ -0,0 +1,53 @@
---
# Copyright 2021, BBC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Include {{ pki_method }} certificate variables
include_vars:
file: "{{ pki_method ~ '_cert.yml' }}"
- name: Create certificate directories
file:
state: directory
path: "{{ item.path }}"
owner: "{{ item.owner | default(omit) }}"
mode: "{{ item.mode | default(omit) }}"
with_items:
- "{{ pki_cert_dirs }}"
when: pki_create_certificates | default(true)
delegate_to: "{{ pki_setup_host }}"
- name: Create Server certificates
include_tasks: "{{ pki_method }}/create_cert.yml"
loop: "{{ pki_cert_defs }}"
loop_control:
loop_var: cert
vars:
cert_dir: "{{ pki_dir }}/certs"
when:
- pki_create_certificates | default(true)
- name: Install Server certificates
copy:
src: "{{ install.src }}"
dest: "{{ install.dest }}"
owner: "{{ install.owner | default(omit) }}"
group: "{{ install.group | default(omit) }}"
mode: "0644"
loop: "{{ pki_install_certificates }}"
loop_control:
loop_var: install
when: pki_install_certificates is defined and pki_install_certificates | length > 0
notify:
- cert installed

View File

@ -0,0 +1,125 @@
---
# Copyright 2021, BBC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Create certificate {{ cert.name }}
vars:
next_serial_no: "{{ serial_no['content'] | b64decode | int + 1 }}"
delegate_to: "{{ pki_setup_host }}"
block:
- name: Create directories for certificate authority {{ ca.name }}
file:
state: directory
path: "{{ item.path }}"
owner: "{{ item.owner | default(omit) }}"
mode: "{{ item.mode | default(omit) }}"
with_items:
- path: "{{ ca_dir }}"
mode: "0744"
- path: "{{ ca_dir ~ '/csr' }}"
mode: "0700"
- path: "{{ ca_dir ~ '/private' }}"
mode: "0700"
- path: "{{ ca_dir ~ '/certs' }}"
mode: "0444"
- name: Initialise the serial number for {{ ca.name }}
copy:
content: "999"
dest : "{{ ca_dir ~ '/serial' }}"
force: no
- name: Generate CA private key for {{ ca.name }}
openssl_privatekey:
path: "{{ ca_dir ~ '/private/' ~ ca.name ~ '.key.pem' }}"
register: ca_privkey
- name: Read the serial number for {{ ca.name }}
slurp:
src: "{{ pki_dir ~ '/roots/' ~ ca.name ~ '/serial' }}"
register: serial_no
- name: Create the CA CSR for {{ ca.name }}
openssl_csr:
path: "{{ ca_dir }}/csr/ca_csr-{{ next_serial_no }}.csr"
privatekey_path: "{{ ca_privkey.filename }}"
common_name: "{{ ca.cn }}"
basic_constraints_critical: yes
basic_constraints: "{{ ca.basic_constraints }}"
key_usage: "{{ ca.key_usage }}"
country_name: "{{ ca.country_name | default(omit) }}"
state_or_province_name: "{{ ca.state_or_province_name | default(omit) }}"
locality_name: "{{ ca.locality_name | default(omit) }}"
organization_name: "{{ ca.organizataion_name | default(omit) }}"
organizational_unit_name: "{{ ca.organization_unit_name | default(omit) }}"
subject: "{{ cert.subject | default(omit) }}"
register: ca_csr
when:
- ca_privkey is changed or pki_regen_ca == ca.name or (pki_regen_ca | lower) == 'true'
- name: Write out the new serial number for {{ ca.name }}
copy:
content: "{{ next_serial_no }}"
dest: "{{ ca_dir }}/serial"
when: ca_csr is changed
- name: Sign the selfsigned Root CA CSR for {{ ca.name }}
openssl_certificate:
path: "{{ ca_cert_prefix ~ '-' ~ next_serial_no ~ '.crt' }}"
csr_path: "{{ ca_csr.filename }}"
provider: 'selfsigned'
privatekey_path: "{{ ca_privkey.filename }}"
selfsigned_not_after: "{{ ca.not_after }}"
register: ca_selfsigned_crt
when:
- ca.provider == 'selfsigned'
- ca_csr is changed
notify:
- ca cert changed
- name: Sign the intermediate CA CSR for {{ ca.name }}
vars:
openssl_certificate:
path: "{{ ca_cert_prefix ~ '-' ~ next_serial_no ~ '.crt' }}"
csr_path: "{{ ca_csr.filename }}"
provider: 'ownca'
ownca_privatekey_path: "{{ pki_dir ~ '/roots/' ~ ca.signed_by ~ '/private/' ~ ca.signed_by ~ '.key.pem' }}"
ownca_path: "{{ pki_dir ~ '/roots/' ~ ca.signed_by ~ '/certs/' ~ ca.signed_by ~ '.crt' }}"
ownca_not_after: "{{ ca.not_after }}"
register: ca_ownca_crt
when:
- ca.provider == 'ownca'
- ca_csr is changed
notify:
- ca cert changed
- name: Symlink the certificate name to the most recently generated
file:
src: "{{ ca_selfsigned_crt.filename | default(ca_ownca_crt.filename) }}"
dest: "{{ ca_cert_prefix ~ '.crt' }}"
state: link
when: ca_ownca_crt is changed or ca_selfsigned_crt is changed
- name: Get certificate info for {{ ca.name }}
openssl_certificate_info:
path: "{{ ca_selfsigned_crt.filename | default(ca_ownca_crt.filename) }}"
register: ca_cert_info
when: ca_ownca_crt is changed or ca_selfsigned_crt is changed
- name: Save certificate info for {{ ca.name }}
copy:
content: "{{ ca_cert_info | to_nice_yaml }}"
dest: "{{ (ca_selfsigned_crt.filename | default(ca_ownca_crt.filename)) ~ '.info' }}"
when: ca_ownca_crt is changed or ca_selfsigned_crt is changed

View File

@ -0,0 +1,76 @@
---
# Copyright 2021, BBC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Create certificate {{ cert.name }}
vars:
_cert_file: "{{ cert_dir ~ '/certs/' ~ cert.name ~ '.crt' }}"
_cert_chain_file: "{{ cert_dir ~ '/certs/' ~ cert.name ~ '-chain.crt' }}"
_ca_file: "{{ pki_dir ~ '/roots/' ~ cert.signed_by ~ '/certs/' ~ cert.signed_by ~ '.crt' }}"
delegate_to: "{{ pki_setup_host }}"
block:
- name: Generate certificate private key for {{ cert.name }}
openssl_privatekey:
path: "{{ cert_dir ~ '/private/' ~ cert.name ~ '.key.pem' }}"
register: cert_privkey
- name: Create the CSR for {{ cert.name }}
openssl_csr:
path: "{{ cert_dir ~ '/csr/' ~ cert.name ~ '.csr' }}"
privatekey_path: "{{ cert_privkey.filename }}"
common_name: "{{ cert.cn | default(omit) }}"
basic_constraints_critical: yes
basic_constraints: "{{ cert.basic_constraints | default(omit) }}"
key_usage: "{{ cert.key_usage | default(omit) }}"
subject_alt_name: "{{ cert.san | default(omit) }}"
country_name: "{{ ca.country_name | default(omit) }}"
state_or_province_name: "{{ ca.state_or_province_name | default(omit) }}"
locality_name: "{{ ca.locality_name | default(omit) }}"
organization_name: "{{ ca.organizataion_name | default(omit) }}"
organizational_unit_name: "{{ ca.organization_unit_name | default(omit) }}"
subject: "{{ cert.subject | default(omit) }}"
force: "{{ pki_regen_cert == cert.name or ((pki_regen_cert | lower) == 'true') }}"
register: cert_csr
- name: Sign the certificate CSR for {{ cert.name }}
community.crypto.x509_certificate:
path: "{{ _cert_file }}"
csr_path: "{{ cert_csr.filename }}"
ownca_path: "{{ _ca_file }}"
ownca_privatekey_path: "{{ pki_dir ~ '/roots/' ~ cert.signed_by ~ '/private/' ~ cert.signed_by ~ '.key.pem' }}"
provider: ownca
register: cert_crt
when:
- cert.provider == 'ownca'
- cert_csr is changed
notify:
- cert changed
- name: Get certificate info for {{ cert.name }}
openssl_certificate_info:
path: "{{ cert_crt.filename }}"
register: cert_info
when: cert_crt is changed
- name: Save certificate info for {{ cert.name }}
copy:
content: "{{ cert_info | to_nice_yaml }}"
dest: "{{ cert_dir ~ '/certs/' ~ cert.name ~ '.info' }}"
when: cert_crt is changed
- name: Create certificate chain for {{ cert.name }}
shell:
cmd: "cat {{ _cert_file }} {{ _ca_file }} > {{ _cert_chain_file }}"
creates: "{{ _cert_chain_file }}"

View File

@ -0,0 +1,28 @@
---
# Copyright 2021, BBC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Copy CA certificates
copy:
src: "{{ item.src | default(pki_dir ~ '/roots/' ~ item.name ~ '/certs/' ~ item.name ~ '.crt') }}"
dest: "{{ pki_trust_store_location[ansible_facts['pkg_mgr']] }}/{{ item.filename | default(item.name ~ '.crt') }}"
when:
- (item.condition is defined and item.condition | bool) or (item.condition is not defined)
register: ca_copy
loop: "{{ pki_install_ca }}"
- name: Update CA store
command: "{{ pki_ca_install_command[ansible_facts['pkg_mgr']] }}"
when:
- ca_copy is changed

40
vars/main.yml Normal file
View File

@ -0,0 +1,40 @@
---
# Copyright 2021, BBC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Gather CA definitions from hostvars
pki_ca_defs: |-
{% if pki_authorities is defined %}
{% set _cas = pki_authorities %}
{% else %}
{% set _ca_search_hits = vars.keys() | select('match', '^' ~ pki_search_authorities_pattern ~ '.*') %}
{% set _cas = [] %}
{% for _ca in _ca_search_hits | default([]) %}
{% set _ = _cas.extend(lookup('vars', _ca)) %}
{% endfor %}
{% endif %}
{{ _cas }}
# Gather certificate definitions from hostvars
pki_cert_defs: |-
{% if pki_certificates is defined %}
{% set _certs = pki_certificates %}
{% else %}
{% set _cert_search_hits = vars.keys() | select('match', '^' ~ pki_search_certificates_pattern ~ '.*') %}
{% set _certs = [] %}
{% for _cert in _cert_search_hits | default([]) %}
{% set _ = _certs.extend(lookup('vars', _cert)) %}
{% endfor %}
{% endif %}
{{ _certs }}

View File

@ -13,5 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
## APT Cache options
cache_timeout: 600
# directories for certificate authorities on the CA host
pki_ca_dirs:
- path: "{{ pki_dir }}"
- path: "{{ pki_dir ~ '/roots' }}"
pki_ca_install_command:
apt: "update-ca-certificates"
dnf: "update-ca-trust extract"

26
vars/standalone_cert.yml Normal file
View File

@ -0,0 +1,26 @@
---
# Copyright 2021, BBC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# directories for server certificates on the CA host
_pki_cert_dirs:
- path: "{{ pki_dir }}"
- path: "{{ pki_dir ~ '/certs' }}"
mode: "0744"
- path: "{{ pki_dir ~ '/certs/csr' }}"
mode: "0700"
- path: "{{ pki_dir ~ '/certs/private' }}"
mode: "0700"
- path: "{{ pki_dir ~ '/certs/certs' }}"
mode: "0444"

View File

@ -16,8 +16,7 @@
- project:
templates:
- check-requirements
- openstack-ansible-deploy-aio_metal-jobs
- openstack-ansible-deploy-aio_distro_metal-jobs
- openstack-ansible-deploy-infra_lxc-jobs
- openstack-ansible-linters-jobs
- publish-openstack-docs-pti
- build-release-notes-jobs-python3