Sumário
- Quão ruim é de fato uma queda por certificado?
- Por que a renovação automática não basta?
- Como verifico a expiração de um certificado pela linha de comando?
- Pegar o caso "renovado, mas não recarregado"
- Quais limiares um alerta de certificado deve usar?
- Como transformo isso em um alerta que realmente me alcança?
- Passo 1 — Crie dois canais, não um
- Passo 2 — Rode a verificação
- Passo 3 — Agende e alerte quando o próprio agendamento falhar
- Passo 4 — Divida a escada com condições
- Passo 5 — Teste antes de confiar
- E se meu monitoramento já verifica certificados?
- O que o Echobell não faz
- Perguntas frequentes
- A Let's Encrypt realmente parou de enviar e-mails de expiração?
- Quanto tempo valem os certificados TLS em 2026?
- Devo alertar sobre expiração mesmo usando ARI?
- E os certificados que não estão em um servidor web?
- Uma verificação diária não vira ruído?
- O time inteiro pode receber o alerta de certificado?
- Funciona no macOS?
- É seguro mandar detalhes do certificado numa notificação?
- Relacionados
Duas coisas mudaram por baixo da infraestrutura de certificados de todo mundo, e a maioria dos times não se ajustou a nenhuma delas.
Primeiro: a rede de segurança foi retirada. A Let's Encrypt encerrou seu serviço de aviso de expiração em 4 de junho de 2025 — aqueles e-mails que salvaram discretamente milhares de sites quando a automação quebrava. O raciocínio fazia sentido (a maior parte dos assinantes já renova automaticamente, guardar milhões de endereços de e-mail é um passivo de privacidade e o serviço custava "dezenas de milhares de dólares por ano"), e o anúncio termina mandando você procurar monitoramento de terceiros (Let's Encrypt). Muita gente leu isso, concordou e nunca fez a segunda metade.
Segundo: a margem de erro desabou. Desde 15 de março de 2026, um certificado TLS público pode valer no máximo 200 dias, caindo para 100 dias em 15 de março de 2027 e para 47 dias em 15 de março de 2029, conforme a cédula SC-081v3 do CA/Browser Forum. A Let's Encrypt está indo mais rápido que o teto: certificados de 6 dias ficaram disponíveis para todos em 15 de janeiro de 2026, e o plano leva o perfil padrão a 64 dias em fevereiro de 2027 e a 45 dias em fevereiro de 2028 (Let's Encrypt).
As duas mudanças empurram na mesma direção. A renovação acontece com mais frequência, então tem mais chances de quebrar — e, quando quebra, ninguém te manda e-mail. Este guia é a verificação que fecha essa lacuna: um script de shell testado, limiares que fazem sentido para certificados de vida curta e um jeito de fazer o último caso tocar o seu telefone com o Echobell.
Quão ruim é de fato uma queda por certificado?
Ruim o bastante para mais de um terço das organizações ter passado por isso no último ano. No 2026 Global Certificate Management Outlook da DigiCert — uma pesquisa da Propeller Insights com 1.001 tomadores de decisão de TI e cibersegurança nos EUA, Reino Unido e Austrália, realizada em maio de 2026 — mais de um terço das organizações relatou uma indisponibilidade causada por certificado expirado no último ano. Quase três quartos acumularam ao menos cinco horas de indisponibilidade ligada a certificados, uma em cada cinco chegou a 25 horas ou mais, e quase uma em cada quatro disse que seu incidente mais grave custou mais de US$ 250.000 (DigiCert).
O mais interessante nesses números é a duração. Cinco horas não é o tempo de renovar um certificado — renovar leva segundos. Cinco horas é o tempo que alguém levou para descobrir.
Por que a renovação automática não basta?
Porque a automação de renovação falha em silêncio, e um cron que parou de rodar não produz saída nenhuma. Cada um destes casos é real e comum, e nenhum faz barulho:
- O timer não roda mais. Uma atualização de distribuição, um contêiner reconstruído ou um
systemctl disablede três meses atrás do qual ninguém lembra. Umcertbot.timerque não dispara parece exatamente igual a um que dispara certinho. - A renovação deu certo, mas o serviço nunca recarregou. O certificado novo está no disco; o nginx, o HAProxy ou o Postfix continua com o antigo na memória. É a forma mais comum de uma configuração "totalmente automatizada" expirar mesmo assim.
- O caminho do desafio quebrou. Alguém colocou um redirecionamento, uma regra de WAF ou um
Denyna frente de/.well-known/acme-challenge/, e o HTTP-01 falha. Ou expirou o token de API do provedor de DNS usado no DNS-01. - A renovação aconteceu em apenas um nó. Dois balanceadores, um cron. O segundo segue servindo o certificado antigo até não conseguir mais.
- O certificado nem está em um servidor web. Clientes mTLS internos, um broker Kafka, um servidor LDAP, um concentrador VPN, um certificado de push do gerenciamento de dispositivos. Nada na internet pública o enxerga e nenhum cliente ACME o gerencia.
- A renovação está fixada no intervalo errado. A Let's Encrypt é explícita: "renovar em um intervalo fixo de 60 dias não será mais suficiente" quando o perfil padrão cair para 64 e depois 45 dias (Let's Encrypt).
O último merece ênfase, porque é a falha para a qual o calendário está empurrando todo mundo. Se a sua cadência de renovação é um número que alguém digitou em 2022, a validade dos certificados agora está vindo ao encontro dele.
Como verifico a expiração de um certificado pela linha de comando?
Um único pipeline de openssl, sem dependências. Para um host no ar:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -startdate -enddate
Para um arquivo em disco:
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -enddate
A flag -servername não é opcional em um IP compartilhado: sem SNI você recebe o certificado que o servidor considera padrão, que pode não ser aquele com que você se preocupa.
Para uma resposta sim/não, pule totalmente o parsing de datas. openssl x509 -checkend <segundos> sai com 0 se o certificado sobrevive àquela janela e com 1 se expira dentro dela (inclusive se já expirou):
openssl x509 -in fullchain.pem -noout -checkend $((14 * 86400)) \
|| echo "expira em menos de 14 dias"
Esse contrato de código de saída é toda a primitiva de monitoramento. Tudo abaixo é só encanamento em volta dela.
Pegar o caso "renovado, mas não recarregado"
Compare o que está no disco com o que está realmente sendo servido. É a verificação que quase ninguém roda, e ela pega justamente a falha que a automação de renovação não consegue enxergar:
served=$(openssl s_client -connect 127.0.0.1:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -fingerprint -sha256)
ondisk=$(openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -fingerprint -sha256)
[ "$served" = "$ondisk" ] || echo "o serviço está servindo um certificado antigo — precisa recarregar"
Os dois comandos imprimem o mesmo formato sha256 Fingerprint=AB:CD:..., então uma comparação simples de strings basta. Rode alguns minutos depois da janela do seu timer de renovação.
Quais limiares um alerta de certificado deve usar?
Frações do tempo de vida do certificado, não contagens fixas de dias. Uma regra de "avisar com 30 dias" fazia sentido para certificados de 90 dias. Aplicada a um de 47 dias, dispara em um certificado perfeitamente saudável; aplicada a um de 6 dias, dispara o tempo todo.
Ancore a escada no ponto de renovação. A Let's Encrypt recomenda renovar "aproximadamente a dois terços do tempo de vida do certificado atual" — ou seja, um terço de vida restante é quando a renovação já deveria ter acontecido. Tudo depois disso é evidência de que não aconteceu:
| Vida restante | O que significa | Tipo de notificação |
|---|---|---|
| 1/3 | A janela de renovação abriu | Nada — isso é normal |
| 1/6 | A janela foi perdida uma vez | Push normal |
| 1/12 | A renovação está falhando, não atrasada | Urgente |
| < 1/24, expirado ou inacessível | Você está a horas de uma queda | Ligação |
Em números concretos, para um certificado de 47 dias fica mais ou menos assim: silêncio até 7,8 dias, push aos 3,9 dias, urgente aos 2 dias, ligação abaixo de 1 dia. Para um de 90 dias: 15 dias, 7,5 dias, 3,75 dias. Para um de 6 dias são horas, e aí uma escada pensada para humanos deixa de ser a ferramenta certa — apoie-se no ACME Renewal Information (ARI, publicado como RFC 9773), que deixa a AC dizer ao seu cliente quando renovar, e alerte apenas em falhas repetidas do cliente.
Calcular a fração são quatro linhas, e isso deixa o mesmo script correto para todos os seus certificados, seja qual for o emissor:
to_epoch() {
date -u -d "$1" +%s 2>/dev/null || date -u -j -f '%b %d %T %Y %Z' "$1" +%s 2>/dev/null
}
pct_left() { # lê um PEM na entrada padrão e imprime o percentual de vida restante
local pem nb na
pem=$(cat)
nb=$(to_epoch "$(printf '%s' "$pem" | openssl x509 -noout -startdate | cut -d= -f2)")
na=$(to_epoch "$(printf '%s' "$pem" | openssl x509 -noout -enddate | cut -d= -f2)")
echo $(( (na - $(date -u +%s)) * 100 / (na - nb) ))
}
A primeira forma de date é GNU, a segunda é BSD/macOS; o || escolhe a que você tiver.
Como transformo isso em um alerta que realmente me alcança?
O Echobell transforma um webhook ou um e-mail em push normal, alerta urgente ou uma ligação de verdade, que atravessa o modo Foco e o Não perturbe (veja como furar o modo Foco do iOS). Para certificados isso importa porque o último degrau da tabela acima é justamente o que você vai pegar às 3h de um domingo.
Passo 1 — Crie dois canais, não um
Crie um canal no app, defina o tipo de notificação como Urgente e chame-o de "Certificados expirando". Crie um segundo com o tipo Ligação e chame-o de "Certificado prestes a expirar". Copie a URL do webhook nos detalhes de cada canal; ela tem a forma https://hook.echobell.one/t/<channel-token>. Trate-as como segredos: quem tiver a da Ligação pode fazer seu telefone tocar (guia de webhooks).
Escreva os templates para que a notificação permita agir direto da tela bloqueada, sem desbloquear nada:
Título: TLS {{state}}: {{host}}
Corpo: faltam {{daysLeft}} dias, expira em {{notAfter}} — emitido por {{issuer}}
Qualquer chave JSON que você enviar vira variável (templates).
Passo 2 — Rode a verificação
Este é o script das seções anteriores, montado e testado de ponta a ponta. Salve como /usr/local/bin/cert-watch:
#!/usr/bin/env bash
# cert-watch — envia um POST ao Echobell quando um certificado TLS está perto de expirar.
set -uo pipefail
HOOK="${ECHOBELL_CERT_HOOK:?defina ECHOBELL_CERT_HOOK com a URL de webhook do seu canal}"
WARN_DAYS="${WARN_DAYS:-14}"
post() {
curl -sS -m 10 -X POST "$HOOK" \
-H 'content-type: application/json' \
-d "{\"host\":\"$1\",\"daysLeft\":$2,\"notAfter\":\"$3\",\"state\":\"$4\"}" \
>/dev/null
}
days_left() {
local end
end=$(date -u -d "$1" +%s 2>/dev/null) ||
end=$(date -u -j -f '%b %d %T %Y %Z' "$1" +%s 2>/dev/null) || return 1
echo $(( (end - $(date -u +%s)) / 86400 ))
}
check() {
local host="$1" port="$2" pem state not_after days
pem=$(openssl s_client -connect "$host:$port" -servername "$host" \
</dev/null 2>/dev/null | openssl x509 2>/dev/null)
if [ -z "$pem" ]; then
post "$host" 0 "" "unreachable"
return
fi
if printf '%s' "$pem" | openssl x509 -noout -checkend 0 >/dev/null 2>&1; then
printf '%s' "$pem" | openssl x509 -noout -checkend $((WARN_DAYS * 86400)) >/dev/null 2>&1 && return 0
state="expiring"
else
state="expired"
fi
not_after=$(printf '%s' "$pem" | openssl x509 -noout -enddate | cut -d= -f2)
days=$(days_left "$not_after") || days=-999
post "$host" "$days" "$not_after" "$state"
}
for target in "$@"; do
case "$target" in
*:*) check "${target%:*}" "${target##*:}" ;;
*) check "$target" 443 ;;
esac
done
Ele reporta três estados — expiring, expired e unreachable — e fica calado quando está tudo certo. Os alvos são host ou host:port, então certificados fora da web também ficam cobertos:
ECHOBELL_CERT_HOOK="https://hook.echobell.one/t/<token>" \
cert-watch example.com api.example.com mail.example.com:993 ldap.internal:636
unreachable é deliberadamente um alerta, e não um pulo silencioso. Uma verificação que trata "não consegui olhar" como "está tudo bem" é justamente o motivo de certificados expirarem.
Passo 3 — Agende e alerte quando o próprio agendamento falhar
Uma vez por dia basta para certificados de 45 dias ou mais; duas vezes por dia se você usa certificados de 6 dias. Um timer do systemd:
# /etc/systemd/system/cert-watch.service
[Unit]
Description=TLS certificate expiry check
[Service]
Type=oneshot
EnvironmentFile=/etc/echobell.env
ExecStart=/usr/local/bin/cert-watch example.com api.example.com mail.example.com:993
# /etc/systemd/system/cert-watch.timer
[Unit]
Description=Daily TLS certificate expiry check
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
Persistent=true importa: sem ele, uma máquina desligada no horário previsto simplesmente pula aquela execução.
Depois feche o ciclo sobre a própria verificação. Uma unidade Type=oneshot que sai com código diferente de zero aciona OnFailure=, então um único drop-in faz uma renovação quebrada se anunciar sozinha:
# /etc/systemd/system/certbot.service.d/echobell.conf
[Unit]
OnFailure=echobell-alert@%n.service
# /etc/systemd/system/echobell-alert@.service
[Unit]
Description=Echobell alert for %i
[Service]
Type=oneshot
EnvironmentFile=/etc/echobell.env
ExecStart=/usr/local/bin/echobell-notify %i
Onde /usr/local/bin/echobell-notify tem três linhas:
#!/usr/bin/env bash
curl -sS -m 10 -X POST "$ECHOBELL_CERT_HOOK" \
-H 'content-type: application/json' \
-d "{\"host\":\"$(hostname -f)\",\"state\":\"renewal-failed\",\"unit\":\"$1\",\"daysLeft\":-1}"
O certbot renew sai com código diferente de zero quando qualquer renovação falha — exatamente o sinal que você quer e exatamente o sinal que hoje não chega a lugar nenhum. Atenção: o --deploy-hook do certbot só roda em caso de sucesso, então não serve para isso; o caminho de falha precisa vir da unidade.
Passo 4 — Divida a escada com condições
Os dois canais recebem o mesmo payload; as condições decidem qual realmente dispara. No canal Urgente:
state == "expiring" && daysLeft > 3
No canal de Ligação:
state == "expired" || state == "unreachable" || state == "renewal-failed" || daysLeft <= 3
Repare que <= converte os dois lados com Number(), então um daysLeft enviado como string ainda é comparado numericamente. As condições não têm operador "contém", e é por isso que o script envia um campo state explícito em vez de um texto livre que você teria que reconhecer por padrão.
Passo 5 — Teste antes de confiar
Aponte o script para um host com certificado sabidamente ruim e veja o alerta chegar:
ECHOBELL_CERT_HOOK="https://hook.echobell.one/t/<token>" cert-watch expired.badssl.com
Faça isso com o Não perturbe ativado no telefone que realmente vai receber e ligue Repetir ligação com falha no app, para que uma chamada suprimida pelo Foco seja tentada de novo. Um caminho de escalonamento que você nunca disparou é um palpite.
E se meu monitoramento já verifica certificados?
Então ligue o webhook que ele já tem a um canal e pule o script. A maioria das ferramentas de monitoramento já conhece a data de expiração; o que costuma faltar é um caminho que sobreviva a um humano dormindo.
- O Uptime Kuma tem notificação de expiração de certificado embutida — aponte-a para um canal (guia do Uptime Kuma, configuração de ligações).
- Prometheus + Alertmanager com o blackbox exporter dão
probe_ssl_earliest_cert_expiry; crie um alerta nessa métrica e roteie para um canal (guia do Prometheus, ligações com o Alertmanager). - As regras de alerta do Grafana fazem POST direto para um canal (guia do Grafana).
- Upptime e UptimeRobot cobrem os endpoints públicos (Upptime, UptimeRobot).
- Qualquer coisa que só mande e-mail — o portal da própria AC, os avisos de ACM de um provedor de nuvem, uma PKI interna — resolve-se com uma regra de encaminhamento. Cada canal tem seu próprio endereço, e
from,to,subject,textehtmlficam disponíveis como variáveis (gatilhos por e-mail).
A única coisa que nenhuma dessas opções substitui é a verificação rodando de fora da máquina que serve o certificado. Se o seu monitoramento mora no mesmo host, uma falha que derruba o host leva o alerta junto.
O que o Echobell não faz
Ser preciso aqui importa, porque gestão de certificados é uma categoria cheia de produtos que fazem muito mais do que isso.
O Echobell faz: transforma um webhook ou e-mail em push normal, alerta urgente ou ligação que toca; filtra com condições; formata com templates; entrega um disparo a todos os inscritos de um canal compartilhado, cada um escolhendo sua urgência.
O Echobell não faz:
- Descobrir ou inventariar seus certificados. Ele não escaneia sua rede, não varre logs de certificate transparency e não vai te contar do certificado que ninguém lembra de ter emitido. O script acima só verifica os hosts que você listar. A proliferação de certificados é um problema real, e isto não é a solução para ela.
- Renovar nada. Não tem cliente ACME nem acesso às suas chaves. Ele avisa que a renovação quebrou; consertar continua sendo com você.
- Verificar certificados por conta própria. Não há sonda hospedada. Algo que você executa — um timer, um job de CI, o monitoramento que já existe — precisa ir olhar.
- Fornecer escalas de plantão, políticas de escalonamento ou confirmação. Não existe "se ninguém atender em cinco minutos, ligue para o próximo". Se você precisa disso, precisa de uma plataforma de incidentes — veja a comparação de alternativas ao Opsgenie.
- Garantir a entrega. Uma ligação depende da infraestrutura de push, da rede e de um telefone carregado. Ela encurta a distância entre a quebra e a percepção; não é um controle no qual se apoiar de forma absoluta.
Perguntas frequentes
A Let's Encrypt realmente parou de enviar e-mails de expiração?
Sim. O serviço de avisos terminou em 4 de junho de 2025, e a Let's Encrypt apagou os endereços de e-mail guardados junto aos registros de emissão. O anúncio recomenda monitoramento de terceiros e cita o Red Sift Certificates Lite, gratuito para até 250 certificados, como uma opção. Se você não recebe um aviso de expiração da Let's Encrypt há mais de um ano, é por isso — não porque nada esteve perto de expirar.
Quanto tempo valem os certificados TLS em 2026?
No máximo 200 dias para certificados TLS de confiança pública, desde 15 de março de 2026. O teto cai para 100 dias em 15 de março de 2027 e para 47 dias em 15 de março de 2029, conforme a cédula SC-081v3. Cada AC emite abaixo do teto por segurança — a DigiCert, por exemplo, emite certificados de 199 dias justamente "para evitar exceder a validade máxima permitida". A Let's Encrypt vai além e mais rápido no próprio cronograma, com certificados de 6 dias disponíveis desde janeiro de 2026.
Devo alertar sobre expiração mesmo usando ARI?
Sim, mas em eventos diferentes. O ARI (RFC 9773) diz ao seu cliente ACME quando renovar, o que elimina por completo a falha do intervalo fixo — mas não garante que a renovação tenha sucesso, que o serviço recarregue nem que o cliente ainda esteja rodando. Alerte sobre falhas repetidas de renovação e sobre o certificado servido divergir do que está no disco, não sobre uma contagem regressiva que você já não precisa administrar.
E os certificados que não estão em um servidor web?
Esses são justamente os que mais expiram, porque nenhum cliente ACME os observa e nenhum navegador reclama até algo quebrar. O script aceita host:port, então IMAP na 993, LDAPS na 636, um broker Kafka na 9093 ou uma API interna na 8443 funcionam do mesmo jeito. Certificados que nunca tocam um socket — assinatura de código, certificados de notificação push, certificados de cliente em uma frota de dispositivos — precisam que você extraia a data de onde eles vivem e a envie para o mesmo canal.
Uma verificação diária não vira ruído?
Não se ela ficar calada quando não há nada — que é exatamente por isso que o script não publica nada acima do limiar. O desenho barulhento é o que informa "certificado OK" todos os dias: em duas semanas ninguém lê, e no dia em que parar de chegar ninguém percebe. Se quiser um heartbeat, coloque-o em um canal Normal separado e nunca no que toca. Veja como resolver a fadiga de alertas para a versão geral deste argumento.
O time inteiro pode receber o alerta de certificado?
Pode. Compartilhe o canal e cada inscrito recebe o disparo, escolhendo o próprio tipo de notificação. Uma divisão que funciona: quem está de plantão assina o canal de Ligação e os demais o Urgente, assim uma expiração às 3h acorda uma pessoa em vez de seis.
Funciona no macOS?
Funciona, com uma ressalva: o date do BSD não aceita -d, e é por isso que days_left e to_epoch tentam primeiro a forma GNU e recorrem a date -u -j -f. O openssl do macOS é LibreSSL por padrão e suporta -checkend e -fingerprint de forma idêntica. Se você instalar o OpenSSL pelo Homebrew, nada muda.
É seguro mandar detalhes do certificado numa notificação?
Os campos usados aqui — nome do host, data de expiração, emissor — são públicos; qualquer um lê isso do seu servidor com o mesmo comando openssl. Não amplie o payload com chaves privadas, caminhos internos ou qualquer coisa de um certificado em host não público além do nome. O Echobell guarda o conteúdo e o histórico das notificações apenas no seu dispositivo, mantendo no servidor só contas, canais e inscrições (modelo de privacidade) — um bom padrão, mas não um motivo para enviar mais do que o necessário.