From e004236b4cfa9735cc898ea372dcb99c199dd4b4 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 20 Dec 2023 00:12:57 +0100 Subject: rename: x509/ownca to x509/static-ca --- dan/sk-testvm.yml | 10 +- inventory/host_vars/ch-mon.yml | 2 +- inventory/host_vars/sk-testvm.yml | 10 +- roles/x509/ownca/base/tasks/main.yml | 5 - roles/x509/ownca/cert/finalize/tasks/main.yml | 2 - roles/x509/ownca/cert/meta/main.yml | 4 - roles/x509/ownca/cert/prepare/defaults/main.yml | 56 ----------- roles/x509/ownca/cert/prepare/handlers/main.yml | 16 ---- roles/x509/ownca/cert/prepare/tasks/main.yml | 105 --------------------- .../ownca/cert/prepare/templates/updated.sh.j2 | 15 --- roles/x509/ownca/contrib/gen-ca.py | 72 -------------- roles/x509/static-ca/base/tasks/main.yml | 5 + roles/x509/static-ca/cert/finalize/tasks/main.yml | 2 + roles/x509/static-ca/cert/meta/main.yml | 4 + .../x509/static-ca/cert/prepare/defaults/main.yml | 56 +++++++++++ .../x509/static-ca/cert/prepare/handlers/main.yml | 16 ++++ roles/x509/static-ca/cert/prepare/tasks/main.yml | 105 +++++++++++++++++++++ .../static-ca/cert/prepare/templates/updated.sh.j2 | 15 +++ roles/x509/static-ca/contrib/gen-ca.py | 72 ++++++++++++++ 19 files changed, 286 insertions(+), 286 deletions(-) delete mode 100644 roles/x509/ownca/base/tasks/main.yml delete mode 100644 roles/x509/ownca/cert/finalize/tasks/main.yml delete mode 100644 roles/x509/ownca/cert/meta/main.yml delete mode 100644 roles/x509/ownca/cert/prepare/defaults/main.yml delete mode 100644 roles/x509/ownca/cert/prepare/handlers/main.yml delete mode 100644 roles/x509/ownca/cert/prepare/tasks/main.yml delete mode 100644 roles/x509/ownca/cert/prepare/templates/updated.sh.j2 delete mode 100755 roles/x509/ownca/contrib/gen-ca.py create mode 100644 roles/x509/static-ca/base/tasks/main.yml create mode 100644 roles/x509/static-ca/cert/finalize/tasks/main.yml create mode 100644 roles/x509/static-ca/cert/meta/main.yml create mode 100644 roles/x509/static-ca/cert/prepare/defaults/main.yml create mode 100644 roles/x509/static-ca/cert/prepare/handlers/main.yml create mode 100644 roles/x509/static-ca/cert/prepare/tasks/main.yml create mode 100644 roles/x509/static-ca/cert/prepare/templates/updated.sh.j2 create mode 100755 roles/x509/static-ca/contrib/gen-ca.py diff --git a/dan/sk-testvm.yml b/dan/sk-testvm.yml index 88af0dc5..bf7c41dd 100644 --- a/dan/sk-testvm.yml +++ b/dan/sk-testvm.yml @@ -13,10 +13,10 @@ vars: acme_client: uacme # acme_client: acmetool - cert_provider: "{{ acme_client }}" + # cert_provider: "{{ acme_client }}" # cert_provider: static # cert_provider: selfsigned - # cert_provider: ownca + cert_provider: static-ca roles: - role: apt-repo/spreadspace - role: kubernetes/base @@ -32,7 +32,7 @@ template: generic tls: certificate_provider: "{{ cert_provider }}" - certificate_config: "{{ lookup('vars', cert_provider+'_cert_config__default', default={}) }}" + certificate_config: "{{ lookup('vars', (cert_provider | replace('-','_'))+'_cert_config__default', default={}) }}" hsts: no hostnames: - testvm.elev8.at @@ -46,7 +46,7 @@ template: generic tls: certificate_provider: "{{ cert_provider }}" - certificate_config: "{{ lookup('vars', cert_provider+'_cert_config__test', default={}) }}" + certificate_config: "{{ lookup('vars', (cert_provider | replace('-','_'))+'_cert_config__test', default={}) }}" hsts: no hostnames: - login.spreadspace.org @@ -62,7 +62,7 @@ template: generic tls: certificate_provider: "{{ cert_provider }}" - certificate_config: "{{ lookup('vars', cert_provider+'_cert_config__test', default={}) }}" + certificate_config: "{{ lookup('vars', (cert_provider | replace('-','_'))+'_cert_config__test', default={}) }}" hsts: no hostnames: - test.spreadspace.org diff --git a/inventory/host_vars/ch-mon.yml b/inventory/host_vars/ch-mon.yml index de6cc9be..4ede061a 100644 --- a/inventory/host_vars/ch-mon.yml +++ b/inventory/host_vars/ch-mon.yml @@ -263,7 +263,7 @@ monitoring_landingpage_hostnames: - "mon.chaos-at-home.org" monitoring_landingpage_title: "chaos@home Monitoring Host" monitoring_landingpage_tls: - certificate_provider: ownca + certificate_provider: static-ca certificate_config: mode: "0750" owner: root diff --git a/inventory/host_vars/sk-testvm.yml b/inventory/host_vars/sk-testvm.yml index 9a484968..12362457 100644 --- a/inventory/host_vars/sk-testvm.yml +++ b/inventory/host_vars/sk-testvm.yml @@ -412,7 +412,7 @@ selfsigned_cert_config__test: -_ownca_cert_config__common: &ownca_cert_config__common +_static_ca_cert_config__common: &static_ca_cert_config__common ca: key_content: | -----BEGIN RSA PRIVATE KEY----- @@ -497,8 +497,8 @@ _ownca_cert_config__common: &ownca_cert_config__common VcNvbiSZ7MpW/SdanWVaAVxlZS9BAaPozU5V/Rg= -----END CERTIFICATE----- -ownca_cert_config__default: - <<: *ownca_cert_config__common +static_ca_cert_config__default: + <<: *static_ca_cert_config__common cert: organization_name: "elev8" organizational_unit_name: "ansible" @@ -512,8 +512,8 @@ ownca_cert_config__default: create_subject_key_identifier: yes not_after: +1000w -ownca_cert_config__test: - <<: *ownca_cert_config__common +static_ca_cert_config__test: + <<: *static_ca_cert_config__common cert: organization_name: "spreadspace" organizational_unit_name: "ansible" diff --git a/roles/x509/ownca/base/tasks/main.yml b/roles/x509/ownca/base/tasks/main.yml deleted file mode 100644 index e91eda4a..00000000 --- a/roles/x509/ownca/base/tasks/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- name: install needed packages - apt: - name: "{{ python_basename }}-cryptography" - state: present diff --git a/roles/x509/ownca/cert/finalize/tasks/main.yml b/roles/x509/ownca/cert/finalize/tasks/main.yml deleted file mode 100644 index c5b6cafe..00000000 --- a/roles/x509/ownca/cert/finalize/tasks/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# nothing to do here diff --git a/roles/x509/ownca/cert/meta/main.yml b/roles/x509/ownca/cert/meta/main.yml deleted file mode 100644 index 602ee3f8..00000000 --- a/roles/x509/ownca/cert/meta/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -dependencies: - - role: x509/ownca/cert/prepare - - role: x509/ownca/cert/finalize diff --git a/roles/x509/ownca/cert/prepare/defaults/main.yml b/roles/x509/ownca/cert/prepare/defaults/main.yml deleted file mode 100644 index 30241273..00000000 --- a/roles/x509/ownca/cert/prepare/defaults/main.yml +++ /dev/null @@ -1,56 +0,0 @@ ---- -ownca_cert_hostnames: "{{ x509_certificate_hostnames }}" -ownca_cert_name: "{{ x509_certificate_name | default(ownca_cert_hostnames[0]) }}" - -ownca_cert_base_dir: "/etc/ssl" - -ownca_cert_default_renew_margin: "+30d" -ownca_cert_config: "{{ x509_certificate_config }}" -# ownca_cert_config: -# path: "{{ ownca_cert_base_dir }}/{{ ownca_cert_name }}" -# mode: "0750" -# owner: root -# group: www-data -# ca: -# key_content: | -# -----BEGIN RSA PRIVATE KEY----- -# ... -# -----END RSA PRIVATE KEY----- -# cert_content: | -# -----BEGIN CERTIFICATE----- -# ... -# -----END CERTIFICATE----- -# key: -# mode: "0640" -# owner: root -# group: www-data -# type: RSA -# size: 4096 -# cert: -# mode: "0644" -# owner: root -# group: www-data -# common_name: foo -# san_extra: -# - "IP:192.0.2.1" -# country_name: "AT" -# locality_name: "Graz" -# organization_name: "spreadspace" -# organizational_unit_name: "ansible" -# state_or_province_name: "Styria" -# basic_constraints: -# - "CA:TRUE" -# - "pathLenConstraint:0" -# basic_constraints_critical: no -# key_usage: -# - digitalSignature -# - keyAgreement -# key_usage_critical: yes -# extended_key_usage: -# - serverAuth -# extended_key_usage_critical: yes -# create_subject_key_identifier: yes -# digest: SHA256 -# not_before: +0h -# not_after: +520w -# renew_margin: +42d diff --git a/roles/x509/ownca/cert/prepare/handlers/main.yml b/roles/x509/ownca/cert/prepare/handlers/main.yml deleted file mode 100644 index 589d6dde..00000000 --- a/roles/x509/ownca/cert/prepare/handlers/main.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -- name: reload services for x509 certificates - loop: "{{ x509_certificate_reload_services | default([]) }}" - loop_control: - loop_var: x509_certificate_reload_service - service: - name: "{{ x509_certificate_reload_service }}" - state: reloaded - -- name: restart services for x509 certificates - loop: "{{ x509_certificate_restart_services | default([]) }}" - loop_control: - loop_var: x509_certificate_restart_service - service: - name: "{{ x509_certificate_restart_service }}" - state: restarted diff --git a/roles/x509/ownca/cert/prepare/tasks/main.yml b/roles/x509/ownca/cert/prepare/tasks/main.yml deleted file mode 100644 index 00d19c59..00000000 --- a/roles/x509/ownca/cert/prepare/tasks/main.yml +++ /dev/null @@ -1,105 +0,0 @@ ---- -- name: compute path to ownca certificate directory - set_fact: - ownca_cert_path: "{{ ownca_cert_config.path | default([ownca_cert_base_dir, ownca_cert_name] | path_join) }}" - -- name: create directory for ownca certificate - file: - path: "{{ ownca_cert_path }}" - state: directory - mode: "{{ ownca_cert_config.mode | default('0700') }}" - owner: "{{ ownca_cert_config.owner | default(omit) }}" - group: "{{ ownca_cert_config.group | default(omit) }}" - notify: - - reload services for x509 certificates - - restart services for x509 certificates - -- name: generate key for ownca certificate - openssl_privatekey: - path: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-key.pem" - mode: "{{ ownca_cert_config.key.mode | default('0600') }}" - owner: "{{ ownca_cert_config.key.owner | default(omit) }}" - group: "{{ ownca_cert_config.key.group | default(omit) }}" - type: "{{ ownca_cert_config.key.type | default(omit) }}" - size: "{{ ownca_cert_config.key.size | default(omit) }}" - notify: - - reload services for x509 certificates - - restart services for x509 certificates - register: _ownca_key_ - -- name: generate csr for ownca certificate - community.crypto.openssl_csr: - path: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-csr.pem" - mode: "{{ ownca_cert_config.cert.mode | default('0644') }}" - owner: "{{ ownca_cert_config.cert.owner | default(omit) }}" - group: "{{ ownca_cert_config.cert.group | default(omit) }}" - privatekey_path: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-key.pem" - create_subject_key_identifier: "{{ ownca_cert_config.cert.create_subject_key_identifier | default(omit) }}" - digest: "{{ ownca_cert_config.cert.digest | default(omit) }}" - common_name: "{{ ownca_cert_config.cert.common_name | default(ownca_cert_name) }}" - subject_alt_name: "{{ ['DNS:'] | product(ownca_cert_hostnames) | map('join') | union(ownca_cert_config.cert.san_extra | default([])) | list }}" - subject_alt_name_critical: yes - use_common_name_for_san: no - country_name: "{{ ownca_cert_config.cert.country_name | default(omit) }}" - locality_name: "{{ ownca_cert_config.cert.locality_name | default(omit) }}" - organization_name: "{{ ownca_cert_config.cert.organization_name | default(omit) }}" - organizational_unit_name: "{{ ownca_cert_config.cert.organizational_unit_name | default(omit) }}" - state_or_province_name: "{{ ownca_cert_config.cert.state_or_province_name | default(omit) }}" - basic_constraints: "{{ ownca_cert_config.cert.basic_constraints | default(omit) }}" - basic_constraints_critical: "{{ ownca_cert_config.cert.basic_constraints_critical | default(omit) }}" - key_usage: "{{ ownca_cert_config.cert.key_usage | default(omit) }}" - key_usage_critical: "{{ ownca_cert_config.cert.key_usage_critical | default(omit) }}" - extended_key_usage: "{{ ownca_cert_config.cert.extended_key_usage | default(omit) }}" - extended_key_usage_critical: "{{ ownca_cert_config.cert.extended_key_usage_critical | default(omit) }}" - -- name: check if ownca certificate already exists - stat: - path: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-crt.pem" - register: _ownca_cert_file_ - -- name: check validity of existing ownca certificate - when: _ownca_cert_file_.stat.exists - openssl_certificate_info: - path: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-crt.pem" - valid_at: - renew_margin: "{{ ownca_cert_config.cert.renew_margin | default(ownca_cert_default_renew_margin) }}" - register: _ownca_cert_info_ - -- name: generate ownca certificate - community.crypto.x509_certificate: - path: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-crt.pem" - mode: "{{ ownca_cert_config.cert.mode | default('0644') }}" - owner: "{{ ownca_cert_config.cert.owner | default(omit) }}" - group: "{{ ownca_cert_config.cert.group | default(omit) }}" - csr_path: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-csr.pem" - provider: ownca - ownca_content: "{{ ownca_cert_config.ca.cert_content }}" - ownca_privatekey_content: "{{ ownca_cert_config.ca.key_content }}" - ownca_digest: "{{ ownca_cert_config.cert.digest | default(omit) }}" - ownca_not_before: "{{ ownca_cert_config.cert.not_before | default(omit) }}" - ownca_not_after: "{{ ownca_cert_config.cert.not_after | default(omit) }}" - force: "{{ _ownca_cert_file_.stat.exists and (not _ownca_cert_info_.valid_at.renew_margin) }}" - notify: - - reload services for x509 certificates - - restart services for x509 certificates - register: _ownca_cert_ - -- name: export paths to certificate files - set_fact: - x509_certificate_path_key: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-key.pem" - x509_certificate_path_cert: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-crt.pem" - x509_certificate_path_chain: "" - x509_certificate_path_fullchain: "{{ ownca_cert_path }}/{{ ownca_cert_name }}-crt.pem" - -- name: generate custom post-renewal script - when: x509_certificate_renewal is defined - template: - src: updated.sh.j2 - dest: "{{ ownca_cert_path }}/updated.sh" - mode: 0755 - -- name: call custom post-renewal script - when: - - x509_certificate_renewal is defined - - (_ownca_key_ is changed) or (_ownca_cert_ is changed) - command: "{{ ownca_cert_path }}/updated.sh" diff --git a/roles/x509/ownca/cert/prepare/templates/updated.sh.j2 b/roles/x509/ownca/cert/prepare/templates/updated.sh.j2 deleted file mode 100644 index f0757832..00000000 --- a/roles/x509/ownca/cert/prepare/templates/updated.sh.j2 +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -{% if 'install' in x509_certificate_renewal %} -{% for file in x509_certificate_renewal.install %} - -install{% if 'mode' in file %} -m {{ file.mode }}{% endif %}{% if 'owner' in file %} -o {{ file.owner }}{% endif %}{% if 'group' in file %} -g {{ file.group }}{% endif %} /dev/null "{{ file.dest }}.new" -{% for src in file.src %} -cat "{{ lookup('vars', 'x509_certificate_path_' + src) }}" >> "{{ file.dest }}.new" -{% endfor %} -mv "{{ file.dest }}.new" "{{ file.dest }}" -{% endfor %} -{% endif %} -{% if 'reload' in x509_certificate_renewal %} - -{{ x509_certificate_renewal.reload | trim }} -{% endif %} diff --git a/roles/x509/ownca/contrib/gen-ca.py b/roles/x509/ownca/contrib/gen-ca.py deleted file mode 100755 index 8f99da6c..00000000 --- a/roles/x509/ownca/contrib/gen-ca.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 - -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID -import datetime -import argparse - -parser = argparse.ArgumentParser("spreadspace ansible CA-generator") -parser.add_argument("-n", "--variable-name", dest='varname', help="ansible variable name to be used", type=str, required=True) -parser.add_argument("-CN", "--common-name", dest='CN', help="Common Name field of the CA's subject", type=str, required=True) -parser.add_argument("-O", "--organization-name", dest='O', help="Organization Name field of the CA's subject", type=str) -parser.add_argument("-OU", "--organizational-unit", dest='OU', help="Organizational Unit field of the CA's subject", type=str) -parser.add_argument("-C", "--country-name", dest='C', help="Country Name field of the CA's subject", type=str) -parser.add_argument("-ST", "--state-or-provice", dest='ST', help="State-or-Province field of the CA's subject", type=str) -parser.add_argument("-L", "--locality", dest='L', help="Locality Name field of the CA's subject", type=str) -args = parser.parse_args() - -subject_fields = [] -if args.CN: - subject_fields.append(x509.NameAttribute(NameOID.COMMON_NAME, args.CN)) -if args.O: - subject_fields.append(x509.NameAttribute(NameOID.ORGANIZATION_NAME, args.O)) -if args.OU: - subject_fields.append(x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, args.OU)) -if args.C: - subject_fields.append(x509.NameAttribute(NameOID.COUNTRY_NAME, args.C)) -if args.ST: - subject_fields.append(x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, args.ST)) -if args.L: - subject_fields.append(x509.NameAttribute(NameOID.LOCALITY_NAME, args.L)) - -subject = issuer = x509.Name(subject_fields) -private_key = rsa.generate_private_key( - public_exponent=65537, - key_size=4096, -) -public_key = private_key.public_key() - -builder = x509.CertificateBuilder() -builder = builder.subject_name(subject) -builder = builder.issuer_name(issuer) -builder = builder.not_valid_before(datetime.datetime.today() - datetime.timedelta(days=1)) -builder = builder.not_valid_after(datetime.datetime.today() + datetime.timedelta(weeks=2080)) # about 20years -builder = builder.serial_number(x509.random_serial_number()) -builder = builder.public_key(public_key) -builder = builder.add_extension(x509.BasicConstraints(ca=True, path_length=1), critical=True) -certificate = builder.sign(private_key=private_key, algorithm=hashes.SHA256()) - - -private_key_pem = private_key.private_bytes(encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()) -certificate_pem = certificate.public_bytes(serialization.Encoding.PEM) - - -print("## Add this to vault file") -print("") -print("vault_%s_key: |" % (args.varname)) -for line in private_key_pem.splitlines(): - print(" {}".format(line.decode('utf-8'))) - -print("") -print("") -print("") -print("") -print("## Add this to vars file") -print("") -print("%s_key: \"{{ vault_%s_key }}\"" % (args.varname, args.varname)) -print("%s_cert: |" % (args.varname)) -for line in certificate_pem.splitlines(): - print(" {}".format(line.decode('utf-8'))) diff --git a/roles/x509/static-ca/base/tasks/main.yml b/roles/x509/static-ca/base/tasks/main.yml new file mode 100644 index 00000000..e91eda4a --- /dev/null +++ b/roles/x509/static-ca/base/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- name: install needed packages + apt: + name: "{{ python_basename }}-cryptography" + state: present diff --git a/roles/x509/static-ca/cert/finalize/tasks/main.yml b/roles/x509/static-ca/cert/finalize/tasks/main.yml new file mode 100644 index 00000000..c5b6cafe --- /dev/null +++ b/roles/x509/static-ca/cert/finalize/tasks/main.yml @@ -0,0 +1,2 @@ +--- +# nothing to do here diff --git a/roles/x509/static-ca/cert/meta/main.yml b/roles/x509/static-ca/cert/meta/main.yml new file mode 100644 index 00000000..bfaf1153 --- /dev/null +++ b/roles/x509/static-ca/cert/meta/main.yml @@ -0,0 +1,4 @@ +--- +dependencies: + - role: x509/static-ca/cert/prepare + - role: x509/static-ca/cert/finalize diff --git a/roles/x509/static-ca/cert/prepare/defaults/main.yml b/roles/x509/static-ca/cert/prepare/defaults/main.yml new file mode 100644 index 00000000..5287cc93 --- /dev/null +++ b/roles/x509/static-ca/cert/prepare/defaults/main.yml @@ -0,0 +1,56 @@ +--- +static_ca_cert_hostnames: "{{ x509_certificate_hostnames }}" +static_ca_cert_name: "{{ x509_certificate_name | default(static_ca_cert_hostnames[0]) }}" + +static_ca_cert_base_dir: "/etc/ssl" + +static_ca_cert_default_renew_margin: "+30d" +static_ca_cert_config: "{{ x509_certificate_config }}" +# static_ca_cert_config: +# path: "{{ static_ca_cert_base_dir }}/{{ static_ca_cert_name }}" +# mode: "0750" +# owner: root +# group: www-data +# ca: +# key_content: | +# -----BEGIN RSA PRIVATE KEY----- +# ... +# -----END RSA PRIVATE KEY----- +# cert_content: | +# -----BEGIN CERTIFICATE----- +# ... +# -----END CERTIFICATE----- +# key: +# mode: "0640" +# owner: root +# group: www-data +# type: RSA +# size: 4096 +# cert: +# mode: "0644" +# owner: root +# group: www-data +# common_name: foo +# san_extra: +# - "IP:192.0.2.1" +# country_name: "AT" +# locality_name: "Graz" +# organization_name: "spreadspace" +# organizational_unit_name: "ansible" +# state_or_province_name: "Styria" +# basic_constraints: +# - "CA:TRUE" +# - "pathLenConstraint:0" +# basic_constraints_critical: no +# key_usage: +# - digitalSignature +# - keyAgreement +# key_usage_critical: yes +# extended_key_usage: +# - serverAuth +# extended_key_usage_critical: yes +# create_subject_key_identifier: yes +# digest: SHA256 +# not_before: +0h +# not_after: +520w +# renew_margin: +42d diff --git a/roles/x509/static-ca/cert/prepare/handlers/main.yml b/roles/x509/static-ca/cert/prepare/handlers/main.yml new file mode 100644 index 00000000..589d6dde --- /dev/null +++ b/roles/x509/static-ca/cert/prepare/handlers/main.yml @@ -0,0 +1,16 @@ +--- +- name: reload services for x509 certificates + loop: "{{ x509_certificate_reload_services | default([]) }}" + loop_control: + loop_var: x509_certificate_reload_service + service: + name: "{{ x509_certificate_reload_service }}" + state: reloaded + +- name: restart services for x509 certificates + loop: "{{ x509_certificate_restart_services | default([]) }}" + loop_control: + loop_var: x509_certificate_restart_service + service: + name: "{{ x509_certificate_restart_service }}" + state: restarted diff --git a/roles/x509/static-ca/cert/prepare/tasks/main.yml b/roles/x509/static-ca/cert/prepare/tasks/main.yml new file mode 100644 index 00000000..538bb58d --- /dev/null +++ b/roles/x509/static-ca/cert/prepare/tasks/main.yml @@ -0,0 +1,105 @@ +--- +- name: compute path to static-ca certificate directory + set_fact: + static_ca_cert_path: "{{ static_ca_cert_config.path | default([static_ca_cert_base_dir, static_ca_cert_name] | path_join) }}" + +- name: create directory for static-ca certificate + file: + path: "{{ static_ca_cert_path }}" + state: directory + mode: "{{ static_ca_cert_config.mode | default('0700') }}" + owner: "{{ static_ca_cert_config.owner | default(omit) }}" + group: "{{ static_ca_cert_config.group | default(omit) }}" + notify: + - reload services for x509 certificates + - restart services for x509 certificates + +- name: generate key for static-ca certificate + openssl_privatekey: + path: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-key.pem" + mode: "{{ static_ca_cert_config.key.mode | default('0600') }}" + owner: "{{ static_ca_cert_config.key.owner | default(omit) }}" + group: "{{ static_ca_cert_config.key.group | default(omit) }}" + type: "{{ static_ca_cert_config.key.type | default(omit) }}" + size: "{{ static_ca_cert_config.key.size | default(omit) }}" + notify: + - reload services for x509 certificates + - restart services for x509 certificates + register: _static_ca_key_ + +- name: generate csr for static-ca certificate + community.crypto.openssl_csr: + path: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-csr.pem" + mode: "{{ static_ca_cert_config.cert.mode | default('0644') }}" + owner: "{{ static_ca_cert_config.cert.owner | default(omit) }}" + group: "{{ static_ca_cert_config.cert.group | default(omit) }}" + privatekey_path: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-key.pem" + create_subject_key_identifier: "{{ static_ca_cert_config.cert.create_subject_key_identifier | default(omit) }}" + digest: "{{ static_ca_cert_config.cert.digest | default(omit) }}" + common_name: "{{ static_ca_cert_config.cert.common_name | default(static_ca_cert_name) }}" + subject_alt_name: "{{ ['DNS:'] | product(static_ca_cert_hostnames) | map('join') | union(static_ca_cert_config.cert.san_extra | default([])) | list }}" + subject_alt_name_critical: yes + use_common_name_for_san: no + country_name: "{{ static_ca_cert_config.cert.country_name | default(omit) }}" + locality_name: "{{ static_ca_cert_config.cert.locality_name | default(omit) }}" + organization_name: "{{ static_ca_cert_config.cert.organization_name | default(omit) }}" + organizational_unit_name: "{{ static_ca_cert_config.cert.organizational_unit_name | default(omit) }}" + state_or_province_name: "{{ static_ca_cert_config.cert.state_or_province_name | default(omit) }}" + basic_constraints: "{{ static_ca_cert_config.cert.basic_constraints | default(omit) }}" + basic_constraints_critical: "{{ static_ca_cert_config.cert.basic_constraints_critical | default(omit) }}" + key_usage: "{{ static_ca_cert_config.cert.key_usage | default(omit) }}" + key_usage_critical: "{{ static_ca_cert_config.cert.key_usage_critical | default(omit) }}" + extended_key_usage: "{{ static_ca_cert_config.cert.extended_key_usage | default(omit) }}" + extended_key_usage_critical: "{{ static_ca_cert_config.cert.extended_key_usage_critical | default(omit) }}" + +- name: check if static-ca certificate already exists + stat: + path: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-crt.pem" + register: _static_ca_cert_file_ + +- name: check validity of existing static-ca certificate + when: _static_ca_cert_file_.stat.exists + openssl_certificate_info: + path: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-crt.pem" + valid_at: + renew_margin: "{{ static_ca_cert_config.cert.renew_margin | default(static_ca_cert_default_renew_margin) }}" + register: _static_ca_cert_info_ + +- name: generate static-ca certificate + community.crypto.x509_certificate: + path: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-crt.pem" + mode: "{{ static_ca_cert_config.cert.mode | default('0644') }}" + owner: "{{ static_ca_cert_config.cert.owner | default(omit) }}" + group: "{{ static_ca_cert_config.cert.group | default(omit) }}" + csr_path: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-csr.pem" + provider: ownca + ownca_content: "{{ static_ca_cert_config.ca.cert_content }}" + ownca_privatekey_content: "{{ static_ca_cert_config.ca.key_content }}" + ownca_digest: "{{ static_ca_cert_config.cert.digest | default(omit) }}" + ownca_not_before: "{{ static_ca_cert_config.cert.not_before | default(omit) }}" + ownca_not_after: "{{ static_ca_cert_config.cert.not_after | default(omit) }}" + force: "{{ _static_ca_cert_file_.stat.exists and (not _static_ca_cert_info_.valid_at.renew_margin) }}" + notify: + - reload services for x509 certificates + - restart services for x509 certificates + register: _static_ca_cert_ + +- name: export paths to certificate files + set_fact: + x509_certificate_path_key: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-key.pem" + x509_certificate_path_cert: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-crt.pem" + x509_certificate_path_chain: "" + x509_certificate_path_fullchain: "{{ static_ca_cert_path }}/{{ static_ca_cert_name }}-crt.pem" + +- name: generate custom post-renewal script + when: x509_certificate_renewal is defined + template: + src: updated.sh.j2 + dest: "{{ static_ca_cert_path }}/updated.sh" + mode: 0755 + +- name: call custom post-renewal script + when: + - x509_certificate_renewal is defined + - (_static_ca_key_ is changed) or (_static_ca_cert_ is changed) + command: "{{ static_ca_cert_path }}/updated.sh" diff --git a/roles/x509/static-ca/cert/prepare/templates/updated.sh.j2 b/roles/x509/static-ca/cert/prepare/templates/updated.sh.j2 new file mode 100644 index 00000000..f0757832 --- /dev/null +++ b/roles/x509/static-ca/cert/prepare/templates/updated.sh.j2 @@ -0,0 +1,15 @@ +#!/bin/sh +{% if 'install' in x509_certificate_renewal %} +{% for file in x509_certificate_renewal.install %} + +install{% if 'mode' in file %} -m {{ file.mode }}{% endif %}{% if 'owner' in file %} -o {{ file.owner }}{% endif %}{% if 'group' in file %} -g {{ file.group }}{% endif %} /dev/null "{{ file.dest }}.new" +{% for src in file.src %} +cat "{{ lookup('vars', 'x509_certificate_path_' + src) }}" >> "{{ file.dest }}.new" +{% endfor %} +mv "{{ file.dest }}.new" "{{ file.dest }}" +{% endfor %} +{% endif %} +{% if 'reload' in x509_certificate_renewal %} + +{{ x509_certificate_renewal.reload | trim }} +{% endif %} diff --git a/roles/x509/static-ca/contrib/gen-ca.py b/roles/x509/static-ca/contrib/gen-ca.py new file mode 100755 index 00000000..8f99da6c --- /dev/null +++ b/roles/x509/static-ca/contrib/gen-ca.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID +import datetime +import argparse + +parser = argparse.ArgumentParser("spreadspace ansible CA-generator") +parser.add_argument("-n", "--variable-name", dest='varname', help="ansible variable name to be used", type=str, required=True) +parser.add_argument("-CN", "--common-name", dest='CN', help="Common Name field of the CA's subject", type=str, required=True) +parser.add_argument("-O", "--organization-name", dest='O', help="Organization Name field of the CA's subject", type=str) +parser.add_argument("-OU", "--organizational-unit", dest='OU', help="Organizational Unit field of the CA's subject", type=str) +parser.add_argument("-C", "--country-name", dest='C', help="Country Name field of the CA's subject", type=str) +parser.add_argument("-ST", "--state-or-provice", dest='ST', help="State-or-Province field of the CA's subject", type=str) +parser.add_argument("-L", "--locality", dest='L', help="Locality Name field of the CA's subject", type=str) +args = parser.parse_args() + +subject_fields = [] +if args.CN: + subject_fields.append(x509.NameAttribute(NameOID.COMMON_NAME, args.CN)) +if args.O: + subject_fields.append(x509.NameAttribute(NameOID.ORGANIZATION_NAME, args.O)) +if args.OU: + subject_fields.append(x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, args.OU)) +if args.C: + subject_fields.append(x509.NameAttribute(NameOID.COUNTRY_NAME, args.C)) +if args.ST: + subject_fields.append(x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, args.ST)) +if args.L: + subject_fields.append(x509.NameAttribute(NameOID.LOCALITY_NAME, args.L)) + +subject = issuer = x509.Name(subject_fields) +private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=4096, +) +public_key = private_key.public_key() + +builder = x509.CertificateBuilder() +builder = builder.subject_name(subject) +builder = builder.issuer_name(issuer) +builder = builder.not_valid_before(datetime.datetime.today() - datetime.timedelta(days=1)) +builder = builder.not_valid_after(datetime.datetime.today() + datetime.timedelta(weeks=2080)) # about 20years +builder = builder.serial_number(x509.random_serial_number()) +builder = builder.public_key(public_key) +builder = builder.add_extension(x509.BasicConstraints(ca=True, path_length=1), critical=True) +certificate = builder.sign(private_key=private_key, algorithm=hashes.SHA256()) + + +private_key_pem = private_key.private_bytes(encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()) +certificate_pem = certificate.public_bytes(serialization.Encoding.PEM) + + +print("## Add this to vault file") +print("") +print("vault_%s_key: |" % (args.varname)) +for line in private_key_pem.splitlines(): + print(" {}".format(line.decode('utf-8'))) + +print("") +print("") +print("") +print("") +print("## Add this to vars file") +print("") +print("%s_key: \"{{ vault_%s_key }}\"" % (args.varname, args.varname)) +print("%s_cert: |" % (args.varname)) +for line in certificate_pem.splitlines(): + print(" {}".format(line.decode('utf-8'))) -- cgit v1.2.3