tripleo-common/tripleo_common/utils/passwords.py
Juan Antonio Osorio Robles 4bc5c71a3e Create mistral action to rotate fernet keys from passwords variable
This grabs the fernet keys from the passwords environment variable and
performs a rotation based on those keys. Subsequently, this parameter
will be passed to t-h-t in order for puppet to persist the rotation on a
stack update. Also, further work will be done in order for the deployer
to be able to do this without having to do a full stack update.

bp keystone-fernet-rotation
Change-Id: I18a3669e04021ad499973073a91e6bf78741ed20
2017-06-14 10:02:43 +03:00

125 lines
4.5 KiB
Python

# Copyright 2016 Red Hat, Inc.
# All Rights Reserved.
#
# 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.
import base64
import logging
import os
import paramiko
import struct
import time
import uuid
import passlib.utils as passutils
import six
from tripleo_common import constants
_MIN_PASSWORD_SIZE = 25
KEYSTONE_FERNET_REPO = '/etc/keystone/fernet-keys/'
LOG = logging.getLogger(__name__)
def generate_passwords(mistralclient=None, stack_env=None):
"""Create the passwords needed for deploying OpenStack via t-h-t.
This will create the set of passwords required by the undercloud and
overcloud installers that use tripleo-heat-templates and return them
as a dict. The mistralclient is optional and only used to obtain
the previously stored SnmpdReadonlyUserPassword supplied by the
undercloud to the overcloud deployment.
"""
passwords = {}
for name in constants.PASSWORD_PARAMETER_NAMES:
# Support users upgrading from Mitaka or otherwise creating a plan for
# a Heat stack that already exists.
if stack_env and name in stack_env.get('parameter_defaults', {}):
passwords[name] = stack_env['parameter_defaults'][name]
elif name.startswith("Ceph"):
if name == "CephClusterFSID":
# The FSID must be a UUID
passwords[name] = six.text_type(uuid.uuid1())
else:
# CephX keys aren't random strings
passwords[name] = create_cephx_key()
# Since by default generate_password uses all digits and ascii upper
# & lowercase letters, it provides ~5.95 entropy per character.
# Make the size of the default authkey 4096 bytes, which should give us
# ~24000 bits of randomness
elif name.startswith("PacemakerRemoteAuthkey"):
passwords[name] = passutils.generate_password(
size=4096)
# The underclouds SnmpdReadonlyUserPassword is stored in a mistral env
# for the overcloud.
elif mistralclient and name == 'SnmpdReadonlyUserPassword':
passwords[name] = get_snmpd_readonly_user_password(mistralclient)
elif name in ('KeystoneCredential0', 'KeystoneCredential1',
'KeystoneFernetKey0', 'KeystoneFernetKey1'):
passwords[name] = create_keystone_credential()
elif name == 'KeystoneFernetKeys':
passwords[name] = create_fernet_keys_repo_structure_and_keys()
elif name == 'MigrationSshKey':
passwords[name] = create_ssh_keypair()
else:
passwords[name] = passutils.generate_password(
size=_MIN_PASSWORD_SIZE)
return passwords
def create_fernet_keys_repo_structure_and_keys():
return {
KEYSTONE_FERNET_REPO + '0': {
'content': create_keystone_credential()},
KEYSTONE_FERNET_REPO + '1': {
'content': create_keystone_credential()}
}
def create_cephx_key():
# NOTE(gfidente): Taken from
# https://github.com/ceph/ceph-deploy/blob/master/ceph_deploy/new.py#L21
key = os.urandom(16)
header = struct.pack("<hiih", 1, int(time.time()), 0, len(key))
return base64.b64encode(header + key)
def get_snmpd_readonly_user_password(mistralclient):
mistral_env = mistralclient.environments.get("tripleo.undercloud-config")
try:
return mistral_env.variables['undercloud_ceilometer_snmpd_password']
except KeyError:
LOG.error("Undercloud ceilometer SNMPd password missing!")
raise
def create_keystone_credential():
return base64.urlsafe_b64encode(os.urandom(32))
def create_ssh_keypair(comment=None, bits=2048):
"""Generate an ssh keypair for use on the overcloud"""
if comment is None:
comment = "Generated by TripleO"
key = paramiko.RSAKey.generate(bits)
keyout = six.StringIO()
key.write_private_key(keyout)
private_key = keyout.getvalue()
public_key = '{} {} {}'.format(key.get_name(), key.get_base64(), comment)
return {
'private_key': private_key,
'public_key': public_key,
}