ansible-community / ansible-vault

:key: Ansible role for Hashicorp Vault
BSD 2-Clause "Simplified" License
364 stars 194 forks source link

define mTLS for each node in HA raft storage #256

Open FalcoSuessgott opened 3 years ago

FalcoSuessgott commented 3 years ago

Hi,

looking at https://github.com/ansible-community/ansible-vault/blob/master/templates/vault_backend_raft.j2:

{% for server in groups[vault_raft_group_name] | difference([inventory_hostname]) %}
{% if not (vault_tls_disable | bool) %}
retry_join {
    leader_api_addr = "{{ hostvars[server]['vault_api_addr'] | default(vault_protocol + '://' + hostvars[server]['ansible_' + vault_iface]['ipv4']['address'] + ':' + (vault_port|string)) }}"
    {% if vault_raft_leader_tls_servername is defined %}
    leader_tls_servername = "{{ vault_raft_leader_tls_servername }}"
    {% endif %}
    leader_ca_cert_file = "{{ vault_backend_tls_config_path }}/{{ vault_backend_tls_ca_file }}"
    leader_client_cert_file = "{{ vault_backend_tls_config_path }}/{{ vault_backend_tls_cert_file }}"
    leader_client_key_file = "{{ vault_backend_tls_config_path }}/{{ vault_backend_tls_key_file }}"
{% endfor %}
}

I was wondering, if it is possible to archive the following config (https://www.vaultproject.io/docs/configuration/storage/raft#leader_client_key):

  retry_join {
    leader_api_addr = "http://127.0.0.2:8200"
    leader_ca_cert_file = "/path/to/ca1"
    leader_client_cert_file = "/path/to/client/cert1"
    leader_client_key_file = "/path/to/client/key1"
  }
  retry_join {
    leader_api_addr = "http://127.0.0.3:8200"
    leader_ca_cert_file = "/path/to/ca2"
    leader_client_cert_file = "/path/to/client/cert2"
    leader_client_key_file = "/path/to/client/key2"
  }
  retry_join {
    leader_api_addr = "http://127.0.0.4:8200"
    leader_ca_cert_file = "/path/to/ca3"
    leader_client_cert_file = "/path/to/client/cert3"
    leader_client_key_file = "/path/to/client/key3"
  }
}

Is there another way to archive this config or is it maybe not necessary to specify cert, keys and ca for each node when I still want to have mTLS?

FalcoSuessgott commented 3 years ago

Ok I was able to implement this doing the following:

disable copying keys:

vault_tls_copy_keys: false

and do it on your own in a pre_task:

    - name: Copy Certs
      copy:
        src: "{{ vault_tls_src_files }}"
        dest: "{{ vault_tls_config_path }}"
        owner: "{{ vault_user }}"
        group: "{{ vault_group }}"
        mode: '0644'

then inject your own vault_backend_raft template:

vault_backend_raft: "files/custom_templates/vault_backend_raft.j2"

my templates looks like this:

