diff options
author | Brenton Leanhardt <bleanhar@redhat.com> | 2016-01-06 14:30:09 -0500 |
---|---|---|
committer | Brenton Leanhardt <bleanhar@redhat.com> | 2016-01-06 14:30:09 -0500 |
commit | 31a18b4e6096451bd81603b92a2d4cf7d21cecef (patch) | |
tree | 26137b20f9df24d47958948baffd3b56880c9b03 | |
parent | af803894ad2e214948264d105f539bb7514e92ce (diff) | |
parent | 82db6897085a1278e6b982a403875ed8671190bb (diff) | |
download | openshift-31a18b4e6096451bd81603b92a2d4cf7d21cecef.tar.gz openshift-31a18b4e6096451bd81603b92a2d4cf7d21cecef.tar.bz2 openshift-31a18b4e6096451bd81603b92a2d4cf7d21cecef.tar.xz openshift-31a18b4e6096451bd81603b92a2d4cf7d21cecef.zip |
Merge pull request #1018 from abutcher/secrets
Clean up idempotency issues with session secrets.
-rw-r--r-- | filter_plugins/oo_filters.py | 19 | ||||
-rw-r--r-- | filter_plugins/openshift_master.py | 1 | ||||
-rw-r--r-- | playbooks/common/openshift-master/config.yml | 49 | ||||
-rwxr-xr-x | roles/openshift_facts/library/openshift_facts.py | 67 | ||||
-rw-r--r-- | roles/openshift_master/tasks/main.yml | 5 | ||||
-rw-r--r-- | roles/openshift_master/templates/master.yaml.v1.j2 | 2 | ||||
-rw-r--r-- | roles/openshift_master/templates/sessionSecretsFile.yaml.v1.j2 | 6 |
7 files changed, 112 insertions, 37 deletions
diff --git a/filter_plugins/oo_filters.py b/filter_plugins/oo_filters.py index 48e27a24a..326c36f6c 100644 --- a/filter_plugins/oo_filters.py +++ b/filter_plugins/oo_filters.py @@ -8,12 +8,11 @@ Custom filters for use in openshift-ansible from ansible import errors from operator import itemgetter import OpenSSL.crypto -import os.path +import os import pdb import re import json - class FilterModule(object): ''' Custom ansible filters ''' @@ -366,9 +365,6 @@ class FilterModule(object): "keyfile": "/etc/origin/master/named_certificates/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(named_certs_dir), unicode): raise errors.AnsibleFilterError("|failed expects named_certs_dir is unicode") @@ -468,6 +464,16 @@ class FilterModule(object): pass return clusters + @staticmethod + def oo_generate_secret(num_bytes): + ''' generate a session secret ''' + + if not issubclass(type(num_bytes), int): + raise errors.AnsibleFilterError("|failed expects num_bytes is int") + + secret = os.urandom(num_bytes) + return secret.encode('base-64').strip() + def filters(self): ''' returns a mapping of filters to methods ''' return { @@ -486,5 +492,6 @@ class FilterModule(object): "oo_parse_heat_stack_outputs": self.oo_parse_heat_stack_outputs, "oo_parse_named_certificates": self.oo_parse_named_certificates, "oo_haproxy_backend_masters": self.oo_haproxy_backend_masters, - "oo_pretty_print_cluster": self.oo_pretty_print_cluster + "oo_pretty_print_cluster": self.oo_pretty_print_cluster, + "oo_generate_secret": self.oo_generate_secret } diff --git a/filter_plugins/openshift_master.py b/filter_plugins/openshift_master.py index f12017967..8d7c62ad1 100644 --- a/filter_plugins/openshift_master.py +++ b/filter_plugins/openshift_master.py @@ -463,7 +463,6 @@ class FilterModule(object): IdentityProviderBase.validate_idp_list(idp_list) return yaml.safe_dump([idp.to_dict() for idp in idp_list], default_flow_style=False) - def filters(self): ''' returns a mapping of filters to methods ''' return {"translate_idps": self.translate_idps} diff --git a/playbooks/common/openshift-master/config.yml b/playbooks/common/openshift-master/config.yml index 759656e63..677c274c4 100644 --- a/playbooks/common/openshift-master/config.yml +++ b/playbooks/common/openshift-master/config.yml @@ -236,29 +236,32 @@ - role: haproxy when: groups.oo_masters_to_config | length > 1 -- name: Generate master session keys +- name: Check for cached session secrets hosts: oo_first_master + roles: + - role: openshift_facts + post_tasks: + - openshift_facts: + role: master + local_facts: + session_auth_secrets: "{{ openshift_master_session_auth_secrets | default(openshift.master.session_auth_secrets | default(None)) }}" + session_encryption_secrets: "{{ openshift_master_session_encryption_secrets | default(openshift.master.session_encryption_secrets | default(None)) }}" + +- name: Generate master session secrets + hosts: oo_first_master + vars: + g_session_secrets_present: "{{ (openshift.master.session_auth_secrets | default([]) and openshift.master.session_encryption_secrets | default([])) | length > 0 }}" + g_session_auth_secrets: "{{ [ 24 | oo_generate_secret ] }}" + g_session_encryption_secrets: "{{ [ 24 | oo_generate_secret ] }}" + roles: + - role: openshift_facts tasks: - - fail: - msg: "Both openshift_master_session_auth_secrets and openshift_master_session_encryption_secrets must be provided if either variable is set" - when: (openshift_master_session_auth_secrets is defined and openshift_master_session_encryption_secrets is not defined) or (openshift_master_session_encryption_secrets is defined and openshift_master_session_auth_secrets is not defined) - - fail: - msg: "openshift_master_session_auth_secrets and openshift_master_encryption_secrets must be equal length" - when: (openshift_master_session_auth_secrets is defined and openshift_master_session_encryption_secrets is defined) and (openshift_master_session_auth_secrets | length != openshift_master_session_encryption_secrets | length) - - name: Install OpenSSL package - action: "{{ ansible_pkg_mgr }} name=openssl state=present" - when: not openshift.common.is_atomic | bool - - name: Generate session authentication key - command: /usr/bin/openssl rand -base64 24 - register: session_auth_output - when: openshift_master_session_auth_secrets is undefined - - name: Generate session encryption key - command: /usr/bin/openssl rand -base64 24 - register: session_encryption_output - when: openshift_master_session_encryption_secrets is undefined - - set_fact: - session_auth_secret: "{{ openshift_master_session_auth_secrets | default([session_auth_output.stdout]) }}" - session_encryption_secret: "{{ openshift_master_session_encryption_secrets | default([session_encryption_output.stdout]) }}" + - openshift_facts: + role: master + local_facts: + session_auth_secrets: "{{ g_session_auth_secrets }}" + session_encryption_secrets: "{{ g_session_encryption_secrets }}" + when: not g_session_secrets_present | bool - name: Parse named certificates hosts: localhost @@ -314,8 +317,8 @@ sync_tmpdir: "{{ hostvars.localhost.g_master_mktemp.stdout }}" openshift_master_ha: "{{ groups.oo_masters_to_config | length > 1 }}" openshift_master_count: "{{ groups.oo_masters_to_config | length }}" - openshift_master_session_auth_secrets: "{{ hostvars[groups['oo_first_master'][0]]['session_auth_secret'] }}" - openshift_master_session_encryption_secrets: "{{ hostvars[groups['oo_first_master'][0]]['session_encryption_secret'] }}" + openshift_master_session_auth_secrets: "{{ hostvars[groups.oo_first_master.0].openshift.master.session_auth_secrets }}" + openshift_master_session_encryption_secrets: "{{ hostvars[groups.oo_first_master.0].openshift.master.session_encryption_secrets }}" pre_tasks: - name: Ensure certificate directory exists file: diff --git a/roles/openshift_facts/library/openshift_facts.py b/roles/openshift_facts/library/openshift_facts.py index 911a684fc..b2acd789d 100755 --- a/roles/openshift_facts/library/openshift_facts.py +++ b/roles/openshift_facts/library/openshift_facts.py @@ -1043,6 +1043,7 @@ class OpenShiftFacts(object): facts (dict): facts for the host Args: + module (AnsibleModule): an AnsibleModule object role (str): role for setting local facts filename (str): local facts file to use local_facts (dict): local facts to set @@ -1263,14 +1264,78 @@ class OpenShiftFacts(object): del facts[key] if new_local_facts != local_facts: + self.validate_local_facts(new_local_facts) changed = True - if not module.check_mode: save_local_facts(self.filename, new_local_facts) self.changed = changed return new_local_facts + def validate_local_facts(self, facts=None): + """ Validate local facts + + Args: + facts (dict): local facts to validate + """ + invalid_facts = dict() + invalid_facts = self.validate_master_facts(facts, invalid_facts) + if invalid_facts: + msg = 'Invalid facts detected:\n' + for key in invalid_facts.keys(): + msg += '{0}: {1}\n'.format(key, invalid_facts[key]) + module.fail_json(msg=msg, + changed=self.changed) + + # disabling pylint errors for line-too-long since we're dealing + # with best effort reduction of error messages here. + # disabling errors for too-many-branches since we require checking + # many conditions. + # pylint: disable=line-too-long, too-many-branches + @staticmethod + def validate_master_facts(facts, invalid_facts): + """ Validate master facts + + Args: + facts (dict): local facts to validate + invalid_facts (dict): collected invalid_facts + + Returns: + dict: Invalid facts + """ + if 'master' in facts: + # openshift.master.session_auth_secrets + if 'session_auth_secrets' in facts['master']: + session_auth_secrets = facts['master']['session_auth_secrets'] + if not issubclass(type(session_auth_secrets), list): + invalid_facts['session_auth_secrets'] = 'Expects session_auth_secrets is a list.' + elif 'session_encryption_secrets' not in facts['master']: + invalid_facts['session_auth_secrets'] = ('openshift_master_session_encryption secrets must be set ' + 'if openshift_master_session_auth_secrets is provided.') + elif len(session_auth_secrets) != len(facts['master']['session_encryption_secrets']): + invalid_facts['session_auth_secrets'] = ('openshift_master_session_auth_secrets and ' + 'openshift_master_session_encryption_secrets must be ' + 'equal length.') + else: + for secret in session_auth_secrets: + if len(secret) < 32: + invalid_facts['session_auth_secrets'] = ('Invalid secret in session_auth_secrets. ' + 'Secrets must be at least 32 characters in length.') + # openshift.master.session_encryption_secrets + if 'session_encryption_secrets' in facts['master']: + session_encryption_secrets = facts['master']['session_encryption_secrets'] + if not issubclass(type(session_encryption_secrets), list): + invalid_facts['session_encryption_secrets'] = 'Expects session_encryption_secrets is a list.' + elif 'session_auth_secrets' not in facts['master']: + invalid_facts['session_encryption_secrets'] = ('openshift_master_session_auth_secrets must be ' + 'set if openshift_master_session_encryption_secrets ' + 'is provided.') + else: + for secret in session_encryption_secrets: + if len(secret) not in [16, 24, 32]: + invalid_facts['session_encryption_secrets'] = ('Invalid secret in session_encryption_secrets. ' + 'Secrets must be 16, 24, or 32 characters in length.') + return invalid_facts def main(): """ main """ diff --git a/roles/openshift_master/tasks/main.yml b/roles/openshift_master/tasks/main.yml index a22654678..397122631 100644 --- a/roles/openshift_master/tasks/main.yml +++ b/roles/openshift_master/tasks/main.yml @@ -9,7 +9,6 @@ Invalid OAuth grant method: {{ openshift_master_oauth_grant_method }} when: openshift_master_oauth_grant_method is defined and openshift_master_oauth_grant_method not in openshift_master_valid_grant_methods - # HA Variable Validation - fail: msg: "openshift_master_cluster_method must be set to either 'native' or 'pacemaker' for multi-master installations" @@ -55,9 +54,9 @@ portal_net: "{{ openshift_master_portal_net | default(None) }}" session_max_seconds: "{{ openshift_master_session_max_seconds | default(None) }}" session_name: "{{ openshift_master_session_name | default(None) }}" + session_secrets_file: "{{ openshift_master_session_secrets_file | default(None) }}" session_auth_secrets: "{{ openshift_master_session_auth_secrets | default(None) }}" session_encryption_secrets: "{{ openshift_master_session_encryption_secrets | default(None) }}" - session_secrets_file: "{{ openshift_master_session_secrets_file | default(None) }}" access_token_max_seconds: "{{ openshift_master_access_token_max_seconds | default(None) }}" auth_token_max_seconds: "{{ openshift_master_auth_token_max_seconds | default(None) }}" identity_providers: "{{ openshift_master_identity_providers | default(None) }}" @@ -221,7 +220,7 @@ template: dest: "{{ openshift.master.session_secrets_file }}" src: sessionSecretsFile.yaml.v1.j2 - force: no + when: openshift.master.session_auth_secrets is defined and openshift.master.session_encryption_secrets is defined notify: - restart master - restart master api diff --git a/roles/openshift_master/templates/master.yaml.v1.j2 b/roles/openshift_master/templates/master.yaml.v1.j2 index 317049c44..647476b7f 100644 --- a/roles/openshift_master/templates/master.yaml.v1.j2 +++ b/roles/openshift_master/templates/master.yaml.v1.j2 @@ -127,7 +127,9 @@ oauthConfig: sessionConfig: sessionMaxAgeSeconds: {{ openshift.master.session_max_seconds }} sessionName: {{ openshift.master.session_name }} +{% if openshift.master.session_auth_secrets is defined and openshift.master.session_encryption_secrets is defined %} sessionSecretsFile: {{ openshift.master.session_secrets_file }} +{% endif %} tokenConfig: accessTokenMaxAgeSeconds: {{ openshift.master.access_token_max_seconds }} authorizeTokenMaxAgeSeconds: {{ openshift.master.auth_token_max_seconds }} diff --git a/roles/openshift_master/templates/sessionSecretsFile.yaml.v1.j2 b/roles/openshift_master/templates/sessionSecretsFile.yaml.v1.j2 index d12d9db90..3d4b573a9 100644 --- a/roles/openshift_master/templates/sessionSecretsFile.yaml.v1.j2 +++ b/roles/openshift_master/templates/sessionSecretsFile.yaml.v1.j2 @@ -1,7 +1,7 @@ apiVersion: v1 kind: SessionSecrets secrets: -{% for secret in openshift_master_session_auth_secrets %} -- authentication: "{{ openshift_master_session_auth_secrets[loop.index0] }}" - encryption: "{{ openshift_master_session_encryption_secrets[loop.index0] }}" +{% for secret in openshift.master.session_auth_secrets %} +- authentication: "{{ openshift.master.session_auth_secrets[loop.index0] }}" + encryption: "{{ openshift.master.session_encryption_secrets[loop.index0] }}" {% endfor %} |