SSL: автоматичне оновлення

Сервер Linux — nginx, Apache, Caddy, HAProxy, Docker

Скрипт для nginx, Apache, Caddy, HAProxy і Docker раз на добу забирає свіжий сертифікат, звіряє його з вашим ключем і перезапускає вебсервер без простою.

Оновлюється сам

Підходить, якщо у вас є доступ до сервера по SSH і права root (VPS, виділений сервер, свій комп'ютер у стійці). Потрібні curl і openssl — вони є майже в кожному Linux.

Що знадобиться

  • Посилання на сертифікат із кабінету: https://api.zrazu.com/ssl-pull/zsp_ВАШ_ТОКЕН.
  • Закритий ключ, з яким замовляли сертифікат (файл key.pem чи схожий). Якщо CSR створював кабінет ZRAZU — це файл, який ви тоді зберегли.
  • 10 хвилин.

Крок 1. Покладіть ключ на сервер

sudo mkdir -p /etc/ssl/zrazu/example.com
sudo chmod 700 /etc/ssl/zrazu/example.com
sudo nano /etc/ssl/zrazu/example.com/key.pem
sudo chmod 600 /etc/ssl/zrazu/example.com/key.pem

У nano вставте весь текст ключа разом із рядками -----BEGIN … PRIVATE KEY----- і -----END … PRIVATE KEY-----, збережіть (Ctrl+O, Enter) і вийдіть (Ctrl+X). Ключ без пароля: якщо ваш ключ запаролений, зніміть пароль командою openssl pkey -in старий.key -out /etc/ssl/zrazu/example.com/key.pem.

Крок 2. Скрипт оновлення

Створіть файл sudo nano /usr/local/bin/zrazu-ssl-example.com.sh, вставте текст нижче і змініть лише рядок RELOAD під свій вебсервер:

  • nginx — nginx -t && systemctl reload nginx (так стоїть);
  • Apache (Debian, Ubuntu) — apachectl configtest && systemctl reload apache2; Apache (CentOS, Rocky, Alma) — apachectl configtest && systemctl reload httpd;
  • Caddy — systemctl reload caddy;
  • HAProxy — systemctl reload haproxy і COMBINED=1 (HAProxy читає сертифікат і ключ одним файлом combined.pem);
  • nginx у Docker — docker exec НАЗВА_КОНТЕЙНЕРА nginx -s reload (каталог /etc/ssl/zrazu підключіть у контейнер як том).
#!/bin/sh
# ZRAZU: оновлення SSL-сертифіката за особистим посиланням. Запускається cron раз на добу.
set -eu
# cron дає короткий PATH без /usr/sbin, а nginx і apachectl лежать саме там.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PULL="https://api.zrazu.com/ssl-pull/zsp_ВАШ_ТОКЕН"
DIR="/etc/ssl/zrazu/example.com"
KEY="$DIR/key.pem"
RELOAD="nginx -t && systemctl reload nginx"
COMBINED=0

umask 077
tmp=$(mktemp "$DIR/.new.XXXXXX")
trap 'rm -f "$tmp"' EXIT
curl -fsS --retry 3 --max-time 60 -o "$tmp" "$PULL/fullchain.pem"
grep -q "BEGIN CERTIFICATE" "$tmp" || { echo "ZRAZU: за посиланням не сертифікат"; exit 1; }
if [ -f "$DIR/.applied" ] && cmp -s "$tmp" "$DIR/.applied"; then
  echo "ZRAZU: сертифікат не змінився"
  exit 0
fi
cert_pub=$(openssl x509 -in "$tmp" -noout -pubkey | openssl sha256)
key_pub=$(openssl pkey -in "$KEY" -pubout | openssl sha256)
if [ "$cert_pub" != "$key_pub" ]; then
  echo "ZRAZU: сертифікат випущено на інший ключ — нічого не міняю, покладіть новий ключ у $KEY"
  exit 1
fi
openssl x509 -in "$tmp" -noout -checkend 0 >/dev/null || { echo "ZRAZU: сертифікат уже не діє"; exit 1; }
chmod 644 "$tmp"
mv -f "$tmp" "$DIR/fullchain.pem"
if [ "$COMBINED" = 1 ]; then
  cat "$DIR/fullchain.pem" "$KEY" > "$DIR/combined.pem.new"
  mv -f "$DIR/combined.pem.new" "$DIR/combined.pem"
fi
sh -c "$RELOAD"
cp "$DIR/fullchain.pem" "$DIR/.applied"
echo "ZRAZU: новий сертифікат встановлено, діє до $(openssl x509 -in "$DIR/fullchain.pem" -noout -enddate | cut -d= -f2)"

Скрипт нічого не міняє, якщо вебсервер уже працює з цим сертифікатом, якщо файл за посиланням не сертифікат, якщо сертифікат не підходить до вашого ключа чи вже не діє. Конфігурацію вебсервера він перевіряє перед перезапуском, тож зламаний конфіг сайт не покладе; а якщо перезапуск не вдався, наступного дня скрипт спробує ще раз.

Зробіть файл виконуваним і запустіть один раз руками:

sudo chmod 700 /usr/local/bin/zrazu-ssl-example.com.sh
sudo /usr/local/bin/zrazu-ssl-example.com.sh

Має з'явитися «новий сертифікат встановлено» і файл /etc/ssl/zrazu/example.com/fullchain.pem.

Крок 3. Вкажіть файли у вебсервері (один раз)

nginx — у блоці server сайту:

ssl_certificate     /etc/ssl/zrazu/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/zrazu/example.com/key.pem;

Apache 2.4.8 і новіший — у <VirtualHost *:443>:

SSLCertificateFile    /etc/ssl/zrazu/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/zrazu/example.com/key.pem

Caddy — у блоці сайту tls /etc/ssl/zrazu/example.com/fullchain.pem /etc/ssl/zrazu/example.com/key.pem. HAProxy — у bind :443 ssl crt /etc/ssl/zrazu/example.com/combined.pem.

Перевірте конфіг (nginx -t чи apachectl configtest) і перезапустіть вебсервер.

Крок 4. Розклад

echo "17 4 * * * root /usr/local/bin/zrazu-ssl-example.com.sh >> /var/log/zrazu-ssl.log 2>&1" | sudo tee /etc/cron.d/zrazu-ssl-example.com

Скрипт запускатиметься щодня о 04:17 за часом сервера. Хвилину можна взяти будь-яку — краще не рівно 00.

Перевірка

  1. sudo tail /var/log/zrazu-ssl.log — завтра там з'явиться «сертифікат не змінився»: розклад працює.
  2. Сайт віддає наш сертифікат: echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -issuer -enddate або сторінка https://zrazu.com/tools/ssl/example.com/.
  3. У кабінеті, у картці сертифіката, видно, що сервер забрав чинний сертифікат.

Якщо не вийшло

  • curl: (22) … 404 — посилання вимкнене чи замінене. У картці сертифіката створіть нове і замініть рядок PULL у скрипті.
  • curl: (22) … 429 — забагато запитів (понад 60 на годину). Раз на добу — з великим запасом.
  • «сертифікат випущено на інший ключ» — сертифікат перевипускали з новим ключем. Покладіть новий ключ у key.pem і запустіть скрипт ще раз.
  • nginx: [emerg] після перезапуску — помилка в конфігурації сайту, не в сертифікаті; старий сертифікат лишається в роботі, доки помилку не виправите.

Ця сторінка в Markdown