From 6c7355000545a4c86593e4fb9a4eb253c94ddfa4 Mon Sep 17 00:00:00 2001 From: Jesse Pretorius Date: Fri, 13 Jul 2018 17:56:27 +0100 Subject: [PATCH] Make certificate generation host configurable We do not necessarily always want to use the deployment host for certificate generation. This patch allows the deployer to set an alternative host to delegate these tasks to in the same pattern as has been done for the service registration tasks. Additionally, due to the way this is done we can also: 1. Remove the task to install pyOpenSSL, because it is already in the Ansible runtime venv for both role tests and the integrated build. We have set the python interpreter correctly to ensure that we have access to it. 2. Consolidate the 'install' and 'config' tasks into a single task file. Given that most of the 'install' tasks were doing runtime configuration, it seems best to rather move them into a single file and use the 'config' tag. Change-Id: If2f1ab03ccd9400fe05e2948ddfe33c8796dbd97 --- defaults/main.yml | 14 +++- tasks/main.yml | 8 +-- tasks/octavia_certs.yml | 116 ++++++++++++++++++++++++++------ tasks/octavia_certs_install.yml | 89 ------------------------ 4 files changed, 109 insertions(+), 118 deletions(-) delete mode 100644 tasks/octavia_certs_install.yml diff --git a/defaults/main.yml b/defaults/main.yml index 2a3562bd..3236ea28 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -391,9 +391,21 @@ octavia_amphora_driver: amphora_haproxy_rest_driver octavia_compute_driver: compute_nova_driver octavia_network_driver: allowed_address_pairs_driver +# # Certificate generation -# this directory needs to be accessible +# + +# Set the host which will execute the openssl_* modules +# for the certificate generation. The host must already +# have access to pyOpenSSL. +octavia_cert_setup_host: "{{ openstack_cert_setup_host | default('localhost') }}" + +# Set the directory where the certificates will be stored +# on the above host. If the host is localhost, then the user +# running the playbook must have access to it. octavia_cert_dir: "{{ lookup('env', 'HOME') }}/openstack-ansible/octavia" +octavia_cert_dir_owner: "{{ lookup('env', 'USER') }}" + octavia_cert_key_length_server: '4096' # key length octavia_cert_cipher_server: 'aes256' octavia_cert_cipher_client: 'aes256' diff --git a/tasks/main.yml b/tasks/main.yml index 8ba77a8a..e43b5f29 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -32,15 +32,9 @@ tags: - always -- include: octavia_certs_install.yml - when: octavia_generate_certs | bool - delegate_to: localhost - tags: - - octavia-install - - include: octavia_certs.yml + static: no when: octavia_generate_certs | bool - delegate_to: localhost tags: - octavia-config diff --git a/tasks/octavia_certs.yml b/tasks/octavia_certs.yml index e445cb87..c05db81b 100644 --- a/tasks/octavia_certs.yml +++ b/tasks/octavia_certs.yml @@ -13,26 +13,102 @@ # See the License for the specific language governing permissions and # limitations under the License. -- name: Create the server CA private key - openssl_privatekey: - path: "{{ octavia_ca_private_key }}" - passphrase: "{{ octavia_ca_private_key_passphrase }}" - cipher: "{{ octavia_cert_cipher_server }}" - size: "{{ octavia_cert_key_length_server }}" - -- name: Create server CA certificate - command: > - openssl req -x509 -passin pass:'{{ octavia_ca_private_key_passphrase }}' -new -nodes -key {{ octavia_ca_private_key }} \ - -config {{ octavia_cert_dir }}/openssl.cnf \ - -subj "{{ octavia_cert_server_ca_subject }}" \ - -days {{ octavia_cert_validity_days }} \ - -out {{ octavia_ca_certificate }} - args: - chdir: "{{ octavia_cert_dir }}" - creates: "{{ octavia_ca_certificate }}" - -- name: Generate Octavia client certificate +# We set the python interpreter to the ansible runtime venv if +# the delegation is to localhost so that we get access to the +# appropriate python libraries in that venv. If the delegation +# is to another host, we assume that it is accessible by the +# system python instead. +- name: Prepare octavia_cert_setup_host for certificate generation + delegate_to: "{{ octavia_cert_setup_host }}" + vars: + ansible_python_interpreter: >- + {{ (octavia_cert_setup_host == 'localhost') | ternary(ansible_playbook_python, ansible_python['executable']) }} block: + - name: Create certificate directories + file: + path: "{{ item.path }}" + state: directory + mode: "{{ item.mode }}" + owner: "{{ octavia_cert_dir_owner }}" + with_items: + - { path: "{{ octavia_cert_dir }}", mode: '0750' } + - { path: "{{ octavia_cert_dir }}/newcerts", mode: '0750'} + - { path: "{{ octavia_cert_dir }}/private", mode: '0750'} + + # ansible's openssl_certificate can't create X509 extensions + # but you need CA: true in Basic Constraints to have a CA cert + + # set up openssl for use + - name: Touch index.txt + file: + path: "{{ octavia_cert_dir }}/index.txt" + state: touch + mode: 0755 + + - name: Init serial + copy: + content: "01" + dest: "{{ octavia_cert_dir }}/serial" + force: no + + - name: Generate openssl.conf + template: + src: "templates/openssl.conf.j2" + dest: "{{ octavia_cert_dir }}/openssl.cnf" + mode: 0440 + +# These are run at the very first installation of Octavia +# While Octavia acts as a CA for the server certificates, +# for the amphora it only needs a client certificate and +# the (public) certificate authority certificate. +# Generating the secret key here and storing it +# on the deploy host allows us to rotate the client +# certificate without recycling the amphora since +# we can keep the same CA. + +- name: Generate keys/certificates on octavia_cert_setup_host + delegate_to: "{{ octavia_cert_setup_host }}" + vars: + ansible_python_interpreter: >- + {{ (octavia_cert_setup_host == 'localhost') | ternary(ansible_playbook_python, ansible_python['executable']) }} + when: octavia_generate_client_cert | bool + block: + - name: Create the client CAs private key + openssl_privatekey: + path: "{{ octavia_client_ca_key }}" + passphrase: "{{ octavia_cert_client_password }}" + cipher: "{{ octavia_cert_cipher_client }}" + size: "{{ octavia_cert_key_length_client }}" + + - name: Create client CA certificate + command: > + openssl req -x509 -passin pass:'{{ octavia_cert_client_password }}' -new -nodes -key {{ octavia_client_ca_key }} \ + -config {{ octavia_cert_dir }}/openssl.cnf \ + -subj "{{ octavia_cert_client_ca_subject }}" \ + -days {{ octavia_cert_validity_days }} \ + -out {{ octavia_client_ca }} + args: + chdir: "{{ octavia_cert_dir }}" + creates: "{{ octavia_client_ca }}" + + - name: Create the server CA private key + openssl_privatekey: + path: "{{ octavia_ca_private_key }}" + passphrase: "{{ octavia_ca_private_key_passphrase }}" + cipher: "{{ octavia_cert_cipher_server }}" + size: "{{ octavia_cert_key_length_server }}" + + - name: Create server CA certificate + command: > + openssl req -x509 -passin pass:'{{ octavia_ca_private_key_passphrase }}' -new -nodes -key {{ octavia_ca_private_key }} \ + -config {{ octavia_cert_dir }}/openssl.cnf \ + -subj "{{ octavia_cert_server_ca_subject }}" \ + -days {{ octavia_cert_validity_days }} \ + -out {{ octavia_ca_certificate }} + args: + chdir: "{{ octavia_cert_dir }}" + creates: "{{ octavia_ca_certificate }}" + - name: Create the client cert private key openssl_privatekey: path: "{{ octavia_cert_dir }}/client.key" @@ -64,5 +140,3 @@ creates: "{{ octavia_client_cert }}" tags: - skip_ansible_lint - - when: octavia_generate_client_cert|bool diff --git a/tasks/octavia_certs_install.yml b/tasks/octavia_certs_install.yml deleted file mode 100644 index 7ccb54ac..00000000 --- a/tasks/octavia_certs_install.yml +++ /dev/null @@ -1,89 +0,0 @@ ---- -# Copyright 2018, 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: Ensure python OpenSSL dependencies are installed. - pip: - name: pyOpenSSL - state: present - extra_args: >- - {{ octavia_developer_mode | ternary(pip_install_developer_constraints | default('--constraint /opt/developer-pip-constraints.txt'), '') }} - {{ (pip_install_upper_constraints is defined) | ternary('--constraint ' + pip_install_upper_constraints | default(''),'') }} - {{ pip_install_options | default('') }} - become: true - -- name: Generate Cert Dirs - file: - path: "{{ item.path }}" - state: directory - mode: "{{ item.mode }}" - owner: "{{ lookup('env', 'USER') }}" - with_items: - - { path: "{{ octavia_cert_dir }}", mode: '0750' } - - { path: "{{ octavia_cert_dir }}/newcerts", mode: '0750'} - - { path: "{{ octavia_cert_dir }}/private", mode: '0750'} - -# ansible's openssl_certificate can't create X509 extensions -# but you need CA: true in Basic Constrainst to have a CA cert - -# set up openssl for use -- name: Touch index.txt - file: - path: "{{ octavia_cert_dir }}/index.txt" - state: touch - mode: 0755 - -- name: Init serial - copy: - content: "01" - dest: "{{ octavia_cert_dir }}/serial" - force: no - -- name: Generate openssl.conf - template: - src: "templates/openssl.conf.j2" - dest: "{{ octavia_cert_dir }}/openssl.cnf" - mode: 0440 - -# These are run at the very first installation of Octavia -# While Octavia acts as a CA for the server certificates, -# for the amphora it only needs a client certificate and -# the (public) certificate authority certificate. -# Generating the secret key here and storing it -# on the deploy host allows us to rotate the client -# certificate without recycling the amphora since -# we can keep the same CA. - -- name: Generate client certificate - block: - - name: Create the client CAs private key - openssl_privatekey: - path: "{{ octavia_client_ca_key }}" - passphrase: "{{ octavia_cert_client_password }}" - cipher: "{{ octavia_cert_cipher_client }}" - size: "{{ octavia_cert_key_length_client }}" - - - name: Create client CA certificate - command: > - openssl req -x509 -passin pass:'{{ octavia_cert_client_password }}' -new -nodes -key {{ octavia_client_ca_key }} \ - -config {{ octavia_cert_dir }}/openssl.cnf \ - -subj "{{ octavia_cert_client_ca_subject }}" \ - -days {{ octavia_cert_validity_days }} \ - -out {{ octavia_client_ca }} - args: - chdir: "{{ octavia_cert_dir }}" - creates: "{{ octavia_client_ca }}" - - when: octavia_generate_client_cert | bool == True -