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 -