From fd7509cd43da21835cace1e7a28f9109a6382df2 Mon Sep 17 00:00:00 2001 From: Jonathan Rosser Date: Thu, 6 May 2021 14:20:12 +0000 Subject: [PATCH] Use external PKI role to manage haproxy self-signed certificates The external PKI role can generate a self signed CA and Intermediate certificate, and then create a server certificate for haproxy if no defaults are overridden. The new openstack_pki_* settings allow an external self signed CA to be used, but still create valid haproxy server certificates from that external CA in an openstack-ansible deployment. The original beheviour providing user supplied certificates in the haproxy_user_ssl_* variables will still work, disabling the generation of certificates but using the external PKI role to just install the supplied certs and keys. Depends-On: https://review.opendev.org/c/openstack/openstack-ansible/+/788031 Change-Id: I7482f55e991bacd9dccd2748c236dcd9d01124f3 --- defaults/main.yml | 79 +++++++++++++++++++++++++++- handlers/main.yml | 2 + tasks/haproxy_ssl.yml | 27 ++++++---- tasks/haproxy_ssl_key_create.yml | 40 -------------- tasks/haproxy_ssl_key_distribute.yml | 36 ------------- tasks/haproxy_ssl_key_store.yml | 29 ---------- tasks/haproxy_ssl_self_signed.yml | 23 -------- tasks/haproxy_ssl_user_provided.yml | 53 ------------------- tasks/main.yml | 23 +++++--- 9 files changed, 112 insertions(+), 200 deletions(-) delete mode 100644 tasks/haproxy_ssl_key_create.yml delete mode 100644 tasks/haproxy_ssl_key_distribute.yml delete mode 100644 tasks/haproxy_ssl_key_store.yml delete mode 100644 tasks/haproxy_ssl_self_signed.yml delete mode 100644 tasks/haproxy_ssl_user_provided.yml diff --git a/defaults/main.yml b/defaults/main.yml index 8d91872..810900c 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -84,14 +84,89 @@ haproxy_bind_on_non_local: False haproxy_ssl: true haproxy_ssl_all_vips: false haproxy_ssl_dh_param: 2048 -haproxy_ssl_self_signed_regen: no haproxy_ssl_cert: /etc/ssl/certs/haproxy.cert haproxy_ssl_key: /etc/ssl/private/haproxy.key haproxy_ssl_pem: /etc/ssl/private/haproxy.pem haproxy_ssl_ca_cert: /etc/ssl/certs/haproxy-ca.pem -haproxy_ssl_self_signed_subject: "/C=US/ST=Texas/L=San Antonio/O=IT/CN={{ external_lb_vip_address }}/subjectAltName=IP.1={{ external_lb_vip_address }}" haproxy_ssl_cipher_suite: "{{ ssl_cipher_suite | default('ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS') }}" haproxy_ssl_bind_options: "force-tlsv12" + +# haproxy self signed certificate + +# Storage location for SSL certificate authority +haproxy_pki_dir: "{{ openstack_pki_dir | default('/etc/pki/haproxy-ca') }}" + +# Delegated host for operating the certificate authority +haproxy_pki_setup_host: "{{ openstack_pki_setup_host | default('localhost') }}" + +# Create a certificate authority if one does not already exist +haproxy_pki_create_ca: "{{ openstack_pki_authorities is not defined | bool }}" +haproxy_pki_regen_ca: '' +haproxy_pki_authorities: + - name: "HAProxyRoot" + country: "GB" + state_or_province_name: "England" + organization_name: "Example Corporation" + organizational_unit_name: "IT Security" + cn: "HAProxy Root CA" + provider: selfsigned + basic_constraints: "CA:TRUE" + key_usage: + - digitalSignature + - cRLSign + - keyCertSign + not_after: "+3650d" + - name: "HAProxyIntermediate" + country: "GB" + state_or_province_name: "England" + organization_name: "Example Corporation" + organizational_unit_name: "IT Security" + cn: "HAProxy Intermediate CA" + provider: ownca + basic_constraints: "CA:TRUE,pathlen:0" + key_usage: + - digitalSignature + - cRLSign + - keyCertSign + not_after: "+3650d" + signed_by: "HAProxyRoot" + +# Installation details for certificate authorities +haproxy_pki_install_ca: + - name: "HAProxyRoot" + condition: "{{ haproxy_pki_create_ca }}" + +# HAProxy server certificate +haproxy_pki_keys_path: "{{ haproxy_pki_dir ~ '/certs/private/' }}" +haproxy_pki_certs_path: "{{ haproxy_pki_dir ~ '/certs/certs/' }}" +haproxy_pki_intermediate_cert_name: "{{ openstack_pki_service_intermediate_cert_name | default('HAProxyIntermediate') }}" +haproxy_pki_intermediate_cert_path: "{{ haproxy_pki_dir ~ '/roots/' ~ haproxy_pki_intermediate_cert_name ~ '/certs/' ~ haproxy_pki_intermediate_cert_name ~ '.crt' }}" +haproxy_pki_regen_cert: '' +haproxy_pki_certificates: + - name: "haproxy_{{ ansible_facts['hostname'] }}" + provider: ownca + cn: "{{ ansible_facts['hostname'] }}" + san: "{{ 'DNS:' ~ ansible_facts['hostname'] ~ ',DNS:' ~ ansible_facts['fqdn'] ~ ',IP:' ~ haproxy_bind_external_lb_vip_address }}" + signed_by: "{{ haproxy_pki_intermediate_cert_name }}" + +# Installation details for SSL certificates +haproxy_pki_install_certificates: + - src: "{{ haproxy_user_ssl_cert | default(haproxy_pki_certs_path ~ 'haproxy_' ~ ansible_facts['hostname'] ~ '.crt') }}" + dest: "{{ haproxy_ssl_cert }}" + owner: "root" + group: "root" + mode: "0644" + - src: "{{ haproxy_user_ssl_key | default(haproxy_pki_keys_path ~ 'haproxy_' ~ ansible_facts['hostname'] ~ '.key.pem') }}" + dest: "{{ haproxy_ssl_key }}" + owner: "haproxy" + group: "haproxy" + mode: "0600" + - src: "{{ haproxy_user_ssl_ca_cert | default(haproxy_pki_intermediate_cert_path) }}" + dest: "{{ haproxy_ssl_ca_cert }}" + owner: "haproxy" + group: "haproxy" + mode: "0644" + # activate letsencrypt option haproxy_ssl_letsencrypt_enable: false # choose the certbot install method, 'distro' for a package manager repo, or downloaded with the certbot-auto script 'certbot-auto' diff --git a/handlers/main.yml b/handlers/main.yml index d495a3c..27d9838 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -17,6 +17,8 @@ shell: > cat {{ haproxy_ssl_cert }} {{ haproxy_user_ssl_ca_cert is defined | ternary(haproxy_ssl_ca_cert,'') }} {{ haproxy_ssl_key }} > {{ haproxy_ssl_pem }} notify: Reload haproxy + listen: + - cert installed - name: Regenerate haproxy configuration assemble: diff --git a/tasks/haproxy_ssl.yml b/tasks/haproxy_ssl.yml index 13bec3c..ee6a4ec 100644 --- a/tasks/haproxy_ssl.yml +++ b/tasks/haproxy_ssl.yml @@ -13,18 +13,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -- name: Ensure the private ssl directory exists - file: - dest: "/etc/ssl/private" - state: "directory" - tags: - - haproxy-ssl - #NOTE (jrosser) the self signed certificate is also needed for bootstrapping #letsencrypt, as haproxy will not start with ssl config but a missing certificate -- include_tasks: haproxy_ssl_self_signed.yml +- name: Create and install SSL certificates + include_role: + name: pki + tasks_from: "{{ openstack_pki_authorities is defined | ternary('main_certs.yml', 'main.yml') }}" + vars: + pki_setup_host: "{{ haproxy_pki_setup_host }}" + pki_dir: "{{ haproxy_pki_dir }}" + pki_create_ca: "{{ haproxy_pki_create_ca }}" + pki_regen_ca: "{{ haproxy_pki_regen_ca }}" + pki_authorities: "{{ haproxy_pki_authorities }}" + pki_install_ca: "{{ haproxy_pki_install_ca }}" + pki_create_certificates: "{{ haproxy_user_ssl_cert is not defined and haproxy_user_ssl_key is not defined }}" + pki_regen_cert: "{{ haproxy_pki_regen_cert }}" + pki_certificates: "{{ haproxy_pki_certificates }}" + pki_install_certificates: "{{ haproxy_pki_install_certificates }}" when: - haproxy_ssl | bool - - haproxy_user_ssl_cert is not defined or haproxy_user_ssl_key is not defined - -- include_tasks: haproxy_ssl_user_provided.yml + - haproxy_user_ssl_cert is not defined or haproxy_user_ssl_key is not defined \ No newline at end of file diff --git a/tasks/haproxy_ssl_key_create.yml b/tasks/haproxy_ssl_key_create.yml deleted file mode 100644 index 1e0efb0..0000000 --- a/tasks/haproxy_ssl_key_create.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -# Copyright 2015, Rackspace US, Inc. -# -# 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: Remove self signed certs and keys for regen - file: - dest: "{{ item }}" - state: "absent" - with_items: - - "{{ haproxy_ssl_pem }}" - - "{{ haproxy_ssl_key }}" - - "{{ haproxy_ssl_cert }}" - when: haproxy_ssl_self_signed_regen | bool - tags: - - haproxy-ssl - -- name: Create self-signed ssl cert - command: > - openssl req -new -nodes -sha256 -x509 -subj - "{{ haproxy_ssl_self_signed_subject }}" - -days 3650 - -keyout {{ haproxy_ssl_key }} - -out {{ haproxy_ssl_cert }} - -extensions v3_ca - creates={{ haproxy_ssl_cert }} - notify: - - regen pem - tags: - - haproxy-ssl diff --git a/tasks/haproxy_ssl_key_distribute.yml b/tasks/haproxy_ssl_key_distribute.yml deleted file mode 100644 index b9e5afe..0000000 --- a/tasks/haproxy_ssl_key_distribute.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -# Copyright 2014, Rackspace US, Inc. -# -# 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: Distribute self signed ssl key - copy: - dest: "{{ haproxy_ssl_key }}" - content: "{{ hostvars[groups[haproxy_ansible_group_name][0]]['haproxy_ssl_key_fact'] | b64decode }}" - mode: "0640" - when: hostvars[groups[haproxy_ansible_group_name][0]]['haproxy_ssl_key_fact'] is defined - notify: - - regen pem - tags: - - haproxy-ssl - -- name: Distribute self signed ssl cert - copy: - dest: "{{ haproxy_ssl_cert }}" - content: "{{ hostvars[groups[haproxy_ansible_group_name][0]]['haproxy_ssl_cert_fact'] | b64decode }}" - mode: "0640" - when: hostvars[groups[haproxy_ansible_group_name][0]]['haproxy_ssl_cert_fact'] is defined - notify: - - regen pem - tags: - - haproxy-ssl diff --git a/tasks/haproxy_ssl_key_store.yml b/tasks/haproxy_ssl_key_store.yml deleted file mode 100644 index 9321aa6..0000000 --- a/tasks/haproxy_ssl_key_store.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# Copyright 2015, Rackspace US, Inc. -# -# 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: Store ssl cert - slurp: - src: "{{ haproxy_ssl_cert }}" - register: _haproxy_ssl_cert - -- name: Store ssl key - slurp: - src: "{{ haproxy_ssl_key }}" - register: _haproxy_ssl_key - -- name: Register a fact for the cert and key - set_fact: - haproxy_ssl_cert_fact: "{{ _haproxy_ssl_cert.content }}" - haproxy_ssl_key_fact: "{{ _haproxy_ssl_key.content }}" diff --git a/tasks/haproxy_ssl_self_signed.yml b/tasks/haproxy_ssl_self_signed.yml deleted file mode 100644 index a896be8..0000000 --- a/tasks/haproxy_ssl_self_signed.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -# Copyright 2015, Rackspace US, Inc. -# -# 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. - -- include_tasks: haproxy_ssl_key_create.yml - when: inventory_hostname == ansible_play_hosts[0] - -- include_tasks: haproxy_ssl_key_store.yml - when: inventory_hostname == ansible_play_hosts[0] - -- include_tasks: haproxy_ssl_key_distribute.yml - when: inventory_hostname != ansible_play_hosts[0] diff --git a/tasks/haproxy_ssl_user_provided.yml b/tasks/haproxy_ssl_user_provided.yml deleted file mode 100644 index 656ae77..0000000 --- a/tasks/haproxy_ssl_user_provided.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -# Copyright 2015, Rackspace US, Inc. -# -# 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: Drop user provided ssl cert - copy: - src: "{{ haproxy_user_ssl_cert }}" - dest: "{{ haproxy_ssl_cert }}" - owner: "root" - group: "root" - mode: "0644" - when: haproxy_user_ssl_cert is defined - notify: - - regen pem - tags: - - haproxy-ssl - -- name: Drop user provided ssl key - copy: - src: "{{ haproxy_user_ssl_key }}" - dest: "{{ haproxy_ssl_key }}" - owner: "root" - group: "root" - mode: "0640" - when: haproxy_user_ssl_key is defined - notify: - - regen pem - tags: - - haproxy-ssl - -- name: Drop user provided ssl CA cert - copy: - src: "{{ haproxy_user_ssl_ca_cert }}" - dest: "{{ haproxy_ssl_ca_cert }}" - owner: "root" - group: "root" - mode: "0644" - when: haproxy_user_ssl_ca_cert is defined - notify: - - regen pem - tags: - - haproxy-ssl diff --git a/tasks/main.yml b/tasks/main.yml index 69a0c55..3a81118 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -31,12 +31,23 @@ tags: - haproxy_server-install -- include_tasks: haproxy_ssl.yml - when: haproxy_ssl | bool - args: - apply: - tags: - - haproxy_server-config + #NOTE (jrosser) the self signed certificate is also needed for bootstrapping + #letsencrypt, as haproxy will not start with ssl config but a missing certificate +- name: Create and install SSL certificates + include_role: + name: pki + vars: + pki_setup_host: "{{ haproxy_pki_setup_host }}" + pki_dir: "{{ haproxy_pki_dir }}" + pki_create_ca: "{{ haproxy_pki_create_ca }}" + pki_authorities: "{{ haproxy_pki_authorities }}" + pki_install_ca: "{{ haproxy_pki_install_ca }}" + pki_create_certificates: "{{ haprpxy_user_ssl_cert is not defined and haproxy_user_ssl_key is not defined }}" + pki_certificates: "{{ haproxy_pki_certificates }}" + pki_install_certificates: "{{ haproxy_pki_install_certificates }}" + when: + - haproxy_ssl | bool + - haproxy_user_ssl_cert is not defined or haproxy_user_ssl_key is not defined - import_tasks: haproxy_post_install.yml tags: