Use certmonger for automatic cert generation
This will enable us to use a real CA to request the service certificates. bp tls-via-certmonger Depends-On: I32ded4e33abffd51f220fb8a7dc6263aace72acd Change-Id: I5009273110154f0327ad542d75e83ff67bf72613
This commit is contained in:
parent
b487f96a54
commit
faeed2494e
|
@ -62,6 +62,18 @@ include ::rabbitmq
|
|||
include ::tripleo::firewall
|
||||
include ::tripleo::selinux
|
||||
|
||||
if hiera('tripleo::haproxy::service_certificate', undef) {
|
||||
class {'::tripleo::profile::base::haproxy':
|
||||
enable_load_balancer => true,
|
||||
}
|
||||
include ::tripleo::keepalived
|
||||
# NOTE: This is required because the haproxy configuration should be changed
|
||||
# before any keystone operations are triggered. Without this, it will try to
|
||||
# access the new endpoints that point to haproxy even if haproxy hasn't
|
||||
# started yet.
|
||||
Class['::tripleo::haproxy'] -> Anchor['keystone::install::begin']
|
||||
}
|
||||
|
||||
# MySQL
|
||||
include ::tripleo::profile::base::database::mysql
|
||||
# Raise the mysql file limit
|
||||
|
@ -202,7 +214,7 @@ class { '::ironic::db::mysql':
|
|||
# pre-install swift here so we can build rings
|
||||
include ::swift
|
||||
|
||||
if hiera('service_certificate', undef) {
|
||||
if hiera('tripleo::haproxy::service_certificate', undef) {
|
||||
$keystone_public_endpoint = join(['https://', hiera('controller_public_vip'), ':13000'])
|
||||
$enable_proxy_headers_parsing = true
|
||||
} else {
|
||||
|
@ -418,16 +430,6 @@ class { '::ironic::drivers::pxe':
|
|||
pxe_bootfile_name => $pxe_bootfile_name
|
||||
}
|
||||
|
||||
|
||||
if hiera('service_certificate', undef) {
|
||||
class { '::tripleo::haproxy':
|
||||
# with our current version of hiera, we can't set tripleo::haproxy::service_certificate in hieradata
|
||||
# because the value might be empty and puppet would fail to compile the catalog.
|
||||
service_certificate => hiera('service_certificate', undef),
|
||||
}
|
||||
include ::tripleo::keepalived
|
||||
}
|
||||
|
||||
if str2bool(hiera('enable_tempest', true)) {
|
||||
# tempest
|
||||
# TODO: when puppet-tempest supports install by package, do that instead
|
||||
|
|
|
@ -7,10 +7,24 @@ debug: {{UNDERCLOUD_DEBUG}}
|
|||
controller_host: {{LOCAL_IP}} #local-ipv4
|
||||
controller_admin_vip: {{UNDERCLOUD_ADMIN_VIP}}
|
||||
controller_public_vip: {{UNDERCLOUD_PUBLIC_VIP}}
|
||||
service_certificate: {{UNDERCLOUD_SERVICE_CERTIFICATE}}
|
||||
ntp::servers:
|
||||
-
|
||||
|
||||
# SSL
|
||||
tripleo::haproxy::service_certificate: {{UNDERCLOUD_SERVICE_CERTIFICATE}}
|
||||
generate_service_certificates: {{GENERATE_SERVICE_CERTIFICATE}}
|
||||
tripleo::profile::base::haproxy::certificates_specs:
|
||||
undercloud-haproxy-public:
|
||||
service_pem: {{UNDERCLOUD_SERVICE_CERTIFICATE}}
|
||||
service_certificate: '/etc/pki/tls/certs/undercloud-front.crt'
|
||||
service_key: '/etc/pki/tls/private/undercloud-front.key'
|
||||
hostname: "%{hiera('controller_public_vip')}"
|
||||
postsave_cmd: "/usr/bin/instack-haproxy-cert-update '/etc/pki/tls/certs/undercloud-front.crt' '/etc/pki/tls/private/undercloud-front.key' {{UNDERCLOUD_SERVICE_CERTIFICATE}}"
|
||||
principal: {{SERVICE_PRINCIPAL}}
|
||||
|
||||
# CA defaults
|
||||
certmonger_ca: {{CERTIFICATE_GENERATION_CA}}
|
||||
|
||||
# Common Hiera data gets applied to all nodes
|
||||
ssh::server::storeconfigs_enabled: false
|
||||
|
||||
|
@ -495,6 +509,7 @@ zaqar::message_pipeline: 'zaqar.notification.notifier'
|
|||
zaqar::max_messages_post_size: 524288
|
||||
|
||||
# HAproxy
|
||||
tripleo::profile::base::haproxy::step: 1
|
||||
tripleo::haproxy::haproxy_stats_password: {{UNDERCLOUD_HAPROXY_STATS_PASSWORD}}
|
||||
tripleo::haproxy::controller_virtual_ip: "%{hiera('controller_admin_vip')}"
|
||||
tripleo::haproxy::controller_hosts: "%{hiera('controller_host')}"
|
||||
|
|
|
@ -290,8 +290,7 @@ class TestGenerateEnvironment(BaseTestCase):
|
|||
self.assertEqual('https://192.0.2.2:13808/v1/AUTH_%(tenant_id)s',
|
||||
env['UNDERCLOUD_ENDPOINT_SWIFT_PUBLIC'])
|
||||
|
||||
@mock.patch('instack_undercloud.undercloud._generate_certificate')
|
||||
def test_generate_endpoints_ssl_auto(self, mock_gen_cert):
|
||||
def test_generate_endpoints_ssl_auto(self):
|
||||
conf = config_fixture.Config()
|
||||
self.useFixture(conf)
|
||||
conf.config(generate_service_certificate=True)
|
||||
|
@ -558,41 +557,3 @@ class TestPostConfig(base.BaseTestCase):
|
|||
mock_instance.flavors.list.return_value = mock_flavors
|
||||
undercloud._delete_default_flavors(mock_instance)
|
||||
mock_instance.flavors.delete.assert_called_once_with('8ar')
|
||||
|
||||
|
||||
class FakeException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('instack_undercloud.undercloud._run_command')
|
||||
class TestGenerateCertificate(base.BaseTestCase):
|
||||
def test_normal(self, mock_run_command, mock_exists):
|
||||
with mock.patch('instack_undercloud.undercloud.open'):
|
||||
fake_env = {}
|
||||
undercloud._generate_certificate(fake_env)
|
||||
self.assertEqual('/etc/pki/instack-certs/undercloud-192.0.2.2.pem',
|
||||
fake_env['UNDERCLOUD_SERVICE_CERTIFICATE'])
|
||||
|
||||
def test_exists(self, mock_run_command, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
with mock.patch('instack_undercloud.undercloud.open') as mock_open:
|
||||
fake_env = {}
|
||||
undercloud._generate_certificate(fake_env)
|
||||
self.assertFalse(mock_open.called)
|
||||
self.assertEqual('/etc/pki/instack-certs/undercloud-192.0.2.2.pem',
|
||||
fake_env['UNDERCLOUD_SERVICE_CERTIFICATE'])
|
||||
|
||||
@mock.patch('os.remove')
|
||||
def test_command_fails(self, mock_remove, mock_run_command, mock_exists):
|
||||
mock_run_command.side_effect = FakeException
|
||||
mock_exists.side_effect = [False, True, True]
|
||||
self.assertRaises(FakeException, undercloud._generate_certificate, {})
|
||||
self.assertEqual(3, mock_remove.call_count)
|
||||
|
||||
@mock.patch('os.remove')
|
||||
def test_file_missing(self, mock_remove, mock_run_command, mock_exists):
|
||||
mock_run_command.side_effect = FakeException
|
||||
mock_exists.side_effect = [False, True, False]
|
||||
self.assertRaises(FakeException, undercloud._generate_certificate, {})
|
||||
self.assertEqual(2, mock_remove.call_count)
|
||||
|
|
|
@ -162,6 +162,18 @@ _opts = [
|
|||
'this certificate will also be added to the system\'s '
|
||||
'trusted certificate store.')
|
||||
),
|
||||
cfg.StrOpt('certificate_generation_ca',
|
||||
default='local',
|
||||
help=('The certmonger nickname of the CA from which the '
|
||||
'certificate will be requested. This is used only if '
|
||||
'the generate_service_certificate option is set.')
|
||||
),
|
||||
cfg.StrOpt('service_principal',
|
||||
default='',
|
||||
help=('The kerberos principal for the service that will use '
|
||||
'the certificate. This is only needed if your CA '
|
||||
'requires a kerberos principal. e.g. with FreeIPA.')
|
||||
),
|
||||
cfg.StrOpt('local_interface',
|
||||
default='eth1',
|
||||
help=('Network interface on the Undercloud that will be '
|
||||
|
@ -753,82 +765,6 @@ def _write_password_file(instack_env):
|
|||
password_file.write('%s=%s\n' % (opt.name, value))
|
||||
|
||||
|
||||
SSL_CONFIG_TEMPLATE = '''[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
x509_extensions = v3_req
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = xx
|
||||
ST = xx
|
||||
L = xxxx
|
||||
O = xxxx
|
||||
CN = %(public_vip)s
|
||||
|
||||
[v3_req]
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer
|
||||
basicConstraints = CA:TRUE
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
IP = %(public_vip)s
|
||||
'''
|
||||
|
||||
|
||||
def _generate_certificate(instack_env):
|
||||
public_vip = CONF.undercloud_public_vip
|
||||
home_pem = os.path.expanduser('~/undercloud-%s.pem' % public_vip)
|
||||
undercloud_pem = ('/etc/pki/instack-certs/undercloud-%s.pem' %
|
||||
public_vip)
|
||||
if os.path.exists(home_pem):
|
||||
LOG.info('%s already exists. Not generating a new '
|
||||
'certificate', home_pem)
|
||||
instack_env['UNDERCLOUD_SERVICE_CERTIFICATE'] = undercloud_pem
|
||||
return
|
||||
ssl_config = SSL_CONFIG_TEMPLATE % {'public_vip': public_vip}
|
||||
ssl_config_file = tempfile.mkstemp()[1]
|
||||
try:
|
||||
with open(ssl_config_file, 'w') as f:
|
||||
f.write(ssl_config)
|
||||
privkey = tempfile.mkstemp()[1]
|
||||
cacert = tempfile.mkstemp()[1]
|
||||
args = ['openssl', 'genrsa', '-out', privkey, '2048']
|
||||
_run_command(args, name='openssl private key')
|
||||
args = ['openssl', 'req', '-new', '-x509', '-key', privkey, '-out',
|
||||
cacert, '-days', '3650', '-config', ssl_config_file]
|
||||
_run_command(args, name='openssl cacert')
|
||||
with open(home_pem, 'w') as u:
|
||||
with open(cacert) as c:
|
||||
u.write(c.read())
|
||||
with open(privkey) as p:
|
||||
u.write(p.read())
|
||||
args = ['sudo', 'mkdir', '-p', '/etc/pki/instack-certs']
|
||||
_run_command(args, name='mkdir instack-certs')
|
||||
args = ['sudo', 'cp', '-f', home_pem, undercloud_pem]
|
||||
_run_command(args, name='cp undercloud.pem')
|
||||
args = ['sudo', 'semanage', 'fcontext', '-a', '-t', 'etc_t',
|
||||
'"/etc/pki/instack-certs(/.*)?"']
|
||||
_run_command(args, name='semanage')
|
||||
args = ['sudo', 'restorecon', '-R', '/etc/pki/instack-certs']
|
||||
_run_command(args, name='restorecon')
|
||||
instack_env['UNDERCLOUD_SERVICE_CERTIFICATE'] = undercloud_pem
|
||||
LOG.info('Generated new service certificate in %s', undercloud_pem)
|
||||
cacert_path = ('/etc/pki/ca-trust/source/anchors/cacert-%s.pem' %
|
||||
public_vip)
|
||||
args = ['sudo', 'cp', '-f', cacert, cacert_path]
|
||||
_run_command(args, name='copy cacert')
|
||||
args = ['sudo', 'update-ca-trust', 'extract']
|
||||
_run_command(args, name='update-ca-trust')
|
||||
LOG.info('Added %s to system certificate store', cacert_path)
|
||||
finally:
|
||||
os.remove(ssl_config_file)
|
||||
if os.path.exists(privkey):
|
||||
os.remove(privkey)
|
||||
if os.path.exists(cacert):
|
||||
os.remove(cacert)
|
||||
|
||||
|
||||
def _generate_environment(instack_root):
|
||||
"""Generate an environment dict for instack
|
||||
|
||||
|
@ -923,7 +859,9 @@ def _generate_environment(instack_root):
|
|||
_write_password_file(instack_env)
|
||||
|
||||
if CONF.generate_service_certificate:
|
||||
_generate_certificate(instack_env)
|
||||
public_vip = CONF.undercloud_public_vip
|
||||
instack_env['UNDERCLOUD_SERVICE_CERTIFICATE'] = (
|
||||
'/etc/pki/tls/certs/undercloud-%s.pem' % public_vip)
|
||||
|
||||
return instack_env
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
CERT_FILE="$1"
|
||||
KEY_FILE="$2"
|
||||
OUTPUT_FILE="$3"
|
||||
|
||||
if [[ -z "$CERT_FILE" || -z "$KEY_FILE" || -z "$OUTPUT_FILE" ]]; then
|
||||
echo "You need to provide CERT_FILE KEY_FILE and finally OUTPUT_FILE" \
|
||||
"as arguments in that order."
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$CERT_FILE" || ! -f "$KEY_FILE" ]]; then
|
||||
echo "Certificate and key files must exist!"
|
||||
exit 1
|
||||
fi
|
||||
cat $CERT_FILE $KEY_FILE > $OUTPUT_FILE
|
||||
systemctl reload haproxy
|
|
@ -26,6 +26,7 @@ scripts =
|
|||
scripts/instack-create-overcloudrc
|
||||
scripts/instack-install-undercloud
|
||||
scripts/instack-virt-setup
|
||||
scripts/instack-haproxy-cert-update
|
||||
|
||||
data_files =
|
||||
share/instack-undercloud/ = elements/*
|
||||
|
|
|
@ -52,6 +52,16 @@
|
|||
# store. (boolean value)
|
||||
#generate_service_certificate = false
|
||||
|
||||
# The certmonger nickname of the CA from which the certificate will be
|
||||
# requested. This is used only if the generate_service_certificate
|
||||
# option is set. (string value)
|
||||
#certificate_generation_ca = local
|
||||
|
||||
# The kerberos principal for the service that will use the
|
||||
# certificate. This is only needed if your CA requires a kerberos
|
||||
# principal. e.g. with FreeIPA. (string value)
|
||||
#service_principal =
|
||||
|
||||
# Network interface on the Undercloud that will be handling the PXE
|
||||
# boots and DHCP for Overcloud instances. (string value)
|
||||
#local_interface = eth1
|
||||
|
|
Loading…
Reference in New Issue