From b2856e22f5a331df3d760b09cb78ca162dbf3472 Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Wed, 13 May 2020 15:47:21 +0100 Subject: [PATCH] Refactor the credentials role Separate a base "credentials" role and a "float-credentials" one that instantiates the float-derived service credentials. This allows users to install credentials from Ansible roles that do not have a corresponding float service. --- playbooks/base.yml | 2 +- plugins/inventory/float.py | 10 +- roles/credentials/README.md | 19 ++- roles/credentials/tasks/install_certs.yml | 76 ---------- .../credentials/tasks/install_credentials.yml | 23 ---- roles/credentials/tasks/main.yml | 130 ++++++++++++------ roles/float-credentials/README.md | 2 + roles/float-credentials/meta/main.yml | 7 + roles/float-credentials/tasks/main.yml | 26 ++++ 9 files changed, 147 insertions(+), 148 deletions(-) delete mode 100644 roles/credentials/tasks/install_certs.yml delete mode 100644 roles/credentials/tasks/install_credentials.yml create mode 100644 roles/float-credentials/README.md create mode 100644 roles/float-credentials/meta/main.yml create mode 100644 roles/float-credentials/tasks/main.yml diff --git a/playbooks/base.yml b/playbooks/base.yml index 49e8eb2f..c69d94af 100644 --- a/playbooks/base.yml +++ b/playbooks/base.yml @@ -3,7 +3,7 @@ - hosts: all roles: - base - - credentials + - float-credentials - vagrant-compat - hosts: net-overlay diff --git a/plugins/inventory/float.py b/plugins/inventory/float.py index 9a316732..995c7cf3 100644 --- a/plugins/inventory/float.py +++ b/plugins/inventory/float.py @@ -358,7 +358,6 @@ def _host_vars(name, inventory, services, assignments): 'float_disabled_services': [], 'float_enabled_containers': [], '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), @@ -366,8 +365,7 @@ def _host_vars(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({ + hv['float_host_service_credentials'].append({ 'credentials': c, 'service': 'LOCAL', 'mode': 'client', 'x509_params': {}}) @@ -390,15 +388,13 @@ def _host_vars(name, inventory, services, assignments): 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({ + hv['float_host_service_credentials'].append({ 'credentials': c, 'service': s, 'mode': 'server', 'x509_params': params}) if c.get('enable_client', True): - hv['float_host_service_credentials_certs'].append({ + hv['float_host_service_credentials'].append({ 'credentials': c, 'service': s, 'mode': 'client', 'x509_params': {}}) else: diff --git a/roles/credentials/README.md b/roles/credentials/README.md index 038bc3f3..de7a8aa2 100644 --- a/roles/credentials/README.md +++ b/roles/credentials/README.md @@ -3,7 +3,24 @@ credentials Ansible role that installs all the [service credentials](../docs/service_mesh.md#mutual-service-authentication) on -the hosts where they're needed. +the hosts where they're needed. This role works in combination with +the 'x509' action plugin. Private keys never leave the target host, we create a CSR and sign it on the Ansible host. + +X509 credentials are stored in /etc/credentials/x509 under directories +named after the services. Every service directory contains a copy of +the public CA certificate, so it can be bind-mounted in a container +easily. + +Private keys have mode 440, are owned by root and by a dedicated group +named *service*-credentials. When the service is actually installed, +later, maybe by an Ansible role, it can add the service user to this +group. + +Use by including this role and setting the *credentials* variable to a +list of entries specifying the desired credentials. This is already +done once system-wide by the *float-credentials* role with the +credentials automagically derived from the service definitions by +*float*. diff --git a/roles/credentials/tasks/install_certs.yml b/roles/credentials/tasks/install_certs.yml deleted file mode 100644 index ae705651..00000000 --- a/roles/credentials/tasks/install_certs.yml +++ /dev/null @@ -1,76 +0,0 @@ ---- - -#- set_fact: -# x509_params: "{{ float_service_credentials_params[service_name_item + '-' + credentials.name] | default({}) }}" -# when: "service_name_item is defined" - -- name: Set up internal PKI credentials - block: - - - file: - path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}" - state: directory - loop: "{{ float_host_service_credentials_certs }}" - - - name: "Check the internal PKI certificates" - 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 internal PKI CSRs" - 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: "Sign internal PKI certificates" - 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 internal PKI certificates" - 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 keys" - 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 }}" - - # This should use the systemd module but it doesn't take lists of services. - - name: "Restart associated services" - shell: "systemctl restart {{ services[item.0.service].systemd_services | join(' ') }}" - when: "item.1.changed" - loop: "{{ float_host_service_credentials_certs | zip(x509_sign.results) | list }}" - - rescue: - - debug: - msg: "Failed to set up one or more credentials" - diff --git a/roles/credentials/tasks/install_credentials.yml b/roles/credentials/tasks/install_credentials.yml deleted file mode 100644 index ec3cce43..00000000 --- a/roles/credentials/tasks/install_credentials.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- - -- name: "Create service credentials group" - group: - name: "{{ item.credentials.name }}-credentials" - system: true - loop: "{{ float_host_service_credentials }}" - -- name: "Create service credentials dirs" - file: - 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/{{ item.credentials.name }}/ca.pem" - owner: root - group: root - mode: 0644 - loop: "{{ float_host_service_credentials }}" - diff --git a/roles/credentials/tasks/main.yml b/roles/credentials/tasks/main.yml index 45c12e31..3863396e 100644 --- a/roles/credentials/tasks/main.yml +++ b/roles/credentials/tasks/main.yml @@ -1,55 +1,105 @@ --- -# Distribute the SSO public key to all hosts. +# This package needs to be on hosts in order to generate the CSRs. +- name: Install x509ca package + apt: + name: x509ca + state: present + +# Get the credential names from the list of certs. +- set_fact: + credentials_names: "{{ credentials | map(attribute='credentials') | map(attribute='name') | unique | list }}" -- file: - path: /etc/sso +- name: "Create service credentials group" + group: + name: "{{ item }}-credentials" + system: true + loop: "{{ credentials_names }}" + +- name: "Create service credentials dirs" + file: + path: "/etc/credentials/x509/{{ item }}" state: directory - owner: root - group: root - mode: 0755 + loop: "{{ credentials_names }}" -- name: Install SSO public key +- name: Copy CA copy: - src: "{{ credentials_dir }}/sso/public.key" - dest: /etc/sso/public.key + src: "{{ credentials_dir }}/x509/ca.pem" + dest: "/etc/credentials/x509/{{ item }}/ca.pem" + owner: root + group: root mode: 0644 + loop: "{{ credentials_names }}" -# Distribute X509 credentials to all hosts as needed. This role works -# in combination with the 'x509' action plugin. +# Create and sign all certificates in a series of loops (with some +# unfortunately complex change-detection logic). +- name: Set up internal PKI credentials + block: -# X509 credentials are stored in /etc/credentials/x509 under -# directories named after the services. Every service directory -# contains a copy of the public CA certificate, so it can be -# bind-mounted in a container easily. + - file: + path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}" + state: directory + loop: "{{ credentials }}" -# Private keys have mode 440, are owned by root and by a dedicated -# group named <service>-credentials. When the service is actually -# installed, later, maybe by an Ansible role, it can add the service -# user to this group. + - name: "Check the internal PKI certificates" + 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: "{{ credentials }}" + check_mode: no + register: x509_should_update -- name: Install x509ca package - apt: - name: x509ca - state: present + # TODO: set the right permissions (credentials.name-credentials) + - name: "Create internal PKI CSRs" + 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: "{{ credentials | zip(x509_should_update.results) | list }}" + register: x509_csr -- import_tasks: install_credentials.yml -- import_tasks: install_certs.yml + - name: "Sign internal PKI certificates" + 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: "{{ credentials | zip(x509_csr.results) | list }}" + register: x509_sign -# Remove credentials that shouldn't be here. -# - file: path="/etc/credentials/x509/{{ item.1.name }}" state=absent -# with_subelements: -# - "{{ services }}" -# - service_credentials -# - { skip_missing: true } + - name: "Install the signed internal PKI certificates" + 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: "{{ credentials | zip(x509_sign.results) | list }}" -# Create a group for public credentials. -- name: Create public-credentials group - group: - name: public-credentials - system: yes + - name: "Set permissions on the private keys" + file: + path: "/etc/credentials/x509/{{ item.credentials.name }}/{{ item.mode }}/private_key.pem" + group: "{{ item.credentials.name }}-credentials" + mode: 0640 + loop: "{{ credentials }}" + + # This should use the systemd module but it doesn't take lists of services. + - name: "Restart associated services" + shell: "systemctl restart {{ services[item.0.service].systemd_services | join(' ') }}" + when: "item.1.changed" + loop: "{{ credentials | zip(x509_sign.results) | list }}" + + rescue: + - debug: + msg: "Failed to set up one or more credentials" -# Create the root directory for public credentials. -- file: - path: /etc/credentials/public - state: directory diff --git a/roles/float-credentials/README.md b/roles/float-credentials/README.md new file mode 100644 index 00000000..5edfe877 --- /dev/null +++ b/roles/float-credentials/README.md @@ -0,0 +1,2 @@ +This role includes the *credentials* role with the list of credentials +derived from the service descriptions in services.yml. diff --git a/roles/float-credentials/meta/main.yml b/roles/float-credentials/meta/main.yml new file mode 100644 index 00000000..bd091102 --- /dev/null +++ b/roles/float-credentials/meta/main.yml @@ -0,0 +1,7 @@ +--- + +dependencies: + - role: credentials + vars: + credentials: "{{ float_host_service_credentials }}" + diff --git a/roles/float-credentials/tasks/main.yml b/roles/float-credentials/tasks/main.yml new file mode 100644 index 00000000..23a0988e --- /dev/null +++ b/roles/float-credentials/tasks/main.yml @@ -0,0 +1,26 @@ +--- + +# Distribute the SSO public key to all hosts. +- file: + path: /etc/sso + state: directory + owner: root + group: root + mode: 0755 + +- name: Install SSO public key + copy: + src: "{{ credentials_dir }}/sso/public.key" + dest: /etc/sso/public.key + mode: 0644 + +# Create a group for public credentials. +- name: Create public-credentials group + group: + name: public-credentials + system: yes + +# Create the root directory for public credentials. +- file: + path: /etc/credentials/public + state: directory -- GitLab