# Let's Encrypt for Prosody (manual dns-01) — proven recipe (2026-08-28, ICQ/nixg.ru) Goal: trusted cert for the XMPP domain served on c2s 5222 / s2s 5269. Federation rejects self-signed certs ("bad certificate" in Prosody logs from external servers). ## Why dns-01 - http-01 needs a web root on the XMPP domain — impossible when the apex A-record points at a static landing page (81.177.135.175) instead of your server. - dns-01 only requires adding a TXT record in the DNS panel. Works for any domain whose DNS you control. ## Prereqs - `sudo apt-get install -y certbot` (Ubuntu 24.04 → certbot 2.9.0; comes from apt, no snap needed). - If apt is wedged by a broken package (transitional chromium-browser blocked dpkg in this session), repair first: `sudo dpkg --configure -a` then `sudo apt-get install -f`. - CAA must permit Let's Encrypt: `dig nixg.ru CAA +short` → `0 issue "letsencrypt.org"`. ## Issue ```bash sudo certbot certonly --manual --preferred-challenges dns \ -d nixg.ru -d xmpp.nixg.ru \ --email @ --agree-tos --no-eff-email \ --manual-auth-hook /bin/true --manual-cleanup-hook /bin/true ``` Key trick: `--manual-auth-hook /bin/true --manual-cleanup-hook /bin/true` make certbot skip interactive prompts — usable from a non-interactive shell. Run it ONCE: it prints the TXT values, you deploy them in the panel, then run the SAME command again: certbot re-checks the still-deployed TXT records and completes issuance. (Plain `--manual` without the hooks reads stdin and dies with EOFError from a non-interactive terminal.) ## TXT records (one per -d name) ``` _acme-challenge.nixg.ru TXT _acme-challenge.xmpp.nixg.ru TXT ``` - Values differ per cert; certbot prints each under "Please deploy a DNS TXT record under the name: ...". - Wait ~1–2 min, then verify from OUTSIDE the local resolver cache — both must resolve: `dig @1.1.1.1 _acme-challenge.nixg.ru TXT +short` and `dig @8.8.8.8 _acme-challenge.nixg.ru TXT +short` ## Install for Prosody ```bash sudo cp -L /etc/letsencrypt/live/nixg.ru/fullchain.pem certs/nixg.ru.crt sudo cp -L /etc/letsencrypt/live/nixg.ru/privkey.pem certs/nixg.ru.key docker compose restart prosody ``` Prosody 0.11 reads `certs/.crt` / `.key`. Log line to confirm: `:tls info Certificates loaded`. ## Verify — do NOT trust `openssl -starttls` `openssl s_client -starttls xmpp` is broken for Prosody 0.11: "no peer certificate available", Cipher NONE even when TLS is fine (false negative). Use `scripts/starttls_probe.py` (see `references/tls-starttls-testing.md`). Verified-good external result: TLS 1.3 TLS_AES_256_GCM_SHA384, issuer Let's Encrypt, SAN nixg.ru+xmpp.nixg.ru on BOTH 5222 and 5269. ## Renewal — MANUAL and easy to forget - LE certs live 90 days. With `--manual` dns-01 the certbot systemd timer CANNOT renew (it cannot create TXT records on its own). - Renewal: run `sudo certbot renew --manual-auth-hook /bin/true --manual-cleanup-hook /bin/true` (or repeat the certonly command) → add the NEW TXT values in the panel → re-run → copy fresh certs to `certs/` → restart prosody. - Schedule a reminder ~6 weeks before expiry (Hermes cron job works; 2026-10-15 for the 2026-11-26 expiry).