created tasks to handle minimal-ca, self-signed and letsencrypt-certbot certificates
This commit is contained in:
parent
84c2277e80
commit
066d95535e
114
README.md
114
README.md
|
|
@ -1,23 +1,28 @@
|
||||||
# Ansible Role for certificate generation
|
# Certificate Generator Role
|
||||||
|
|
||||||
**Tested:**
|
**Tested:**
|
||||||
* Debian 11
|
* Debian 11
|
||||||
|
|
||||||
## Functionality
|
## Functionality
|
||||||
|
|
||||||
* Package installation
|
* **Package installation**
|
||||||
* Ansible dependencies (_minimal_)
|
* Ansible dependencies (_minimal_)
|
||||||
*
|
* Crypto Dependencies
|
||||||
* Configuration
|
|
||||||
* Two Possible Modes
|
|
||||||
* Generate Self-Signed certificate
|
* **Configuration**
|
||||||
* Create an internal-ca and generate certificates using it
|
* **Four Possible Modes**:
|
||||||
* Default config:
|
* Generate **Self-Signed** certificate
|
||||||
|
* Use a **minimal Certificate Authority** to create signed certificates
|
||||||
|
* Configure **LetsEncrypt-Certbot** to generate publicly valid certificates
|
||||||
|
* Supported for Nginx and Apache
|
||||||
|
* Host needs to have a valid public dns record pointed at it
|
||||||
|
* Needs to be publicly reachable over port 80/tcp
|
||||||
|
* _Use a proper **Certificate Authority** (_full PKI_) to create **signed certificates**_ => not yet available
|
||||||
|
|
||||||
|
|
||||||
|
* **Default config**:
|
||||||
* Mode => Self-Signed
|
* Mode => Self-Signed
|
||||||
* Default opt-ins:
|
|
||||||
*
|
|
||||||
* Default opt-outs:
|
|
||||||
*
|
|
||||||
|
|
||||||
|
|
||||||
## Info
|
## Info
|
||||||
|
|
@ -28,6 +33,10 @@
|
||||||
* **Note:** Most of this functionality can be opted in or out using the main defaults file and variables!
|
* **Note:** Most of this functionality can be opted in or out using the main defaults file and variables!
|
||||||
|
|
||||||
|
|
||||||
|
* **Note:** The certificate file-name (_name variable as defined or else CommonName_) will be updated:
|
||||||
|
* spaces are transformed into underlines
|
||||||
|
* all Characters except "0-9a-zA-Z." are removed
|
||||||
|
* the file-extension (_crt/chain.crt/key/csr_) will be appended
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
|
@ -36,20 +45,89 @@
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Define the config as needed:
|
### Notes
|
||||||
|
The **self-signed and minimal-ca** modes will only create a single certificate per run.
|
||||||
|
|
||||||
|
Re-runs can save some overhead by using the 'certs' tag.
|
||||||
|
|
||||||
|
|
||||||
|
The **LetsEncrypt** mode will create/remove multiple certificates as defined.
|
||||||
|
|
||||||
|
|
||||||
|
### Config
|
||||||
|
|
||||||
|
Example for LetsEncrypt config:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
app:
|
certs:
|
||||||
|
mode: 'le_certbot'
|
||||||
|
path: '/etc/apache2/ssl'
|
||||||
|
letsencrypt:
|
||||||
|
certs:
|
||||||
|
myNiceSite:
|
||||||
|
domains: ['myRandomSite.net', 'ansibleguy.net']
|
||||||
|
email: 'certs@template.ansibleguy.net'
|
||||||
|
service: 'apache'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Example for Self-Signed config:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
certs:
|
||||||
|
mode: 'selfsigned'
|
||||||
|
path: '/etc/nginx/ssl'
|
||||||
|
group_key: 'nginx'
|
||||||
|
owner_cert: 'nginx'
|
||||||
|
cert:
|
||||||
|
cn: 'My great certificate!'
|
||||||
|
org: 'AnsibleGuy'
|
||||||
|
country: 'AT'
|
||||||
|
email: 'certs@template.ansibleguy.net'
|
||||||
|
domains: ['mySoGreat.site', 'ansibleguy.net']
|
||||||
|
ips: ['192.168.44.2']
|
||||||
|
pwd: !vault ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Example for minimal-CA config:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
certs:
|
||||||
|
mode: 'ca'
|
||||||
|
path: '/etc/ca/certs'
|
||||||
|
mode_key: '0400'
|
||||||
|
cert:
|
||||||
|
name: 'custom_file_name' # extension will be appended
|
||||||
|
cn: 'My great certificate!'
|
||||||
|
org: 'AnsibleGuy'
|
||||||
|
country: 'AT'
|
||||||
|
email: 'certs@template.ansibleguy.net'
|
||||||
|
domains: ['mySoGreat.site', 'ansibleguy.net']
|
||||||
|
ca:
|
||||||
|
path: '/etc/ca'
|
||||||
|
cn: 'SUPER CertificateAuthority'
|
||||||
|
org: 'AnsibleGuy'
|
||||||
|
country: 'AT'
|
||||||
|
email: 'certs@template.ansibleguy.net'
|
||||||
|
pwd: !vault ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the minimal-CA you can create multiple certificates signed by the CA by re-running the role with changed 'cert' settings.
|
||||||
|
|
||||||
|
|
||||||
|
You might want to use 'ansible-vault' to encrypt your passwords:
|
||||||
|
```bash
|
||||||
|
ansible-vault encrypt_string
|
||||||
|
```
|
||||||
|
|
||||||
|
### Execution
|
||||||
|
|
||||||
Run the playbook:
|
Run the playbook:
|
||||||
```bash
|
```bash
|
||||||
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml
|
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml --ask-vault-pass
|
||||||
```
|
```
|
||||||
|
|
||||||
There are also some useful **tags** available:
|
There are also some useful **tags** available:
|
||||||
* base => only configure basics; sites will not be touched
|
* certs => ignore ca tasks; only generate certs
|
||||||
* sites
|
* selfsigned
|
||||||
* config
|
* config
|
||||||
* certs
|
* certs
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,85 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
# default config => is overwritten by provided config
|
# default config => is overwritten by provided config
|
||||||
default_app: {}
|
default_certs:
|
||||||
|
mode: 'selfsigned' # selfsigned, ca, ca_min, le_certbot
|
||||||
|
path: '/etc/certs'
|
||||||
|
|
||||||
APP_CONFIG: "{{ default_app | combine(app, recursive=true) }}"
|
cert:
|
||||||
|
name:
|
||||||
|
key_size: 4096 # 1024, 2048, 4096
|
||||||
|
key_type: 'RSA'
|
||||||
|
cipher: 'AES-256-CBC' # see: 'openssl list -cipher-algorithms'
|
||||||
|
digest: 'sha256'
|
||||||
|
regenerate: 'partial_idempotence'
|
||||||
|
pwd:
|
||||||
|
domains: []
|
||||||
|
ips: []
|
||||||
|
|
||||||
default_instance_config: {}
|
# certificate config
|
||||||
|
cn: 'Ansible Certificate'
|
||||||
|
org:
|
||||||
|
ou:
|
||||||
|
country:
|
||||||
|
state:
|
||||||
|
locality:
|
||||||
|
email: # if using letsencrypt you might pass an email per domain => see letsencrypt-certs
|
||||||
|
key_usage: 'serverAuth' # serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, ocspSigning
|
||||||
|
ocsp_staple: false
|
||||||
|
crl_distribution: []
|
||||||
|
# - full_name:
|
||||||
|
# - "URI:https://ca.example.com/revocations.crl"
|
||||||
|
# crl_issuer:
|
||||||
|
# - "URI:https://ca.example.com/"
|
||||||
|
# reasons:
|
||||||
|
# - key_compromise
|
||||||
|
# - ca_compromise
|
||||||
|
# - cessation_of_operation
|
||||||
|
valid_days: 730
|
||||||
|
|
||||||
|
mode_key: '0640'
|
||||||
|
mode_cert: '0644'
|
||||||
|
|
||||||
|
owner_key: 'root'
|
||||||
|
group_key: 'root'
|
||||||
|
owner_cert: 'root'
|
||||||
|
group_cert: 'root'
|
||||||
|
|
||||||
|
extension_cert: 'crt'
|
||||||
|
extension_key: 'key'
|
||||||
|
extension_csr: 'csr'
|
||||||
|
|
||||||
|
letsencrypt:
|
||||||
|
path: '/etc/letsencrypt'
|
||||||
|
service: # apache, nginx
|
||||||
|
renew_timer: 'Mon *-*-* 01:00:00'
|
||||||
|
verbosity: 'v'
|
||||||
|
certs: {} # see 'default_le_certbot_cert_config'
|
||||||
|
renew: false # if a renewal should be started by the role; the renewal service will auto-renew the certificates otherwise
|
||||||
|
|
||||||
|
ca:
|
||||||
|
path: '/etc/certs/ca'
|
||||||
|
valid_days: 7300
|
||||||
|
key_size: 8192 # 1024, 2048, 4096, 8192
|
||||||
|
key_type: 'RSA'
|
||||||
|
cipher: 'AES-256-CBC' # see: 'openssl list -cipher-algorithms'
|
||||||
|
digest: 'sha512'
|
||||||
|
regenerate: 'partial_idempotence'
|
||||||
|
pwd:
|
||||||
|
|
||||||
|
# certificate config
|
||||||
|
cn: 'CA Certificate'
|
||||||
|
org:
|
||||||
|
ou:
|
||||||
|
country:
|
||||||
|
state:
|
||||||
|
locality:
|
||||||
|
email:
|
||||||
|
|
||||||
|
|
||||||
|
CERT_CONFIG: "{{ default_certs | combine(certs, recursive=true) }}"
|
||||||
|
|
||||||
|
default_le_certbot_cert_config:
|
||||||
|
domains: []
|
||||||
|
state: 'present'
|
||||||
|
email:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from re import sub as regex_replace
|
from re import sub as regex_replace
|
||||||
|
from re import match as regex_match
|
||||||
|
|
||||||
|
|
||||||
class FilterModule(object):
|
class FilterModule(object):
|
||||||
|
|
@ -6,16 +7,35 @@ class FilterModule(object):
|
||||||
def filters(self):
|
def filters(self):
|
||||||
return {
|
return {
|
||||||
"safe_key": self.safe_key,
|
"safe_key": self.safe_key,
|
||||||
"fallback": self.fallback,
|
"valid_domain": self.valid_domain,
|
||||||
|
"valid_ip": self.valid_ip,
|
||||||
|
"check_email": self.check_email,
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def safe_key(key: str) -> str:
|
def safe_key(key: str) -> str:
|
||||||
return regex_replace('[^0-9a-zA-Z]+', '', key.replace(' ', '_'))
|
return regex_replace(r'[^0-9a-zA-Z\.]+', '', key.replace(' ', '_'))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fallback(opt1: str, opt2: str) -> str:
|
def valid_domain(domain: str) -> bool:
|
||||||
if opt1 not in [None, '', 'None', 'none', ' ']:
|
expr = r'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$'
|
||||||
return opt1
|
return True if regex_match(expr, domain) is not None else False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def valid_ip(ip: str) -> bool:
|
||||||
|
expr_ipv4 = r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$'
|
||||||
|
expr_ipv6 = r'^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$'
|
||||||
|
|
||||||
|
if regex_match(expr_ipv4, ip) is not None or regex_match(expr_ipv6, ip) is not None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_email(certs: dict) -> bool:
|
||||||
|
for settings in certs.values():
|
||||||
|
if 'email' not in settings or settings['email'] in ['', ' ', None, 'null', 'None']:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
return opt2
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
# ansible-playbook -K -D -i inventory/hosts.yml playbook.yml
|
# ansible-playbook -K -D -i inventory/hosts.yml playbook.yml --ask-vault-pass
|
||||||
|
|
||||||
- hosts: all # should be limited
|
- hosts: localhost # should be limited
|
||||||
become: true
|
become: true
|
||||||
gather_facts: yes
|
gather_facts: yes
|
||||||
roles:
|
roles:
|
||||||
- ansibleguy.ROLE
|
- ansibleguy.infra_certs
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
# install: ansible-galaxy install -r requirements.yml
|
# install: ansible-galaxy install -r requirements.yml
|
||||||
|
|
||||||
collections: []
|
collections: []
|
||||||
# - name: 'community.general'
|
- name: 'community.crypto'
|
||||||
# source: 'https://galaxy.ansible.com'
|
source: 'https://galaxy.ansible.com'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
# creating ca with full pki
|
||||||
|
# to be continued (;
|
||||||
|
|
||||||
|
- name: Certificates | Debian | Internal | CA | Not yet implemented
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "The certificate mode 'ca_full' is not yet implemented!"
|
||||||
|
tags: ca
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Apache | Install package
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: ['python3-certbot-apache']
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Apache | Checking sites
|
||||||
|
ansible.builtin.shell: 'ls /etc/apache2/sites-enabled/'
|
||||||
|
changed_when: false
|
||||||
|
register: enabled_apache_sites
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Apache | Deploying temporary apache site
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: 'templates/etc/apache2/sites-enabled/le_dummy.conf.j2'
|
||||||
|
dest: '/etc/apache2/sites-enabled/tmp_le_dummy.conf'
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: 0644
|
||||||
|
register: tmp_site_enable
|
||||||
|
when: enabled_apache_sites.stdout == ''
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Apache | Reloading apache
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: 'apache2.service'
|
||||||
|
state: reloaded
|
||||||
|
when:
|
||||||
|
- enabled_apache_sites.stdout == ''
|
||||||
|
- tmp_site_enable.changed
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Apache | Disable temporary site
|
||||||
|
ansible.builtin.file:
|
||||||
|
state: absent
|
||||||
|
path: '/etc/apache2/sites-enabled/tmp_le_dummy.conf'
|
||||||
|
register: tmp_site_disable
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Apache | Reloading apache
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: 'apache2.service'
|
||||||
|
state: reloaded
|
||||||
|
when: tmp_site_disable.changed
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Deploying temporary apache site
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: 'templates/etc/apache2/sites-available/le_dummy.conf.j2'
|
||||||
|
dest: '/etc/apache2/sites-available/tmp_le_dummy.conf'
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Enable apache site
|
||||||
|
ansible.builtin.file:
|
||||||
|
state: link
|
||||||
|
src: '/etc/apache2/sites-available/tmp_le_dummy.conf'
|
||||||
|
dest: '/etc/apache2/sites-enabled/tmp_le_dummy.conf'
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Apache | Debian | LetsEncrypt Certbot | Dependencies | Reload apache
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: 'apache2.service'
|
||||||
|
state: reloaded
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
# todo: check domains registered in current certificate (certbot certificates) and remove it if there are more than configured before re-configuring it
|
||||||
|
|
||||||
|
- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Creating directory"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ le_path }}"
|
||||||
|
state: directory
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: 0755
|
||||||
|
|
||||||
|
- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Command to be executed"
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "certbot certonly --non-interactive --agree-tos --no-redirect
|
||||||
|
--{{ CERT_CONFIG.letsencrypt.service }} --cert-name {{ le_name }}
|
||||||
|
-{{ CERT_CONFIG.letsencrypt.verbosity }}
|
||||||
|
--rsa-key-size {{ le_cert.key_size | default(CERT_CONFIG.cert.key_size, true) }}
|
||||||
|
--config-dir {{ CERT_CONFIG.letsencrypt.path }}
|
||||||
|
{% for domain in le_cert.domains %}{% if domain | valid_domain %}--domain {{ domain }} {% endif %}{% endfor %}
|
||||||
|
{% if le_cert.email is not none %}--email {{ le_cert.email }} {% elif CERT_CONFIG.cert.email | default(none, true) is not none %}--email {{ CERT_CONFIG.cert.email }} {% endif %}"
|
||||||
|
when: existing_certs_raw.stdout.find(name) == -1
|
||||||
|
|
||||||
|
- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Starting certbot"
|
||||||
|
ansible.builtin.command: "certbot certonly --non-interactive --agree-tos --no-redirect
|
||||||
|
--{{ CERT_CONFIG.letsencrypt.service }} --cert-name {{ le_name }}
|
||||||
|
-{{ CERT_CONFIG.letsencrypt.verbosity }}
|
||||||
|
--rsa-key-size {{ le_cert.key_size | default(CERT_CONFIG.cert.key_size, true) }}
|
||||||
|
--config-dir {{ CERT_CONFIG.letsencrypt.path }}
|
||||||
|
{% for domain in le_cert.domains %}{% if domain | valid_domain %}--domain {{ domain }} {% endif %}{% endfor %}
|
||||||
|
{% if le_cert.email is not none %}--email {{ le_cert.email }} {% elif CERT_CONFIG.cert.email | default(none, true) is not none %}--email {{ CERT_CONFIG.cert.email }} {% endif %}"
|
||||||
|
when: existing_certs_raw.stdout.find(name) == -1
|
||||||
|
|
||||||
|
- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Linking cert"
|
||||||
|
ansible.builtin.file:
|
||||||
|
state: link
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ item.dst }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
follow: true
|
||||||
|
force: true
|
||||||
|
loop:
|
||||||
|
- {'dst': "{{ CERT_CONFIG.path }}/{{ le_name }}.{{ CERT_CONFIG.extension_cert }}", 'src': "{{ le_path }}/cert.pem"}
|
||||||
|
- {'dst': "{{ CERT_CONFIG.path }}/{{ le_name }}.chain.{{ CERT_CONFIG.extension_cert }}", 'src': "{{ le_path }}/chain.pem"}
|
||||||
|
- {'dst': "{{ CERT_CONFIG.path }}/{{ le_name }}.fullchain.{{ CERT_CONFIG.extension_cert }}", 'src': "{{ le_path }}/fullchain.pem"}
|
||||||
|
|
||||||
|
- name: "Certificates | Debian | LetsEncrypt Certbot | {{ le_name }} | Linking key"
|
||||||
|
ansible.builtin.file:
|
||||||
|
state: link
|
||||||
|
src: "{{ le_path }}/privkey.pem"
|
||||||
|
dest: "{{ CERT_CONFIG.path }}/{{ le_name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_key }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_key }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_key }}"
|
||||||
|
follow: true
|
||||||
|
force: true
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Checking config
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "The required configuration was not provided!
|
||||||
|
Needed: 'certs.letsencrypt.certs', 'certs.letsencrypt.service',
|
||||||
|
'certs.letsencrypt.email or certs.letsencrypt.email.certs.email'"
|
||||||
|
when: >
|
||||||
|
CERT_CONFIG.letsencrypt.certs | length == 0 or
|
||||||
|
CERT_CONFIG.letsencrypt.service is none | default(none, true) or
|
||||||
|
(CERT_CONFIG.letsencrypt.email | default(none, true) is none and not CERT_CONFIG.letsencrypt.certs|check_email)
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Checking service
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: "You need to supply a supported LetsEncrypt Certbot service to use! (apache/nginx)"
|
||||||
|
when: "CERT_CONFIG.letsencrypt.service | default(none, true) is none or CERT_CONFIG.letsencrypt.service not in ['apache', 'nginx']"
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Configure for Apache2
|
||||||
|
ansible.builtin.import_tasks: apache.yml
|
||||||
|
when: CERT_CONFIG.letsencrypt.service == 'apache'
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Configure for Nginx
|
||||||
|
ansible.builtin.import_tasks: nginx.yml
|
||||||
|
when: CERT_CONFIG.letsencrypt.service == 'nginx'
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Pulling existing certs
|
||||||
|
ansible.builtin.shell: 'certbot certificates'
|
||||||
|
register: existing_certs_raw
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Adding certificates
|
||||||
|
ansible.builtin.include_tasks: cert.yml
|
||||||
|
when:
|
||||||
|
- le_cert.domains | length > 0
|
||||||
|
- le_cert.state == 'present'
|
||||||
|
vars:
|
||||||
|
le_cert: "{{ default_le_certbot_cert_config | combine(cert_item.value, recursive=true) }}"
|
||||||
|
le_name: "{{ cert_item.key | safe_key }}"
|
||||||
|
le_path: "{{ CERT_CONFIG.letsencrypt.path }}/live/{{ name }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: cert_item
|
||||||
|
with_dict: "{{ CERT_CONFIG.letsencrypt.certs }}"
|
||||||
|
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Removing certificates
|
||||||
|
ansible.builtin.command: "certbot revoke --cert-name {{ le_name }} && certbot delete --cert-name {{ le_name }}"
|
||||||
|
when:
|
||||||
|
- le_cert.state != 'present'
|
||||||
|
- existing_certs_raw.stdout.find(le_name) != -1
|
||||||
|
vars:
|
||||||
|
le_cert: "{{ default_le_certbot_cert_config | combine(cert_item.value, recursive=true) }}"
|
||||||
|
le_name: "{{ cert_item.key | safe_key }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: cert_item
|
||||||
|
with_dict: "{{ CERT_CONFIG.letsencrypt.certs }}"
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Cleanup for Apache2
|
||||||
|
ansible.builtin.import_tasks: apache_cleanup.yml
|
||||||
|
when: CERT_CONFIG.letsencrypt.service == 'apache'
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Cleanup for Nginx
|
||||||
|
ansible.builtin.import_tasks: nginx_cleanup.yml
|
||||||
|
when: CERT_CONFIG.letsencrypt.service == 'nginx'
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Adding service for certbot renewal
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "templates/etc/systemd/system/{{ item }}.j2"
|
||||||
|
dest: "/etc/systemd/system/{{ item }}"
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: 0644
|
||||||
|
with_items:
|
||||||
|
- 'ansibleguy.infra_certs.LetsEncryptCertbot.service'
|
||||||
|
- 'ansibleguy.infra_certs.LetsEncryptCertbot.timer'
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Enabling cert-renewal timer
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
name: 'ansibleguy.infra_certs.LetsEncryptCertbot.timer'
|
||||||
|
enabled: yes
|
||||||
|
state: started
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Running renewal
|
||||||
|
ansible.builtin.command: 'certbot renew --force-renewal'
|
||||||
|
when: CERT_CONFIG.letsencrypt.renew
|
||||||
|
ignore_errors: true
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Install package
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: ['python3-certbot-nginx']
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Checking sites
|
||||||
|
ansible.builtin.shell: 'ls /etc/nginx/sites-enabled/'
|
||||||
|
changed_when: false
|
||||||
|
register: enabled_nginx_sites
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Deploying temporary apache site
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: 'templates/etc/nginx/sites-enabled/le_dummy.j2'
|
||||||
|
dest: '/etc/nginx/sites-enabled/tmp_le_dummy'
|
||||||
|
owner: 'root'
|
||||||
|
group: 'root'
|
||||||
|
mode: 0644
|
||||||
|
register: tmp_site_enable
|
||||||
|
when: enabled_nginx_sites.stdout == ''
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Reloading apache
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: 'nginx.service'
|
||||||
|
state: reloaded
|
||||||
|
when:
|
||||||
|
- enabled_nginx_sites.stdout == ''
|
||||||
|
- tmp_site_enable.changed
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Disable temporary site
|
||||||
|
ansible.builtin.file:
|
||||||
|
state: absent
|
||||||
|
path: '/etc/nginx/sites-enabled/tmp_le_dummy'
|
||||||
|
register: tmp_site_disable
|
||||||
|
|
||||||
|
- name: Certificates | Debian | LetsEncrypt Certbot | Nginx | Reloading apache
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: 'nginx.service'
|
||||||
|
state: reloaded
|
||||||
|
when: tmp_site_disable.changed
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
|
|
||||||
- name: ROLE | Debian | Task
|
|
||||||
ansible.builtin.apt:
|
|
||||||
pkg: "{{ something }}"
|
|
||||||
tags: [base]
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
# creating a minimal ca
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Minimal CA | Creating ca directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ CERT_CONFIG.ca.path }}"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Minimal CA | Generate ca private key (encrypted key)
|
||||||
|
community.crypto.openssl_privatekey:
|
||||||
|
path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
passphrase: "{{ CERT_CONFIG.ca.pwd }}"
|
||||||
|
cipher: "{{ CERT_CONFIG.ca.cipher }}"
|
||||||
|
size: "{{ CERT_CONFIG.ca.key_size }}"
|
||||||
|
type: "{{ CERT_CONFIG.ca.key_type }}"
|
||||||
|
regenerate: "{{ CERT_CONFIG.ca.regenerate }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_key }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_key }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_key }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.ca.pwd | default(none, true) is not none
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Minimal CA | Generate ca private key (plain key)
|
||||||
|
community.crypto.openssl_privatekey:
|
||||||
|
path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
size: "{{ CERT_CONFIG.ca.key_size }}"
|
||||||
|
type: "{{ CERT_CONFIG.ca.key_type }}"
|
||||||
|
regenerate: "{{ CERT_CONFIG.ca.regenerate }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_key }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_key }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_key }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.ca.pwd | default(none, true) is none
|
||||||
|
|
||||||
|
# NOTE: for details see https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Minimal CA | Generating ca signing-request (encrypted key)
|
||||||
|
community.crypto.openssl_csr:
|
||||||
|
path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
privatekey_passphrase: "{{ CERT_CONFIG.ca.pwd }}"
|
||||||
|
basic_constraints: ['CA:TRUE', 'pathlen:2']
|
||||||
|
basic_constraints_critical: true
|
||||||
|
key_usage: ['cRLSign', 'digitalSignature', 'keyCertSign']
|
||||||
|
key_usage_critical: true
|
||||||
|
digest: "{{ CERT_CONFIG.ca.digest }}"
|
||||||
|
common_name: "{{ CERT_CONFIG.ca.cn }}"
|
||||||
|
organization_name: "{{ CERT_CONFIG.ca.org }}"
|
||||||
|
country_name: "{{ CERT_CONFIG.ca.country }}"
|
||||||
|
state_or_province_name: "{{ CERT_CONFIG.ca.state }}"
|
||||||
|
locality_name: "{{ CERT_CONFIG.ca.locality }}"
|
||||||
|
email_address: "{{ CERT_CONFIG.ca.email }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.ca.pwd | default(none, true) is not none
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Minimal CA | Generating ca signing-request (plain key)
|
||||||
|
community.crypto.openssl_csr:
|
||||||
|
path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
basic_constraints: ['CA:TRUE', 'pathlen:2']
|
||||||
|
basic_constraints_critical: true
|
||||||
|
key_usage: ['cRLSign', 'digitalSignature', 'keyCertSign']
|
||||||
|
key_usage_critical: true
|
||||||
|
digest: "{{ CERT_CONFIG.ca.digest }}"
|
||||||
|
common_name: "{{ CERT_CONFIG.ca.cn }}"
|
||||||
|
organization_name: "{{ CERT_CONFIG.ca.org }}"
|
||||||
|
country_name: "{{ CERT_CONFIG.ca.country }}"
|
||||||
|
state_or_province_name: "{{ CERT_CONFIG.ca.state }}"
|
||||||
|
locality_name: "{{ CERT_CONFIG.ca.locality }}"
|
||||||
|
email_address: "{{ CERT_CONFIG.ca.email }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.ca.pwd | default(none, true) is none
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Minimal CA | Generating ca certificate (encrypted key)
|
||||||
|
community.crypto.x509_certificate:
|
||||||
|
path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
csr_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
privatekey_passphrase: "{{ CERT_CONFIG.ca.pwd }}"
|
||||||
|
provider: selfsigned
|
||||||
|
valid_in: "{{ CERT_CONFIG.ca.valid_days }}d"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.ca.pwd | default(none, true) is not none
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Minimal CA | Generating ca certificate (plain key)
|
||||||
|
community.crypto.x509_certificate:
|
||||||
|
path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
csr_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
provider: selfsigned
|
||||||
|
valid_in: "{{ CERT_CONFIG.ca.valid_days }}d"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.ca.pwd | default(none, true) is none
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | Generate private key (encrypted)
|
||||||
|
community.crypto.openssl_privatekey:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
cipher: "{{ CERT_CONFIG.cert.cipher }}"
|
||||||
|
size: "{{ CERT_CONFIG.cert.key_size }}"
|
||||||
|
type: "{{ CERT_CONFIG.cert.key_type }}"
|
||||||
|
passphrase: "{{ CERT_CONFIG.cert.pwd }}"
|
||||||
|
regenerate: "{{ CERT_CONFIG.cert.regenerate }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_key }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_key }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_key }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.cert.pwd | default(none, true) is not none
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | Generate private key (plain)
|
||||||
|
community.crypto.openssl_privatekey:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
size: "{{ CERT_CONFIG.cert.key_size }}"
|
||||||
|
type: "{{ CERT_CONFIG.cert.key_type }}"
|
||||||
|
regenerate: "{{ CERT_CONFIG.cert.regenerate }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_key }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_key }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_key }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.cert.pwd | default(none, true) is none
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | Setting SAN
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
cert_san: "{% for domain in CERT_CONFIG.cert.domains %}
|
||||||
|
{% if domain | valid_domain %}DNS:{{ domain }}{% if not loop.last %},{% endif %}{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for ip in CERT_CONFIG.cert.ips %}
|
||||||
|
{% if ip | valid_ip %},IP:{{ ip }}{% endif %}
|
||||||
|
{% endfor %}"
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | Generating signing-request (encrypted key)
|
||||||
|
community.crypto.openssl_csr:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
privatekey_passphrase : "{{ CERT_CONFIG.cert.pwd }}"
|
||||||
|
digest: "{{ CERT_CONFIG.cert.digest }}"
|
||||||
|
common_name: "{{ CERT_CONFIG.cert.cn }}"
|
||||||
|
organization_name: "{{ CERT_CONFIG.cert.org }}"
|
||||||
|
country_name: "{{ CERT_CONFIG.cert.country }}"
|
||||||
|
state_or_province_name: "{{ CERT_CONFIG.cert.state }}"
|
||||||
|
locality_name: "{{ CERT_CONFIG.cert.locality }}"
|
||||||
|
email_address: "{{ CERT_CONFIG.cert.email }}"
|
||||||
|
extended_key_usage: "{{ CERT_CONFIG.cert.key_usage }}"
|
||||||
|
ocsp_must_staple: "{{ CERT_CONFIG.cert.ocsp_staple }}"
|
||||||
|
crl_distribution_points: "{{ CERT_CONFIG.cert.crl_distribution }}"
|
||||||
|
subject_alt_name: "{{ cert_san | replace(' ', '') | default('DNS:localhost', true) }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.cert.pwd | default(none, true) is not none
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | Generating signing-request (plain key)
|
||||||
|
community.crypto.openssl_csr:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
digest: "{{ CERT_CONFIG.cert.digest }}"
|
||||||
|
common_name: "{{ CERT_CONFIG.cert.cn }}"
|
||||||
|
organization_name: "{{ CERT_CONFIG.cert.org }}"
|
||||||
|
country_name: "{{ CERT_CONFIG.cert.country }}"
|
||||||
|
state_or_province_name: "{{ CERT_CONFIG.cert.state }}"
|
||||||
|
locality_name: "{{ CERT_CONFIG.cert.locality }}"
|
||||||
|
email_address: "{{ CERT_CONFIG.cert.email }}"
|
||||||
|
extended_key_usage: "{{ CERT_CONFIG.cert.key_usage }}"
|
||||||
|
ocsp_must_staple: "{{ CERT_CONFIG.cert.ocsp_staple }}"
|
||||||
|
crl_distribution_points: "{{ CERT_CONFIG.cert.crl_distribution }}"
|
||||||
|
subject_alt_name: "{{ cert_san | replace(' ', '') | default('DNS:localhost', true) }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when: CERT_CONFIG.cert.pwd | default(none, true) is none
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | Self-Signed | Generating certificate (encrypted key)
|
||||||
|
community.crypto.x509_certificate:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
privatekey_passphrase: "{{ CERT_CONFIG.cert.pwd }}"
|
||||||
|
csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
provider: selfsigned
|
||||||
|
valid_in: "{{ CERT_CONFIG.cert.valid_days }}d"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- CERT_CONFIG.cert.pwd | default(none, true) is not none
|
||||||
|
- CERT_CONFIG.mode == 'selfsigned'
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | Self-Signed | Generating certificate (plain key)
|
||||||
|
community.crypto.x509_certificate:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
provider: selfsigned
|
||||||
|
valid_in: "{{ CERT_CONFIG.cert.valid_days }}d"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- CERT_CONFIG.cert.pwd | default(none, true) is none
|
||||||
|
- CERT_CONFIG.mode == 'selfsigned'
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | CA-Signed | Generating certificate (encrypted key; encrypted ca-key)
|
||||||
|
community.crypto.x509_certificate:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
privatekey_passphrase: "{{ CERT_CONFIG.cert.pwd }}"
|
||||||
|
csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
valid_in: "{{ CERT_CONFIG.cert.valid_days }}d"
|
||||||
|
provider: ownca
|
||||||
|
ownca_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
ownca_privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
ownca_privatekey_passphrase: "{{ CERT_CONFIG.ca.pwd }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- CERT_CONFIG.ca.pwd | default(none, true) is not none
|
||||||
|
- CERT_CONFIG.cert.pwd | default(none, true) is not none
|
||||||
|
- CERT_CONFIG.mode == 'ca'
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | CA-Signed | Generating certificate (plain key; encrypted ca-key)
|
||||||
|
community.crypto.x509_certificate:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
valid_in: "{{ CERT_CONFIG.cert.valid_days }}d"
|
||||||
|
provider: ownca
|
||||||
|
ownca_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
ownca_privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
ownca_privatekey_passphrase: "{{ CERT_CONFIG.ca.pwd }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- CERT_CONFIG.ca.pwd | default(none, true) is not none
|
||||||
|
- CERT_CONFIG.cert.pwd | default(none, true) is none
|
||||||
|
- CERT_CONFIG.mode == 'ca'
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | CA-Signed | Generating certificate (encrypted key; plain ca-key)
|
||||||
|
community.crypto.x509_certificate:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
privatekey_passphrase: "{{ CERT_CONFIG.cert.pwd }}"
|
||||||
|
csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
valid_in: "{{ CERT_CONFIG.cert.valid_days }}d"
|
||||||
|
provider: ownca
|
||||||
|
ownca_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
ownca_privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- CERT_CONFIG.ca.pwd | default(none, true) is none
|
||||||
|
- CERT_CONFIG.cert.pwd | default(none, true) is not none
|
||||||
|
- CERT_CONFIG.mode == 'ca'
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | CA-Signed | Generating certificate (plain key; plain ca-key)
|
||||||
|
community.crypto.x509_certificate:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
privatekey_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
csr_path: "{{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_csr }}"
|
||||||
|
valid_in: "{{ CERT_CONFIG.cert.valid_days }}d"
|
||||||
|
provider: ownca
|
||||||
|
ownca_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
ownca_privatekey_path: "{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_key }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- CERT_CONFIG.ca.pwd | default(none, true) is none
|
||||||
|
- CERT_CONFIG.cert.pwd | default(none, true) is none
|
||||||
|
- CERT_CONFIG.mode == 'ca'
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | CA-Signed | Creating chained certificate
|
||||||
|
ansible.builtin.shell: "cat {{ CERT_CONFIG.path }}/{{ name }}.{{ CERT_CONFIG.extension_cert }}
|
||||||
|
{{ CERT_CONFIG.ca.path }}/ca.{{ CERT_CONFIG.extension_cert }} >
|
||||||
|
{{ CERT_CONFIG.path }}/{{ name }}.chain.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
args:
|
||||||
|
creates: "{{ CERT_CONFIG.path }}/{{ name }}.chain.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
when: CERT_CONFIG.mode == 'ca'
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert | CA-Signed | Setting privileges on chained certificate
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ CERT_CONFIG.path }}/{{ name }}.chain.{{ CERT_CONFIG.extension_cert }}"
|
||||||
|
mode: "{{ CERT_CONFIG.mode_cert }}"
|
||||||
|
owner: "{{ CERT_CONFIG.owner_cert }}"
|
||||||
|
group: "{{ CERT_CONFIG.group_cert }}"
|
||||||
|
when: CERT_CONFIG.mode == 'ca'
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Installing dependencies
|
||||||
|
ansible.builtin.package:
|
||||||
|
pkg: ['python3-cryptography']
|
||||||
|
tags: [certs, ca]
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Creating cert directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ CERT_CONFIG.path }}"
|
||||||
|
state: directory
|
||||||
|
tags: [certs, ca]
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Minimal CA
|
||||||
|
ansible.builtin.import_tasks: ca_minimal.yml
|
||||||
|
when: CERT_CONFIG.mode == 'ca'
|
||||||
|
tags: [ca]
|
||||||
|
|
||||||
|
- name: Certificates | Internal | Cert
|
||||||
|
ansible.builtin.import_tasks: cert.yml
|
||||||
|
when: "CERT_CONFIG.mode in ['ca', 'selfsigned']"
|
||||||
|
tags: [certs]
|
||||||
|
|
@ -1,5 +1,25 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- name: ROLE | Processing debian config
|
- name: Certificates | Checking config
|
||||||
ansible.builtin.import_tasks: debian/main.yml
|
ansible.builtin.fail:
|
||||||
when: "ansible_distribution|lower in ['debian', 'ubuntu']"
|
msg: "The required configuration was not provided!
|
||||||
|
Needed: 'certs'"
|
||||||
|
when: certs is undefined
|
||||||
|
|
||||||
|
- name: Certificates | Setting name
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
name: "{% if CERT_CONFIG.cert.name is not none %}{{ CERT_CONFIG.cert.name | safe_key }}{% else %}{{ CERT_CONFIG.cert.cn | safe_key }}{% endif %}"
|
||||||
|
|
||||||
|
- name: Certificates | Internal signed
|
||||||
|
ansible.builtin.include_tasks: internal/main.yml
|
||||||
|
when: "CERT_CONFIG.mode in ['ca_full', 'ca', 'selfsigned']"
|
||||||
|
|
||||||
|
- name: Certificates | Internal | CA
|
||||||
|
ansible.builtin.include_tasks: debian/ca_full.yml
|
||||||
|
when: CERT_CONFIG.mode == 'ca_full'
|
||||||
|
|
||||||
|
- name: Certificates | Debian | Letsencrypt
|
||||||
|
ansible.builtin.include_tasks: debian/letsencrypt/main.yml
|
||||||
|
when:
|
||||||
|
- CERT_CONFIG.mode == 'le_certbot'
|
||||||
|
- "ansible_distribution|lower in ['debian', 'ubuntu']"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
# ansibleguy.infra_certs - dummy site used for letsencrypt certbot
|
||||||
|
|
||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName dummy.letsencrypt.localhost
|
||||||
|
ServerAdmin webmaster@localhost
|
||||||
|
ErrorLog {{ APACHE_CONFIG.log.path }}/error.log
|
||||||
|
CustomLog {{ APACHE_CONFIG.log.path }}/access.log combined
|
||||||
|
</VirtualHost>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
# ansibleguy.infra_certs - dummy site used for letsencrypt certbot
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name dummy.letsencrypt.localhost;
|
||||||
|
error_log {{ NGINX_CONFIG.log.path }}/error.log;
|
||||||
|
access_log {{ NGINX_CONFIG.log.path }}/access.log;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
# ansibleguy.infra_certs
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Service to renew LetsEncrypt Certificates using certbot
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=certbot renew -{{ CERT_CONFIG.letsencrypt.verbosity }} --non-interactive --agree-tos --renew-with-new-domains
|
||||||
|
SuccessExitStatus=0
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
# {{ ansible_managed }}
|
||||||
|
# ansibleguy.infra_certs
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Timer to renew LetsEncrypt Certificates using certbot
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar={{ CERT_CONFIG.letsencrypt.renew_timer }}
|
||||||
|
Persistent=false
|
||||||
|
WakeSystem=false
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Loading…
Reference in New Issue