#!/bin/bash
# Переезд серверов Ubuntu/Debian со старого корня CN=V-CA-LNX-LANPLM Root CA
# на новый CN=V-PLM-CA.
#
#   sudo bash LANPLM_Cert_migrate.sh
#
# Порядок намеренный: сначала ставится новый корень, потом убирается старый.
# Если новый CA недоступен — скрипт выходит, ничего не удалив, чтобы сервер
# не остался без доверия ни к одному CA.
#
# Требует step-cli. Установка, если его нет:
#   apt-get update && apt-get install -y --no-install-recommends curl gpg ca-certificates
#   curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg \
#       -o /etc/apt/trusted.gpg.d/smallstep.asc
#   echo 'deb [signed-by=/etc/apt/trusted.gpg.d/smallstep.asc] \
#       https://packages.smallstep.com/stable/debian debs main' \
#       > /etc/apt/sources.list.d/smallstep.list
#   apt-get update && apt-get -y install step-cli

set -u

[ "$(id -u)" = 0 ] || { echo "ОШИБКА: запускать от root (sudo)"; exit 1; }

FAIL=0

# --- новый CA (CN=V-PLM-CA) ---
# ca-url для bootstrap идёт с портом, ACME-эндпоинт для certbot — без него,
# как и было со старым CA.
NEW_CA_HOST="${NEW_CA_HOST:-v-plm-sub-ca.lanplm.ru}"
NEW_CA_URL="${NEW_CA_URL:-https://$NEW_CA_HOST}"
NEW_FPR="${NEW_FPR:-d8b9f77e1947c97f04e292bf1201d6b16fceb9765b87163acc6996d998fc378b}"
NEW_ACME="${NEW_ACME:-https://$NEW_CA_HOST/acme/acme/directory}"

# --- старый CA ---
OLD_SHA1=5032B0980DA341D188A33888A8C7B36E5EFE8169
OLD_HOST="${OLD_HOST:-192.168.223.15}"

# Отпечаток первого сертификата в файле; понимает и PEM, и DER.
fp1() {
    { openssl x509 -in "$1" -noout -fingerprint -sha1 2>/dev/null ||
      openssl x509 -in "$1" -inform DER -noout -fingerprint -sha1 2>/dev/null; } |
    tr -d ':' | sed 's/.*=//'
}

# --- различия дистрибутивов ---
# Debian/Ubuntu: /usr/local/share/ca-certificates + update-ca-certificates
# Fedora/RHEL:   /etc/pki/ca-trust/source/anchors + update-ca-trust
if command -v update-ca-trust >/dev/null 2>&1; then
    TRUST_UPDATE="update-ca-trust extract"
    BUNDLE=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
else
    TRUST_UPDATE="update-ca-certificates --fresh"
    BUNDLE=/etc/ssl/certs/ca-certificates.crt
fi

echo "== 1. новый корень =="
if command -v step >/dev/null 2>&1; then
    step ca bootstrap --ca-url "$NEW_CA_URL" --fingerprint "$NEW_FPR" --install --force &&
        echo "step: новый CA установлен" ||
        { echo "ОШИБКА: bootstrap не прошёл, старый корень не трогаю"; exit 1; }
else
    echo "ОШИБКА: нет step-cli, ставить новый корень нечем"; exit 1
fi

echo "== 2. старый корень =="
# Ищем по отпечатку: step ставит файл с именем вида <subject>_<серийник>.crt,
# по имени такое не найти. Связки из нескольких сертификатов не трогаем.
find /usr/local/share/ca-certificates /usr/share/ca-certificates /etc/ssl/certs \
     /etc/pki/ca-trust/source/anchors /etc/docker/certs.d -type f \
     ! -name 'ca-certificates.crt' ! -name '*-ca-bundle.pem' -print0 2>/dev/null |
while IFS= read -r -d '' f; do
    n=$(grep -c 'BEGIN CERTIFICATE' "$f" 2>/dev/null)
    [ "${n:-99}" -le 1 ] || continue                 # связка из нескольких PEM
    [ "$(fp1 "$f")" = "$OLD_SHA1" ] || continue      # 0 PEM-блоков = возможно DER, fp1 разберётся
    rm -v "$f"
    if [ -f /etc/ca-certificates.conf ]; then
        # Только строки, оканчивающиеся именно этим именем файла.
        b=$(printf '%s' "${f##*/}" | sed 's/[].[^$*\\%]/\\&/g')
        sed -i "\%\(^\|/\)$b\$%d" /etc/ca-certificates.conf
    fi
    case "$f" in
        /etc/docker/certs.d/*)
            # update-ca-certificates этот каталог не трогает: кладём новый корень
            # на место старого, иначе docker потеряет доступ к registry.
            cp "$(step path)/certs/root_ca.crt" "$f" &&
                echo "docker: заменён на новый корень: $f" ;;
    esac
done
if $TRUST_UPDATE >/dev/null 2>&1; then
    echo "связка пересобрана ($TRUST_UPDATE)"
else
    echo "ОШИБКА: не удалось пересобрать связку ($TRUST_UPDATE)"; exit 1
fi

echo "== 3. задания certbot =="
# Иначе продление будет ходить на ACME-эндпоинт старого CA и падать.
OLD_HOST_RE=$(printf '%s' "$OLD_HOST" | sed 's/\./\\./g')
grep -rlZ "$OLD_HOST_RE" /etc/letsencrypt/renewal/ 2>/dev/null |
while IFS= read -r -d '' r; do
    sed -i "s|https://$OLD_HOST_RE[^[:space:]]*/acme/acme/directory|$NEW_ACME|g" "$r"
    echo "переключен на новый CA: $r"
done

echo "== 4. проверка =="
T=$(mktemp -d); OLD_HIT=0; NEW_HIT=0
awk -v d="$T" '/BEGIN CERT/{if(out)close(out); n++; out=d "/c" n ".pem"} n{print > out}' \
    "$BUNDLE" 2>/dev/null
for c in "$T"/*.pem; do
    [ -e "$c" ] || break
    [ "$(fp1 "$c")" = "$OLD_SHA1" ] && OLD_HIT=1
    [ "$(openssl x509 -in "$c" -noout -fingerprint -sha256 2>/dev/null |
         tr -d ':' | sed 's/.*=//' | tr 'A-Z' 'a-z')" = "$NEW_FPR" ] && NEW_HIT=1
done
rm -rf "$T"
[ "$OLD_HIT" = 1 ] && { echo "ПРОВАЛ: старый корень ещё в связке"; FAIL=1; } ||
    echo "ОК: старого корня нет"
[ "$NEW_HIT" = 1 ] && echo "ОК: новый корень в связке" ||
    { echo "ВНИМАНИЕ: нового корня в связке нет"; FAIL=1; }

echo
echo "Дальше руками:"
echo "  1. ACME-аккаунт certbot привязан к старому серверу — перерегистрировать:"
echo "     certbot register --server $NEW_ACME  (и обновить account= в renewal-конфигах)"
echo "     или перевыпустить через certbot certonly --server $NEW_ACME"
echo "  2. Перевыпуск сертификатов:"
echo "     REQUESTS_CA_BUNDLE=\$(step path)/certs/root_ca.crt certbot renew --force-renewal"

exit "$FAIL"
