From d8e96466fbe102277d68db6d47133c72cef80906 Mon Sep 17 00:00:00 2001 From: Adam Gandelman Date: Thu, 7 Feb 2013 21:03:44 -0800 Subject: [PATCH] First pass single-node ssl master. --- config.yaml | 10 ++++++++ hooks/keystone-hooks | 58 +++++++++++++++++++++++++++++++------------- hooks/utils.py | 28 ++++++++++++++++++--- revision | 2 +- 4 files changed, 77 insertions(+), 21 deletions(-) diff --git a/config.yaml b/config.yaml index 2a654c76..48f02126 100644 --- a/config.yaml +++ b/config.yaml @@ -75,3 +75,13 @@ options: default: "keystone" type: string description: "Database username" + https-service-endpoints: + default: "False" + type: string + description: "Manage SSL certificates for all service endpoints." + ssl-ca: + default: "None" + type: string + description: | + CA to use when issueing certificates to OpenStack services. If unset, + a CA certificate will be automatically generated. diff --git a/hooks/keystone-hooks b/hooks/keystone-hooks index ccbf7bc0..0d928f38 100755 --- a/hooks/keystone-hooks +++ b/hooks/keystone-hooks @@ -1,14 +1,18 @@ #!/usr/bin/python + import sys import time +from base64 import b64encode +from urlparse import urlparse + from utils import * from lib.openstack_common import * config = config_get() -packages = "keystone python-mysqldb pwgen" +packages = "keystone python-mysqldb pwgen openssl python-openssl" service = "keystone" # used to verify joined services are valid openstack components. @@ -72,6 +76,8 @@ def install_hook(): execute("service keystone stop", echo=True) execute("keystone-manage db_sync") execute("service keystone start", echo=True) + + time.sleep(5) ensure_initial_admin(config) @@ -108,6 +114,29 @@ def db_changed(): (id, unit)) identity_changed(relation_id=id, remote_unit=unit) +def ensure_valid_service(service): + if service not in valid_services.keys(): + juju_log("WARN: Invalid service requested: '%s'" % service) + realtion_set({ "admin_token": -1 }) + return + +def add_endpoint(region, service, public_url, admin_url, internal_url): + desc = valid_services[service]["desc"] + service_type = valid_services[service]["type"] + create_service_entry(service, service_type, desc) + create_endpoint_template(region=region, service=service, + public_url=public_url, + admin_url=admin_url, + internal_url=internal_url) + +def maybe_https(url): + if config['https-service-endpoints'] in ['True', 'true']: + parsed = urlparse(url) + if parsed.scheme == 'http': + url = 'https://%s%s' % (parsed.netloc, + parsed.path) + return url + def identity_joined(): """ Do nothing until we get information about requested service """ pass @@ -118,21 +147,6 @@ def identity_changed(relation_id=None, remote_unit=None): Optionally allow this hook to be re-fired for an existing relation+unit, for context see see db_changed(). """ - def ensure_valid_service(service): - if service not in valid_services.keys(): - juju_log("WARN: Invalid service requested: '%s'" % service) - realtion_set({ "admin_token": -1 }) - return - - def add_endpoint(region, service, public_url, admin_url, internal_url): - desc = valid_services[service]["desc"] - service_type = valid_services[service]["type"] - create_service_entry(service, service_type, desc) - create_endpoint_template(region=region, service=service, - public_url=public_url, - admin_url=admin_url, - internal_url=internal_url) - settings = relation_get_dict(relation_id=relation_id, remote_unit=remote_unit) @@ -141,13 +155,13 @@ def identity_changed(relation_id=None, remote_unit=None): 'internal_url']) if single.issubset(settings): # other end of relation advertised only one endpoint - if 'None' in [v for k,v in settings.iteritems()]: # Some backend services advertise no endpoint but require a # hook execution to update auth strategy. return ensure_valid_service(settings['service']) + add_endpoint(region=settings['region'], service=settings['service'], public_url=settings['public_url'], admin_url=settings['admin_url'], @@ -237,6 +251,16 @@ def identity_changed(relation_id=None, remote_unit=None): "service_password": service_password, "service_tenant": config['service-tenant'] } + + if config['https-service-endpoints'] in ['True', 'true']: + ca = get_ca() + service = os.getenv('JUJU_REMOTE_UNIT').split('/')[0] + cert, key = ca.get_cert_and_key(common_name=service) + ca_bundle= ca.get_ca_bundle() + relation_data['ssl_cert'] = b64encode(cert) + relation_data['ssl_key'] = b64encode(key) + relation_data['ca_cert'] = b64encode(ca_bundle) + relation_set(relation_data) def config_changed(): diff --git a/hooks/utils.py b/hooks/utils.py index 30790123..676e7505 100755 --- a/hooks/utils.py +++ b/hooks/utils.py @@ -7,12 +7,17 @@ import time from lib.openstack_common import * +import ssl + keystone_conf = "/etc/keystone/keystone.conf" stored_passwd = "/var/lib/keystone/keystone.passwd" stored_token = "/var/lib/keystone/keystone.token" +SSL_DIR = '/var/lib/keystone/juju_ssl/' +SSL_CA_NAME = 'Ubuntu Cloud' + def execute(cmd, die=False, echo=False): - """ Executes a command + """ Executes a command if die=True, script will exit(1) if command does not return 0 if echo=True, output of command will be printed to stdout @@ -414,8 +419,11 @@ def ensure_initial_admin(config): public_url = "http://%s:%s/v2.0" % (config["hostname"], config["service-port"]) admin_url = "http://%s:%s/v2.0" % (config["hostname"], config["admin-port"]) internal_url = "http://%s:%s/v2.0" % (config["hostname"], config["service-port"]) - create_endpoint_template("RegionOne", "keystone", public_url, - admin_url, internal_url) + create_endpoint_template("RegionOne", "keystone", + public_url, + admin_url, + internal_url) + def update_user_password(username, password): import manager @@ -480,3 +488,17 @@ def do_openstack_upgrade(install_src, packages): execute('service keystone start', echo=True) time.sleep(5) juju_log('Completed Keystone upgrade: %s -> %s' % (old_vers, new_vers)) + +CA = [] +def get_ca(name='Ubuntu Cloud'): + if not CA: + if not os.path.isdir(SSL_DIR): + os.mkdir(SSL_DIR) + d_name = '_'.join(name.lower().split(' ')) + ca = ssl.JujuCA(name=name, + ca_dir=os.path.join(SSL_DIR, + '%s_intermediate_ca' % d_name), + root_ca_dir=os.path.join(SSL_DIR, + '%s_root_ca' % d_name)) + CA.append(ca) + return CA[0] diff --git a/revision b/revision index 9e42f3ef..cdffbbc4 100644 --- a/revision +++ b/revision @@ -1 +1 @@ -165 +166