diff --git a/ansible-scylla-node/defaults/main.yml b/ansible-scylla-node/defaults/main.yml index 742e6b06..dae0822f 100644 --- a/ansible-scylla-node/defaults/main.yml +++ b/ansible-scylla-node/defaults/main.yml @@ -275,21 +275,21 @@ scylla_yaml_params: internode_compression: all # SSL configuration for Scylla nodes: -# localhost_cert_path is the path where the certificates will be stored in the localhost before they're coppied to the nodes. If not set, -# the playbook will consider it to be the same path where the inventory is located. +# localhost_cert_path and localhost_cert_key_path are the paths where the certificates and keys will be stored in the localhost before they're coppied to the nodes. # cert_path is the path where the certificates will be placed in the scylla nodes. +# localhost_truststore_path is the path to the truststore that should be coppied to the nodes. You can remove this variable if you don't want to provide a truststore. # If no certificates are present, this playbook will generate a selfsigned CA and node certificates locally and push them out to all the nodes. -# WARNING: if a local ${localhost_cert_path}/ssl/ directory is not present and populated by existing certificates for all nodes, it will be generated -# and certificates on the nodes can get overwritten with new certificates. +# WARNING: if certs are no given by the user they will be automatically generated if scylla_ssl is enabled and certificates on the nodes can get overwritten # To use existing certificates: -# 1. To use your own CA, place it in ${localhost_cert_path}/ssl/ca/{{ scylla_cluster_name }}-ca.crt and its respective key in -# ${localhost_cert_path}/ssl/ca/{{ scylla_cluster_name }}-ca.pem and derived per-node certificates will be generated from it, if they are not present. -# 2. To use your own node certificates, create a directory per hostname (same as in the inventory) in ${localhost_cert_path}/ssl/{{ hostname }} -# and place the .pem and .crt files as {{ hostname }}.pem and {{ hostname }}.crt respectively and then place the CA you used to sign the certificates in -# ${localhost_cert_path}/ssl/ca/{{ scylla_cluster_name }}-ca.crt and its respective key in ${localhost_cert_path}/ssl/ca/{{ scylla_cluster_name }}-ca.pem. -# To skip this configuration entirely, set enabled: false for both options or comment out the scylla_ssl variable. +# 1. To use your own CA, place it in ${inventory_dir}/ssl/ca/{{ scylla_cluster_name }}-ca.crt and its respective key in +# ${inventory_dir}/ssl/ca/{{ scylla_cluster_name }}-ca.pem and derived per-node certificates will be generated from it, if they are not present. +# 2. To use your own node certificates and keys, just place them in 'localhost_cert_path' and 'localhost_cert_key_path' +# To skip this configuration entirely, set enabled: false for both options scylla_ssl: - # localhost_cert_path: path/to/certificates + localhost_cert_path: "{{ inventory_dir }}/ssl/scylla.crt" + localhost_cert_key_path: "{{ inventory_dir }}/scylla.pem" + localhost_truststore_path: "{{ inventory_dir }}/ssl/scylla.crt" + localhost_ca_cert_path: "{{ inventory_dir }}/ssl/ca/" cert_path: /etc/scylla internode: enabled: true diff --git a/ansible-scylla-node/tasks/common.yml b/ansible-scylla-node/tasks/common.yml index 1048f831..e1b84244 100644 --- a/ansible-scylla-node/tasks/common.yml +++ b/ansible-scylla-node/tasks/common.yml @@ -224,6 +224,13 @@ when: (scylla_package_prefix + '-node-exporter') in ansible_facts.packages become: true +- name: enable ssl options + include_tasks: ssl.yml + when: + - scylla_ssl is defined + - (scylla_ssl.internode.enabled|bool) or + (scylla_ssl.client.enabled|bool) + - name: configure scylla.yaml template: src: templates/scylla.yaml.j2 @@ -300,13 +307,6 @@ become: true loop: "{{ lookup('dict', scylla_yaml_params) }}" -- name: enable ssl options - include_tasks: ssl.yml - when: - - scylla_ssl is defined - - (scylla_ssl.internode.enabled|bool) or - (scylla_ssl.client.enabled|bool) - - name: Copy system keys include_tasks: handle_encryption_at_rest_keys.yml vars: diff --git a/ansible-scylla-node/tasks/ssl.yml b/ansible-scylla-node/tasks/ssl.yml index 9953233d..6a6348f4 100644 --- a/ansible-scylla-node/tasks/ssl.yml +++ b/ansible-scylla-node/tasks/ssl.yml @@ -1,10 +1,12 @@ --- + - set_fact: - _localhost_cert_path: "{{ scylla_ssl.localhost_cert_path | default(inventory_dir) }}" + _localhost_cert_path: "{{ scylla_ssl.localhost_cert_path | default(inventory_dir + '/ssl/' + inventory_hostname + '/' + inventory_hostname + '.crt') }}" + _localhost_cert_key_path: "{{ scylla_ssl.localhost_cert_key_path | default(inventory_dir + '/ssl/' + inventory_hostname + '/' + inventory_hostname + '.pem') }}" - name: For every node, check if crt file exists stat: - path: "{{ _localhost_cert_path }}/ssl/{{ hostvars[item]['inventory_hostname'] }}/{{ hostvars[item]['inventory_hostname'] }}.crt" + path: "{{ hostvars[item]['_localhost_cert_path'] }}" register: _node_crt loop: "{{ groups['scylla'] }}" delegate_to: localhost @@ -12,7 +14,7 @@ - name: For every node, check if key file exists stat: - path: "{{ _localhost_cert_path }}/ssl/{{ hostvars[item]['inventory_hostname'] }}/{{ hostvars[item]['inventory_hostname'] }}.pem" + path: "{{ hostvars[item]['_localhost_cert_key_path'] }}" register: _node_key loop: "{{ groups['scylla'] }}" delegate_to: localhost @@ -25,15 +27,21 @@ when: _crt_and_key_exist is not defined or _crt_and_key_exist == True run_once: true +- name: Check if at least one crt was provided + set_fact: + _some_crt_and_key_exist: "{{ item.stat.exists }}" + loop: "{{ _node_crt.results + _node_key.results }}" + when: _some_crt_and_key_exist is not defined or _some_crt_and_key_exist == False + - name: Check if CA exists block: - name: Check if CA crt file exists stat: - path: "{{ _localhost_cert_path }}/ssl/ca/{{ scylla_cluster_name }}-ca.crt" + path: "{{ inventory_dir }}/ssl/ca/{{ scylla_cluster_name }}-ca.crt" register: _ca_crt - name: Check if CA key file exists stat: - path: "{{ _localhost_cert_path }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" + path: "{{ inventory_dir }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" register: _ca_key - set_fact: _ca_crt_and_key_exist: "{{ _ca_crt.stat.exists|bool and _ca_key.stat.exists|bool }}" @@ -41,23 +49,24 @@ run_once: true - fail: - msg: "If you want to use your own node certificates you also have to provide a CA" - when: _crt_and_key_exist and not _ca_crt_and_key_exist + msg: "You've provided crts only for a subset of your nodes. Either provide a CA that should be used for generating equivalent certificates for the others or delete the existing crts in order for the role to generate a new self-signed CA and certs for all the nodes" + run_once: true + when: not _crt_and_key_exist and _some_crt_and_key_exist and not _ca_crt_and_key_exist - name: If crt and keys were not provided for all nodes and no CA was provided, generate a self-signed CA block: - name: Create dir for the CA file: - path: "{{ _localhost_cert_path }}/ssl/ca" + path: "{{ inventory_dir }}/ssl/ca" state: directory - name: Generate an OpenSSL private key for the CA. openssl_privatekey: - path: "{{ _localhost_cert_path }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" + path: "{{ inventory_dir }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" - name: Generate an OpenSSL Certificate Signing Request for the CA community.crypto.openssl_csr_pipe: - privatekey_path: "{{ _localhost_cert_path }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" + privatekey_path: "{{ inventory_dir }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" common_name: "{{ scylla_cluster_name }}.internal" use_common_name_for_san: false # since we do not specify SANs, don't use CN as a SAN basic_constraints: @@ -70,44 +79,79 @@ - name: Generate a Self Signed OpenSSL certificate for the CA community.crypto.x509_certificate: - path: "{{ _localhost_cert_path }}/ssl/ca/{{scylla_cluster_name }}-ca.crt" - privatekey_path: "{{ _localhost_cert_path }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" + path: "{{ inventory_dir }}/ssl/ca/{{scylla_cluster_name }}-ca.crt" + privatekey_path: "{{ inventory_dir }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" csr_content: "{{ ca_csr.csr }}" provider: selfsigned + + - set_fact: + _ca_crt_and_key_exist: True when: _crt_and_key_exist == False and _ca_crt_and_key_exist == False delegate_to: localhost run_once: true - name: Generate keys signed by the local CA block: + - name: Create a directory for the crt + file: + path: "{{ _localhost_cert_path | dirname }}" + state: directory + delegate_to: localhost + - name: Create a directory for the key file: - path: "{{ _localhost_cert_path }}/ssl/{{ inventory_hostname }}" + path: "{{ _localhost_cert_key_path | dirname }}" state: directory delegate_to: localhost - name: Generate an OpenSSL private key. openssl_privatekey: - path: "{{ _localhost_cert_path }}/ssl/{{ inventory_hostname }}/{{ inventory_hostname }}.pem" + path: "{{ _localhost_cert_key_path }}" delegate_to: localhost - name: Generate an OpenSSL Certificate Signing Request openssl_csr: - path: "{{ _localhost_cert_path }}/ssl/{{ inventory_hostname }}/{{ inventory_hostname }}.csr" - privatekey_path: "{{ _localhost_cert_path }}/ssl/{{ inventory_hostname }}/{{ inventory_hostname }}.pem" + path: "{{ _localhost_cert_path }}.csr" + privatekey_path: "{{ _localhost_cert_key_path }}" common_name: "{{ inventory_hostname }}.{{ scylla_cluster_name }}.internal" delegate_to: localhost - name: Generate an OpenSSL certificate signed with our CA certificate openssl_certificate: - path: "{{ _localhost_cert_path }}/ssl/{{ inventory_hostname }}/{{ inventory_hostname }}.crt" - csr_path: "{{ _localhost_cert_path }}/ssl/{{ inventory_hostname }}/{{ inventory_hostname }}.csr" - ownca_path: "{{ _localhost_cert_path }}/ssl/ca/{{scylla_cluster_name }}-ca.crt" - ownca_privatekey_path: "{{ _localhost_cert_path }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" + path: "{{ _localhost_cert_path }}" + csr_path: "{{ _localhost_cert_path }}.csr" + ownca_path: "{{ inventory_dir }}/ssl/ca/{{scylla_cluster_name }}-ca.crt" + ownca_privatekey_path: "{{ inventory_dir }}/ssl/ca/{{ scylla_cluster_name }}-ca.pem" provider: ownca delegate_to: localhost when: _crt_and_key_exist == False +- name: Copy truststore to the nodes + block: + - name: Copy truststore + copy: + src: "{{ scylla_ssl.localhost_truststore_path }}" + dest: "{{ scylla_ssl.cert_path }}/truststore.crt" + owner: root + group: root + mode: '0644' + become: true + when: scylla_ssl.localhost_truststore_path is defined + + - name: Copy CA + copy: + src: "{{ inventory_dir }}/ssl/ca/{{scylla_cluster_name }}-ca.crt" + dest: "{{ scylla_ssl.cert_path }}/truststore.crt" + owner: root + group: root + mode: '0644' + become: true + when: scylla_ssl.localhost_truststore_path is not defined and _ca_crt_and_key_exist + + - name: Set _truststore_exists + set_fact: + _truststore_exists: "{{ scylla_ssl.localhost_truststore_path is defined or _ca_crt_and_key_exist }}" + - name: Copy the certificates into their proper locations copy: src: "{{ item }}" @@ -117,14 +161,13 @@ mode: '0644' become: true loop: - - "{{ _localhost_cert_path }}/ssl/ca/{{ scylla_cluster_name }}-ca.crt" - - "{{ _localhost_cert_path }}/ssl/{{ inventory_hostname }}/{{ inventory_hostname }}.crt" - - "{{ _localhost_cert_path }}/ssl/{{ inventory_hostname }}/{{ inventory_hostname }}.pem" + - "{{ _localhost_cert_path }}" + - "{{ _localhost_cert_key_path }}" - name: Generate cqlshrc template: src: templates/cqlshrc.j2 - dest: "{{ _localhost_cert_path }}/cqlshrc" + dest: "{{ inventory_dir }}/cqlshrc" delegate_to: localhost run_once: true diff --git a/ansible-scylla-node/templates/scylla.yaml.j2 b/ansible-scylla-node/templates/scylla.yaml.j2 index 2a564046..e93c5ae1 100644 --- a/ansible-scylla-node/templates/scylla.yaml.j2 +++ b/ansible-scylla-node/templates/scylla.yaml.j2 @@ -49,17 +49,21 @@ native_shard_aware_transport_port_ssl: 19142 client_encryption_options: enabled: true - certificate: {{ scylla_ssl.cert_path }}/{{ inventory_hostname }}.crt - keyfile: {{ scylla_ssl.cert_path }}/{{ inventory_hostname }}.pem - truststore: {{ scylla_ssl.cert_path }}/{{ scylla_cluster_name }}-ca.crt + certificate: {{ scylla_ssl.cert_path }}/{{ _localhost_cert_path | basename }} + keyfile: {{ scylla_ssl.cert_path }}/{{ _localhost_cert_key_path | basename }} +{% if _truststore_exists %} + truststore: {{ scylla_ssl.cert_path }}/truststore.crt +{% endif %} {% endif %} {% if scylla_ssl is defined and scylla_ssl.internode.enabled %} server_encryption_options: internode_encryption: {{ scylla_ssl.internode.internode_encryption }} - certificate: {{ scylla_ssl.cert_path }}/{{ inventory_hostname }}.crt - keyfile: {{ scylla_ssl.cert_path }}/{{ inventory_hostname }}.pem - truststore: {{ scylla_ssl.cert_path }}/{{ scylla_cluster_name }}-ca.crt + certificate: {{ scylla_ssl.cert_path }}/{{ _localhost_cert_path | basename }} + keyfile: {{ scylla_ssl.cert_path }}/{{ _localhost_cert_key_path | basename }} +{% if _truststore_exists %} + truststore: {{ scylla_ssl.cert_path }}/truststore.crt +{% endif %} {% endif %} {% if handle_system_keys|bool %} system_key_directory: {{ system_key_directory }}