Uma associacao DANE e o SHA-256 sobre o SubjectPublicKeyInfo do certificado
que esta a ser servido. Se o que esta no DNS e o que o servidor apresenta se
afastarem, qualquer remetente que valide DANE recusa o correio -- e nada no
painel dizia porque.
Passa a mostrar o numero, para poder ser comparado com o registo publicado.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Com TLS_MODE=acme o certificado e emitido por este servico, nao vigiado em
disco, portanto nao ha watcher para estar saudavel. O painel reportava false e
mostrava "indisponivel" em permanencia, ao lado de um certificado perfeitamente
valido. Passa a ser null, e a pagina nao mostra veredito nenhum.
O README ganha uma seccao sobre os dois modos de certificado. O modo traefik
falha de duas maneiras silenciosas -- sem router para o PRIMARY_MAIL_HOST o
nome nunca chega ao acme.json, e o acme.json fica em 600 do root, ilegivel para
o utilizador nao-root do contentor. Nos dois casos o HTTPS continua a funcionar
porque quem o serve e o proxy, enquanto as portas de correio caem no
autoassinado e sao recusadas por qualquer cliente que verifique. Foi
exactamente o que aconteceu nesta instalacao.
Actualizado tambem o que ja nao esta por acabar: a resposta de ausencia, a
assinatura e as regras Sieve sao consumidas, e a reconciliacao de DNS conduz o
estado em vez de o relatar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/admin/security mostrava "Nao foi possivel carregar os dados" porque
/api/v1/admin/tls respondia 503. A rota so era ligada quando o certificado
vinha do watcher do acme.json do Traefik, o unico provider que tinha metodos
de estado proprios.
Nesta instalacao o TRAEFIK_ACME_DIR nao esta definido e o STATIC_CERT_PATH
tambem nao, portanto o servidor cai no certificado autoassinado efemero -- ou
seja, o painel calava-se exactamente no estado em que o operador mais precisa
de ser avisado.
- ProviderStatus deriva o estado de qualquer port.CertProvider, lendo a folha
do certificado que ele realmente serve
- o painel passa a ser ligado sempre que ha JWT_SECRET
- passa a dizer quem emitiu e se e autoassinado, com um aviso proprio: nas
portas de correio um autoassinado e recusado por qualquer cliente que
verifique, o que pesa mais do que uma validade ainda longe
CheckSignatureFrom nao serve para detectar isto: tambem exige que o emissor
seja uma CA, coisa que uma folha descartavel nunca declara, e por isso dava a
fallback degradada como correctamente emitida.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DKIM rotation broke the domain it was run on. It retired the live key and
inserted a placeholder as ACTIVE - private_key_enc was the literal string
"dummy-encrypted-key" - so FindActiveKeys then failed for the whole
domain and outbound mail stopped being signed, with the real key already
gone. Reproduced against a database before changing anything.
The root cause runs deeper: the auth service derives its encryption key
with a plain SHA-256 of the master key while the protocols service uses
HKDF with its own info string, so nothing the auth service seals can ever
be opened by the signer. Rotation therefore moves to the protocols
service, which owns that vault and the real key generator. It retires the
previous key with a seven-day overlap in the same transaction rather than
deleting it, because mail already sent under it is still being verified.
Domain onboarding had three defects in one function. It built the alias
destination by interpolating the domain name into the SQL text, which was
injectable; it reused one placeholder for a CITEXT and a VARCHAR column,
which made the statement fail and happened to hide the injection; and it
ignored its scope argument entirely, so any DOMAIN_ADMIN could create
domains. It also provisioned postmaster, abuse and security, missing the
dmarc and tlsrpt aliases that the published records point at - PLAN.md
section 7.4b is explicit that reports bounce without them.
The TLS panel returned fixed values: acme_status "VALID" and 72 days to
expiry, whatever the process actually held. It now reads the certificate
watcher, and a compile-time assertion keeps the wiring from silently
regressing into "disabled". The audit log and the readiness scorecard were
fixtures too, and their field names did not match the API they now read.
Two tests failed and a third asserted a fabricated constant: one depended
on test order through a shared fixture that had a second factor enrolled,
one used a fixed domain name and so passed once per database, and one
expected "records: 13" from a function that never touches DNS. All three
are fixed, and the suite now passes twice in a row against one database.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>