commit 61872f3d2f4775c40ae7a91c84d1f88ca8bdb6b4
parent 6abe9986a60d75ae60e5dcc32568eb5944255f32
Author: MTRNord <MTRNord@users.noreply.github.com>
Date: Fri, 17 Apr 2026 15:18:15 +0200
auto update tlsa
Signed-off-by: MTRNord <MTRNord@users.noreply.github.com>
Diffstat:
3 files changed, 179 insertions(+), 0 deletions(-)
diff --git a/apps/talos_cluster/stalwart/kustomization.yaml b/apps/talos_cluster/stalwart/kustomization.yaml
@@ -7,4 +7,6 @@ resources:
- secret.yaml
- ingress.yaml
- routing.yaml
+ - tlsa-secret.yaml
+ - tlsa-updater.yaml
diff --git a/apps/talos_cluster/stalwart/tlsa-secret.yaml b/apps/talos_cluster/stalwart/tlsa-secret.yaml
@@ -0,0 +1,28 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: tlsa-updater-pdns
+ namespace: stalwart
+type: Opaque
+stringData:
+ api-key: ENC[AES256_GCM,data:msqWCCyezDreHnq48zZmenc/8/7HWHdevejJMsXC71aSypUTHO/H9b3zfYequEYPJ/TKixhF+ZLchMAXAC8EVQ==,iv:yF7QLueYaFUbEsrEY0BKfj2ku0uKlfGEDDRLG/3WQWs=,tag:Q+Q7QfvtrKP/6tCakue88w==,type:str]
+sops:
+ kms: []
+ gcp_kms: []
+ azure_kv: []
+ hc_vault: []
+ age:
+ - recipient: age1esjyg2qfy49awv0ptkzvpk425adczjr38m37w2mmcahzc4p8n54sll2nzh
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzaWJONkNPVkl0TkhjYUxt
+ MGk0aTNrMVlUQXNpTGRLMzgxemVZVE1keWg0ClhLdDhVUEhjZG91RE16OXdGUDhO
+ bWFhQzgxQUMzUEVrT2kweWFndzlnUDgKLS0tIEtLZ0NzdE1yQVJ1WUJtclUrS2pM
+ VmFiWnlkdDVIdXhUcjhFUldEeWZ6QzAK18qxlygVSItoXDvJG7s2K2lYU8F4p4Wz
+ BSn5YmD/Xqj4lUHVCJSk+g2K+oeotvT0e83JbW7mqtoMtL0US0P1Jw==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-04-17T13:14:42Z"
+ mac: ENC[AES256_GCM,data:/61x4lFhnCAJAvxHVNTymUAi/IGZCKHgDqkihMYpQ92asIVFG56d0tFn5uzDUctd7J6e/rl7sLuFhuSneJ7cFfdXkuVk9hk/ZaxFbYBwThe+AevxuMGvIrl3GLVfkmJa9oieyLV7L3qOqPvk+c70Bn3tCMIBRPyteDoFSOFXxv0=,iv:M5/NyBv3cWho1avXZlOx/FUJzdNkKDajfvoRTrHO8XE=,tag:svtLye5I15B4y0BWuy1beQ==,type:str]
+ pgp: []
+ encrypted_regex: ^(apiKey|appUserPassword|otelUserPassword|harborAdminPassword|totpVaultKey|kimaiAppSecret|kimaiAdminPassword|GITHUB_CLIENT_ID|GITHUB_CLIENT_SECRET|GITHUB_PRIVATE_KEY|woosh|root_password|rspamd_password|pgdb_password|matrix_access_token|pgdb_remote_url|hmac_secret_key|adminPassword|adminEmail|jenkinsAdminEmail|securityRealm|gerrit.config|routing_key|DATABASE_URL|SMTP_PASSWORD|SECRET_KEY_BASE|admin_password|extraCommands|key|clickhouseDatabaseURL|databaseURL|client_id|client_secret|secret_key_base|otp_secret|private_key|public_key|primaryKey|deterministicKey|keyDerivationSalt|token|clientId|secretKey|installationId|installationKey|uriOverride|adminToken.value|password.value|sql_password|erlangCookie|AUTHENTICATION_PASSWORD|ROOM_API_SECRET_KEY|adminPassword|configPassword|adminUser|configUser|MAIL_PASSWORD|APP_KEY|api_key|api_secret|keys|livekit_key|livekit_secret|secret_key|admin_pass|admin_email|mariadbPassword|mariadbRootPassword|privateKey|data|stringData|PASSWD|password|pass|postgresPassword|smtp_auth_password|addresses|smtp_auth_username|authorization_credentials|postgresqlPassword|redminePassword|smtpPassword|registration_shared_secret|shared_secret|secret|admin_token|integrationKey|integration_key|rootPassword|adminPassword|adminUser|adminEmail|emailPassword|secretKey|appId|clientSecret|webhookSecret|content)$
+ version: 3.9.1
diff --git a/apps/talos_cluster/stalwart/tlsa-updater.yaml b/apps/talos_cluster/stalwart/tlsa-updater.yaml
@@ -0,0 +1,149 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: tlsa-updater
+ namespace: stalwart
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: tlsa-updater
+ namespace: stalwart
+rules:
+ - apiGroups: [""]
+ resources: ["secrets"]
+ resourceNames: ["stalwart-mail-cert"]
+ verbs: ["get"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: tlsa-updater
+ namespace: stalwart
+subjects:
+ - kind: ServiceAccount
+ name: tlsa-updater
+ namespace: stalwart
+roleRef:
+ kind: Role
+ apiGroup: rbac.authorization.k8s.io
+ name: tlsa-updater
+---
+apiVersion: batch/v1
+kind: CronJob
+metadata:
+ name: tlsa-updater
+ namespace: stalwart
+spec:
+ schedule: "17 * * * *"
+ concurrencyPolicy: Forbid
+ successfulJobsHistoryLimit: 3
+ failedJobsHistoryLimit: 3
+ jobTemplate:
+ spec:
+ template:
+ spec:
+ serviceAccountName: tlsa-updater
+ restartPolicy: OnFailure
+ containers:
+ - name: tlsa-updater
+ image: alpine:3
+ env:
+ - name: PDNS_URL
+ value: "http://10.0.64.129:8081"
+ - name: PDNS_ZONE
+ value: "midnightthoughts.space."
+ - name: MAIL_HOST
+ value: "mail.midnightthoughts.space."
+ - name: PDNS_API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: tlsa-updater-pdns
+ key: api-key
+ command:
+ - /bin/sh
+ - -c
+ - |
+ set -e
+ apk add -q --no-cache openssl curl jq
+
+ TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
+ CA=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
+
+ SECRET=$(curl -sf --cacert "$CA" \
+ -H "Authorization: Bearer $TOKEN" \
+ "https://kubernetes.default.svc/api/v1/namespaces/stalwart/secrets/stalwart-mail-cert")
+
+ # Full cert chain (leaf + intermediates)
+ CERT_CHAIN=$(echo "$SECRET" | jq -r '.data["tls.crt"]' | base64 -d)
+
+ # Split full chain into individual cert files
+ echo "$CERT_CHAIN" | awk '
+ /-----BEGIN CERTIFICATE-----/ { n++ }
+ { print > "/tmp/cert-" n ".pem" }
+ '
+
+ RECORDS_JSON="[]"
+ IDX=1
+ while [ -f "/tmp/cert-${IDX}.pem" ]; do
+ openssl x509 -in "/tmp/cert-${IDX}.pem" -outform DER -out "/tmp/cert-${IDX}.der" 2>/dev/null
+ openssl x509 -in "/tmp/cert-${IDX}.pem" -pubkey -noout 2>/dev/null \
+ | openssl pkey -pubin -outform DER -out "/tmp/cert-${IDX}.spki.der" 2>/dev/null
+
+ H01=$(openssl dgst -sha256 "/tmp/cert-${IDX}.der" | awk '{print $2}')
+ H02=$(openssl dgst -sha512 "/tmp/cert-${IDX}.der" | awk '{print $2}')
+ H11=$(openssl dgst -sha256 "/tmp/cert-${IDX}.spki.der" | awk '{print $2}')
+ H12=$(openssl dgst -sha512 "/tmp/cert-${IDX}.spki.der" | awk '{print $2}')
+
+ echo "Cert $IDX: 3/2 0 1=$H01"
+
+ if [ "$IDX" -eq 1 ]; then
+ # Leaf cert: DANE-EE (3 x x) + PKIX-EE (2 x x)
+ NEW=$(jq -n \
+ --arg h01 "$H01" --arg h02 "$H02" \
+ --arg h11 "$H11" --arg h12 "$H12" \
+ '[
+ {content: ("3 0 1 " + $h01), disabled: false},
+ {content: ("3 0 2 " + $h02), disabled: false},
+ {content: ("3 1 1 " + $h11), disabled: false},
+ {content: ("3 1 2 " + $h12), disabled: false},
+ {content: ("2 0 1 " + $h01), disabled: false},
+ {content: ("2 0 2 " + $h02), disabled: false},
+ {content: ("2 1 1 " + $h11), disabled: false},
+ {content: ("2 1 2 " + $h12), disabled: false}
+ ]')
+ else
+ # Intermediate certs: PKIX-EE (2 x x) only
+ NEW=$(jq -n \
+ --arg h01 "$H01" --arg h02 "$H02" \
+ --arg h11 "$H11" --arg h12 "$H12" \
+ '[
+ {content: ("2 0 1 " + $h01), disabled: false},
+ {content: ("2 0 2 " + $h02), disabled: false},
+ {content: ("2 1 1 " + $h11), disabled: false},
+ {content: ("2 1 2 " + $h12), disabled: false}
+ ]')
+ fi
+
+ RECORDS_JSON=$(printf '%s\n%s' "$RECORDS_JSON" "$NEW" | jq -s '.[0] + .[1]')
+ IDX=$((IDX + 1))
+ done
+
+ echo "Total TLSA records: $(echo "$RECORDS_JSON" | jq 'length')"
+
+ for PORT in 25 465 587 143 993; do
+ NAME="_${PORT}._tcp.${MAIL_HOST}"
+ echo "Updating $NAME ..."
+ BODY=$(jq -n \
+ --arg name "$NAME" \
+ --argjson records "$RECORDS_JSON" \
+ '{rrsets: [{name: $name, type: "TLSA", ttl: 300, changetype: "REPLACE", records: $records}]}')
+ curl -sf -X PATCH \
+ -H "X-API-Key: $PDNS_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d "$BODY" \
+ "${PDNS_URL}/api/v1/servers/localhost/zones/${PDNS_ZONE}" > /dev/null
+ echo " OK"
+ done
+
+ echo "TLSA update complete"