diff --git a/roles/consul/defaults/main.yml b/roles/consul/defaults/main.yml index 6d76953..94d88d1 100644 --- a/roles/consul/defaults/main.yml +++ b/roles/consul/defaults/main.yml @@ -88,6 +88,12 @@ consul_base_conf: enabled: False # The default_policy is also used for intentions in the service mesh default_policy: deny + enable_token_persistence: True + # You can set tokens used by the agent + # tokens: + # default: ab47bc38-d97f-19af-93a5-17b528d154c9 + # agent: 5459979a-3f23-8b1f-ff8a-2478856e9216 + tokens: {} tls: # No TLS will be stup unless this is set to True @@ -110,18 +116,21 @@ consul_extra_conf: {} consul_host_conf: {} consul_conf: "{{ consul_base_conf | combine(consul_extra_conf, recursive=True) | combine(consul_host_conf, recursive=True) }}" -# To get certificates from vault -consul_base_vault_tls: - enabled: False - # address: https://active.vault.service.consul:8200 - # token: XXXXXX +# TLS certs and token retrival from vault +consul_base_vault_secrets: + # vault_address: https://active.vault.service.consul:8200 + # vault_token: XXXXXX pki: + enabled: False path: /pki/consul role: consul-{{ consul_conf.server | ternary('server', 'client') }} - ttl: 24h -consul_extra_vault_tls: {} -consul_host_vault_tls: {} -consul_vault_tls: "{{ consul_base_vault_tls | combine(consul_extra_vault_tls, recursive=True) | combine(consul_host_vault_tls, recursive=True) }}" + tokens: + enabled: False + path: /consul + role: consul-agent +consul_extra_vault_secrets: {} +consul_host_vault_secrets: {} +consul_vault_secrets: "{{ consul_base_vault_secrets | combine(consul_extra_vault_secrets, recursive=True) | combine(consul_host_vault_secrets, recursive=True) }}" # For example # consul_extra_conf: diff --git a/roles/consul/tasks/conf.yml b/roles/consul/tasks/conf.yml index 39e1169..851b639 100644 --- a/roles/consul/tasks/conf.yml +++ b/roles/consul/tasks/conf.yml @@ -94,42 +94,40 @@ loop: "{{ consul_backup_configs.stdout_lines }}" tags: consul -- when: consul_vault_tls.enabled - block: - - - name: Deploy consul-template config - template: src=consul-template.hcl.j2 dest={{ consul_root_dir }}/consul-template/consul-template.hcl - notify: restart consul-template-consul - - - name: Deploy consul-template agent cert template - template: src=agent_cert.tpl.j2 dest={{ consul_root_dir }}/consul-template/{{ item.where }} owner=root group=root - loop: - - what: certificate - where: agent.crt.tpl - - what: private_key - where: agent.key.tpl - - what: issuing_ca - where: ca.crt.tpl - notify: restart consul-template-consul - - - name: Check if certificate exists - stat: path={{ consul_conf.tls.defaults.cert_file }} - register: consul_tls_cert_file - +- name: Deploy consul-template config + template: src=consul-template.hcl.j2 dest={{ consul_root_dir }}/consul-template/consul-template.hcl mode=600 owner=root group=root + notify: restart consul-template-consul + when: consul_vault_secrets.pki.enabled or consul_vault_secrets.tokens.enabled tags: consul -- when: consul_vault_tls.enabled and consul_conf.server - block: +- name: Deploy consul-template agent cert template + template: src=agent_cert.tpl.j2 dest={{ consul_root_dir }}/consul-template/{{ item.where }} owner=root group=root + loop: + - what: certificate + where: agent.crt.tpl + - what: private_key + where: agent.key.tpl + - what: issuing_ca + where: ca.crt.tpl + notify: restart consul-template-consul + when: consul_vault_secrets.pki.enabled + tags: consul - - name: Deploy consul-template cli cert template - template: src=cli_cert.tpl.j2 dest={{ consul_root_dir }}/consul-template/{{ item.where }} owner=root group=root - loop: - - what: certificate - where: cli.crt.tpl - - what: private_key - where: cli.key.tpl - notify: restart consul-template-consul +- name: Deploy consul-template cli cert template + template: src=cli_cert.tpl.j2 dest={{ consul_root_dir }}/consul-template/{{ item.where }} owner=root group=root + loop: + - what: certificate + where: cli.crt.tpl + - what: private_key + where: cli.key.tpl + notify: restart consul-template-consul + when: consul_vault_secrets.pki.enabled and consul_conf.server + tags: consul +- name: Deploy the consul-template agent token template + template: src=agent.token.tpl.j2 dest={{ consul_root_dir }}/consul-template/agent.token.tpl owner=root group=root + notify: restart consul-template-consul + when: consul_vault_secrets.tokens.enabled tags: consul - name: Set ACL on the TLS dir diff --git a/roles/consul/tasks/services.yml b/roles/consul/tasks/services.yml index 1d2eb4c..76930b9 100644 --- a/roles/consul/tasks/services.yml +++ b/roles/consul/tasks/services.yml @@ -6,6 +6,9 @@ tags: consul - name: Handle consul-template-consul service - service: name=consul-template-consul state={{ consul_vault_tls.enabled | ternary('started', 'stopped') }} enabled={{ consul_vault_tls.enabled | ternary(True, False) }} + service: + name: consul-template-consul + state: "{{ (consul_vault_secrets.pki.enabled or consul_vault_secrets.tokens.enabled) | ternary('started', 'stopped') }}" + enabled: "{{ (consul_vault_secrets.pki.enabled or consul_vault_secrets.tokens.enabled) | ternary(True, False) }}" tags: consul diff --git a/roles/consul/templates/agent.token.tpl.j2 b/roles/consul/templates/agent.token.tpl.j2 new file mode 100644 index 0000000..a1554db --- /dev/null +++ b/roles/consul/templates/agent.token.tpl.j2 @@ -0,0 +1,3 @@ +[[ with secret "{{ consul_vault_secrets.tokens.path }}/creds/{{ consul_vault_secrets.tokens.role }}" ]] +[[ .Data.token ]] +[[ end ]] diff --git a/roles/consul/templates/agent_cert.tpl.j2 b/roles/consul/templates/agent_cert.tpl.j2 index 382fff1..cb3bc9d 100644 --- a/roles/consul/templates/agent_cert.tpl.j2 +++ b/roles/consul/templates/agent_cert.tpl.j2 @@ -1,14 +1,14 @@ {% if consul_conf.server %} -[[ with secret "{{ consul_vault_tls.pki.path }}/issue/{{ consul_vault_tls.pki.role }}" "common_name=server-{{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ consul_conf.datacenter | default('dc1') }}.{{ consul_conf.domain | default('consul') }}" "ttl={{ consul_vault_tls.pki.ttl }}" "alt_names=localhost,consul.service.{{ consul_conf.domain | default('consul') }},server.{{ consul_conf.datacenter | default('dc1') }}.{{ consul_conf.domain | default('consul') }}" ]] +[[ with secret "{{ consul_vault_secrets.pki.path }}/issue/{{ consul_vault_secrets.pki.role }}" "common_name=server-{{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ consul_conf.datacenter | default('dc1') }}.{{ consul_conf.domain | default('consul') }}" "alt_names=localhost,consul.service.{{ consul_conf.domain | default('consul') }},server.{{ consul_conf.datacenter | default('dc1') }}.{{ consul_conf.domain | default('consul') }}" ]] [[ .Data.{{ item.what }} ]] [[ end ]] {% if item.what == 'certificate' %} -[[ with secret "{{ consul_vault_tls.pki.path }}/cert/ca" ]] +[[ with secret "{{ consul_vault_secrets.pki.path }}/cert/ca" ]] [[ .Data.certificate ]] [[ end ]] {% endif %} {% else %} -[[ with secret "{{ consul_vault_tls.pki.path }}/cert/ca" ]] +[[ with secret "{{ consul_vault_secrets.pki.path }}/cert/ca" ]] [[ .Data.certificate ]] [[ end ]] {% endif %} diff --git a/roles/consul/templates/cli_cert.tpl.j2 b/roles/consul/templates/cli_cert.tpl.j2 index 5439983..d1f4a4c 100644 --- a/roles/consul/templates/cli_cert.tpl.j2 +++ b/roles/consul/templates/cli_cert.tpl.j2 @@ -1,3 +1,3 @@ -[[ with secret "{{ consul_vault_tls.pki.path }}/issue/{{ consul_vault_tls.pki.role }}" "ttl={{ consul_vault_tls.pki.ttl }}" "common_name=cli-{{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ consul_conf.datacenter | default('dc1') }}.{{ consul_conf.domain | default('consul') }}" ]] +[[ with secret "{{ consul_vault_secrets.pki.path }}/issue/{{ consul_vault_secrets.pki.role }}" "common_name=cli-{{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ consul_conf.datacenter | default('dc1') }}.{{ consul_conf.domain | default('consul') }}" ]] [[ .Data.{{ item.what }} ]] [[ end ]] diff --git a/roles/consul/templates/consul-template-consul.service.j2 b/roles/consul/templates/consul-template-consul.service.j2 index b70b7fd..5eaab2e 100644 --- a/roles/consul/templates/consul-template-consul.service.j2 +++ b/roles/consul/templates/consul-template-consul.service.j2 @@ -8,6 +8,7 @@ ConditionFileNotEmpty={{ consul_root_dir }}/consul-template/consul-template.hcl [Service] Type=simple ExecStart=/usr/local/bin/consul-template -config={{ consul_root_dir }}/consul-template/consul-template.hcl +SuccessExitStatus=12 ExecReload=/bin/kill --signal HUP $MAINPID KillSignal=SIGINT Restart=on-failure diff --git a/roles/consul/templates/consul-template.hcl.j2 b/roles/consul/templates/consul-template.hcl.j2 index 176336a..a0419d5 100644 --- a/roles/consul/templates/consul-template.hcl.j2 +++ b/roles/consul/templates/consul-template.hcl.j2 @@ -1,6 +1,6 @@ vault { - address = "{{ consul_vault_tls.address }}" - token = "{{ consul_vault_tls.token }}" + address = "{{ consul_vault_secrets.vault_address }}" + token = "{{ consul_vault_secrets.vault_token }}" unwrap_token = false } @@ -11,10 +11,11 @@ template { destination = "{{ consul_conf.tls.defaults.ca_file }}" perms = 0644 exec { - command = "systemctl reload consul" + command = "sh -c 'systemctl reload consul || true'" } } +{% if consul_vault_secrets.pki.enabled %} {% if consul_conf.server %} template { source = "{{ consul_root_dir }}/consul-template/agent.crt.tpl" @@ -23,7 +24,7 @@ template { destination = "{{ consul_conf.tls.defaults.cert_file }}" perms = 0644 exec { - command = "systemctl reload consul" + command = "sh -c 'systemctl reload consul || true'" } } @@ -34,7 +35,7 @@ template { destination = "{{ consul_conf.tls.defaults.key_file }}" perms = 0640 exec { - command = ["sh", "-c", "chgrp {{ consul_user }} {{ consul_conf.tls.defaults.key_file }} && systemctl reload consul"] + command = "sh -c 'chgrp {{ consul_user }} {{ consul_conf.tls.defaults.key_file }} && systemctl reload consul || true'" } } @@ -53,3 +54,17 @@ template { perms = 0640 } {% endif %} +{% endif %} + +{% if consul_vault_secrets.tokens.enabled %} +template { + source = "{{ consul_root_dir }}/consul-template/agent.token.tpl" + left_delimiter = "[[" + right_delimiter = "]]" + destination = "{{ consul_root_dir }}/tmp/agent.token" + perms = 0600 + exec { + command = "sh -c 'consul acl set-agent-token default $(grep -P \'^[^\s]\' {{ consul_root_dir }}/tmp/agent.token)'" + } +} +{% endif %} diff --git a/roles/consul/templates/consul.hcl.j2 b/roles/consul/templates/consul.hcl.j2 index c547c3f..c74135b 100644 --- a/roles/consul/templates/consul.hcl.j2 +++ b/roles/consul/templates/consul.hcl.j2 @@ -73,8 +73,19 @@ connect { {% endif %} acl { - enabled = {{ consul_conf.acl.enabled | ternary('true', 'false') }} +{% for key in ['enabled', 'enable_token_persistence'] %} +{% if consul_conf.acl[key] is defined %} + {{ key }} = {{ consul_conf.acl[key] | ternary('true', 'false') }} +{% endif %} +{% endfor %} default_policy = "{{ consul_conf.acl.default_policy }}" + tokens { +{% for key in ['initial_management', 'default', 'agent', 'agent_recovery', 'replication'] %} +{% if consul_conf.acl.tokens[key] is defined %} + {{ key }} = "{{ consul_conf.acl.tokens[key] }}" +{% endif %} +{% endfor %} + } } {% if consul_conf.tls.enabled %} @@ -108,5 +119,4 @@ auto_encrypt { tls = true } {% endif %} - {% endif %} diff --git a/roles/consul/templates/profile.sh.j2 b/roles/consul/templates/profile.sh.j2 index b45216b..ee61577 100644 --- a/roles/consul/templates/profile.sh.j2 +++ b/roles/consul/templates/profile.sh.j2 @@ -2,7 +2,7 @@ export CONSUL_HTTP_ADDR=https://localhost:{{ consul_services.https.port }} export CONSUL_HTTP_SSL=true export CONSUL_CACERT={{ consul_conf.tls.defaults.ca_file }} -{% if consul_vault_tls.enabled %} +{% if consul_vault_secrets.pki.enabled %} export CONSUL_CLIENT_CERT={{ consul_root_dir }}/tls/cli.crt export CONSUL_CLIENT_KEY={{ consul_root_dir }}/tls/cli.key export CONSUL_TLS_SERVER_NAME=server.{{ consul_conf.datacenter | default('dc1') }}.{{ consul_conf.domain | default('consul') }} diff --git a/roles/nomad/defaults/main.yml b/roles/nomad/defaults/main.yml index 3ebceb6..1706625 100644 --- a/roles/nomad/defaults/main.yml +++ b/roles/nomad/defaults/main.yml @@ -57,7 +57,7 @@ nomad_base_conf: # TLS Settings - # See the nomad_vault_tls configuration if you want to integrate with vault to obtain and renew the certificates + # See the nomad_vault_secrets configuration if you want to integrate with vault to obtain and renew the certificates tls: http: False rpc: False @@ -228,23 +228,23 @@ nomad_host_services: {} nomad_services: "{{ nomad_base_services | combine(nomad_extra_services, recursive=True) | combine(nomad_host_services, recursive=True) }}" # When using vault to setup TLS for Nomad -nomad_base_vault_tls: - enabled: False - address: "{{ nomad_conf.vault.address | default(omit) }}" +nomad_base_vault_secrets: + vault_address: "{{ nomad_conf.vault.address | default(omit) }}" # Token to use to issue certificates # token: XXXXXXXXX pki: + enabled: False # The path of the PKI secret where cert will be issued path: /pki/nomad role: nomad-{{ nomad_conf.server.enabled | ternary('server', 'client') }} - ttl: 24h + # ttl: 6h # if not set, will use the default ttl of the role consul_pki: + enabled: False path: /pki/consul - role: nomad-client # Only nomad clients will use the gRPC endpoint and requires a client cert for consul - ttl: 24h - root_pki: - path: /pki/root # This is used to fetch the top level root CA, as envoy can't validate the chain unless it has it complete up to the auto-signed root + root_path: /pki/root # This is used to fetch the top level root CA, as envoy can't validate the chain unless it has it complete up to the auto-signed root + role: nomad-client # Only nomad clients will use this + # ttl: 6h # if not set, will use the default ttl of the role -nomad_extra_vault_tls: {} -nomad_host_vault_tls: {} -nomad_vault_tls: "{{ nomad_base_vault_tls | combine(nomad_extra_vault_tls, recursive=True) | combine(nomad_host_vault_tls, recursive=True) }}" +nomad_extra_vault_secrets: {} +nomad_host_vault_secrets: {} +nomad_vault_secrets: "{{ nomad_base_vault_secrets | combine(nomad_extra_vault_secrets, recursive=True) | combine(nomad_host_vault_secrets, recursive=True) }}" diff --git a/roles/nomad/tasks/conf.yml b/roles/nomad/tasks/conf.yml index d5d39b7..889ec05 100644 --- a/roles/nomad/tasks/conf.yml +++ b/roles/nomad/tasks/conf.yml @@ -113,38 +113,34 @@ loop: "{{ nomad_backup_configs.stdout_lines }}" tags: nomad -- when: nomad_vault_tls.enabled - block: - - - name: Deploy consul-template config - template: src=consul-template.hcl.j2 dest={{ nomad_root_dir }}/consul-template/consul-template.hcl - notify: restart consul-template-nomad - - - name: Deploy consul-template agent cert template - template: src=agent_cert.tpl.j2 dest={{ nomad_root_dir }}/consul-template/{{ item.where }} owner=root group=root - loop: - - what: certificate - where: agent.crt.tpl - - what: private_key - where: agent.key.tpl - - what: issuing_ca - where: ca.crt.tpl - notify: restart consul-template-nomad - +- name: Deploy consul-template config + template: src=consul-template.hcl.j2 dest={{ nomad_root_dir }}/consul-template/consul-template.hcl mode=600 owner=root group=root + notify: restart consul-template-nomad + when: nomad_vault_secrets.pki.enabled or nomad_vault_secrets.tokens.enabled tags: nomad -- when: nomad_vault_tls.enabled and nomad_conf.server.enabled - block: - - - name: Deploy consul-template cli cert template - template: src=cli_cert.tpl.j2 dest={{ nomad_root_dir }}/consul-template/{{ item.where }} owner=root group=root - loop: - - what: certificate - where: cli.crt.tpl - - what: private_key - where: cli.key.tpl - notify: restart consul-template-nomad +- name: Deploy consul-template agent cert template + template: src=agent_cert.tpl.j2 dest={{ nomad_root_dir }}/consul-template/{{ item.where }} owner=root group=root + loop: + - what: certificate + where: agent.crt.tpl + - what: private_key + where: agent.key.tpl + - what: issuing_ca + where: ca.crt.tpl + notify: restart consul-template-nomad + when: nomad_vault_secrets.pki.enabled + tags: nomad +- name: Deploy consul-template cli cert template + template: src=cli_cert.tpl.j2 dest={{ nomad_root_dir }}/consul-template/{{ item.where }} owner=root group=root + loop: + - what: certificate + where: cli.crt.tpl + - what: private_key + where: cli.key.tpl + notify: restart consul-template-nomad + when: nomad_vault_secrets.pki.enabled and nomad_conf.server.enabled tags: nomad - name: Set ACL on the TLS dir @@ -172,6 +168,6 @@ - what: issuing_ca where: consul_ca.crt.tpl notify: restart consul-template-nomad - when: nomad_conf.client.enabled and nomad_conf.consul.ssl + when: nomad_vault_secrets.consul_pki.enabled and nomad_conf.client.enabled and nomad_conf.consul.ssl tags: nomad diff --git a/roles/nomad/tasks/install.yml b/roles/nomad/tasks/install.yml index 7ee6f1d..7a3040f 100644 --- a/roles/nomad/tasks/install.yml +++ b/roles/nomad/tasks/install.yml @@ -114,6 +114,7 @@ template: src=consul-template-nomad.service.j2 dest=/etc/systemd/system/consul-template-nomad.service register: nomad_consul_tpl_unit notify: restart consul-template-nomad + when: nomad_vault_secrets.pki.enabled or nomad_vault_secrets.consul_pki.enabled tags: nomad - name: Reload systemd diff --git a/roles/nomad/tasks/services.yml b/roles/nomad/tasks/services.yml index c6d106b..8f4d1a6 100644 --- a/roles/nomad/tasks/services.yml +++ b/roles/nomad/tasks/services.yml @@ -6,5 +6,8 @@ tags: nomad - name: Handle consul-template-nomad service - service: name=consul-template-nomad state={{ nomad_vault_tls.enabled | ternary('started', 'stopped') }} enabled={{ nomad_vault_tls.enabled | ternary(True, False) }} + service: + name: consul-template-nomad + state: "{{ (nomad_vault_secrets.pki.enabled or nomad_vault_secrets.consul_pki.enabled or nomad_vault_secrets.tokens.enabled) | ternary('started', 'stopped') }}" + enabled: "{{ (nomad_vault_secrets.pki.enabled or nomad_vault_secrets.consul_pki.enabled or nomad_vault_secrets.tokens.enabled) | ternary(True, False) }}" tags: nomad diff --git a/roles/nomad/templates/agent_cert.tpl.j2 b/roles/nomad/templates/agent_cert.tpl.j2 index 88e475c..43fa8a9 100644 --- a/roles/nomad/templates/agent_cert.tpl.j2 +++ b/roles/nomad/templates/agent_cert.tpl.j2 @@ -1,8 +1,8 @@ -[[ with secret "{{ nomad_vault_tls.pki.path }}/issue/{{ nomad_vault_tls.pki.role }}" "common_name={{ (nomad_conf.server.enabled) | ternary('server', 'client') }}-{{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ nomad_conf.region | default('global') }}.nomad" "ttl={{ nomad_vault_tls.pki.ttl }}" "alt_names=localhost,{{ (nomad_conf.server.enabled) | ternary('server', 'client') }}.{{ nomad_conf.region | default('global') }}.nomad{% if nomad_conf.server.enabled and nomad_conf.client.enabled %},client.{{ nomad_conf.region | default('global') }}.nomad{% endif %}{% if consul_conf is defined %},nomad{{ nomad_conf.server.enabled | ternary('', '-client') }}.service.{{ consul_conf.domain | default('consul') }}{% endif %}" ]] +[[ with secret "{{ nomad_vault_secrets.pki.path }}/issue/{{ nomad_vault_secrets.pki.role }}" "common_name={{ (nomad_conf.server.enabled) | ternary('server', 'client') }}-{{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ nomad_conf.region | default('global') }}.nomad" "alt_names=localhost,{{ (nomad_conf.server.enabled) | ternary('server', 'client') }}.{{ nomad_conf.region | default('global') }}.nomad{% if nomad_conf.server.enabled and nomad_conf.client.enabled %},client.{{ nomad_conf.region | default('global') }}.nomad{% endif %}{% if consul_conf is defined %},nomad{{ nomad_conf.server.enabled | ternary('', '-client') }}.service.{{ consul_conf.domain | default('consul') }}{% endif %}"{% if nomad_vault_secrets.pki.ttl is defined %} "ttl={{ nomad_vault_secrets.pki.ttl }}"{% endif %} ]] [[ .Data.{{ item.what }} ]] [[ end ]] {% if item.what == 'certificate' %} -[[ with secret "{{ nomad_vault_tls.pki.path }}/cert/ca" ]] +[[ with secret "{{ nomad_vault_secrets.pki.path }}/cert/ca" ]] [[ .Data.certificate ]] [[ end ]] {% endif %} diff --git a/roles/nomad/templates/cli_cert.tpl.j2 b/roles/nomad/templates/cli_cert.tpl.j2 index 9bc7767..b5b6369 100644 --- a/roles/nomad/templates/cli_cert.tpl.j2 +++ b/roles/nomad/templates/cli_cert.tpl.j2 @@ -1,3 +1,3 @@ -[[ with secret "{{ nomad_vault_tls.pki.path }}/issue/{{ nomad_vault_tls.pki.role }}" "ttl={{ nomad_vault_tls.pki.ttl }}" "common_name=cli-{{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ nomad_conf.region | default('global') }}.nomad" ]] +[[ with secret "{{ nomad_vault_secrets.pki.path }}/issue/{{ nomad_vault_secrets.pki.role }}" "common_name=cli-{{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ nomad_conf.region | default('global') }}.nomad" ]] [[ .Data.{{ item.what }} ]] [[ end ]] diff --git a/roles/nomad/templates/consul-template-nomad.service.j2 b/roles/nomad/templates/consul-template-nomad.service.j2 index 6e36845..02a596b 100644 --- a/roles/nomad/templates/consul-template-nomad.service.j2 +++ b/roles/nomad/templates/consul-template-nomad.service.j2 @@ -3,11 +3,13 @@ Description="HashiCorp consul-template" Documentation=https://github.com/hashicorp/consul-template Requires=network-online.target After=network-online.target +Before=nomad.service ConditionFileNotEmpty={{ nomad_root_dir }}/consul-template/consul-template.hcl [Service] Type=simple ExecStart=/usr/local/bin/consul-template -config={{ nomad_root_dir }}/consul-template/consul-template.hcl +SuccessExitStatus=12 ExecReload=/bin/kill --signal HUP $MAINPID KillSignal=SIGINT Restart=on-failure diff --git a/roles/nomad/templates/consul-template.hcl.j2 b/roles/nomad/templates/consul-template.hcl.j2 index 0e3a7e7..bece1b7 100644 --- a/roles/nomad/templates/consul-template.hcl.j2 +++ b/roles/nomad/templates/consul-template.hcl.j2 @@ -1,9 +1,12 @@ vault { - address = "{{ nomad_vault_tls.address }}" - token = "{{ nomad_vault_tls.token }}" + address = "{{ nomad_vault_secrets.vault_address }}" + token = "{{ nomad_vault_secrets.vault_token }}" unwrap_token = false } +# Sleep 10 sec before sending nomad service a reload to prevent it from crashing as +# Nomad doesn't support getting a reload while it's still initializing +{% if nomad_vault_secrets.pki.enabled %} template { source = "{{ nomad_root_dir }}/consul-template/agent.crt.tpl" left_delimiter = "[[" @@ -11,7 +14,7 @@ template { destination = "{{ nomad_conf.tls.cert_file }}" perms = 0644 exec { - command = "systemctl reload nomad" + command = "sh -c 'sleep 10 && systemctl reload nomad || true'" } } @@ -22,7 +25,7 @@ template { destination = "{{ nomad_conf.tls.key_file }}" perms = 0640 exec { - command = ["sh", "-c", "chgrp {{ nomad_user }} {{ nomad_conf.tls.key_file }} && systemctl reload nomad"] + command = ["sh", "-c", "chgrp {{ nomad_user }} {{ nomad_conf.tls.key_file }} && sleep 10 && systemctl reload nomad || true"] } } @@ -33,7 +36,7 @@ template { destination = "{{ nomad_conf.tls.ca_file }}" perms = 0644 exec { - command = "systemctl reload nomad" + command = "sh -c 'sleep 10 && systemctl reload nomad || true'" } } @@ -53,13 +56,18 @@ template { perms = 0640 } {% endif %} +{% endif %} -{% if nomad_conf.client.enabled and nomad_conf.consul.ssl %} + +{% if nomad_vault_secrets.consul_pki.enabled and nomad_conf.client.enabled and nomad_conf.consul.ssl %} template { source = "{{ nomad_root_dir }}/consul-template/consul.crt.tpl" left_delimiter = "[[" right_delimiter = "]]" destination = "{{ nomad_conf.consul.cert_file }}" + exec { + command = "sh -c 'sleep 10 && systemctl reload nomad || true'" + } } template { @@ -68,6 +76,9 @@ template { right_delimiter = "]]" destination = "{{ nomad_conf.consul.key_file }}" perms = 0640 + exec { + command = "sh -c 'sleep 10 && systemctl reload nomad || true'" + } } template { @@ -77,8 +88,8 @@ template { destination = "{{ nomad_conf.consul.ca_file }}" perms = 0644 exec { - command = "systemctl reload nomad" + command = "sh -c 'sleep 10 && systemctl reload nomad || true'" } } - {% endif %} + diff --git a/roles/nomad/templates/consul_cert.tpl.j2 b/roles/nomad/templates/consul_cert.tpl.j2 index dd7dd55..3478201 100644 --- a/roles/nomad/templates/consul_cert.tpl.j2 +++ b/roles/nomad/templates/consul_cert.tpl.j2 @@ -1,8 +1,8 @@ -[[ with secret "{{ nomad_vault_tls.consul_pki.path }}/issue/{{ nomad_vault_tls.consul_pki.role }}" "common_name={{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ (consul_conf is defined and consul_conf.domain is defined) | ternary(consul_conf.domain, 'consul') }}" "ttl={{ nomad_vault_tls.consul_pki.ttl }}" ]] +[[ with secret "{{ nomad_vault_secrets.consul_pki.path }}/issue/{{ nomad_vault_secrets.consul_pki.role }}" "common_name={{ ansible_fqdn | regex_replace('\\.', '-') }}.{{ (consul_conf is defined and consul_conf.domain is defined) | ternary(consul_conf.domain, 'consul') }}"{% if nomad_vault_secrets.consul_pki.ttl is defined %} "ttl={{ nomad_vault_secrets.consul_pki.ttl }}"{% endif %} ]] [[ .Data.{{ item.what }} ]] [[ end ]] {% if item.what == 'issuing_ca' %} -[[ with secret "{{ nomad_vault_tls.root_pki.path }}/cert/ca" ]] +[[ with secret "{{ nomad_vault_secrets.consul_pki.root_path }}/cert/ca" ]] [[ .Data.certificate ]] [[ end ]] {% endif %} diff --git a/roles/nomad/templates/nomad.hcl.j2 b/roles/nomad/templates/nomad.hcl.j2 index 1896739..fd29791 100644 --- a/roles/nomad/templates/nomad.hcl.j2 +++ b/roles/nomad/templates/nomad.hcl.j2 @@ -210,11 +210,20 @@ vault { {{ key }} = {{ nomad_conf.vault[key] | ternary('true', 'false') }} {% endif %} {% endfor %} +{% if nomad_conf.server.enabled %} {% for key in ['address', 'create_from_role', 'task_token_ttl', 'ca_file', 'ca_path', 'cert_file', 'key_file', 'namespace', 'tls_server_name', 'token'] %} {% if nomad_conf.vault[key] is defined %} {{ key }} = "{{ nomad_conf.vault[key] }}" {% endif %} {% endfor %} +{% elif nomad_conf.client.enabled and not nomad_conf.server.enabled %} +{% for key in ['address', 'ca_file', 'ca_path', 'cert_file', 'key_file', 'namespace', 'tls_server_name'] %} +{% if nomad_conf.vault[key] is defined %} + {{ key }} = "{{ nomad_conf.vault[key] }}" +{% endif %} +{% endfor %} + +{% endif %} } tls { diff --git a/roles/nomad/templates/pre-backup.j2 b/roles/nomad/templates/pre-backup.j2 index affc645..00ca6b2 100644 --- a/roles/nomad/templates/pre-backup.j2 +++ b/roles/nomad/templates/pre-backup.j2 @@ -6,7 +6,7 @@ set -eo pipefail NOMAD_ADDR=https://localhost:{{ nomad_services.http.port }} \ NOMAD_CACERT={{ nomad_conf.tls.ca_file }} \ {% endif %} -{% if nomad_vault_tls.enabled %} +{% if nomad_vault_secrets.pki.enabled %} NOMAD_CLIENT_CERT={{ nomad_root_dir }}/tls/cli.crt \ NOMAD_CLIENT_KEY={{ nomad_root_dir }}/tls/cli.key \ {% endif %} diff --git a/roles/nomad/templates/profile.sh.j2 b/roles/nomad/templates/profile.sh.j2 index 84ef228..2482e03 100644 --- a/roles/nomad/templates/profile.sh.j2 +++ b/roles/nomad/templates/profile.sh.j2 @@ -1,7 +1,7 @@ {% if nomad_conf.tls.http and nomad_conf.server.enabled %} export NOMAD_ADDR=https://localhost:{{ nomad_services.http.port }} export NOMAD_CACERT={{ nomad_conf.tls.ca_file }} -{% if nomad_vault_tls.enabled %} +{% if nomad_vault_secrets.pki.enabled %} export NOMAD_CLIENT_CERT={{ nomad_root_dir }}/tls/cli.crt export NOMAD_CLIENT_KEY={{ nomad_root_dir }}/tls/cli.key export NOMAD_TLS_SERVER_NAME=server.{{ nomad_conf.region | default('global') }}.nomad diff --git a/roles/vault/defaults/main.yml b/roles/vault/defaults/main.yml index 98b7af1..96eba01 100644 --- a/roles/vault/defaults/main.yml +++ b/roles/vault/defaults/main.yml @@ -126,3 +126,32 @@ vault_host_conf: {} # Merge all the conf vault_conf: "{{ vault_base_conf | combine(vault_extra_conf, recursive=True) | combine(vault_host_conf, recursive=True) }}" +# This can be used to spawn a consul-template service which will obtain and renew client cert +# to reach Nomad API, so the Nomad secret can be used securely +vault_base_secrets: + # The vault API to query. Default is our own API + vault_address: "{{ vault_conf.api_addr }}" + # The vault token to use + vault_token: XXXXXXX + nomad: + enabled: False + # The Nomad API address + address: https://nomad.service.consul:4646 + # The Nomad management token vault will use to issue tokens for users + token: XXXXXXX + pki: + # The path where the PKI used by Nomad is mounted. The PKI must be mounted and configured + path: /pki/nomad + # The role used to issue the certificate + role: nomad-user + # The TTL of the certificate issued for vault + ttl: 72h + # The common name of the certificate + cn: vault + secret: + # The path where the Nomad secret engine is mounted + # Note: the secret must be already mounted + path: nomad +vault_extra_secrets: {} +vault_host_secrets: {} +vault_secrets: "{{ vault_base_secrets | combine(vault_extra_secrets, recursive=True) | combine(vault_host_secrets, recursive=True) }}" diff --git a/roles/vault/handlers/main.yml b/roles/vault/handlers/main.yml index 0b9a86f..e8393a7 100644 --- a/roles/vault/handlers/main.yml +++ b/roles/vault/handlers/main.yml @@ -6,3 +6,6 @@ - name: reload vault service: name=vault state=reloaded + +- name: restart consul-template-vault + service: name=consul-template-vault state=restarted diff --git a/roles/vault/meta/main.yml b/roles/vault/meta/main.yml index dc58dfa..c9fe3ab 100644 --- a/roles/vault/meta/main.yml +++ b/roles/vault/meta/main.yml @@ -2,3 +2,4 @@ dependencies: - role: mkdir + - role: consul_template diff --git a/roles/vault/tasks/conf.yml b/roles/vault/tasks/conf.yml index a99a6c4..e7e526a 100644 --- a/roles/vault/tasks/conf.yml +++ b/roles/vault/tasks/conf.yml @@ -22,3 +22,20 @@ - name: Setup logrotate template: src=logrotate.conf.j2 dest=/etc/logrotate.d/vault tags: vault + +- when: vault_secrets.nomad.enabled + block: + + - name: Deploy the consul-template conf + template: src=consul-template.hcl.j2 dest={{ vault_root_dir }}/consul-template/consul-template.hcl + notify: restart consul-template-vault + + - name: Deploy Nomad certificate bundle template for consul-template + template: src=nomad_client_bundle.json.tpl.j2 dest={{ vault_root_dir }}/consul-template/nomad_client_bundle.json.tpl + notify: restart consul-template-vault + + - name: Deploy the update cert hook + template: src=update_nomad_cert.j2 dest={{ vault_root_dir }}/bin/update_nomad_cert mode=755 + notify: restart consul-template-vault + + tags: vault diff --git a/roles/vault/tasks/directories.yml b/roles/vault/tasks/directories.yml index a20ee92..870fa1b 100644 --- a/roles/vault/tasks/directories.yml +++ b/roles/vault/tasks/directories.yml @@ -44,4 +44,5 @@ owner: root group: "{{ vault_user }}" mode: 750 + - dir: consul-template tags: vault diff --git a/roles/vault/tasks/install.yml b/roles/vault/tasks/install.yml index be53277..dce2464 100644 --- a/roles/vault/tasks/install.yml +++ b/roles/vault/tasks/install.yml @@ -6,6 +6,7 @@ - tar - zstd - unzip + - jq tags: vault - when: vault_install_mode != 'none' @@ -48,9 +49,16 @@ notify: restart vault tags: vault +- name: Install consul-template unit + template: src=consul-template-vault.service.j2 dest=/etc/systemd/system/consul-template-vault.service + notify: restart consul-template-vault + when: vault_secrets.nomad.enabled + register: vault_secrets_nomad_unit + tags: vault + - name: Reload systemd systemd: daemon_reload=True - when: vault_unit.changed + when: vault_unit.changed or (vault_secrets_nomad_unit is defined and vault_secrets_nomad_unit.changed) tags: vault - name: Install dehydrated hook @@ -65,3 +73,4 @@ dest: /etc/profile.d/vault.sh mode: 0755 tags: vault + diff --git a/roles/vault/tasks/services.yml b/roles/vault/tasks/services.yml index ece06af..b5ed217 100644 --- a/roles/vault/tasks/services.yml +++ b/roles/vault/tasks/services.yml @@ -4,3 +4,10 @@ service: name=vault state=started enabled=True register: vault_service_started tags: vault + +- name: Handle consul-template-vault service + service: + name: consul-template-vault + state: "{{ vault_secrets.nomad.enabled | ternary('started', 'stopped') }}" + enabled: "{{ vault_secrets.nomad.enabled | ternary(True, False) }}" + tags: vault diff --git a/roles/vault/templates/consul-template-vault.service.j2 b/roles/vault/templates/consul-template-vault.service.j2 new file mode 100644 index 0000000..0f545a2 --- /dev/null +++ b/roles/vault/templates/consul-template-vault.service.j2 @@ -0,0 +1,20 @@ +[Unit] +Description="HashiCorp consul-template" +Documentation=https://github.com/hashicorp/consul-template +Requires=network-online.target +After=network-online.target +After=vault.service +ConditionFileNotEmpty={{ vault_root_dir }}/consul-template/consul-template.hcl + +[Service] +Type=simple +ExecStart=/usr/local/bin/consul-template -config={{ vault_root_dir }}/consul-template/consul-template.hcl +SuccessExitStatus=12 +ExecReload=/bin/kill --signal HUP $MAINPID +KillSignal=SIGINT +Restart=on-failure +RestartSec=2 + +[Install] +WantedBy=multi-user.target + diff --git a/roles/vault/templates/consul-template.hcl.j2 b/roles/vault/templates/consul-template.hcl.j2 new file mode 100644 index 0000000..26bfea3 --- /dev/null +++ b/roles/vault/templates/consul-template.hcl.j2 @@ -0,0 +1,18 @@ +vault { + address = "{{ vault_secrets.vault_address }}" + token = "{{ vault_secrets.vault_token }}" + unwrap_token = false +} + +{% if vault_secrets.nomad.enabled %} +template { + source = "{{ vault_root_dir }}/consul-template/nomad_client_bundle.json.tpl" + left_delimiter = "[[" + right_delimiter = "]]" + destination = "{{ vault_root_dir }}/tmp/nomad_client_bundle.json" + perms = 0600 + exec { + command = "{{ vault_root_dir }}/bin/update_nomad_cert {{ vault_secrets.nomad.token }} {{ vault_secrets.vault_token }}" + } +} +{% endif %} diff --git a/roles/vault/templates/nomad_client_bundle.json.tpl.j2 b/roles/vault/templates/nomad_client_bundle.json.tpl.j2 new file mode 100644 index 0000000..84f7c2c --- /dev/null +++ b/roles/vault/templates/nomad_client_bundle.json.tpl.j2 @@ -0,0 +1,3 @@ +[[ with secret "{{ vault_secrets.nomad.pki.path }}/issue/{{ vault_secrets.nomad.pki.role }}" "ttl={{ vault_secrets.nomad.pki.ttl }}" "common_name={{ vault_secrets.nomad.pki.cn }}" ]] +[[ .Data | toJSONPretty ]] +[[ end ]] diff --git a/roles/vault/templates/update_nomad_cert.j2 b/roles/vault/templates/update_nomad_cert.j2 new file mode 100644 index 0000000..511f96d --- /dev/null +++ b/roles/vault/templates/update_nomad_cert.j2 @@ -0,0 +1,29 @@ +#!/bin/sh + +set -eo pipefail + +NOMAD_TOKEN=$1 +VAULT_TOKEN=$2 + +NOMAD_CERT_BUNDLE={{ vault_root_dir }}/tmp/nomad_client_bundle.json +VAULT_ADDR={{ vault_conf.api_addr }} + +if [ "$(vault status -format=json| jq .is_self)" != "true" ]; then + echo "We're not the active vault, exiting" +elif [ "$(vault status -format=json | jq .sealed)" != "false" ]; then + echo "Vault is sealed, exiting" +elif [ "$(vault status -format=json | jq .initialized)" != "true" ]; then + echo "Vault is not initialized yet, exiting" +else + echo Updating Vault certificate to access Nomad API + VAULT_TOKEN=$VAULT_TOKEN \ + vault write {{ vault_secrets.nomad.secret.path }}/config/access \ + address="{{ vault_secrets.nomad.address }}" \ + token="$NOMAD_TOKEN" \ + ca_cert="$(cat $NOMAD_CERT_BUNDLE | jq -r .issuing_ca)" \ + client_cert="$(cat $NOMAD_CERT_BUNDLE | jq -r .certificate)" \ + client_key="$(cat $NOMAD_CERT_BUNDLE | jq -r .private_key)" +fi + +echo Removing Nomad client certificate from the filesystem +rm -f $NOMAD_CERT_BUNDLE