From 081d23977a5b77127c707debfaa07b5ee1ac6b30 Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Sun, 10 Nov 2019 11:02:03 +0000 Subject: [PATCH] Streamline credential generation with single-level Ansible loops This makes the credentials role significantly faster, Ansible is not good at optimizing loops that go across an include_tasks directive. We pre-generate some simple lists in float.py for service credentials that are enabled on each host, to simplify the iteration when generating certificates. --- plugins/inventory/float.py | 37 +++++++---- roles/credentials/tasks/install_cert.yml | 56 ----------------- roles/credentials/tasks/install_certs.yml | 63 +++++++++++++++++++ .../credentials/tasks/install_credentials.yml | 20 +++--- roles/credentials/tasks/install_service.yml | 6 -- roles/credentials/tasks/main.yml | 11 +--- 6 files changed, 98 insertions(+), 95 deletions(-) delete mode 100644 roles/credentials/tasks/install_cert.yml create mode 100644 roles/credentials/tasks/install_certs.yml delete mode 100644 roles/credentials/tasks/install_service.yml diff --git a/plugins/inventory/float.py b/plugins/inventory/float.py index a6e89e97..d3b0db5b 100644 --- a/plugins/inventory/float.py +++ b/plugins/inventory/float.py @@ -27,16 +27,14 @@ from ansible.plugins.inventory import BaseFileInventoryPlugin from ansible.module_utils._text import to_native -# Common credentials that are not attached to a service but will be +# Common client credentials that are not attached to a service but will be # installed on all hosts (i.e. clients for basic infra services). DEFAULT_SERVICE_CREDENTIALS = [ { 'name': 'log-client', - 'enable_server': False, }, { 'name': 'backup-agent', - 'enable_server': False, }, ] @@ -352,16 +350,27 @@ def _build_horizontal_upstreams_map(services): # Return autogenerated host-specific variables. def _host_vars(name, inventory, services, assignments): + # Set enabled/disabled services for each host, and explode the + # service credentials for assigned services so that it is easier + # to iterate on them in the 'credentials' Ansible role. hv = { 'float_enabled_services': [], 'float_disabled_services': [], 'float_enabled_containers': [], - 'float_service_credentials_params': {}, + 'float_host_service_credentials': [], + 'float_host_service_credentials_certs': [], 'float_host_overlay_networks': _host_net_overlays(name, inventory), 'float_host_dns_map': _host_service_dns_map( name, inventory, services, assignments), } + # Add default client credentials that are present on all hosts. + for c in DEFAULT_SERVICE_CREDENTIALS: + hv['float_host_service_credentials'].append({'credentials': c}) + hv['float_host_service_credentials_certs'].append({ + 'credentials': c, 'service': 'LOCAL', + 'mode': 'client', 'x509_params': {}}) + # There isn't necessarily a 1:1 mapping between services and # systemd units (in some corner cases of hard separation between # roles, for instance), so we need to compute the set difference. @@ -380,6 +389,18 @@ def _host_vars(name, inventory, services, assignments): _denormalize_container(s, c)) for u in services[s].get('systemd_services', []): enabled_systemd_units.add(u) + for c in services[s].get('service_credentials', []): + hv['float_host_service_credentials'].append({ + 'service': s, 'credentials': c}) + if c.get('enable_server', True): + params = _service_credential_params(name, s, inventory, assignments) + hv['float_host_service_credentials_certs'].append({ + 'credentials': c, 'service': s, + 'mode': 'server', 'x509_params': params}) + if c.get('enable_client', True): + hv['float_host_service_credentials_certs'].append({ + 'credentials': c, 'service': s, + 'mode': 'client', 'x509_params': {}}) else: hv['float_disabled_services'].append(s) for u in services[s].get('systemd_services', []): @@ -389,13 +410,6 @@ def _host_vars(name, inventory, services, assignments): is_master_var = 'float_%s_is_master' % (service_name_var,) hv[is_master_var] = assignments.is_master(s, name) - for c in services[s].get('service_credentials', []): - if c.get('enable_server', True): - # The key here is service+cred_name, to allow the same - # credentials in different services. - cred_key = '%s-%s' % (s, c['name']) - hv['float_service_credentials_params'][cred_key] = ( - _service_credential_params(name, s, inventory, assignments)) hv['float_disabled_systemd_units'] = list( disabled_systemd_units.difference(enabled_systemd_units)) @@ -533,7 +547,6 @@ def run_scheduler(config): all_vars.update({ 'float_plugin_loaded': True, 'services': services, - 'default_service_credentials': DEFAULT_SERVICE_CREDENTIALS, 'float_global_dns_map': _global_dns_map(inventory), # The following variables are just used for debugging purposes (dashboards). 'float_service_assignments': assignments._fwd, diff --git a/roles/credentials/tasks/install_cert.yml b/roles/credentials/tasks/install_cert.yml deleted file mode 100644 index cbff0f6c..00000000 --- a/roles/credentials/tasks/install_cert.yml +++ /dev/null @@ -1,56 +0,0 @@ ---- - -- set_fact: - x509_params: "{{ float_service_credentials_params[service_name_iter + '-' + credentials.name] | default({}) }}" - when: "service_name_iter is defined" - -- file: - path: "/etc/credentials/x509/{{ credentials.name }}/{{ mode }}" - state: directory - -- name: "Check the certificate for {{ credentials.name }}/{{ mode }}" - x509_csr: - credentials_name: "{{ credentials.name }}" - domain: "{{ domain }}" - mode: "{{ mode }}" - params: "{{ x509_params|default({}) }}" - private_key_path: "/etc/credentials/x509/{{ credentials.name }}/{{ mode }}/private_key.pem" - cert_path: "/etc/credentials/x509/{{ credentials.name }}/{{ mode }}/cert.pem" - ca_cert_path: "/etc/credentials/x509/{{ credentials.name }}/ca.pem" - check: true - check_mode: no - register: x509_should_update - -# TODO: set the right permissions (credentials.name-credentials) -- name: "Create the CSR for {{ credentials.name }}/{{ mode }}" - x509_csr: - credentials_name: "{{ credentials.name }}" - domain: "{{ domain }}" - mode: "{{ mode }}" - params: "{{ x509_params|default({}) }}" - private_key_path: "/etc/credentials/x509/{{ credentials.name }}/{{ mode }}/private_key.pem" - check: false - when: "x509_should_update.changed" - register: x509_csr - -- name: "Create the certificate for {{ credentials.name }}/{{ mode }}" - x509_sign: - csr: "{{ x509_csr.csr }}" - mode: "{{ mode }}" - ca_cert_path: "{{ credentials_dir }}/x509/ca.pem" - ca_key_path: "{{ credentials_dir }}/x509/ca_private_key.pem" - when: "x509_should_update.changed" - register: x509_sign - -- name: "Install the signed certificate for {{ credentials.name }}/{{ mode }}" - copy: - dest: "/etc/credentials/x509/{{ credentials.name }}/{{ mode }}/cert.pem" - content: "{{ x509_sign.cert }}" - mode: 0644 - when: "x509_sign.changed" - -- name: Set permissions on the private key - file: - path: "/etc/credentials/x509/{{ credentials.name }}/{{ mode }}/private_key.pem" - group: "{{ credentials.name }}-credentials" - mode: 0640 diff --git a/roles/credentials/tasks/install_certs.yml b/roles/credentials/tasks/install_certs.yml new file mode 100644 index 00000000..4f4a304d --- /dev/null +++ b/roles/credentials/tasks/install_certs.yml @@ -0,0 +1,63 @@ +--- + +#- set_fact: +# x509_params: "{{ float_service_credentials_params[service_name_item + '-' + credentials.name] | default({}) }}" +# when: "service_name_item is defined" + +- file: + path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}" + state: directory + loop: "{{ float_host_service_credentials_certs }}" + +- name: "Check the certificate for {{ item.credentials.name }}/{{ item.mode }}" + x509_csr: + credentials_name: "{{ item.credentials.name }}" + domain: "{{ domain }}" + mode: "{{ item.mode }}" + params: "{{ item.x509_params|default({}) }}" + private_key_path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}/private_key.pem" + cert_path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}/cert.pem" + ca_cert_path: "/etc/credentials/x509/{{ item.credentials.name }}/ca.pem" + check: true + loop: "{{ float_host_service_credentials_certs }}" + check_mode: no + register: x509_should_update + +# TODO: set the right permissions (credentials.name-credentials) +- name: "Create the CSR for {{ item.0.credentials.name }}/{{ item.0.mode }}" + x509_csr: + credentials_name: "{{ item.0.credentials.name }}" + domain: "{{ domain }}" + mode: "{{ item.0.mode }}" + params: "{{ item.0.x509_params|default({}) }}" + private_key_path: "/etc/credentials/x509/{{ item.0.credentials.name }}/{{ item.0.mode }}/private_key.pem" + check: false + when: "item.1.changed" + loop: "{{ float_host_service_credentials_certs | zip(x509_should_update.results) | list }}" + register: x509_csr + +- name: "Create the certificate for {{ item.0.credentials.name }}/{{ item.0.mode }}" + x509_sign: + csr: "{{ item.1.csr }}" + mode: "{{ item.0.mode }}" + ca_cert_path: "{{ credentials_dir }}/x509/ca.pem" + ca_key_path: "{{ credentials_dir }}/x509/ca_private_key.pem" + when: "item.1.changed" + loop: "{{ float_host_service_credentials_certs | zip(x509_csr.results) | list }}" + register: x509_sign + +- name: "Install the signed certificate for {{ item.0.credentials.name }}/{{ item.0.mode }}" + copy: + dest: "/etc/credentials/x509/{{ item.0.credentials.name }}/{{ item.0.mode }}/cert.pem" + content: "{{ item.1.cert }}" + mode: 0644 + when: "item.1.changed" + loop: "{{ float_host_service_credentials_certs | zip(x509_sign.results) | list }}" + +- name: Set permissions on the private key + file: + path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}/private_key.pem" + group: "{{ item.credentials.name }}-credentials" + mode: 0640 + loop: "{{ float_host_service_credentials_certs }}" + diff --git a/roles/credentials/tasks/install_credentials.yml b/roles/credentials/tasks/install_credentials.yml index be289e33..ec3cce43 100644 --- a/roles/credentials/tasks/install_credentials.yml +++ b/roles/credentials/tasks/install_credentials.yml @@ -1,27 +1,23 @@ --- -- name: "Create {{ credentials.name }}-credentials group" +- name: "Create service credentials group" group: - name: "{{ credentials.name }}-credentials" + name: "{{ item.credentials.name }}-credentials" system: true + loop: "{{ float_host_service_credentials }}" -- name: Create service credentials dir +- name: "Create service credentials dirs" file: - path: "/etc/credentials/x509/{{ credentials.name }}" + path: "/etc/credentials/x509/{{ item.credentials.name }}" state: directory + loop: "{{ float_host_service_credentials }}" - name: Copy CA copy: src: "{{ credentials_dir }}/x509/ca.pem" - dest: "/etc/credentials/x509/{{ credentials.name }}/ca.pem" + dest: "/etc/credentials/x509/{{ item.credentials.name }}/ca.pem" owner: root group: root mode: 0644 + loop: "{{ float_host_service_credentials }}" -- include_tasks: install_cert.yml - with_items: - - client - - server - when: "credentials.get('enable_' + mode, True)" - loop_control: - loop_var: mode diff --git a/roles/credentials/tasks/install_service.yml b/roles/credentials/tasks/install_service.yml deleted file mode 100644 index 631ee2ce..00000000 --- a/roles/credentials/tasks/install_service.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- - -- include_tasks: install_credentials.yml - with_items: "{{ services[service_name_iter].get('service_credentials', []) }}" - loop_control: - loop_var: credentials diff --git a/roles/credentials/tasks/main.yml b/roles/credentials/tasks/main.yml index f166aa03..45c12e31 100644 --- a/roles/credentials/tasks/main.yml +++ b/roles/credentials/tasks/main.yml @@ -33,15 +33,8 @@ name: x509ca state: present -- include_tasks: install_credentials.yml - with_items: "{{ default_service_credentials|default([]) }}" - loop_control: - loop_var: credentials - -- include_tasks: install_service.yml - with_items: "{{ float_enabled_services }}" - loop_control: - loop_var: service_name_iter +- import_tasks: install_credentials.yml +- import_tasks: install_certs.yml # Remove credentials that shouldn't be here. # - file: path="/etc/credentials/x509/{{ item.1.name }}" state=absent -- GitLab