storage "raft" {
  path = "{{ vault_raft_data_path }}"
  node_id = "{{ vault_raft_node_id }}"
  {% if vault_raft_performance_multiplier is defined and vault_raft_performance_multiplier %}
  performance_multiplier = "{{ vault_raft_performance_multiplier }}"
  {% endif %}
  {% if vault_raft_trailing_logs is defined and vault_raft_trailing_logs %}
  trailing_logs = "{{ vault_raft_trailing_logs }}"
  {% endif %}
  {% if vault_raft_snapshot_threshold is defined and vault_raft_snapshot_threshold %}
  snapshot_threshold = "{{ vault_raft_snapshot_threshold }}"
  {% endif %}
  {% if vault_raft_max_entry_size is defined and vault_raft_max_entry_size %}
  max_entry_size = "{{ vault_raft_max_entry_size }}"
  {% endif %}
  {% if vault_raft_autopilot_reconcile_interval is defined and vault_raft_autopilot_reconcile_interval %}
  autopilot_reconcile_interval = "{{ vault_raft_autopilot_reconcile_interval }}"
  {% endif %}
  {% if vault_raft_cloud_auto_join is defined and vault_raft_cloud_auto_join %}
  retry_join {
    auto_join = "{{ vault_raft_cloud_auto_join }}"
    {% if vault_raft_cloud_auto_join_scheme is defined and vault_raft_cloud_auto_join_scheme %}
    auto_join_scheme = "{{ vault_raft_cloud_auto_join_scheme }}"
    {% endif %}
    {% if vault_raft_cloud_auto_join_port is defined and vault_raft_cloud_auto_join_port %}
    auto_join_port = "{{ vault_raft_cloud_auto_join_port }}"
    {% endif %}
  }
  {% endif %}
  {% if not vault_raft_cloud_auto_join_exclusive %}
  {% for server in groups[vault_raft_group_name] | difference([inventory_hostname]) %}
    {% if not (vault_tls_disable | bool) %}
  retry_join {
    leader_api_addr = "{{ vault_protocol + '://' +  hostvars[server]['ansible_'+vault_iface]['ipv4']['address'] + ':' + (vault_port|string) }}"
    {% if vault_raft_leader_tls_servername is defined %}
    leader_tls_servername = "{{ hostvars[server]['vault_raft_leader_tls_servername'] }}"
    {% endif %}
    leader_ca_cert_file = "{{ vault_backend_tls_config_path }}/{{ hostvars[server]['vault_tls_ca_file'] }}"
    leader_client_cert_file = "{{ vault_backend_tls_config_path }}/{{ hostvars[server]['vault_tls_cert_file'] }}"
    leader_client_key_file = "{{ vault_backend_tls_config_path }}/{{ hostvars[server]['vault_tls_key_file'] }}"
  }
    {% else %}
  retry_join {
    leader_api_addr =  "http://{{ hostvars[server]['ansible_'+vault_iface]['ipv4']['address'] }}:{{ vault_port }}"
  }
    {% endif %}
  {% endfor %}
  {% endif %}
}

// HashiCorp recommends disabling mlock when using Raft.
disable_mlock = {{ vault_disable_mlock | default('true') | bool | lower }}

for each vault node I have the following host_vars defined:

---
vault_tls_cert_file: '{{ inventory_hostname }}+2.pem'
vault_tls_key_file: '{{ inventory_hostname }}+2-key.pem'
vault_tls_ca_file: 'rootCA.pem'
vault_raft_leader_tls_servername: "{{ hostname_intern }}"

this results into the following vault_main.hcl:

cluster_name = "dc1"
max_lease_ttl = "768h"
default_lease_ttl = "768h"

disable_clustering = "False"
cluster_addr = "https://192.168.199.13:8201"
api_addr = "http://192.168.199.13:8200"

plugin_directory = "/usr/local/lib/vault/plugins"

listener "tcp" {
  address = "192.168.199.13:8200"
  cluster_address = "192.168.199.13:8201"
  tls_cert_file = "/etc/vault/tls/node-01.pem"
  tls_key_file = "/etc/vault/tls/node-01-key.pem"
  tls_client_ca_file="/etc/vault/tls/rootCA.pem"
  tls_min_version  = "tls12"
  tls_prefer_server_cipher_suites = "false"
  tls_disable = "false"
  }

storage "raft" {
  path = "/var/vault"
  node_id = "node-01"
                      retry_join {
    leader_api_addr = "https://192.168.199.19:8200"
        leader_tls_servername = "node-02"
        leader_ca_cert_file = "/etc/vault/tls/rootCA.pem"
    leader_client_cert_file = "/etc/vault/tls/node-02.pem"
    leader_client_key_file = "/etc/vault/tls/node-02-key.pem"
  }
            retry_join {
    leader_api_addr = "https://192.168.199.8:8200"
        leader_tls_servername = "node-03"
        leader_ca_cert_file = "/etc/vault/tls/rootCA.pem"
    leader_client_cert_file = "/etc/vault/tls/node-03.pem"
    leader_client_key_file = "/etc/vault/tls/node-03-key.pem"
  }
        }

// HashiCorp recommends disabling mlock when using Raft.
disable_mlock = true

ui = true

@bbaassssiiee @bbayszczak

The question is now, is this something that should be contributed? Again im aiming to automate this configuration: https://www.vaultproject.io/docs/configuration/storage/raft#leader_client_key

It would be a breaking change and demands some review/testing tho

bbaassssiiee commented 3 years ago

Can you make it non-breaking?