tripleo-common/tripleo_common/utils/passwords.py
Oliver Walsh 5f136811d6 Add MigrationSshKey to generated passwords
This reuses the existing password generation mistral action to generate an ssh
keypair to be used for nova cold migration

Paramiko is used to generate the ssh key, based on the existing approach in the
nova keypair api.

Also update validation ssh key generation to reuse the same method.

Change-Id: I9e7a1862911312ad942233ac8fc828f4e1be1dcf
2017-04-03 09:22:10 +01:00

113 lines
4.1 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
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 == 'MigrationSshKey':
passwords[name] = create_ssh_keypair()
else:
passwords[name] = passutils.generate_password(
size=_MIN_PASSWORD_SIZE)
return passwords
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,
}