diff --git a/defaults/main.yml b/defaults/main.yml index be7ab2d9..e4383450 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -25,7 +25,7 @@ keystone_fatal_deprecations: False keystone_system_user_name: keystone keystone_system_group_name: keystone keystone_system_service_name: apache2 -keystone_system_shell: /bin/false +keystone_system_shell: /bin/bash keystone_system_comment: keystone system user keystone_system_user_home: "/var/lib/{{ keystone_system_user_name }}" @@ -48,6 +48,10 @@ keystone_revocation_expiration_buffer: 1800 ## Fernet config vars keystone_fernet_tokens_key_repository: "/etc/keystone/fernet-keys" keystone_fernet_tokens_max_active_keys: 7 +# Any of the following rotation times are valid: +# reboot, yearly, annually, monthly, weekly, daily, hourly +keystone_fernet_rotation: daily +keystone_fernet_auto_rotation_script: /opt/keystone-fernet-rotate.sh keystone_cache_expiration_time: 5400 diff --git a/tasks/keystone_fernet.yml b/tasks/keystone_fernet.yml index 2954c8c4..83ab41e4 100644 --- a/tasks/keystone_fernet.yml +++ b/tasks/keystone_fernet.yml @@ -17,13 +17,8 @@ when: > inventory_hostname == groups['keystone_all'][0] -- include: keystone_fernet_keys_fetch.yml +- include: keystone_fernet_keys_distribute.yml when: > inventory_hostname == groups['keystone_all'][0] -- include: keystone_fernet_keys_distribute.yml - when: > - inventory_hostname != groups['keystone_all'][0] and - inventory_hostname in groups['keystone_all'] - -- include: keystone_fernet_cleanup.yml +- include: keystone_fernet_keys_autorotate.yml diff --git a/tasks/keystone_fernet_keys_autorotate.yml b/tasks/keystone_fernet_keys_autorotate.yml new file mode 100644 index 00000000..22086eae --- /dev/null +++ b/tasks/keystone_fernet_keys_autorotate.yml @@ -0,0 +1,53 @@ +--- +# 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. + +# This script is being created with mode 0755 intentionally. This is so that the +# script can be executed by root to rotate the keys as needed. The script being +# executed will always change it's user context to the keystone user before +# execution and while the script may be world read/executable its contains only +# the necessary bits that are required to run the rotate and sync commands. +- name: Drop fernet key auto rotate script + template: + src: "keystone-fernet-rotate.sh.j2" + dest: "{{ keystone_fernet_auto_rotation_script }}" + owner: "{{ keystone_system_user_name }}" + group: "{{ keystone_system_group_name }}" + mode: "1755" + tags: + - keystone-fernet-auto-rotate + +# This creates the auto rotation job on the first keystone host. +- name: Create auto rotation job + cron: + name: "Fernet auto rotate job" + special_time: "{{ keystone_fernet_rotation }}" + user: "keystone" + job: "{{ keystone_fernet_auto_rotation_script }}" + cron_file: keystone-fernet-rotate + when: > + inventory_hostname == groups['keystone_all'][0] + tags: + - keystone-fernet-auto-rotate + +# This makes sure that no auto rotation jobs are on any other hosts. +- name: Remove extra auto rotation job + cron: + name: "Fernet auto rotate job" + cron_file: keystone-fernet-rotate + state: "absent" + when: > + inventory_hostname != groups['keystone_all'][0] + tags: + - keystone-fernet-auto-rotate diff --git a/tasks/keystone_fernet_keys_distribute.yml b/tasks/keystone_fernet_keys_distribute.yml index 86770b6c..59302d93 100644 --- a/tasks/keystone_fernet_keys_distribute.yml +++ b/tasks/keystone_fernet_keys_distribute.yml @@ -13,12 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -- name: Distribute the fernet keys to the other keystone containers - synchronize: - src: "/tmp/{{ keystone_fernet_tokens_key_repository|basename }}" - dest: "{{ keystone_fernet_tokens_key_repository|dirname }}" - recursive: yes - delete: yes +- name: Distribute the fernet key repository + shell: | + rsync -e 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' \ + -avz \ + --delete \ + {{ keystone_fernet_tokens_key_repository }}/ \ + {{ keystone_system_user_name }}@{{ hostvars[item]['ansible_ssh_host'] }}:{{ keystone_fernet_tokens_key_repository }}/ + sudo: yes + sudo_user: "{{ keystone_system_user_name }}" + with_items: groups['keystone_all'][1:] tags: - - keystone-setup - - keystone-fernet + - keystone-fernet-distribute diff --git a/tasks/keystone_key_create.yml b/tasks/keystone_key_create.yml new file mode 100644 index 00000000..4484de11 --- /dev/null +++ b/tasks/keystone_key_create.yml @@ -0,0 +1,74 @@ +--- +# 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 old key file(s) if found + file: + path: "{{ item }}" + state: "absent" + with_items: + - "{{ keystone_system_user_home }}/.ssh/authorized_keys" + - "{{ keystone_system_user_home }}/.ssh/id_rsa" + - "{{ keystone_system_user_home }}/.ssh/id_rsa.pub" + tags: + - keystone-key + - keystone-key-create + +- name: Create the keystone SSH key if it doesnt exist + command: | + ssh-keygen -f {{ keystone_system_user_home }}/.ssh/id_rsa -t rsa -q -N "" + sudo: yes + sudo_user: "{{ keystone_system_user_name }}" + tags: + - keystone-key + - keystone-key-create + +- name: Create empty 'authorized_keys' file + file: + path: "{{ keystone_system_user_home }}/.ssh/authorized_keys" + state: "touch" + tags: + - keystone-key + - keystone-key-create + +- name: Change permissions on the generated keys + file: + path: "{{ item.path }}" + group: "{{ keystone_system_user_name }}" + owner: "{{ keystone_system_user_name }}" + mode: "{{ item.mode }}" + with_items: + - { path: "{{ keystone_system_user_home }}/.ssh/authorized_keys", mode: "0700" } + - { path: "{{ keystone_system_user_home }}/.ssh/id_rsa", mode: "0600" } + - { path: "{{ keystone_system_user_home }}/.ssh/id_rsa.pub", mode: "0644" } + tags: + - keystone-key + - keystone-key-create + +- name: Get public key contents + command: | + cat {{ keystone_system_user_home }}/.ssh/id_rsa.pub + register: keystone_pub + changed_when: false + tags: + - keystone-key + - keystone-key-create + +- name: Build authorized keys + shell: | + echo "{{ keystone_pub.stdout }}" | tee -a {{ keystone_system_user_home }}/.ssh/authorized_keys + delegate_to: "{{ groups['keystone_all'][0] }}" + tags: + - keystone-key + - keystone-key-create diff --git a/tasks/keystone_key_distribute.yml b/tasks/keystone_key_distribute.yml new file mode 100644 index 00000000..8b8f52ae --- /dev/null +++ b/tasks/keystone_key_distribute.yml @@ -0,0 +1,33 @@ +--- +# 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: Retrieve authorized keys + 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_system_user_home }}/.ssh/authorized_keys", name: "authorized_keys", file_mode: "0640", dir_mode: "0750" } + register: memcache_keys + until: memcache_keys|success + retries: 5 + delay: 2 + tags: + - keystone-key + - keystone-key-distribute diff --git a/tasks/keystone_fernet_cleanup.yml b/tasks/keystone_key_setup.yml similarity index 66% rename from tasks/keystone_fernet_cleanup.yml rename to tasks/keystone_key_setup.yml index d60e9c8b..ba8e7c1c 100644 --- a/tasks/keystone_fernet_cleanup.yml +++ b/tasks/keystone_key_setup.yml @@ -13,14 +13,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -- name: Clean up the local key clone - local_action: - module: file - path="/tmp/{{ keystone_fernet_tokens_key_repository|basename }}" - state=absent +- include: keystone_key_create.yml + tags: + - keystone-key + - keystone-key-create + +- include: keystone_key_store.yml when: > inventory_hostname == groups['keystone_all'][0] tags: - - keystone-cleanup - - keystone-setup - - keystone-fernet + - keystone-key + - keystone-key-store + +- include: keystone_key_distribute.yml + when: > + inventory_hostname != groups['keystone_all'][0] + tags: + - keystone-key + - keystone-key-distribute diff --git a/tasks/keystone_fernet_keys_fetch.yml b/tasks/keystone_key_store.yml similarity index 55% rename from tasks/keystone_fernet_keys_fetch.yml rename to tasks/keystone_key_store.yml index 162a8865..4c24e778 100644 --- a/tasks/keystone_fernet_keys_fetch.yml +++ b/tasks/keystone_key_store.yml @@ -13,12 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -- name: Fetch the fernet key repository - synchronize: - src: "{{ keystone_fernet_tokens_key_repository }}" - dest: /tmp/ - recursive: yes - mode: pull +- name: Distribute authorized keys for cluster consumption + memcached: + name: "{{ item.name }}" + file_path: "{{ item.src }}" + state: "present" + server: "{{ memcached_servers }}" + encrypt_string: "{{ memcached_encryption_key }}" + with_items: + - { src: "{{ keystone_system_user_home }}/.ssh/authorized_keys", name: "authorized_keys" } + register: memcache_keys + until: memcache_keys|success + retries: 5 + delay: 2 tags: - - keystone-setup - - keystone-fernet + - keystone-key + - keystone-key-store diff --git a/tasks/keystone_pre_install.yml b/tasks/keystone_pre_install.yml index cbf6a144..832f03be 100644 --- a/tasks/keystone_pre_install.yml +++ b/tasks/keystone_pre_install.yml @@ -39,10 +39,12 @@ state: directory owner: "{{ item.owner|default(keystone_system_user_name) }}" group: "{{ item.group|default(keystone_system_group_name) }}" + mode: "{{ item.mode|default(0755) }}" with_items: - { path: "/etc/keystone" } - { path: "{{ keystone_ldap_domain_config_dir }}" } - { path: "/etc/keystone/ssl" } + - { path: "{{ keystone_fernet_tokens_key_repository }}", mode: "2750"} - { path: "/etc/sudoers.d", mode: "0750", owner: "root", group: "root" } - { path: "{{ keystone_system_user_home }}" } - { path: "/var/www/cgi-bin", owner: root, group: root } @@ -50,21 +52,6 @@ tags: - keystone-dirs -- name: Create keystone fernet-keys dir - file: - path: "{{ item.path }}" - state: directory - owner: "{{ item.owner|default(keystone_system_user_name) }}" - group: "{{ item.group|default(keystone_system_group_name) }}" - mode: "{{ item.mode }}" - with_items: - - { path: "{{ keystone_fernet_tokens_key_repository }}", mode: '0750' } - when: > - 'fernet' in keystone_token_provider - tags: - - keystone-dirs - - keystone-fernet - - name: Test for log directory or link shell: | if [ -h "/var/log/keystone" ]; then diff --git a/tasks/main.yml b/tasks/main.yml index 6202f443..20f2c2bd 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -16,7 +16,14 @@ - include: keystone_pre_install.yml - include: keystone_install.yml +- include: keystone_key_setup.yml + tags: + - keystone-key + - keystone-key-distribute + - include: keystone_fernet.yml + tags: + - keystone-fernet when: > 'fernet' in keystone_token_provider diff --git a/templates/keystone-fernet-rotate.sh.j2 b/templates/keystone-fernet-rotate.sh.j2 new file mode 100644 index 00000000..a3d8448f --- /dev/null +++ b/templates/keystone-fernet-rotate.sh.j2 @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# 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. + +# {{ ansible_managed }} + +# This script is being created with mode 0755 intentionally. This is so that the +# script can be executed by root to rotate the keys as needed. The script being +# executed will always change it's user context to the keystone user before +# execution and while the script may be world read/executable its contains only +# the necessary bits that are required to run the rotate and sync commands. + +function autorotate() { + # Rotate the keys + keystone-manage fernet_rotate --keystone-user "{{ keystone_system_user_name }}" \ + --keystone-group "{{ keystone_system_group_name }}" + {% for host in groups['keystone_all'] %} + + {% if inventory_hostname != host %} + + # Fernet sync job to "{{ host }}" + rsync -e 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' \ + -avz \ + --delete \ + {{ keystone_fernet_tokens_key_repository }}/ \ + {{ keystone_system_user_name }}@{{ hostvars[host]['ansible_ssh_host'] }}:{{ keystone_fernet_tokens_key_repository }}/ + + {%- endif %} + + {%- endfor %} + +} + +if [ "$(id -u)" == "0" ];then +# Change the script context to always execute as the "{{ keystone_system_user_name }}" user. +su - "{{ keystone_system_user_name }}" -s "/bin/bash" -c bash << EOC + {{ keystone_fernet_auto_rotation_script }} +EOC +elif [ "$(whoami)" == "{{ keystone_system_user_name }}" ];then + logger $(autorotate) +else + echo "Failed - you do not have permission to rotate, or you've executed the job as the wrong user." + exit 99 +fi diff --git a/templates/keystone.conf.j2 b/templates/keystone.conf.j2 index fbd3eb00..08d0d7fa 100644 --- a/templates/keystone.conf.j2 +++ b/templates/keystone.conf.j2 @@ -54,7 +54,7 @@ max_pool_size = {{ keystone_database_max_pool_size }} pool_timeout = {{ keystone_database_pool_timeout }} -[fernet_keys] +[fernet_tokens] key_repository = {{ keystone_fernet_tokens_key_repository }} max_active_keys = {{ keystone_fernet_tokens_max_active_keys }}