From 524868c632b23385145d140e5e6abab82cda50cf Mon Sep 17 00:00:00 2001 From: Shaun Smekel Date: Fri, 5 Aug 2016 07:44:42 +1000 Subject: [PATCH] Add dockerfiles for keystone fernet This adds the docker aspects of fernet key bootstrapping as well as distributed key rotation. - Bootstrapping is handled in the same way as keystone bootstrap. - A new keystone-fernet and keystone-ssh container is created to allow the nodes to communicate with each other (taken from nova-ssh). - The keystone-fernet is a keystone container with crontab installed. This will handle key rotations through keystone-manage and trigger an rsync to push new tokens to other nodes. The Ansible component is implemented in: https://review.openstack.org/#/c/349366 Change-Id: Id610e00e8c63c7f1bc0974c0aa1b3f44c18e1019 Partially-Implements: blueprint keystone-fernet-token Partially-Implements: blueprint third-party-plugin-support --- .../{ => keystone-base}/Dockerfile.j2 | 36 ++++---- docker/keystone/keystone-fernet/Dockerfile.j2 | 25 ++++++ .../keystone/keystone-fernet/extend_start.sh | 12 +++ .../keystone-fernet/fetch_fernet_tokens.py | 84 +++++++++++++++++++ .../keystone-fernet/keystone_bootstrap.sh | 43 ++++++++++ docker/keystone/keystone-ssh/Dockerfile.j2 | 21 +++++ docker/keystone/keystone-ssh/extend_start.sh | 20 +++++ docker/keystone/keystone/Dockerfile.j2 | 10 +++ .../keystone/{ => keystone}/extend_start.sh | 0 .../{ => keystone}/keystone_bootstrap.sh | 0 kolla/common/config.py | 2 +- 11 files changed, 230 insertions(+), 23 deletions(-) rename docker/keystone/{ => keystone-base}/Dockerfile.j2 (73%) create mode 100644 docker/keystone/keystone-fernet/Dockerfile.j2 create mode 100644 docker/keystone/keystone-fernet/extend_start.sh create mode 100644 docker/keystone/keystone-fernet/fetch_fernet_tokens.py create mode 100644 docker/keystone/keystone-fernet/keystone_bootstrap.sh create mode 100644 docker/keystone/keystone-ssh/Dockerfile.j2 create mode 100644 docker/keystone/keystone-ssh/extend_start.sh create mode 100644 docker/keystone/keystone/Dockerfile.j2 rename docker/keystone/{ => keystone}/extend_start.sh (100%) rename docker/keystone/{ => keystone}/keystone_bootstrap.sh (100%) diff --git a/docker/keystone/Dockerfile.j2 b/docker/keystone/keystone-base/Dockerfile.j2 similarity index 73% rename from docker/keystone/Dockerfile.j2 rename to docker/keystone/keystone-base/Dockerfile.j2 index e4a37baab6..3364377320 100644 --- a/docker/keystone/Dockerfile.j2 +++ b/docker/keystone/keystone-base/Dockerfile.j2 @@ -1,34 +1,32 @@ FROM {{ namespace }}/{{ image_prefix }}openstack-base:{{ tag }} MAINTAINER {{ maintainer }} - {% import "macros.j2" as macros with context %} {% if install_type == 'binary' %} {% if base_distro in ['fedora', 'centos', 'oraclelinux', 'rhel'] %} - {% set keystone_packages = [ - 'openstack-keystone', + {% set keystone_base_packages = [ + 'openstack-keystone', 'python-keystoneclient', 'httpd', 'mod_wsgi', 'python-ldappool' ] %} -{{ macros.install_packages(keystone_packages | customizable("packages")) }} +{{ macros.install_packages(keystone_base_packages | customizable("packages")) }} RUN mkdir -p /var/www/cgi-bin/keystone \ && cp -a /usr/share/keystone/keystone.wsgi /var/www/cgi-bin/keystone/main \ && cp -a /usr/share/keystone/keystone.wsgi /var/www/cgi-bin/keystone/admin \ && sed -i -r 's,^(Listen 80),#\1,' /etc/httpd/conf/httpd.conf {% elif base_distro in ['ubuntu'] %} - - {% set keystone_packages = [ + {% set keystone_base_packages = [ 'keystone', 'apache2', 'libapache2-mod-wsgi', 'python-ldappool' ] %} -{{ macros.install_packages(keystone_packages | customizable("packages")) }} +{{ macros.install_packages(keystone_base_packages | customizable("packages")) }} RUN mkdir -p /var/www/cgi-bin/keystone \ && cp -a /usr/share/keystone/wsgi.py /var/www/cgi-bin/keystone/main \ && cp -a /usr/share/keystone/wsgi.py /var/www/cgi-bin/keystone/admin \ @@ -38,28 +36,27 @@ RUN mkdir -p /var/www/cgi-bin/keystone \ {% endif %} {% elif install_type == 'source' %} {% if base_distro in ['fedora', 'centos', 'oraclelinux', 'rhel'] %} - - {% set keystone_packages = [ + {% set keystone_base_packages = [ 'httpd', 'mod_wsgi', 'python-ldappool' ] %} -{{ macros.install_packages(keystone_packages | customizable("packages")) }} +{{ macros.install_packages(keystone_base_packages | customizable("packages")) }} RUN sed -i -r 's,^(Listen 80),#\1,' /etc/httpd/conf/httpd.conf {% elif base_distro in ['ubuntu', 'debian'] %} - - {% set keystone_packages = [ + {% set keystone_base_packages = [ 'apache2', 'libapache2-mod-wsgi', 'python-ldappool' ] %} -{{ macros.install_packages(keystone_packages | customizable("packages")) }} +{{ macros.install_packages(keystone_base_packages | customizable("packages")) }} RUN echo > /etc/apache2/ports.conf {% endif %} -ADD keystone-archive /keystone-source -RUN ln -s keystone-source/* keystone \ + +ADD keystone-base-archive /keystone-base-source +RUN ln -s keystone-base-source/* keystone \ && useradd --user-group keystone \ && /var/lib/kolla/venv/bin/pip --no-cache-dir install --upgrade -c requirements/upper-constraints.txt /keystone \ && mkdir -p /etc/keystone /var/www/cgi-bin/keystone /var/log/apache2 /home/keystone \ @@ -74,11 +71,6 @@ RUN usermod -a -G kolla keystone \ && chown -R keystone: /var/www/cgi-bin/keystone \ && chmod 755 /var/www/cgi-bin/keystone/* -COPY keystone_bootstrap.sh /usr/local/bin/kolla_keystone_bootstrap -COPY extend_start.sh /usr/local/bin/kolla_extend_start -RUN chmod 755 /usr/local/bin/kolla_extend_start /usr/local/bin/kolla_keystone_bootstrap - -{% block keystone_footer %}{% endblock %} +{% block keystone_base_footer %}{% endblock %} {% block footer %}{% endblock %} - -{{ include_footer }} +{{ include_footer }} \ No newline at end of file diff --git a/docker/keystone/keystone-fernet/Dockerfile.j2 b/docker/keystone/keystone-fernet/Dockerfile.j2 new file mode 100644 index 0000000000..0488ceb91b --- /dev/null +++ b/docker/keystone/keystone-fernet/Dockerfile.j2 @@ -0,0 +1,25 @@ +FROM {{ namespace }}/{{ image_prefix }}keystone-base:{{ tag }} +MAINTAINER {{ maintainer }} +{% import "macros.j2" as macros with context %} + +{% if base_distro in ['fedora', 'centos', 'oraclelinux', 'rhel'] %} + {% set keystone_fernet_packages = [ + 'cronie', + 'rsync' + ] %} +{% elif base_distro in ['ubuntu', 'debian'] %} + {% set keystone_fernet_packages = [ + 'cron', + 'rsync' + ] %} +{% endif %} +{{ macros.install_packages(keystone_fernet_packages | customizable("packages")) }} + +COPY fetch_fernet_tokens.py /usr/bin/ +COPY keystone_bootstrap.sh /usr/local/bin/kolla_keystone_bootstrap +COPY extend_start.sh /usr/local/bin/kolla_extend_start +RUN chmod 755 /usr/local/bin/kolla_extend_start /usr/local/bin/kolla_keystone_bootstrap /usr/bin/fetch_fernet_tokens.py + +{% block keystone_fernet_footer %}{% endblock %} +{% block footer %}{% endblock %} +{{ include_footer }} diff --git a/docker/keystone/keystone-fernet/extend_start.sh b/docker/keystone/keystone-fernet/extend_start.sh new file mode 100644 index 0000000000..0d6a3a539a --- /dev/null +++ b/docker/keystone/keystone-fernet/extend_start.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +FERNET_SYNC=/usr/bin/fernet-node-sync.sh +FERNET_TOKEN_DIR="/etc/keystone/fernet-keys" + +if [[ -f "${FERNET_SYNC}" ]]; then + ${FERNET_SYNC} +fi + +if [[ $(stat -c %U:%G ${FERNET_TOKEN_DIR}) != "keystone:keystone" ]]; then + chown keystone:keystone ${FERNET_TOKEN_DIR} +fi \ No newline at end of file diff --git a/docker/keystone/keystone-fernet/fetch_fernet_tokens.py b/docker/keystone/keystone-fernet/fetch_fernet_tokens.py new file mode 100644 index 0000000000..7ef7be0858 --- /dev/null +++ b/docker/keystone/keystone-fernet/fetch_fernet_tokens.py @@ -0,0 +1,84 @@ +#!/usr/bin/python + +# 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. + +# Basically this module will fetch the fernet tokens and compare them to the +# required time constrains to determine whether the host needs to resync with +# other nodes in the cluster. + +from __future__ import print_function +import argparse +from datetime import datetime +from datetime import timedelta +import json +import os +import sys + +TOKEN_PATH = '/etc/keystone/fernet-keys' + + +def json_exit(msg=None, failed=False, changed=False): + if type(msg) is not dict: + msg = {'msg': str(msg)} + msg.update({'failed': failed, 'changed': changed}) + print(json.dumps(msg)) + sys.exit() + + +def has_file(filename_path): + if not os.path.exists(filename_path): + return False + return True + + +def num_tokens(): + _, _, files = os.walk(TOKEN_PATH).next() + return len(files) + + +def tokens_populated(expected): + return num_tokens() == int(expected) + + +def token_stale(seconds, filename='0'): + max_token_age = datetime.now() - timedelta(seconds=int(seconds)) + filename_path = os.path.join(TOKEN_PATH, filename) + + if not has_file(filename_path): + return True + modified_date = datetime.fromtimestamp(os.path.getmtime(filename_path)) + return modified_date < max_token_age + + +def main(): + parser = argparse.ArgumentParser(description='''Checks to see if a fernet + token no older than a desired time.''') + parser.add_argument('-t', '--time', + help='Time in seconds for a token rotation', + required=True) + parser.add_argument('-f', '--filename', + help='Filename of token to check', + default='0') + parser.add_argument('-n', '--number', + help='Number of tokens that should exist', + required=True) + args = parser.parse_args() + + json_exit({ + 'populated': tokens_populated(args.number), + 'update_required': token_stale(args.time, args.filename), + }) + + +if __name__ == '__main__': + main() diff --git a/docker/keystone/keystone-fernet/keystone_bootstrap.sh b/docker/keystone/keystone-fernet/keystone_bootstrap.sh new file mode 100644 index 0000000000..d361767bbc --- /dev/null +++ b/docker/keystone/keystone-fernet/keystone_bootstrap.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# 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. + +set -x + +USERNAME=$1 +GROUP=$2 + +function fail_json { + echo '{"failed": true, "msg": "'$1'", "changed": true}' + exit 1 +} + +function exit_json { + echo '{"failed": false, "changed": '"${changed}"'}' +} + +changed="false" +keystone_bootstrap=$(keystone-manage --config-file /etc/keystone/keystone.conf fernet_setup --keystone-user ${USERNAME} --keystone-group ${GROUP} 2>&1) +if [[ $? != 0 ]]; then + fail_json "${keystone_bootstrap}" +fi + +changed=$(echo "${keystone_bootstrap}" | awk ' + /Key repository is already initialized/ {count++} + END { + if (count == 1) changed="true"; else changed="false" + print changed + }' +) + +exit_json diff --git a/docker/keystone/keystone-ssh/Dockerfile.j2 b/docker/keystone/keystone-ssh/Dockerfile.j2 new file mode 100644 index 0000000000..2f53897420 --- /dev/null +++ b/docker/keystone/keystone-ssh/Dockerfile.j2 @@ -0,0 +1,21 @@ +FROM {{ namespace }}/{{ image_prefix }}keystone-base:{{ tag }} +MAINTAINER {{ maintainer }} +{% import "macros.j2" as macros with context %} + +{% if base_distro in ['centos', 'fedora', 'oraclelinux', 'rhel'] %} + {% set keystone_ssh_packages = ['openssh-server'] %} +{% elif base_distro in ['ubuntu', 'debian'] %} + {% set keystone_ssh_packages = ['openssh-server'] %} + +RUN mkdir -p /var/run/sshd \ + && chmod 0755 /var/run/sshd + +{% endif %} +{{ macros.install_packages(keystone_ssh_packages | customizable("packages")) }} + +COPY extend_start.sh /usr/local/bin/kolla_extend_start +RUN chmod 755 /usr/local/bin/kolla_extend_start + +{% block keystone_ssh_footer %}{% endblock %} +{% block footer %}{% endblock %} +{{ include_footer }} diff --git a/docker/keystone/keystone-ssh/extend_start.sh b/docker/keystone/keystone-ssh/extend_start.sh new file mode 100644 index 0000000000..23744cead2 --- /dev/null +++ b/docker/keystone/keystone-ssh/extend_start.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [[ ! -L /dev/log ]]; then + ln -sf /var/lib/kolla/heka/log /dev/log +fi + +SSH_HOST_KEY_TYPES=( "rsa" "dsa" "ecdsa" "ed25519" ) + +for key_type in ${SSH_HOST_KEY_TYPES[@]}; do + KEY_PATH=/etc/ssh/ssh_host_${key_type}_key + if [[ ! -f "${KEY_PATH}" ]]; then + ssh-keygen -q -t ${key_type} -f ${KEY_PATH} -N "" + fi +done + +mkdir -p /var/lib/keystone/.ssh + +if [[ $(stat -c %U:%G /var/lib/keystone/.ssh) != "keystone:keystone" ]]; then + sudo chown keystone: /var/lib/keystone/.ssh +fi diff --git a/docker/keystone/keystone/Dockerfile.j2 b/docker/keystone/keystone/Dockerfile.j2 new file mode 100644 index 0000000000..e6baa93492 --- /dev/null +++ b/docker/keystone/keystone/Dockerfile.j2 @@ -0,0 +1,10 @@ +FROM {{ namespace }}/{{ image_prefix }}keystone-base:{{ tag }} +MAINTAINER {{ maintainer }} + +COPY keystone_bootstrap.sh /usr/local/bin/kolla_keystone_bootstrap +COPY extend_start.sh /usr/local/bin/kolla_extend_start +RUN chmod 755 /usr/local/bin/kolla_extend_start /usr/local/bin/kolla_keystone_bootstrap + +{% block keystone_footer %}{% endblock %} +{% block footer %}{% endblock %} +{{ include_footer }} \ No newline at end of file diff --git a/docker/keystone/extend_start.sh b/docker/keystone/keystone/extend_start.sh similarity index 100% rename from docker/keystone/extend_start.sh rename to docker/keystone/keystone/extend_start.sh diff --git a/docker/keystone/keystone_bootstrap.sh b/docker/keystone/keystone/keystone_bootstrap.sh similarity index 100% rename from docker/keystone/keystone_bootstrap.sh rename to docker/keystone/keystone/keystone_bootstrap.sh diff --git a/kolla/common/config.py b/kolla/common/config.py index 4be0bebcea..687b425cb4 100644 --- a/kolla/common/config.py +++ b/kolla/common/config.py @@ -207,7 +207,7 @@ SOURCES = { 'type': 'url', 'location': ('http://tarballs.openstack.org/ironic/' 'ironic-master.tar.gz')}, - 'keystone': { + 'keystone-base': { 'type': 'url', 'location': ('http://tarballs.openstack.org/keystone/' 'keystone-master.tar.gz')},