From 4b35b3e929cbc728b903bf19d8d169e376920832 Mon Sep 17 00:00:00 2001 From: Jesse Pretorius Date: Tue, 7 Jul 2015 12:59:45 +0100 Subject: [PATCH] Keystone SSL cert/key distribution and configuration This patch adds the option to provide an SSL certificate for the Keystone service (either self-signed or user provided) and to configure the endpoints and Keystone service appropriately. * A new boolean variable called 'keystone_ssl' enables/disables the configuration of SSL for the Keystone service. * The server key/certificate (and optionally a CA cert) are distributed to all keystone containers and used for the setup of SSL endpoints if the appropriate protocol is set. * The internal/public and the admin endpoints can be set to be served via http or https seperately via the 'keystone_service_*_proto' variables. * The logic to determine the appropriate load balancing configuration based on the Keystone endpoint protocol has been implemented in the haproxy vars. * Two new variables have been implemented for a user-provided server key and certificate: - keystone_user_ssl_cert: - keystone_user_ssl_key: If either of these is not defined, but a Keystone endpoint has been configured for SSL, then the missing cert/key will be self generated on the first Keystone container and distributed to the other containers. * A new variable has been implemented for a user-provided CA certificate: - keystone_user_ssl_ca_cert: * A new variable called 'keystone_ssl_self_signed_subject' has been implemented to allow the user to override the certificate properties, such as the CN and subjectAltName. Upgrade notes: * The SSL-based client authentication configuration in Apache has been removed as it appears to be unused. * The minimum Ansible version for the os_keystone and haproxy_server roles have been increased to v1.9.0 as it's the minimum version that supports ternary filters. * The boolean 'keystone_ssl_enabled' has been renamed to 'keystone_ssl'. This maintains a pattern set in the haproxy role for enablement of ssl offloading in the load balancer. * The Apache configuration appropriately implements the 'SSLCACertificateFile' instead of the 'SSLCACertificatePath' directive in order to ensure that the appropriate signing certificate is provided to the browser. * The 'keystone_self_signed_regen' variable has been renamed to 'keystone_ssl_self_signed_regen'. * The default names for the deployed keys/certificates have been changed: - /etc/ssl/certs/apache.cert > /etc/ssl/certs/keystone.pem - /etc/ssl/private/apache.key > /etc/ssl/private/keystone.key DocImpact Partial-Bug: #1466827 Implements: blueprint keystone-federation Change-Id: I4c5ea7b6bfc3d7d7230a7440fa501241826c9dee Co-Authored-By: Miguel Grinberg --- playbooks/haproxy-install.yml | 10 ++--- playbooks/roles/haproxy_server/meta/main.yml | 4 +- playbooks/roles/os_keystone/defaults/main.yml | 19 +++++--- .../os_keystone/tasks/keystone_apache.yml | 3 +- .../roles/os_keystone/tasks/keystone_ssl.yml | 25 +++++++++++ .../tasks/keystone_ssl_key_create.yml | 36 ++++++++++++++++ .../tasks/keystone_ssl_key_distribute.yml | 35 +++++++++++++++ .../tasks/keystone_ssl_key_store.yml | 31 +++++++++++++ .../tasks/keystone_ssl_self_signed.yml | 26 +++++++++++ .../tasks/keystone_ssl_user_provided.yml | 43 +++++++++++++++++++ playbooks/roles/os_keystone/tasks/main.yml | 1 + .../templates/keystone-httpd.conf.j2 | 18 ++++---- .../os_keystone/templates/keystone.conf.j2 | 2 +- playbooks/vars/configs/haproxy_config.yml | 28 +++++++----- 14 files changed, 247 insertions(+), 34 deletions(-) create mode 100644 playbooks/roles/os_keystone/tasks/keystone_ssl.yml create mode 100644 playbooks/roles/os_keystone/tasks/keystone_ssl_key_create.yml create mode 100644 playbooks/roles/os_keystone/tasks/keystone_ssl_key_distribute.yml create mode 100644 playbooks/roles/os_keystone/tasks/keystone_ssl_key_store.yml create mode 100644 playbooks/roles/os_keystone/tasks/keystone_ssl_self_signed.yml create mode 100644 playbooks/roles/os_keystone/tasks/keystone_ssl_user_provided.yml diff --git a/playbooks/haproxy-install.yml b/playbooks/haproxy-install.yml index 8acabb7fc6..62e1e799f1 100644 --- a/playbooks/haproxy-install.yml +++ b/playbooks/haproxy-install.yml @@ -31,6 +31,8 @@ - name: Add keystone internal endpoint config include: roles/haproxy_server/tasks/haproxy_service_config.yml when: internal_lb_vip_address != external_lb_vip_address + vars_files: + - vars/configs/haproxy_config.yml vars: haproxy_service_configs: - service: @@ -39,11 +41,9 @@ haproxy_bind: "{{ internal_lb_vip_address }}" haproxy_port: 5000 haproxy_ssl: "{% if haproxy_ssl | bool and keystone_service_internaluri_proto == 'https' %}true{% else %}false{% endif %}" - haproxy_balance_type: http - haproxy_backend_options: - - "forwardfor" - - "httpchk" - - "httplog" + haproxy_balance_type: "{{ (keystone_ssl_internal | bool) | ternary('tcp','http') }}" + haproxy_balance_alg: "{{ (keystone_ssl_internal | bool) | ternary('source', 'leastconn') }}" + haproxy_backend_options: "{{ (keystone_ssl_internal | bool) | ternary(haproxy_backend_options_https, haproxy_backend_options_http) }}" tags: - haproxy-service-config roles: diff --git a/playbooks/roles/haproxy_server/meta/main.yml b/playbooks/roles/haproxy_server/meta/main.yml index cbcc43fc16..39a9abef35 100644 --- a/playbooks/roles/haproxy_server/meta/main.yml +++ b/playbooks/roles/haproxy_server/meta/main.yml @@ -15,10 +15,10 @@ galaxy_info: author: rcbops - description: Installation and setup of HAPtoxy + description: Installation and setup of HAProxy company: Rackspace license: Apache2 - min_ansible_version: 1.6.6 + min_ansible_version: 1.9.0 platforms: - name: Ubuntu versions: diff --git a/playbooks/roles/os_keystone/defaults/main.yml b/playbooks/roles/os_keystone/defaults/main.yml index 2272fc28d0..8264829ead 100644 --- a/playbooks/roles/os_keystone/defaults/main.yml +++ b/playbooks/roles/os_keystone/defaults/main.yml @@ -127,14 +127,23 @@ keystone_apache_log_level: info keystone_wsgi_threads: "{{ ansible_processor_vcpus | default(2) // 2 }}" keystone_wsgi_processes: "{{ ansible_processor_vcpus | default(1) }}" -keystone_ssl_enabled: false -keystone_ssl_cert_path: /etc/ssl/certs -keystone_ssl_cert: "{{ keystone_ssl_cert_path }}/apache.cert" -keystone_ssl_key_path: /etc/ssl/private -keystone_ssl_key: "{{ keystone_ssl_key_path }}/apache.key" +# set keystone_ssl to true to enable SSL configuration on the keystone containers +keystone_ssl: false +keystone_ssl_cert: /etc/ssl/certs/keystone.pem +keystone_ssl_key: /etc/ssl/private/keystone.key +keystone_ssl_ca_cert: /etc/ssl/certs/keystone-ca.pem keystone_ssl_protocol: "{{ ssl_protocol }}" keystone_ssl_cipher_suite: "{{ ssl_cipher_suite }}" +# if using a self-signed certificate, set this to true to regenerate it +keystone_ssl_self_signed_regen: false +keystone_ssl_self_signed_subject: "/C=US/ST=Texas/L=San Antonio/O=IT/CN={{ internal_lb_vip_address }}/subjectAltName=IP.1={{ external_lb_vip_address }}" + +# Set these in user_variables to deploy custom certificates +#keystone_user_ssl_cert: +#keystone_user_ssl_key: +#keystone_user_ssl_ca_cert: + ## Caching # If set this will enable dog pile cache for keystone. # keystone_cache_backend_argument: url:127.0.0.1:11211 diff --git a/playbooks/roles/os_keystone/tasks/keystone_apache.yml b/playbooks/roles/os_keystone/tasks/keystone_apache.yml index e97e8e7dd6..1f25d01154 100644 --- a/playbooks/roles/os_keystone/tasks/keystone_apache.yml +++ b/playbooks/roles/os_keystone/tasks/keystone_apache.yml @@ -60,11 +60,12 @@ - name: Enable/disable mod_ssl for apache2 apache2_module: name: ssl - state: "{{ (keystone_ssl_enabled | bool) | ternary('present', 'absent') }}" + state: "{{ (keystone_ssl | bool) | ternary('present', 'absent') }}" notify: - Restart Apache tags: - keystone-httpd + - keystone-ssl - name: Enable/disable mod_shib2 for apache2 apache2_module: diff --git a/playbooks/roles/os_keystone/tasks/keystone_ssl.yml b/playbooks/roles/os_keystone/tasks/keystone_ssl.yml new file mode 100644 index 0000000000..8c79413300 --- /dev/null +++ b/playbooks/roles/os_keystone/tasks/keystone_ssl.yml @@ -0,0 +1,25 @@ +--- +# 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: keystone_ssl_self_signed.yml + when: > + keystone_ssl | bool and + (keystone_user_ssl_cert is not defined or keystone_user_ssl_key is not defined) + tags: + - keystone-ssl + +- include: keystone_ssl_user_provided.yml + tags: + - keystone-ssl diff --git a/playbooks/roles/os_keystone/tasks/keystone_ssl_key_create.yml b/playbooks/roles/os_keystone/tasks/keystone_ssl_key_create.yml new file mode 100644 index 0000000000..9791f84f70 --- /dev/null +++ b/playbooks/roles/os_keystone/tasks/keystone_ssl_key_create.yml @@ -0,0 +1,36 @@ +--- +# 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 cert for regen + file: + dest: "{{ keystone_ssl_cert }}" + state: "absent" + when: keystone_ssl_self_signed_regen | bool + tags: + - keystone-ssl + +- name: Create self-signed Apache ssl cert + command: > + openssl req -new -nodes -sha256 -x509 -subj + "{{ keystone_ssl_self_signed_subject }}" + -days 3650 + -keyout {{ keystone_ssl_key }} + -out {{ keystone_ssl_cert }} + -extensions v3_ca + creates={{ keystone_ssl_cert }} + notify: Restart Apache + tags: + - keystone-configs + - keystone-ssl diff --git a/playbooks/roles/os_keystone/tasks/keystone_ssl_key_distribute.yml b/playbooks/roles/os_keystone/tasks/keystone_ssl_key_distribute.yml new file mode 100644 index 0000000000..55ccb444b4 --- /dev/null +++ b/playbooks/roles/os_keystone/tasks/keystone_ssl_key_distribute.yml @@ -0,0 +1,35 @@ +--- +# 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 cert and key + memcached: + name: "{{ item.name }}" + file_path: "{{ item.src }}" + state: "retrieve" + file_mode: "{{ item.file_mode }}" + dir_mode: "{{ item.dir_mode }}" + server: "{{ memcached_servers }}" + encrypt_string: "{{ memcached_encryption_key }}" + with_items: + - { src: "{{ keystone_ssl_cert }}", name: "keystone_ssl_cert", file_mode: "0644", dir_mode: "0755" } + - { src: "{{ keystone_ssl_key }}", name: "keystone_ssl_key", file_mode: "0640", dir_mode: "0750" } + register: memcache_keys + until: memcache_keys|success + retries: 5 + delay: 2 + notify: Restart Apache + tags: + - keystone-config + - keystone-ssl diff --git a/playbooks/roles/os_keystone/tasks/keystone_ssl_key_store.yml b/playbooks/roles/os_keystone/tasks/keystone_ssl_key_store.yml new file mode 100644 index 0000000000..6971ac065f --- /dev/null +++ b/playbooks/roles/os_keystone/tasks/keystone_ssl_key_store.yml @@ -0,0 +1,31 @@ +--- +# 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 self signed cert and key + memcached: + name: "{{ item.name }}" + file_path: "{{ item.src }}" + state: "present" + server: "{{ memcached_servers }}" + encrypt_string: "{{ memcached_encryption_key }}" + with_items: + - { src: "{{ keystone_ssl_cert }}", name: "keystone_ssl_cert" } + - { src: "{{ keystone_ssl_key }}", name: "keystone_ssl_key" } + register: memcache_keys + until: memcache_keys|success + retries: 5 + delay: 2 + tags: + - keystone-ssl diff --git a/playbooks/roles/os_keystone/tasks/keystone_ssl_self_signed.yml b/playbooks/roles/os_keystone/tasks/keystone_ssl_self_signed.yml new file mode 100644 index 0000000000..a55991d2be --- /dev/null +++ b/playbooks/roles/os_keystone/tasks/keystone_ssl_self_signed.yml @@ -0,0 +1,26 @@ +--- +# 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: keystone_ssl_key_create.yml + when: > + inventory_hostname == groups['keystone_all'][0] + +- include: keystone_ssl_key_store.yml + when: > + inventory_hostname == groups['keystone_all'][0] + +- include: keystone_ssl_key_distribute.yml + when: > + inventory_hostname != groups['keystone_all'][0] diff --git a/playbooks/roles/os_keystone/tasks/keystone_ssl_user_provided.yml b/playbooks/roles/os_keystone/tasks/keystone_ssl_user_provided.yml new file mode 100644 index 0000000000..1828dc81f7 --- /dev/null +++ b/playbooks/roles/os_keystone/tasks/keystone_ssl_user_provided.yml @@ -0,0 +1,43 @@ +--- +# 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 and key + copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: "root" + group: "root" + mode: "{{ item.mode }}" + with_items: + - { src: "{{ keystone_user_ssl_cert }}", dest: "{{ keystone_ssl_cert }}", mode: "0644" } + - { src: "{{ keystone_user_ssl_key }}", dest: "{{ keystone_ssl_key }}", mode: "0640" } + when: keystone_user_ssl_cert is defined and keystone_user_ssl_key is defined + notify: Restart Apache + tags: + - keystone-configs + - keystone-ssl + +- name: Drop user provided ssl CA cert + copy: + src: "{{ keystone_user_ssl_ca_cert }}" + dest: "{{ keystone_ssl_ca_cert }}" + owner: "root" + group: "root" + mode: "0644" + when: keystone_user_ssl_ca_cert is defined + notify: Restart Apache + tags: + - keystone-configs + - keystone-ssl diff --git a/playbooks/roles/os_keystone/tasks/main.yml b/playbooks/roles/os_keystone/tasks/main.yml index b2b9a66c2f..8e80dc164e 100644 --- a/playbooks/roles/os_keystone/tasks/main.yml +++ b/playbooks/roles/os_keystone/tasks/main.yml @@ -37,6 +37,7 @@ when: > inventory_hostname == groups['keystone_all'][0] +- include: keystone_ssl.yml - include: keystone_apache.yml - include: keystone_token_cleanup.yml diff --git a/playbooks/roles/os_keystone/templates/keystone-httpd.conf.j2 b/playbooks/roles/os_keystone/templates/keystone-httpd.conf.j2 index ac9e57b134..e869bb0545 100644 --- a/playbooks/roles/os_keystone/templates/keystone-httpd.conf.j2 +++ b/playbooks/roles/os_keystone/templates/keystone-httpd.conf.j2 @@ -16,14 +16,13 @@ CustomLog /var/log/keystone/ssl_access.log combined Options +FollowSymLinks - {% if keystone_ssl_enabled == true -%} + {% if keystone_ssl | bool and keystone_service_internaluri_proto == "https" -%} SSLEngine on SSLCertificateFile {{ keystone_ssl_cert }} SSLCertificateKeyFile {{ keystone_ssl_key }} - SSLCACertificatePath {{ keystone_ssl_cert_path }} - SSLCARevocationPath {{ keystone_ssl_cert_path }} - SSLVerifyClient optional - SSLVerifyDepth 10 + {% if keystone_user_ssl_ca_cert is defined -%} + SSLCACertificateFile {{ keystone_ssl_ca_cert }} + {% endif -%} SSLCompression Off SSLProtocol {{ keystone_ssl_protocol }} SSLHonorCipherOrder On @@ -74,14 +73,13 @@ CustomLog /var/log/keystone/ssl_access.log combined Options +FollowSymLinks - {% if keystone_ssl_enabled == true -%} + {% if keystone_ssl | bool and keystone_service_adminuri_proto == "https" -%} SSLEngine on SSLCertificateFile {{ keystone_ssl_cert }} SSLCertificateKeyFile {{ keystone_ssl_key }} - SSLCACertificatePath {{ keystone_ssl_cert_path }} - SSLCARevocationPath {{ keystone_ssl_cert_path }} - SSLVerifyClient optional - SSLVerifyDepth 10 + {% if keystone_user_ssl_ca_cert is defined -%} + SSLCACertificateFile {{ keystone_ssl_ca_cert }} + {% endif -%} SSLCompression Off SSLProtocol {{ keystone_ssl_protocol }} SSLHonorCipherOrder On diff --git a/playbooks/roles/os_keystone/templates/keystone.conf.j2 b/playbooks/roles/os_keystone/templates/keystone.conf.j2 index 4eb82f4422..a60c72af62 100644 --- a/playbooks/roles/os_keystone/templates/keystone.conf.j2 +++ b/playbooks/roles/os_keystone/templates/keystone.conf.j2 @@ -10,7 +10,7 @@ public_endpoint = {{ keystone_public_endpoint }} admin_endpoint = {{ keystone_service_adminuri }} fatal_deprecations = {{ keystone_fatal_deprecations }} -{% if keystone_ssl_enabled == true and keystone_secure_proxy_ssl_header is defined %} +{% if keystone_ssl | bool and keystone_secure_proxy_ssl_header is defined %} secure_proxy_ssl_header = {{ keystone_secure_proxy_ssl_header }} {% endif %} diff --git a/playbooks/vars/configs/haproxy_config.yml b/playbooks/vars/configs/haproxy_config.yml index 8f14a20a03..1c86fe5b03 100644 --- a/playbooks/vars/configs/haproxy_config.yml +++ b/playbooks/vars/configs/haproxy_config.yml @@ -12,6 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. +haproxy_backend_options_http: + - "forwardfor" + - "httpchk" + - "httplog" + +haproxy_backend_options_https: + - "ssl-hello-chk" + +keystone_ssl_admin: "{% if keystone_ssl is defined and keystone_ssl | bool and keystone_service_adminuri_proto == 'https' %}true{% else %}false{% endif %}" +keystone_ssl_internal: "{% if keystone_ssl is defined and keystone_ssl | bool and keystone_service_internaluri_proto == 'https' %}true{% else %}false{% endif %}" +keystone_ssl_public: "{% if keystone_ssl is defined and keystone_ssl | bool and keystone_service_publicuri_proto == 'https' %}true{% else %}false{% endif %}" + haproxy_service_configs: - service: haproxy_service_name: galera @@ -69,22 +81,18 @@ haproxy_service_configs: haproxy_backend_nodes: "{{ groups['keystone_all'] }}" haproxy_port: 35357 haproxy_ssl: "{% if haproxy_ssl | bool and keystone_service_adminuri_proto == 'https' %}true{% else %}false{% endif %}" - haproxy_balance_type: http - haproxy_backend_options: - - "forwardfor" - - "httpchk" - - "httplog" + haproxy_balance_type: "{{ (keystone_ssl_admin | bool) | ternary('tcp', 'http') }}" + haproxy_balance_alg: "{{ (keystone_ssl_admin | bool) | ternary('source', 'leastconn') }}" + haproxy_backend_options: "{{ (keystone_ssl_admin | bool) | ternary(haproxy_backend_options_https, haproxy_backend_options_http) }}" - service: haproxy_service_name: keystone_service haproxy_backend_nodes: "{{ groups['keystone_all'] }}" haproxy_bind: "{% if internal_lb_vip_address == external_lb_vip_address %}*{% else %}{{ external_lb_vip_address }}{% endif %}" haproxy_port: 5000 haproxy_ssl: "{% if haproxy_ssl | bool and keystone_service_publicuri_proto == 'https' %}true{% else %}false{% endif %}" - haproxy_balance_type: http - haproxy_backend_options: - - "forwardfor" - - "httpchk" - - "httplog" + haproxy_balance_type: "{{ (keystone_ssl_public | bool) | ternary('tcp','http') }}" + haproxy_balance_alg: "{{ (keystone_ssl_public | bool) | ternary('source', 'leastconn') }}" + haproxy_backend_options: "{{ (keystone_ssl_public | bool) | ternary(haproxy_backend_options_https, haproxy_backend_options_http) }}" - service: haproxy_service_name: neutron_server haproxy_backend_nodes: "{{ groups['neutron_server'] }}"