From 8da7c1f5bc68110469bedceb0ddad4fdfc8b7e4d Mon Sep 17 00:00:00 2001 From: Andrew Butcher Date: Wed, 28 Oct 2015 10:39:41 -0400 Subject: Add custom certificates to serving info in master configuration. --- filter_plugins/oo_filters.py | 57 +++++++++++++++++++++- inventory/byo/hosts.example | 3 ++ playbooks/common/openshift-master/config.yml | 9 ++++ roles/openshift_master/templates/master.yaml.v1.j2 | 14 ++++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/filter_plugins/oo_filters.py b/filter_plugins/oo_filters.py index a57b0f895..d653b9217 100644 --- a/filter_plugins/oo_filters.py +++ b/filter_plugins/oo_filters.py @@ -7,6 +7,8 @@ Custom filters for use in openshift-ansible from ansible import errors from operator import itemgetter +import OpenSSL.crypto +import os.path import pdb import re import json @@ -327,6 +329,58 @@ class FilterModule(object): return revamped_outputs + @staticmethod + def oo_parse_certificate_names(certificates, data_dir): + ''' Parses names from list of certificate hashes. + + Ex: certificates = [{ "certfile": "/etc/origin/master/custom1.crt", + "keyfile": "/etc/origin/master/custom1.key" }, + { "certfile": "custom2.crt", + "keyfile": "custom2.key" }] + + returns [{ "certfile": "/etc/origin/master/custom1.crt", + "keyfile": "/etc/origin/master/custom1.key", + "names": [ "public-master-host.com", + "other-master-host.com" ] }, + { "certfile": "/etc/origin/master/custom2.crt", + "keyfile": "/etc/origin/master/custom2.key", + "names": [ "some-hostname.com" ] }] + ''' + if not issubclass(type(certificates), list): + raise errors.AnsibleFilterError("|failed expects certificates is a list") + + if not issubclass(type(data_dir), unicode): + raise errors.AnsibleFilterError("|failed expects data_dir is unicode") + + for certificate in certificates: + if 'names' in certificate.keys(): + continue + else: + certificate['names'] = [] + + if not os.path.isfile(certificate['certfile']) and not os.path.isfile(certificate['keyfile']): + # Unable to find cert/key, try to prepend data_dir to paths + certificate['certfile'] = os.path.join(data_dir, certificate['certfile']) + certificate['keyfile'] = os.path.join(data_dir, certificate['keyfile']) + if not os.path.isfile(certificate['certfile']) and not os.path.isfile(certificate['keyfile']): + # Unable to find cert/key in data_dir + raise errors.AnsibleFilterError("|certificate and/or key does not exist %s, %s" % + (certificate['certfile'], certificate['keyfile'])) + + try: + st_cert = open(certificate['certfile'], 'rt').read() + cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, st_cert) + certificate['names'].append(str(cert.get_subject().commonName.decode())) + for i in range(cert.get_extension_count()): + if cert.get_extension(i).get_short_name() == 'subjectAltName': + for name in str(cert.get_extension(i)).replace('DNS:', '').split(', '): + certificate['names'].append(name) + except: + raise errors.AnsibleFilterError("|failed to parse certificate %s" % certificate['certfile']) + + certificate['names'] = list(set(certificate['names'])) + return certificates + def filters(self): ''' returns a mapping of filters to methods ''' return { @@ -342,5 +396,6 @@ class FilterModule(object): "oo_combine_dict": self.oo_combine_dict, "oo_split": self.oo_split, "oo_filter_list": self.oo_filter_list, - "oo_parse_heat_stack_outputs": self.oo_parse_heat_stack_outputs + "oo_parse_heat_stack_outputs": self.oo_parse_heat_stack_outputs, + "oo_parse_certificate_names": self.oo_parse_certificate_names } diff --git a/inventory/byo/hosts.example b/inventory/byo/hosts.example index ad19fe116..c6733567a 100644 --- a/inventory/byo/hosts.example +++ b/inventory/byo/hosts.example @@ -99,6 +99,9 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # set RPM version for debugging purposes #openshift_pkg_version=-3.0.0.0 +# Configure custom master certificates +#openshift_master_named_certificates=[{"certfile": "/path/to/custom1.crt", "keyfile": "/path/to/custom1.key"}] + # host group for masters [masters] ose3-master[1:3]-ansible.test.example.com diff --git a/playbooks/common/openshift-master/config.yml b/playbooks/common/openshift-master/config.yml index 1dec923fc..4662c179a 100644 --- a/playbooks/common/openshift-master/config.yml +++ b/playbooks/common/openshift-master/config.yml @@ -199,9 +199,18 @@ validate_checksum: yes with_items: masters_needing_certs +- name: Inspect named certificates + hosts: oo_first_master + tasks: + - name: Collect certificate names + set_fact: + parsed_named_certificates: "{{ openshift_master_named_certificates | oo_parse_certificate_names(master_cert_config_dir) }}" + when: openshift_master_named_certificates is defined + - name: Configure master instances hosts: oo_masters_to_config vars: + named_certificates: "{{ hostvars[groups['oo_first_master'][0]]['parsed_named_certificates'] | default([])}}" sync_tmpdir: "{{ hostvars.localhost.g_master_mktemp.stdout }}" openshift_master_ha: "{{ groups.oo_masters_to_config | length > 1 }}" embedded_etcd: "{{ openshift.master.embedded_etcd }}" diff --git a/roles/openshift_master/templates/master.yaml.v1.j2 b/roles/openshift_master/templates/master.yaml.v1.j2 index 73a0bc6cc..b429be596 100644 --- a/roles/openshift_master/templates/master.yaml.v1.j2 +++ b/roles/openshift_master/templates/master.yaml.v1.j2 @@ -22,6 +22,9 @@ corsAllowedOrigins: {% for custom_origin in openshift.master.custom_cors_origins | default("") %} - {{ custom_origin }} {% endfor %} +{% for name in (named_certificates | map(attribute='names')) | list | oo_flatten %} + - {{ name }} +{% endfor %} {% if 'disabled_features' in openshift.master %} disabledFeatures: {{ openshift.master.disabled_features | to_json }} {% endif %} @@ -133,3 +136,14 @@ servingInfo: keyFile: master.server.key maxRequestsInFlight: 500 requestTimeoutSeconds: 3600 +{% if named_certificates %} + namedCertificates: +{% for named_certificate in named_certificates %} + - certFile: {{ named_certificate['certfile'] }} + keyFile: {{ named_certificate['keyfile'] }} + names: +{% for name in named_certificate['names'] %} + - "{{ name }}" +{% endfor %} +{% endfor %} +{% endif %} -- cgit v1.2.3 From 3a8b4f1315e28f35e16ace77560f040f08588722 Mon Sep 17 00:00:00 2001 From: Andrew Butcher Date: Tue, 3 Nov 2015 11:26:33 -0500 Subject: Filter internal hostnames from the list of parsed names. --- filter_plugins/oo_filters.py | 16 +++++++++++++--- inventory/byo/hosts.example | 2 ++ playbooks/common/openshift-master/config.yml | 2 +- roles/openshift_facts/library/openshift_facts.py | 14 +++++++++++--- roles/openshift_master/templates/master.yaml.v1.j2 | 2 +- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/filter_plugins/oo_filters.py b/filter_plugins/oo_filters.py index d653b9217..dfd9a111e 100644 --- a/filter_plugins/oo_filters.py +++ b/filter_plugins/oo_filters.py @@ -330,7 +330,8 @@ class FilterModule(object): return revamped_outputs @staticmethod - def oo_parse_certificate_names(certificates, data_dir): + # pylint: disable=too-many-branches + def oo_parse_certificate_names(certificates, data_dir, internal_hostnames): ''' Parses names from list of certificate hashes. Ex: certificates = [{ "certfile": "/etc/origin/master/custom1.crt", @@ -352,6 +353,9 @@ class FilterModule(object): if not issubclass(type(data_dir), unicode): raise errors.AnsibleFilterError("|failed expects data_dir is unicode") + if not issubclass(type(internal_hostnames), list): + raise errors.AnsibleFilterError("|failed expects internal_hostnames is list") + for certificate in certificates: if 'names' in certificate.keys(): continue @@ -364,7 +368,7 @@ class FilterModule(object): certificate['keyfile'] = os.path.join(data_dir, certificate['keyfile']) if not os.path.isfile(certificate['certfile']) and not os.path.isfile(certificate['keyfile']): # Unable to find cert/key in data_dir - raise errors.AnsibleFilterError("|certificate and/or key does not exist %s, %s" % + raise errors.AnsibleFilterError("|certificate and/or key does not exist '%s', '%s'" % (certificate['certfile'], certificate['keyfile'])) try: @@ -376,9 +380,15 @@ class FilterModule(object): for name in str(cert.get_extension(i)).replace('DNS:', '').split(', '): certificate['names'].append(name) except: - raise errors.AnsibleFilterError("|failed to parse certificate %s" % certificate['certfile']) + raise errors.AnsibleFilterError(("|failed to parse certificate '%s', " % certificate['certfile'] + + "please specify certificate names in host inventory")) + certificate['names'] = [name for name in certificate['names'] if name not in internal_hostnames] certificate['names'] = list(set(certificate['names'])) + if not certificate['names']: + raise errors.AnsibleFilterError(("|failed to parse certificate '%s' or " % certificate['certfile'] + + "detected a collision with internal hostname, please specify " + + "certificate names in host inventory")) return certificates def filters(self): diff --git a/inventory/byo/hosts.example b/inventory/byo/hosts.example index c6733567a..f60918e6d 100644 --- a/inventory/byo/hosts.example +++ b/inventory/byo/hosts.example @@ -101,6 +101,8 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # Configure custom master certificates #openshift_master_named_certificates=[{"certfile": "/path/to/custom1.crt", "keyfile": "/path/to/custom1.key"}] +# Detected names may be overridden by specifying the "names" key +#openshift_master_named_certificates=[{"certfile": "/path/to/custom1.crt", "keyfile": "/path/to/custom1.key", "names": ["public-master-host.com"]}] # host group for masters [masters] diff --git a/playbooks/common/openshift-master/config.yml b/playbooks/common/openshift-master/config.yml index 4662c179a..59c4b2370 100644 --- a/playbooks/common/openshift-master/config.yml +++ b/playbooks/common/openshift-master/config.yml @@ -204,7 +204,7 @@ tasks: - name: Collect certificate names set_fact: - parsed_named_certificates: "{{ openshift_master_named_certificates | oo_parse_certificate_names(master_cert_config_dir) }}" + parsed_named_certificates: "{{ openshift_master_named_certificates | oo_parse_certificate_names(master_cert_config_dir, openshift.common.internal_hostnames) }}" when: openshift_master_named_certificates is defined - name: Configure master instances diff --git a/roles/openshift_facts/library/openshift_facts.py b/roles/openshift_facts/library/openshift_facts.py index 163e67f62..28866bd48 100755 --- a/roles/openshift_facts/library/openshift_facts.py +++ b/roles/openshift_facts/library/openshift_facts.py @@ -484,12 +484,16 @@ def set_aggregate_facts(facts): dict: the facts dict updated with aggregated facts """ all_hostnames = set() + internal_hostnames = set() if 'common' in facts: all_hostnames.add(facts['common']['hostname']) all_hostnames.add(facts['common']['public_hostname']) all_hostnames.add(facts['common']['ip']) all_hostnames.add(facts['common']['public_ip']) + internal_hostnames.add(facts['common']['hostname']) + internal_hostnames.add(facts['common']['ip']) + if 'master' in facts: # FIXME: not sure why but facts['dns']['domain'] fails cluster_domain = 'cluster.local' @@ -497,13 +501,17 @@ def set_aggregate_facts(facts): all_hostnames.add(facts['master']['cluster_hostname']) if 'cluster_public_hostname' in facts['master']: all_hostnames.add(facts['master']['cluster_public_hostname']) - all_hostnames.update(['openshift', 'openshift.default', 'openshift.default.svc', - 'openshift.default.svc.' + cluster_domain, 'kubernetes', 'kubernetes.default', - 'kubernetes.default.svc', 'kubernetes.default.svc.' + cluster_domain]) + svc_names = ['openshift', 'openshift.default', 'openshift.default.svc', + 'openshift.default.svc.' + cluster_domain, 'kubernetes', 'kubernetes.default', + 'kubernetes.default.svc', 'kubernetes.default.svc.' + cluster_domain] + all_hostnames.update(svc_names) + internal_hostnames.update(svc_names) first_svc_ip = str(IPNetwork(facts['master']['portal_net'])[1]) all_hostnames.add(first_svc_ip) + internal_hostnames.add(first_svc_ip) facts['common']['all_hostnames'] = list(all_hostnames) + facts['common']['internal_hostnames'] = list(all_hostnames) return facts diff --git a/roles/openshift_master/templates/master.yaml.v1.j2 b/roles/openshift_master/templates/master.yaml.v1.j2 index b429be596..9547a6945 100644 --- a/roles/openshift_master/templates/master.yaml.v1.j2 +++ b/roles/openshift_master/templates/master.yaml.v1.j2 @@ -16,7 +16,7 @@ assetConfig: maxRequestsInFlight: 0 requestTimeoutSeconds: 0 corsAllowedOrigins: -{% for origin in ['127.0.0.1', 'localhost', openshift.common.hostname, openshift.common.ip, openshift.common.public_hostname, openshift.common.public_ip] %} +{% for origin in ['127.0.0.1', 'localhost', openshift.common.hostname, openshift.common.ip, openshift.common.public_hostname, openshift.common.public_ip] | unique %} - {{ origin }} {% endfor %} {% for custom_origin in openshift.master.custom_cors_origins | default("") %} -- cgit v1.2.3