From ff1cfedadf3fce870e6a7ea3cde9b84a3e6d9a4e Mon Sep 17 00:00:00 2001 From: nd Date: Sun, 13 Oct 2019 17:49:12 +0200 Subject: [PATCH] initial commit --- README.md | 70 +++++++++++++++++++++++++++++++++++++ defaults/main.yml | 16 +++++++++ tasks/common_cert.yml | 33 +++++++++++++++++ tasks/letsencrypt_cert.yml | 36 +++++++++++++++++++ tasks/letsencrypt_setup.yml | 7 ++++ tasks/main.yml | 24 +++++++++++++ 6 files changed, 186 insertions(+) create mode 100644 README.md create mode 100644 defaults/main.yml create mode 100644 tasks/common_cert.yml create mode 100644 tasks/letsencrypt_cert.yml create mode 100644 tasks/letsencrypt_setup.yml create mode 100644 tasks/main.yml diff --git a/README.md b/README.md new file mode 100644 index 0000000..1395bee --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# Certificates + +This module creates and signs Certificates using multiple backends, including letsencrypt. + +## Parameters + +All configuration is to be placed inside the `certificates` dict. + +``` +# configuration for all backends, see below for options for all backends +backends: + letsencrypt: *letsencrypt-backend-config* + selfsigned: *selfsigned-backend-config* + +# default options for certificates +defaults: + country: "SU" + province: "CYBER" + city: "Cyberspace" + org: "Tyrell Corporation" + mail: "example@example.com" + ou: "cyber" + cn: ~ + san: [] + +# name: certificate name, value: config for a certificate. See below for definition +certs: *certificate-config* +``` + +**certificate-config:** +All settings here overwrite the default setting for a certificate. +``` +# Country (string) +country: "SU" + +# Province (string) +province: "CYBER" + +# City (string) +city: "Cyberspace" + +# Organisation (string) +org: "Tyrell Corporation" + +# Mailaddress (string) +mail: "example@example.com" + +# organizational unit name (string) +ou: "cyber" + +# common name (string), will be set to first SAN if set to None +cn: ~ + +# subject alt names (list of strings) +san: [] +``` + +### Backends +#### Letsencrypt +#### Selfsigned + +## Paths + +Certificates are stored at a defined location: + +* key: `/etc/ssl/private/.key` +* certificate: `/etc/ssl/.crt` +* CSR: `/etc/ssl/.csr` +* chain: `/etc/ssl/.chain.crt` +* key, certificate and chain combined: `/etc/ssl/private/.complete.pem` diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..cf6d3f2 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,16 @@ +certificates: + backends: + letsencrypt: + remainingdays: 28 + challange: dns-01 + challangeserver: [] + selfsigned: ~ + defaults: + country: "SU" + province: "CYBER" + city: "Cyberspace" + org: "Tyrell Corporation" + mail: "example@example.com" + ou: "cyber" + cn: ~ + certs: {} diff --git a/tasks/common_cert.yml b/tasks/common_cert.yml new file mode 100644 index 0000000..90eef42 --- /dev/null +++ b/tasks/common_cert.yml @@ -0,0 +1,33 @@ +- set_fact: + basepath: "/etc/ssl" +- set_fact: + cert_paths: + csrpath: "{{ basepath + '/' + certname + '.csr' }}" + keypath: "{{ basepath + '/private/' + certname + '.key' }}" + certpath: "{{ basepath + '/' + certname + '.crt' }}" + chainpath: "{{ basepath + '/' + certname + '.chain.crt' }}" + fullpath: "{{ basepath + '/private/' + certname + '.complete.pem' }}" +- set_fact: + cert: "{{ certificates.defaults|combine(cert_paths, certificates.certs[certname]|d({}), {'name': certname} ) }}" + +- debug: + verbosity: 1 + var: cert + +- name: "generate key for {{ certname }}" + openssl_privatekey: + path: "{{ cert.keypath }}" + size: 4096 + type: RSA + mode: 0640 + owner: root + group: ssl-cert + +- name: "generate csr for {{ certname }}" + openssl_csr: + path: "{{ cert.csrpath }}" + privatekey_path: "{{ cert.keypath }}" + common_name: "{% if cert.cn %}{{ cert.cn }}{% else %}{{ cert.san[0] }}{% endif %}" + subject_alt_name: "{{ cert.san | map('regex_replace', '^', 'DNS:') | list }}" + register: task_generate_csr + diff --git a/tasks/letsencrypt_cert.yml b/tasks/letsencrypt_cert.yml new file mode 100644 index 0000000..a628495 --- /dev/null +++ b/tasks/letsencrypt_cert.yml @@ -0,0 +1,36 @@ +- include_tasks: common_cert.yml + +- name: "get challange for {{ certname }}" + acme_certificate: &acmetask + force: "{{ task_generate_csr is changed }}" + acme_version: 2 + terms_agreed: yes + acme_directory: "https://acme-v02.api.letsencrypt.org/directory" + account_key: /etc/ssl/letsencrypt_account.key + csr: "{{ cert.csrpath }}" + dest: "{{ cert.certpath }}" + fullchain_dest: "{{ cert.chainpath }}" + remaining_days: "{{ certificates.backends.letsencrypt.remainingdays }}" + challenge: "{{ certificates.backends.letsencrypt.challange }}" + register: challenge + +- name: "setup challenge server for {{ certname }} (dns challange)" + when: + - challenge is changed + - certificates.backends.letsencrypt.challange == "dns-01" + delegate_to: "{{ item.0 }}" + loop: "{{ certificates.backends.letsencrypt.challangeserver|product(challenge.challenge_data.keys()|list)|list }}" + command: + argv: + - "/usr/local/bin/pdns.py" + - "{{ challenge.challenge_data[item.1]['dns-01'].record }}" + - "{{ challenge.challenge_data[item.1]['dns-01'].resource_value }}" + +- name: "setup challenge server for {{ certname }} (http challange)" + debug: msg=a + + +- name: "get certificate {{ certname }}" + acme_certificate: + <<: *acmetask + data: "{{ challenge }}" diff --git a/tasks/letsencrypt_setup.yml b/tasks/letsencrypt_setup.yml new file mode 100644 index 0000000..f2e6833 --- /dev/null +++ b/tasks/letsencrypt_setup.yml @@ -0,0 +1,7 @@ +- name: generate letsencrypt account key + openssl_privatekey: + path: /etc/ssl/letsencrypt_account.key + size: 4096 + owner: root + group: root + mode: 0600 diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..56b9a80 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,24 @@ +- name: install crypto dependencies + apt: + pkg: + - openssl + - python3-openssl + - python3-cryptography + +- name: add group ssl-cert + group: + name: ssl-cert + system: true + +- name: set private folder owner + file: + path: /etc/ssl/private + mode: 0750 + owner: root + group: ssl-cert + +- import_tasks: letsencrypt_setup.yml +- include_tasks: "{{ certificates.certs[certname].backend|default(certificates.defaults.backend) }}_cert.yml" + loop: "{{ certificates.certs.keys()|list }}" + loop_control: + loop_var: certname