136 lines
6.8 KiB
Bash
Executable File
136 lines
6.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Relógio do SERVIDOR sincronizado com NTP autenticado (NTS).
|
|
# Rodar UMA VEZ no servidor, como root: sudo bash deploy/ntp-seguro.sh
|
|
#
|
|
# ─── POR QUE NO HOST E NÃO NO CONTAINER ──────────────────────────────────────
|
|
# Container não tem relógio próprio: ele lê o clock do kernel do host. Rodar
|
|
# chrony/ntpd dentro do container exigiria CAP_SYS_TIME e mudaria a hora do
|
|
# SERVIDOR INTEIRO a partir de dentro de um processo da aplicação — por isso
|
|
# não se faz. Dentro do container a gente fixa só o FUSO (TZ, no Dockerfile);
|
|
# a HORA vem daqui.
|
|
#
|
|
# ─── POR QUE NTS E NÃO NTP COMUM ─────────────────────────────────────────────
|
|
# NTP puro é UDP sem autenticação: qualquer um no caminho pode responder no
|
|
# lugar do servidor e empurrar a hora da máquina. Isso não é teórico para este
|
|
# sistema — hora errada muda o dia da consolidação, o recorte do período
|
|
# financeiro e a validade de sessão/cookie. NTS (RFC 8915) faz uma troca de
|
|
# chaves por TLS (porta TCP 4460) e assina os pacotes NTP, então uma resposta
|
|
# forjada é descartada. O `chronyc authdata` no fim mostra se está valendo.
|
|
#
|
|
# ─── REDE ────────────────────────────────────────────────────────────────────
|
|
# Precisa de saída para: UDP 123 (NTP) e TCP 4460 (NTS-KE).
|
|
# Se o firewall bloquear a 4460, o NTS não fecha e o chrony fica sem fonte.
|
|
set -euo pipefail
|
|
|
|
[ "$(id -u)" -eq 0 ] || { echo "ERRO: rode com sudo."; exit 1; }
|
|
command -v apt-get >/dev/null || { echo "ERRO: script escrito para Debian/Ubuntu."; exit 1; }
|
|
|
|
echo "==> Hora ANTES:"; date; echo
|
|
|
|
# ── 1. chrony ────────────────────────────────────────────────────────────────
|
|
# NTS só existe no chrony >= 4.0 (Debian 11+ / Ubuntu 22.04+ já têm).
|
|
if ! command -v chronyd >/dev/null; then
|
|
echo "==> Instalando chrony"
|
|
apt-get update -qq
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y chrony
|
|
fi
|
|
|
|
versao="$(chronyd --version 2>&1 | grep -oE '[0-9]+\.[0-9]+' | head -1)"
|
|
if [ "${versao%%.*}" -lt 4 ]; then
|
|
echo "ERRO: chrony $versao não fala NTS (precisa >= 4.0). Atualize o sistema."
|
|
exit 1
|
|
fi
|
|
echo "==> chrony $versao (NTS disponível)"
|
|
|
|
# ── 2. Fontes NTS ────────────────────────────────────────────────────────────
|
|
# Três fontes independentes de propósito: com uma só, ela cai e o relógio fica
|
|
# à deriva sem ninguém notar; com três, o chrony ainda descarta a que divergir.
|
|
CONF_NTS="$(cat <<'CONF'
|
|
# Gerado por deploy/ntp-seguro.sh — fontes de tempo AUTENTICADAS (NTS).
|
|
# `iburst`: acerta rápido no boot, em vez de levar minutos.
|
|
server time.cloudflare.com iburst nts
|
|
server nts.netnod.se iburst nts
|
|
server ptbtime1.ptb.de iburst nts
|
|
|
|
# Cache das chaves NTS: sem isto, todo restart refaz a troca TLS e o relógio
|
|
# demora a fechar quando a internet ainda não subiu.
|
|
ntsdumpdir /var/lib/chrony
|
|
|
|
# Sincroniza também o relógio de hardware (RTC), que é de onde a máquina lê a
|
|
# hora ao ligar — é o que evita voltar errado depois de um reboot/queda de luz.
|
|
rtcsync
|
|
|
|
# makestep: corrige de uma vez (em vez de "escorregar" devagar) quando o erro
|
|
# é grande, mas só nas 3 primeiras medições. É a linha certa para servidor
|
|
# físico. SE ESTE SERVIDOR FOR VM QUE HIBERNA/PAUSA e a hora só aparece errada
|
|
# depois de uma pausa longa, troque pela linha comentada abaixo — ela permite
|
|
# o salto a qualquer momento (inclusive para trás, então saiba o que ganha):
|
|
makestep 1.0 3
|
|
# makestep 1.0 -1
|
|
CONF
|
|
)"
|
|
|
|
CHRONY_CONF=/etc/chrony/chrony.conf
|
|
[ -f "$CHRONY_CONF" ] || CHRONY_CONF=/etc/chrony.conf
|
|
|
|
cp -n "$CHRONY_CONF" "$CHRONY_CONF.bak-ntp-seguro" 2>/dev/null || true
|
|
|
|
# Onde escrever: o Debian/Ubuntu moderno lê o diretório conf.d. Se essa linha
|
|
# não existir na config, escrever lá seria IGNORADO EM SILÊNCIO — nesse caso a
|
|
# gente anexa no próprio chrony.conf, entre marcadores, para continuar sendo
|
|
# possível rodar o script de novo sem duplicar.
|
|
if grep -qE '^\s*confdir\s+.*conf\.d' "$CHRONY_CONF" 2>/dev/null; then
|
|
mkdir -p /etc/chrony/conf.d
|
|
printf '%s\n' "$CONF_NTS" > /etc/chrony/conf.d/nts-reem.conf
|
|
echo "==> Fontes NTS em /etc/chrony/conf.d/nts-reem.conf"
|
|
else
|
|
sed -i '/# >>> nts-reem >>>/,/# <<< nts-reem <<</d' "$CHRONY_CONF"
|
|
{ echo "# >>> nts-reem >>>"; printf '%s\n' "$CONF_NTS"; echo "# <<< nts-reem <<<"; } >> "$CHRONY_CONF"
|
|
echo "==> Fontes NTS anexadas em $CHRONY_CONF"
|
|
fi
|
|
|
|
# ── 3. Desligar as fontes NÃO autenticadas ───────────────────────────────────
|
|
# Deixar o `pool` padrão junto anularia o ganho: o chrony poderia se decidir
|
|
# por uma fonte sem assinatura. Só comentamos (o .bak fica ao lado).
|
|
sed -i -E 's/^([[:space:]]*(pool|server)[[:space:]]+.*)$/# [nts-reem] \1/' "$CHRONY_CONF"
|
|
sed -i -E '/# >>> nts-reem >>>/,/# <<< nts-reem <<</ s/^# \[nts-reem\] (.*)$/\1/' "$CHRONY_CONF"
|
|
for f in /etc/chrony/sources.d/*.sources; do
|
|
[ -e "$f" ] || continue
|
|
mv "$f" "$f.desativado-ntp-seguro"
|
|
done
|
|
|
|
# ── 4. Um serviço de hora só ─────────────────────────────────────────────────
|
|
# systemd-timesyncd e chrony brigam pelo relógio; o timesyncd não fala NTS.
|
|
systemctl disable --now systemd-timesyncd 2>/dev/null || true
|
|
systemctl enable --now chrony 2>/dev/null || systemctl enable --now chronyd
|
|
|
|
echo "==> Reiniciando chrony"
|
|
systemctl restart chrony 2>/dev/null || systemctl restart chronyd
|
|
sleep 5
|
|
chronyc makestep >/dev/null 2>&1 || true
|
|
|
|
# ── 5. Conferência ───────────────────────────────────────────────────────────
|
|
echo
|
|
echo "==> Fontes (o '^*' marca a que está valendo):"
|
|
chronyc -N sources -v || true
|
|
echo
|
|
echo "==> NTS (queremos 'KeyID/Type/KLen' preenchido e Cook > 0 nas 3 linhas):"
|
|
chronyc -N authdata || true
|
|
echo
|
|
echo "==> Estado:"; chronyc tracking || true
|
|
timedatectl || true
|
|
echo
|
|
echo "==> Hora DEPOIS:"; date
|
|
cat <<'FIM'
|
|
|
|
Pronto. O relógio agora se mantém sozinho, inclusive depois de reboot.
|
|
|
|
Se a hora tinha saltado, reinicie os containers para os processos longos
|
|
pegarem o clock corrigido: docker compose restart
|
|
|
|
Para conferir depois, a qualquer momento:
|
|
chronyc tracking # 'System time' deve ficar em milissegundos
|
|
chronyc -N authdata # NTS ativo em cada fonte
|
|
FIM
|