diff --git a/defaults/main.yml b/defaults/main.yml index 3faa736d..3c04b987 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -238,17 +238,6 @@ octavia_amp_ram: 1024 octavia_amp_vcpu: 1 octavia_amp_disk: 2 -# client certs -octavia_client_ca: "{{ octavia_system_home_folder }}/certs/ca_01.pem" -octavia_client_cert: "{{ octavia_system_home_folder }}/certs/client.pem" -# server -octavia_server_ca: "{{ octavia_system_home_folder }}/certs/ca_01.pem" -# ca certs -octavia_ca_private_key: "{{ octavia_system_home_folder }}/certs/private/cakey.pem" -octavia_ca_certificate: "{{ octavia_system_home_folder }}/certs/ca_01.pem" -octavia_ca_private_key_passphrase: foobar -octavia_signing_digest: sha256 - # spare pool - increase to speed up load balancer creation and fail over octavia_spare_amphora_pool_size: 1 @@ -400,7 +389,33 @@ 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 +octavia_cert_dir: "{{ lookup('env', 'HOME') }}/openstack-ansible/octavia" +octavia_cert_key_length_server: '4096' # key length +octavia_cert_cipher_server: 'aes256' +octavia_cert_cipher_client: 'aes256' +octavia_cert_key_length_client: '4096' # key length +octavia_cert_server_ca_common_name: 'www.example.com' # change this to something more real +octavia_cert_client_ca_common_name: 'www.example.com' # change this to something more real +octavia_cert_client_req_common_name: 'www.example.com' # change this to something more real +octavia_generate_client_cert: True # generate self signed client certs +octavia_generate_certs: True + +# client certs +octavia_client_ca_key: "{{ octavia_cert_dir }}/ca_01.key" +octavia_client_ca: "{{ octavia_cert_dir }}/ca_01.pem" +octavia_client_cert: "{{ octavia_cert_dir }}/client.pem" +# server +octavia_server_ca: "{{ octavia_ca_certificate }}" +# ca certs +octavia_ca_private_key: "{{ octavia_cert_dir }}/private/cakey.pem" +octavia_ca_private_key_passphrase: "{{ octavia_cert_password_client }}" +octavia_ca_certificate: "{{ octavia_cert_dir }}/ca_server_01.pem" +octavia_signing_digest: sha256 + ## Tunable overrides octavia_octavia_conf_overrides: {} octavia_api_paste_ini_overrides: {} octavia_policy_overrides: {} + diff --git a/doc/source/configure-octavia.rst b/doc/source/configure-octavia.rst index 9aac629d..c2ed4db6 100644 --- a/doc/source/configure-octavia.rst +++ b/doc/source/configure-octavia.rst @@ -163,18 +163,9 @@ Creating the cryptographic certificates For production installation make sure that you review this very carefully with your own security requirements and potantially use your own CA to sign the certificates. -#. Run the certificate script. - - In the bin directory of the Octavia project you cloned above run: - - .. code-block:: bash - - mkdir /var/lib/octavia/certs - source create_certificates.sh /var/lib/octavia/certs `pwd`/../etc/certificates/openssl.cnf - -.. note:: - The certificates will be created in ``/var/lib/octavia/certs`` where the - ansible script are expecting them. +The system will automatically generate and use self-signed certificates with different +Certificate Authorities for control plane and amphora. Make sure to store a copy in +a safe place for potential disaster recovery. Optional: Configuring Octavia with ssh access to the amphora ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/releasenotes/notes/certificate_generation-a9cdcc8e3b1e186f.yaml b/releasenotes/notes/certificate_generation-a9cdcc8e3b1e186f.yaml new file mode 100644 index 00000000..4d909888 --- /dev/null +++ b/releasenotes/notes/certificate_generation-a9cdcc8e3b1e186f.yaml @@ -0,0 +1,14 @@ +--- + +features: + - | + Octavia requires SSL certificates for communication with the amphora. This + adds the automatic creation of self signed certificates for this purpose. + It uses different certificate authorities for amphora and control plane + thus insuring maximum security. +security: + - | + It is recommended that the certificate generation is always reviewed by + security professionals since algorithms and key-lengths considered secure + change all the time. + diff --git a/tasks/main.yml b/tasks/main.yml index 1beb30c3..8ba77a8a 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -32,6 +32,18 @@ tags: - always +- include: octavia_certs_install.yml + when: octavia_generate_certs | bool + delegate_to: localhost + tags: + - octavia-install + +- include: octavia_certs.yml + when: octavia_generate_certs | bool + delegate_to: localhost + tags: + - octavia-config + - include: octavia_pre_install.yml tags: - octavia-install diff --git a/tasks/octavia_certs.yml b/tasks/octavia_certs.yml new file mode 100644 index 00000000..0a426880 --- /dev/null +++ b/tasks/octavia_certs.yml @@ -0,0 +1,70 @@ +--- +# 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: 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 CSR + openssl_csr: + path: "{{ octavia_cert_dir }}/ca_server_01.csr" + common_name: "{{ octavia_cert_server_ca_common_name }}" + privatekey_path: "{{ octavia_ca_private_key }}" + privatekey_passphrase: "{{ octavia_ca_private_key_passphrase }}" + +- name: Create server CA certificate + openssl_certificate: + path: "{{ octavia_ca_certificate }}" + privatekey_path: "{{ octavia_ca_private_key }}" + privatekey_passphrase: "{{ octavia_ca_private_key_passphrase }}" + csr_path: "{{ octavia_cert_dir }}/ca_server_01.csr" + provider: selfsigned + owner: "{{ lookup('env', 'USER') }}" + +- name: Generate Octavia client certificate + block: + - name: Create the client cert private key + openssl_privatekey: + path: "{{ octavia_cert_dir }}/client.key" + size: "{{ octavia_cert_key_length_client }}" + + - name: Create client cert CSR + openssl_csr: + path: "{{ octavia_cert_dir }}/client.csr" + common_name: "{{ octavia_cert_client_req_common_name }}" + privatekey_path: "{{ octavia_cert_dir }}/client.key" + + - name: Create client certificate + openssl_certificate: + path: "{{ octavia_cert_dir }}/client-.pem" + privatekey_path: "{{ octavia_ca_private_key }}" + privatekey_passphrase: "{{ octavia_ca_private_key_passphrase }}" + csr_path: "{{ octavia_cert_dir }}/client.csr" + provider: selfsigned + owner: "{{ lookup('env', 'USER') }}" + + # use cat to avoid mangling the certs + - name: Generate single pem client.pem + shell: "cat client-.pem client.key >{{ octavia_client_cert }}" + args: + chdir: "{{ octavia_cert_dir }}" + 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 new file mode 100644 index 00000000..36e3329f --- /dev/null +++ b/tasks/octavia_certs_install.yml @@ -0,0 +1,67 @@ +--- +# 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 + +- 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'} + +# 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_password_client }}" + cipher: "{{ octavia_cert_cipher_client }}" + size: "{{ octavia_cert_key_length_client }}" + + - name: Create client CA CSR + openssl_csr: + path: "{{ octavia_cert_dir }}/ca_01.csr" + common_name: "{{ octavia_cert_client_ca_common_name }}" + privatekey_path: "{{ octavia_client_ca_key }}" + privatekey_passphrase: "{{ octavia_cert_password_client }}" + + - name: Create client CA certificate + openssl_certificate: + path: "{{ octavia_client_ca }}" + privatekey_path: "{{ octavia_client_ca_key }}" + privatekey_passphrase: "{{ octavia_cert_password_client }}" + csr_path: "{{ octavia_cert_dir }}/ca_01.csr" + provider: selfsigned + owner: "{{ lookup('env', 'USER') }}" + + when: octavia_generate_client_cert | bool == True + diff --git a/tests/os_octavia-overrides.yml b/tests/os_octavia-overrides.yml index 5bbe2ad4..503ca63f 100644 --- a/tests/os_octavia-overrides.yml +++ b/tests/os_octavia-overrides.yml @@ -98,3 +98,7 @@ octavia_ssh_enabled: True octavia_amphora_driver: "{% if test_octavia_amphora | bool %}amphora_haproxy_rest_driver{% else %}amphora_noop_driver{% endif %}" octavia_compute_driver: "{% if test_octavia_amphora | bool %}compute_nova_driver{% else %}compute_noop_driver{% endif %}" octavia_network_driver: "{% if test_octavia_amphora | bool %}allowed_address_pairs_driver{% else %}network_noop_driver{% endif %}" + +# cert generation +octavia_cert_password_client: 'changeme' + diff --git a/tests/test-configure-octavia.yml b/tests/test-configure-octavia.yml index 7509735f..fa2f1d04 100644 --- a/tests/test-configure-octavia.yml +++ b/tests/test-configure-octavia.yml @@ -18,38 +18,6 @@ become: True gather_facts: True tasks: - - name: Install apt packages - apt: - pkg: "{{ item }}" - state: "{{ octavia_package_state }}" - update_cache: yes - register: install_packages - until: install_packages|success - retries: 5 - delay: 2 - with_items: - - git - - name: Clone Octavia - git: - repo: "https://git.openstack.org/openstack/octavia" - dest: "{{ octavia_system_home_folder }}/octavia" - version: "{{ octavia_git_install_branch }}" - tags: - - skip_ansible_lint - - name: Change permission - file: - path: "{{ octavia_system_home_folder }}/octavia/bin/create_certificates.sh" - mode: 0755 - - name: Generate certs - shell: "{{ octavia_system_home_folder }}/octavia/bin/create_certificates.sh {{ octavia_system_home_folder }}/certs {{ octavia_system_home_folder }}/octavia/etc/certificates/openssl.cnf" - args: - creates: "{{ octavia_system_home_folder }}/certs/ca_01.pem" - tags: - - skip_ansible_lint - - name: Fix certs/private directory access - file: - path: "{{ octavia_system_home_folder }}/certs/private" - mode: 0755 - name: Install pip requirements pip: name: "shade"