Skip to content

Commit

Permalink
Merge pull request #427 from igorribeiroduarte/fix_ssl_validation
Browse files Browse the repository at this point in the history
ansible-scylla-node: Allow the user to use a same certificate for all the nodes
  • Loading branch information
tarzanek authored Dec 10, 2024
2 parents e5e72a2 + 4445f0a commit 80a9836
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 48 deletions.
22 changes: 11 additions & 11 deletions ansible-scylla-node/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions ansible-scylla-node/tasks/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
91 changes: 67 additions & 24 deletions ansible-scylla-node/tasks/ssl.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
---

- 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
run_once: true

- 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
Expand All @@ -25,39 +27,46 @@
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 }}"
delegate_to: localhost
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:
Expand All @@ -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 }}"
Expand All @@ -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

16 changes: 10 additions & 6 deletions ansible-scylla-node/templates/scylla.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down

0 comments on commit 80a9836

Please sign in to comment.