cluster

Infrastructure files for Nordgedanken and Midnightthoughts.
git clone git://archive.git.mtrnord.blog/MTRNord/cluster.git
Log | Files | Refs | README

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:
Mapps/talos_cluster/stalwart/kustomization.yaml | 2++
Aapps/talos_cluster/stalwart/tlsa-secret.yaml | 28++++++++++++++++++++++++++++
Aapps/talos_cluster/stalwart/tlsa-updater.yaml | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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"