From b08ea85b59b00a629f9e44d1cce796c2653fd289 Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 15 Sep 2022 14:04:54 +0300 Subject: [PATCH] add per host for groups certs --- README.md | 12 ++- defaults/main.yaml | 12 ++- meta/main.yaml | 2 + tasks/ca.yaml | 27 +++++-- tasks/cn_certs.yaml | 109 ++++++++++++++++------------ tasks/group_certs.yaml | 106 +++++++++++++++------------ tasks/main.yaml | 20 ++--- tasks/per_host_for_group_certs.yaml | 37 ++++++++++ 8 files changed, 209 insertions(+), 116 deletions(-) create mode 100644 tasks/per_host_for_group_certs.yaml diff --git a/README.md b/README.md index 4689ce2..c894128 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Inventory example: - { host: "server-2", path: "/opt/certs" } groups: - { group: "cacert_clients", path: "/opt/certs" } - - { group: "mygroupname", path: "/opt/certs" } + - { group: "mygroupname1", path: "/opt/certs" } cacert_cn_certs: - name: mycertname.example.com @@ -20,10 +20,10 @@ Inventory example: - { host: "server-2", path: "/opt/certs", concat: "pem" } groups: - { group: "cacert_clients", path: "/opt/certs" } - - { group: "mygroupname", path: "/opt/certs" } + - { group: "mygroupname1", path: "/opt/certs" } cacert_group_certs: - - name: mygroupcert + - name: mygroupcert.example.com append_groups: - consul - patroni @@ -34,5 +34,9 @@ Inventory example: - { host: "server-2", path: "/opt/certs", concat: "pem" } groups: - { group: "cacert_clients", path: "/opt/certs" } - - { group: "mygroupname", path: "/opt/certs" } + - { group: "mygroupname1", path: "/opt/certs" } + cacert_per_host_certs: + groups: + - { group: "mygroupname1", path: "/opt/koji/certs", concat: "pem" } + diff --git a/defaults/main.yaml b/defaults/main.yaml index 64d1981..62cc34a 100644 --- a/defaults/main.yaml +++ b/defaults/main.yaml @@ -24,8 +24,8 @@ cacert_ca_trust_anchors_update: False # - { group: "mygroupname", path: "/opt/certs" } # #cacert_group_certs: -# - name: mygroupcert -# host_groups: +# - name: mygroupcert.example.com +# append_groups: # - consul # - patroni # hosts: @@ -36,4 +36,10 @@ cacert_ca_trust_anchors_update: False # groups: # - { group: "cacert_clients", path: "/opt/certs" } # - { group: "mygroupname", path: "/opt/certs" } - +# +#cacert_per_host_certs: +# groups: +# - { group: "mygroupname1", path: "/opt/certs" } +# - { group: "mygroupname1", path: "/opt/certs", concat: "pem" } +# - { group: "mygroupname2", path: "/opt/certs" } +# - { group: "mygroupname2", path: "/opt/certs", concat: "pem" } diff --git a/meta/main.yaml b/meta/main.yaml index e69de29..23d65c7 100644 --- a/meta/main.yaml +++ b/meta/main.yaml @@ -0,0 +1,2 @@ +--- +dependencies: [] diff --git a/tasks/ca.yaml b/tasks/ca.yaml index ea7314d..6848995 100644 --- a/tasks/ca.yaml +++ b/tasks/ca.yaml @@ -1,6 +1,16 @@ - name: Create CA {{ cacert_ca_name }} when: inventory_hostname in groups[cacert_ca_group] block: + - name: CA and certs | Install cryptography library + dnf: + name: python-cryptography + state: present + + - name: CA and certs | Check if ssl gen dir exist + file: + name: "{{ cacert_ssl_gen_path }}" + state: directory + - name: Generate an OpenSSL private CA key with the default values (4096 bits, RSA) community.crypto.openssl_privatekey: path: "{{ cacert_ssl_gen_path }}/{{ cacert_ca_name }}.key" @@ -19,7 +29,7 @@ key_usage_critical: true common_name: "{{ cacert_ca_name }}" register: cacert_ca_csr - + - name: Generate a Self Signed OpenSSL CA certificate community.crypto.x509_certificate: path: "{{ cacert_ssl_gen_path }}/{{ cacert_ca_name }}.crt" @@ -42,16 +52,19 @@ set_fact: cacert_ca_key: "{{ cacert_ca_key_b64.content | b64decode }}" cacert_ca_cert: "{{ cacert_ca_cert_b64.content | b64decode }}" - delegate_to: "{{ item }}" + delegate_to: "{{ fact_item }}" delegate_facts: true - run_once: true with_items: - "{{ groups[cacert_ca_group] | default([]) }}" - "{{ groups[cacert_clients_group] | default([]) }}" + loop_control: + loop_var: fact_item - name: Distribute CA certificate to hosts become: true - when: inventory_hostname in groups.cacert_clients + when: + - cacert_ca_copy_to.hosts is defined + - inventory_hostname in groups.cacert_clients block: - name: CA and cert | Check if dest dir exist on remote host file: @@ -83,7 +96,7 @@ register: ca_trust_anchors when: - inventory_hostname == host_item.host - + - name: Update CA trust shell: update-ca-trust extract loop: "{{ cacert_ca_copy_to.hosts }}" @@ -96,7 +109,9 @@ - name: Distribute CA certificate to groups become: true - when: inventory_hostname in groups[cacert_clients_group] + when: + - cacert_ca_copy_to.groups is defined + - inventory_hostname in groups[cacert_clients_group] block: - name: CA and cert | Check if dest dir exist on remote host in group file: diff --git a/tasks/cn_certs.yaml b/tasks/cn_certs.yaml index 156dd0f..ca37413 100644 --- a/tasks/cn_certs.yaml +++ b/tasks/cn_certs.yaml @@ -1,23 +1,23 @@ - name: CA and certs | Generate CN certs and keys when: inventory_hostname in groups[cacert_ca_group] block: - - name: Generate an OpenSSL private client key with the default values (4096 bits, RSA) + - name: Generate an OpenSSL private client key {{ item.name }} with the default values (4096 bits, RSA) community.crypto.openssl_privatekey: path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.key" when: cacert_cert is not defined register: cacert_client_key_gen - - name: Generate subject_alt_ips + - name: Generate subject_alt_ips for {{ item.name }} set_fact: client_subject_alt_ips: "{{ item.subject_alt_ips | map('regex_replace', '^', 'IP:') | list }}" when: item.subject_alt_ips is defined - - name: Generate subject_alt_names + - name: Generate subject_alt_names for {{ item.name }} set_fact: client_subject_alt_names: "{{ item.subject_alt_names | map('regex_replace', '^', 'DNS:') | list }}" when: item.subject_alt_names is defined - - name: Generate an OpenSSL Certificate Signing Request for client + - name: Generate an OpenSSL Certificate Signing Request for client {{ item.name }} community.crypto.openssl_csr: path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.csr" privatekey_path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.key" @@ -27,7 +27,7 @@ - item.subject_alt_names is not defined - item.subject_alt_ips is not defined - - name: Generate an OpenSSL Certificate Signing Request for client with subject_alt_name + - name: Generate an OpenSSL Certificate Signing Request for client {{ item.name }} with subject_alt_name community.crypto.openssl_csr: path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.csr" privatekey_path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.key" @@ -36,7 +36,7 @@ register: cacert_client_csr when: item.subject_alt_names is defined or item.subject_alt_ips is defined - - name: Generate an OpenSSL certificate for client signed with your own CA certificate + - name: Generate an OpenSSL certificate for client {{ item.name }} signed with your own CA certificate community.crypto.x509_certificate: path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.crt" csr_path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.csr" @@ -56,40 +56,41 @@ content: "{{ concated_crt_key.stdout }}" dest: "{{ cacert_ssl_gen_path }}/{{ item.name }}.pem" - - name: Get CN key content + - name: Get CN key {{ item.name }} content slurp: src: "{{ cacert_ssl_gen_path }}/{{ item.name }}.key" register: cacert_cn_certs_key_b64 - - name: Get CN cert content + - name: Get CN cert {{ item.name }} content slurp: src: "{{ cacert_ssl_gen_path }}/{{ item.name }}.crt" register: cacert_cn_certs_cert_b64 - - name: Get CN cert and key concat content + - name: Get CN cert and key concat {{ item.name }} content slurp: src: "{{ cacert_ssl_gen_path }}/{{ item.name }}.pem" register: cacert_cn_certs_concat_b64 - - name: Set facts about key and cert + - name: Set facts about {{ item.name }} key and cert set_fact: cacert_cn_certs_key: "{{ cacert_cn_certs_key_b64.content | b64decode }}" cacert_cn_certs_cert: "{{ cacert_cn_certs_cert_b64.content | b64decode }}" cacert_cn_certs_concat: "{{ cacert_cn_certs_concat_b64.content | b64decode }}" delegate_to: "{{ fact_item }}" delegate_facts: true - run_once: true with_items: - "{{ groups[cacert_ca_group] | default([]) }}" - "{{ groups[cacert_clients_group] | default([]) }}" loop_control: loop_var: fact_item -- name: Distribute CN certificates +- name: Distribute CN certificates to hosts become: true - when: inventory_hostname in groups[cacert_clients_group] + when: + - item.hosts is defined + - inventory_hostname in groups[cacert_clients_group] block: - - name: CA and cert | Check if dest dir exist on remote hosts + - name: CA and cert | Check if dest dir exist on remote host file: name: "{{ host_item.path }}" state: directory @@ -99,7 +100,46 @@ when: - inventory_hostname == host_item.host - - name: CA and cert | Check if dest dir exist on remote hosts for groups + - name: Put CN key {{ item.name }} for host + copy: + content: "{{ cacert_cn_certs_key }}" + dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.key" + loop: "{{ item.hosts }}" + loop_control: + loop_var: host_item + when: + - host_item.concat is not defined + - inventory_hostname == host_item.host + + - name: Put CN cert {{ item.name }} for host + copy: + content: "{{ cacert_cn_certs_cert }}" + dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.crt" + loop: "{{ item.hosts }}" + loop_control: + loop_var: host_item + when: + - host_item.concat is not defined + - inventory_hostname == host_item.host + + - name: Put CN key and cert concat {{ item.name }} for host + copy: + content: "{{ cacert_cn_certs_concat }}" + dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.{{ host_item.concat | default('pem') }}" + loop: "{{ item.hosts }}" + loop_control: + loop_var: host_item + when: + - host_item.concat is defined + - inventory_hostname == host_item.host + +- name: Distribute CN certificates to groups + become: true + when: + - item.groups is defined + - inventory_hostname in groups[cacert_clients_group] + block: + - name: CA and cert | Check if dest dir exist on remote host for groups file: name: "{{ group_item.path }}" state: directory @@ -109,37 +149,7 @@ when: - "inventory_hostname in (groups[group_item.group] | map('extract', hostvars, ['inventory_hostname']) | join(','))" - - name: Put CN key for host - copy: - content: "{{ cacert_cn_certs_key }}" - dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.key" - loop: "{{ item.hosts }}" - loop_control: - loop_var: host_item - when: - - inventory_hostname == host_item.host - - - name: Put CN cert for host - copy: - content: "{{ cacert_cn_certs_cert }}" - dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.crt" - loop: "{{ item.hosts }}" - loop_control: - loop_var: host_item - when: - - inventory_hostname == host_item.host - - - name: Put CN key and cert concat for host - copy: - content: "{{ cacert_cn_certs_concat }}" - dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.{{ host_item.concat | default('pem') }}" - loop: "{{ item.hosts }}" - loop_control: - loop_var: host_item - when: - - inventory_hostname == host_item.host - - - name: Put CN key for group + - name: Put CN key {{ item.name }} for group copy: content: "{{ cacert_cn_certs_key }}" dest: "{{ group_item.path }}/{{ group_item.name | default(item.name) }}.key" @@ -147,9 +157,10 @@ loop_control: loop_var: group_item when: + - group_item.concat is not defined - "inventory_hostname in (groups[group_item.group] | map('extract', hostvars, ['inventory_hostname']) | join(','))" - - name: Put CN cert for group + - name: Put CN cert {{ item.name }} for group copy: content: "{{ cacert_cn_certs_cert }}" dest: "{{ group_item.path }}/{{ group_item.name | default(item.name) }}.crt" @@ -157,9 +168,10 @@ loop_control: loop_var: group_item when: + - group_item.concat is not defined - "inventory_hostname in (groups[group_item.group] | map('extract', hostvars, ['inventory_hostname']) | join(','))" - - name: Put CN key and cert concat for group + - name: Put CN key and cert concat {{ item.name }} for group copy: content: "{{ cacert_cn_certs_concat }}" dest: "{{ group_item.path }}/{{ group_item.name | default(item.name) }}.{{ group_item.concat | default('pem') }}" @@ -167,4 +179,5 @@ loop_control: loop_var: group_item when: + - group_item.concat is defined - "inventory_hostname in (groups[group_item.group] | map('extract', hostvars, ['inventory_hostname']) | join(','))" diff --git a/tasks/group_certs.yaml b/tasks/group_certs.yaml index df721f8..72de67a 100644 --- a/tasks/group_certs.yaml +++ b/tasks/group_certs.yaml @@ -1,7 +1,8 @@ - name: CA and certs | Generate group certs and keys - when: inventory_hostname in groups[cacert_ca_group] + when: + - inventory_hostname in groups[cacert_ca_group] block: - - name: Generate subject_alt_ips + - name: Generate subject_alt_ips for {{ item.name }} set_fact: client_subject_alt_ips: "{{ groups[group_item] | map('extract', hostvars, ['ansible_host']) | map('regex_replace', '^', 'IP:') | list }}" loop: "{{ item.append_groups }}" @@ -13,7 +14,7 @@ - debug: msg: "{{ client_subject_alt_ips }}" - - name: Generate subject_alt_names + - name: Generate subject_alt_names for {{ item.name }} set_fact: client_subject_alt_names: "{{ groups[group_item] | map('extract', hostvars, ['inventory_hostname']) | map('regex_replace', '^', 'DNS:') | list }}" loop: "{{ item.append_groups }}" @@ -25,12 +26,12 @@ - debug: msg: "{{ client_subject_alt_names }}" - - name: Generate an OpenSSL private client key with the default values (4096 bits, RSA) + - name: Generate an OpenSSL private client {{ item.name }} key with the default values (4096 bits, RSA) community.crypto.openssl_privatekey: path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.key" register: cacert_client_key_gen - - name: Generate an OpenSSL Certificate Signing Request for client with subject_alt_name + - name: Generate an OpenSSL Certificate Signing Request for client {{ item.name }} with subject_alt_name community.crypto.openssl_csr: path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.csr" privatekey_path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.key" @@ -38,7 +39,7 @@ subject_alt_name: "{{ ((client_subject_alt_ips | default([])) + (client_subject_alt_names | default([]))) }}" register: cacert_client_csr - - name: Generate an OpenSSL certificate for client signed with your own CA certificate + - name: Generate an OpenSSL certificate for client {{ item.name }} signed with your own CA certificate community.crypto.x509_certificate: path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.crt" csr_path: "{{ cacert_ssl_gen_path }}/{{ item.name }}.csr" @@ -58,38 +59,39 @@ content: "{{ concated_crt_key.stdout }}" dest: "{{ cacert_ssl_gen_path }}/{{ item.name }}.pem" - - name: Get group key content + - name: Get group key {{ item.name }} content slurp: src: "{{ cacert_ssl_gen_path }}/{{ item.name }}.key" register: cacert_group_certs_key_b64 - - name: Get group cert content + - name: Get group cert {{ item.name }} content slurp: src: "{{ cacert_ssl_gen_path }}/{{ item.name }}.crt" register: cacert_group_certs_cert_b64 - - name: Get group cert and key concat content + - name: Get group cert and key concat {{ item.name }} content slurp: src: "{{ cacert_ssl_gen_path }}/{{ item.name }}.pem" register: cacert_group_certs_concat_b64 - - name: Set facts about key and cert + - name: Set facts about {{ item.name }} key and cert set_fact: cacert_group_certs_key: "{{ cacert_group_certs_key_b64.content | b64decode }}" cacert_group_certs_cert: "{{ cacert_group_certs_cert_b64.content | b64decode }}" cacert_group_certs_concat: "{{ cacert_group_certs_concat_b64.content | b64decode }}" delegate_to: "{{ fact_item }}" delegate_facts: true - run_once: true with_items: - "{{ groups[cacert_ca_group] | default([]) }}" - "{{ groups[cacert_clients_group] | default([]) }}" loop_control: loop_var: fact_item -- name: Distribute group certificates +- name: Distribute group certificates for hosts become: true - when: inventory_hostname in groups[cacert_clients_group] + when: + - item.hosts is defined + - inventory_hostname in groups[cacert_clients_group] block: - name: CA and cert | Check if dest dir exist on remote host file: @@ -101,6 +103,45 @@ when: - inventory_hostname == host_item.host + - name: Put group key {{ item.name }} for host + copy: + content: "{{ cacert_group_certs_key }}" + dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.key" + loop: "{{ item.hosts }}" + loop_control: + loop_var: host_item + when: + - host_item.concat is not defined + - inventory_hostname == host_item.host + + - name: Put group cert {{ item.name }} for host + copy: + content: "{{ cacert_group_certs_cert }}" + dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.crt" + loop: "{{ item.hosts }}" + loop_control: + loop_var: host_item + when: + - host_item.concat is not defined + - inventory_hostname == host_item.host + + - name: Put group key and cert concat {{ item.name }} for host + copy: + content: "{{ cacert_group_certs_concat }}" + dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.{{ host_item.concat | default('pem') }}" + loop: "{{ item.hosts }}" + loop_control: + loop_var: host_item + when: + - host_item.concat is defined + - inventory_hostname == host_item.host + +- name: Distribute group certificates for groups + become: true + when: + - item.groups is defined + - inventory_hostname in groups[cacert_clients_group] + block: - name: CA and cert | Check if dest dir exist on remote host for group file: name: "{{ group_item.path }}" @@ -111,37 +152,7 @@ when: - "inventory_hostname in (groups[group_item.group] | map('extract', hostvars, ['inventory_hostname']) | join(','))" - - name: Put group key for host - copy: - content: "{{ cacert_group_certs_key }}" - dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.key" - loop: "{{ item.hosts }}" - loop_control: - loop_var: host_item - when: - - inventory_hostname == host_item.host - - - name: Put group cert for host - copy: - content: "{{ cacert_group_certs_cert }}" - dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.crt" - loop: "{{ item.hosts }}" - loop_control: - loop_var: host_item - when: - - inventory_hostname == host_item.host - - - name: Put group key and cert concat for host - copy: - content: "{{ cacert_group_certs_concat }}" - dest: "{{ host_item.path }}/{{ host_item.name | default(item.name) }}.{{ host_item.concat | default('pem') }}" - loop: "{{ item.hosts }}" - loop_control: - loop_var: host_item - when: - - inventory_hostname == host_item.host - - - name: Put group key for group + - name: Put group key {{ item.name }} for group copy: content: "{{ cacert_group_certs_key }}" dest: "{{ group_item.path }}/{{ group_item.name | default(item.name) }}.key" @@ -149,9 +160,10 @@ loop_control: loop_var: group_item when: + - group_item.concat is not defined - "inventory_hostname in (groups[group_item.group] | map('extract', hostvars, ['inventory_hostname']) | join(','))" - - name: Put group cert for group + - name: Put group cert {{ item.name }} for group copy: content: "{{ cacert_group_certs_cert }}" dest: "{{ group_item.path }}/{{ group_item.name | default(item.name) }}.crt" @@ -159,9 +171,10 @@ loop_control: loop_var: group_item when: + - group_item.concat is not defined - "inventory_hostname in (groups[group_item.group] | map('extract', hostvars, ['inventory_hostname']) | join(','))" - - name: Put group key and cert concat for group + - name: Put group key and cert concat {{ item.name }} for group copy: content: "{{ cacert_group_certs_concat }}" dest: "{{ group_item.path }}/{{ group_item.name | default(item.name) }}.{{ group_item.concat | default('pem') }}" @@ -169,5 +182,6 @@ loop_control: loop_var: group_item when: + - group_item.concat is defined - "inventory_hostname in (groups[group_item.group] | map('extract', hostvars, ['inventory_hostname']) | join(','))" diff --git a/tasks/main.yaml b/tasks/main.yaml index 1a564ca..ddb50f0 100644 --- a/tasks/main.yaml +++ b/tasks/main.yaml @@ -1,21 +1,23 @@ -- name: CA and certs | Check if ssl gen dir exist - file: - name: "{{ cacert_ssl_gen_path }}" - state: directory - when: - - inventory_hostname in groups[cacert_ca_group] - - name: CA and certs | Include CA include_tasks: ca.yaml - name: CA and certs | Include CN certs include_tasks: cn_certs.yaml loop: "{{ cacert_cn_certs }}" - when: + when: - cacert_cn_certs is defined - name: CA and certs | Include group certs include_tasks: group_certs.yaml loop: "{{ cacert_group_certs }}" - when: + when: - cacert_group_certs is defined + +- name: CA and certs | Include per host certs for group + include_tasks: per_host_for_group_certs.yaml + loop: "{{ cacert_per_host_certs.groups }}" + loop_control: + loop_var: per_host_for_group_item + when: + - cacert_per_host_certs.groups is defined + diff --git a/tasks/per_host_for_group_certs.yaml b/tasks/per_host_for_group_certs.yaml new file mode 100644 index 0000000..fa56afb --- /dev/null +++ b/tasks/per_host_for_group_certs.yaml @@ -0,0 +1,37 @@ +- name: Construct facts for hosts from group {{ groups[item.group] }} + when: inventory_hostname in groups[cacert_ca_group] + block: + - name: Load cacert variables for Koji + include_vars: "{{ lookup('ansible.builtin.first_found', params) }}" + vars: + params: + files: + - "cacert.yaml" + paths: + - "vars" + + - name: Construct facts for hosts from group {{ groups[item.group] }} + set_fact: + iter_hosts: "{{ temp_hosts }}" + vars: + temp_hosts: >- + [{%- for host in groups[per_host_for_group_item.group] -%} + {"name": "{{ host }}", "hosts": [{"host": "{{ host }}", "path": "{{ per_host_for_group_item.path }}"{{ ',"concat": "' ~ per_host_for_group_item.concat ~ '"' if per_host_for_group_item.concat }} }{{ "" if loop.last else "," }}]} + {%- endfor -%}] + delegate_to: "{{ per_host_for_group_fact_item }}" + delegate_facts: true + with_items: + - "{{ groups[cacert_ca_group] | default([]) }}" + - "{{ groups[cacert_clients_group] | default([]) }}" + loop_control: + loop_var: per_host_for_group_fact_item + + - debug: + msg: "{{ iter_hosts }}" + +- name: Create certificate for hosts in {{cn_group_item.group }} + when: inventory_hostname in groups[cacert_clients_group] + block: + - name: Test + include_tasks: cn_certs.yaml + loop: "{{ iter_hosts }}"