Add Ansible/playbook_netcup_tlsa_dane.yaml

This commit is contained in:
2025-08-31 19:25:21 +00:00
parent cd5032bd1a
commit b86d944e62

View File

@@ -0,0 +1,303 @@
---
- name: Zertifikate auf Mailcow (vom NPM) aktualisieren
hosts: localhost
gather_facts: false
vars:
# Host-Konfiguration
debug_dane: true
nginxproxymanager_host: npm
mailcow_host: mailcow
tlsa_host_label: "_25._tcp.mail"
# Mailcow Variablen
mailcow_cert_path: "/opt/mailcow-dockerized/data/assets/ssl/cert.pem"
mailcow_key_path: "/opt/mailcow-dockerized/data/assets/ssl/key.pem"
mailcow_owner: "root"
mailcow_group: "root"
tasks:
################################################################
# 1) Neues Zertifikat/Key vom NginxProxyManager einlesen
################################################################
- name: "Neues Zertifikat vom NginxProxyManager slurpen"
slurp:
src: "{{ letsencrypt_cert }}"
delegate_to: "{{ nginxproxymanager_host }}"
register: new_cert_slurp
- name: "Neuen Key vom NginxProxyManager slurpen"
slurp:
src: "{{ letsencrypt_key }}"
delegate_to: "{{ nginxproxymanager_host }}"
register: new_key_slurp
################################################################
# 2) Mailcow-Check und -Update
################################################################
- name: "Mailcow: Altes Zertifikat (cert.pem) auslesen"
slurp:
src: "{{ mailcow_cert_path }}"
delegate_to: "{{ mailcow_host }}"
register: mailcow_old_cert
ignore_errors: true
- name: "Mailcow: Alter Key (key.pem) auslesen"
slurp:
src: "{{ mailcow_key_path }}"
delegate_to: "{{ mailcow_host }}"
register: mailcow_old_key
ignore_errors: true
- name: "Mailcow: Prüfen, ob Zertifikat oder Key neu sind"
set_fact:
mailcow_cert_is_new: >-
{{
(new_cert_slurp.content | b64decode) !=
(mailcow_old_cert.content is defined
and (mailcow_old_cert.content | b64decode)
or '')
or
(new_key_slurp.content | b64decode) !=
(mailcow_old_key.content is defined
and (mailcow_old_key.content | b64decode)
or '')
}}
- name: "Mailcow: Debug-Ausgabe, ob das Zertifikat neu ist"
debug:
msg: "Mailcow: Zertifikat neu? {{ mailcow_cert_is_new }}"
- name: "Mailcow: Zertifikatsupdate"
block:
- name: "Mailcow: Docker Compose stoppen"
ansible.builtin.shell: |
docker compose down
args:
chdir: "/opt/mailcow-dockerized/"
delegate_to: "{{ mailcow_host }}"
- name: "Mailcow: Neues Zertifikat (cert.pem) kopieren"
copy:
content: "{{ new_cert_slurp.content | b64decode }}"
dest: "{{ mailcow_cert_path }}"
owner: "{{ mailcow_owner }}"
group: "{{ mailcow_group }}"
mode: "0644"
delegate_to: "{{ mailcow_host }}"
- name: "Mailcow: Neuen Key (key.pem) kopieren"
copy:
content: "{{ new_key_slurp.content | b64decode }}"
dest: "{{ mailcow_key_path }}"
owner: "{{ mailcow_owner }}"
group: "{{ mailcow_group }}"
mode: "0644"
delegate_to: "{{ mailcow_host }}"
- name: "Mailcow: Docker Compose neu starten (force-recreate)"
ansible.builtin.shell: |
docker compose up -d --force-recreate
args:
chdir: "/opt/mailcow-dockerized/"
delegate_to: "{{ mailcow_host }}"
when: mailcow_cert_is_new
################################################################
# 3) TLSA-Record bei Netcup aktualisieren
################################################################
- name: "DANE: TLSA-Hash aus slurped cert.pem erzeugen (3 1 1)"
shell: |
echo "{{ mailcow_old_cert.content | b64decode }}" |
openssl x509 -pubkey -noout |
openssl pkey -pubin -outform DER |
openssl dgst -sha256 -binary |
hexdump -ve '1/1 "%.2x"'
register: tlsa_hash_result
changed_when: false
delegate_to: localhost
- name: "DANE: CA Issuer URI aus slurped cert.pem extrahieren"
shell: |
echo "{{ mailcow_old_cert.content | b64decode }}" |
openssl x509 -text -noout |
grep "CA Issuers" | sed -n 's/.*URI://p'
register: ca_uri_result
changed_when: false
delegate_to: localhost
- name: "DANE: TLSA-Hash der CA erzeugen (2 1 1)"
shell: |
wget -qO- "{{ ca_uri_result.stdout }}" |
openssl x509 -inform DER -outform PEM |
openssl x509 -pubkey -noout |
openssl pkey -pubin -outform DER |
openssl dgst -sha256 -binary |
hexdump -ve '1/1 "%.2x"'
register: tlsa_hash_ca_result
changed_when: false
when: ca_uri_result.stdout | length > 0
delegate_to: localhost
- name: "DANE: TLSA-Hashes speichern"
set_fact:
tlsa_hash_current: "{{ tlsa_hash_result.stdout }}"
tlsa_hash_ca: "{{ tlsa_hash_ca_result.stdout | default('') }}"
- name: "DANE: Netcup API Login"
uri:
url: "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
method: POST
body_format: json
return_content: true
body:
action: "login"
param:
customernumber: "{{ netcup_customer_number }}"
apikey: "{{ netcup_api_key }}"
apipassword: "{{ netcup_api_password }}"
register: netcup_login
delegate_to: localhost
- name: "DANE: Session-ID sichern"
set_fact:
netcup_session_id: "{{ netcup_login.json.responsedata.apisessionid }}"
- name: "DANE: DNS-Records abrufen"
uri:
url: "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
method: POST
body_format: json
return_content: true
body:
action: "infoDnsRecords"
param:
customernumber: "{{ netcup_customer_number }}"
apikey: "{{ netcup_api_key }}"
apisessionid: "{{ netcup_session_id }}"
domainname: "{{ netcup_domain }}"
register: netcup_dns_info
delegate_to: localhost
- name: "DANE: Bestehende TLSA-Records extrahieren"
set_fact:
existing_tlsa_record: >-
{{
(netcup_dns_info.json.responsedata.dnsrecords | selectattr('hostname', 'equalto', tlsa_host_label)
| selectattr('type', 'equalto', 'TLSA')
| selectattr('destination', 'search', '^3 1 1 ')
| list | first)
}}
existing_tlsa_record_ca: >-
{{
(netcup_dns_info.json.responsedata.dnsrecords | selectattr('hostname', 'equalto', tlsa_host_label)
| selectattr('type', 'equalto', 'TLSA')
| selectattr('destination', 'search', '^2 1 1 ')
| list | first)
}}
- name: "DANE: TLSA-Record (3 1 1) aktualisieren"
block:
- name: "DANE: TLSA-Record (3 1 1) wird aktualisiert"
uri:
url: "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
method: POST
body_format: json
status_code: 200
body:
action: "updateDnsRecords"
param:
customernumber: "{{ netcup_customer_number }}"
apikey: "{{ netcup_api_key }}"
apisessionid: "{{ netcup_session_id }}"
domainname: "{{ netcup_domain }}"
dnsrecordset:
dnsrecords:
- id: "{{ existing_tlsa_record.id if existing_tlsa_record is defined else omit }}"
hostname: "{{ tlsa_host_label }}"
type: "TLSA"
destination: "3 1 1 {{ tlsa_hash_current }}"
state: "yes"
delegate_to: localhost
- name: "DANE: TLSA-Record (3 1 1) prüfen"
uri:
url: "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
method: POST
body_format: json
return_content: true
body:
action: "infoDnsRecords"
param:
customernumber: "{{ netcup_customer_number }}"
apikey: "{{ netcup_api_key }}"
apisessionid: "{{ netcup_session_id }}"
domainname: "{{ netcup_domain }}"
register: netcup_verify_311
delegate_to: localhost
- name: "DANE: TLSA-Record (3 1 1) verifizieren"
set_fact:
verified_tlsa_311: >-
{{
(netcup_verify_311.json.responsedata.dnsrecords | selectattr('hostname', 'equalto', tlsa_host_label)
| selectattr('type', 'equalto', 'TLSA')
| selectattr('destination', 'equalto', '3 1 1 ' ~ tlsa_hash_current)
| list | length > 0)
}}
when: existing_tlsa_record is not defined or existing_tlsa_record.destination != ('3 1 1 ' ~ tlsa_hash_current)
- name: "DANE: TLSA-Record (2 1 1) aktualisieren"
block:
- name: "DANE: TLSA-Record (2 1 1) wird aktualisiert"
uri:
url: "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
method: POST
body_format: json
status_code: 200
body:
action: "updateDnsRecords"
param:
customernumber: "{{ netcup_customer_number }}"
apikey: "{{ netcup_api_key }}"
apisessionid: "{{ netcup_session_id }}"
domainname: "{{ netcup_domain }}"
dnsrecordset:
dnsrecords:
- id: "{{ existing_tlsa_record_ca.id if existing_tlsa_record_ca is defined else omit }}"
hostname: "{{ tlsa_host_label }}"
type: "TLSA"
destination: "2 1 1 {{ tlsa_hash_ca }}"
state: "yes"
delegate_to: localhost
- name: "DANE: TLSA-Record (2 1 1) prüfen"
uri:
url: "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
method: POST
body_format: json
return_content: true
body:
action: "infoDnsRecords"
param:
customernumber: "{{ netcup_customer_number }}"
apikey: "{{ netcup_api_key }}"
apisessionid: "{{ netcup_session_id }}"
domainname: "{{ netcup_domain }}"
register: netcup_verify_211
delegate_to: localhost
- name: "DANE: TLSA-Record (2 1 1) verifizieren"
set_fact:
verified_tlsa_211: >-
{{
(netcup_verify_211.json.responsedata.dnsrecords | selectattr('hostname', 'equalto', tlsa_host_label)
| selectattr('type', 'equalto', 'TLSA')
| selectattr('destination', 'equalto', '2 1 1 ' ~ tlsa_hash_ca)
| list | length > 0)
}}
when: tlsa_hash_ca | length > 0 and
(existing_tlsa_record_ca is not defined or
existing_tlsa_record_ca.destination != ('2 1 1 ' ~ tlsa_hash_ca))