SSL: автоматическое обновление

Хостинг с DirectAdmin — сертификат обновляется сам

Задача cron раз в сутки забирает свежий сертификат и ставит его в DirectAdmin через API по ключу входа, которому разрешён только SSL. Пароль не нужен.

Обновляется сам

Подходит для хостинга с панелью DirectAdmin: NIC.UA, Tucha и многие другие. Признак DirectAdmin — адрес входа заканчивается на :2222, в меню есть Account Manager, Advanced Features, System Info & Files.

Как это работает: скрипт раз в сутки забирает сертификат по ссылке и, если он новый, передаёт его панели вместе с вашим ключом — так же, как вы вставили бы его руками в SSL Certificates. В панель он входит не паролем, а отдельным ключом входа (Login Key), которому разрешено одно действие — установить сертификат. Ваш пароль от панели и от хостинга мы не просим и не видим.

Что понадобится

  • Ссылка на сертификат из кабинета: https://api.zrazu.com/ssl-pull/zsp_ВАШ_ТОКЕН.
  • Закрытый ключ, с которым заказывали сертификат. Если CSR создавал кабинет ZRAZU — это файл, который вы тогда сохранили.
  • Раздел Cron Jobs в панели (есть на большинстве тарифов) или любой сервер Linux с curl и openssl.
  • 15 минут.

Шаг 1. Первая установка (если сертификат ещё не стоит)

В Account Manager → SSL Certificates выберите Paste a pre-generated certificate and key, вставьте в одно поле сначала ключ, под ним — сертификат, сохраните. Затем Click Here to paste a CA Root Certificate, вставьте цепочку (chain.pem из карточки сертификата), отметьте Use a CA Cert и сохраните. Подробности — в замене сертификата в панели вручную.

Шаг 2. Ключ входа только для SSL

Account Manager → Login Keys → Create Key (в старых темах — Login Keys на главной):

  • Key Name — zrazu-ssl;
  • Key Value — нажмите Random и скопируйте значение: его покажут только раз;
  • Expires On — Never (ключ нужен каждый день);
  • Commands — отметьте только CMD_API_SSL; без этого ключ открывает всю панель;
  • Allowed IPs — IP, с которого будет запускаться скрипт. Если это cron самого хостинга — IP сервера (System Info & Files → System Information). Поле пустое — ключ действует с любого адреса;
  • Current Password — ваш пароль от панели, чтобы подтвердить создание.

Если пункта Login Keys нет — хостинг отключил эту возможность. Тогда остаётся ручная замена, мы напомним о ней заранее.

Шаг 3. Файлы в домашнем каталоге

В System Info & Files → File Manager создайте в домашнем каталоге (над domains и public_html) папку .zrazu и в ней три файла:

  • key.pem — закрытый ключ вместе со строками -----BEGIN … PRIVATE KEY----- и -----END … PRIVATE KEY-----;
  • login — одна строка в кавычках: имя пользователя панели, двоеточие, значение ключа входа:
user = "ИМЯ_ПОЛЬЗОВАТЕЛЯ:ЗНАЧЕНИЕ_КЛЮЧА"
  • zrazu-ssl.sh — скрипт ниже. Измените в нём только строку PANEL — адрес, по которому вы заходите в панель, с :2222, без пути.

Всем трём файлам поставьте права 600 (Permissions в File Manager): читать их может только ваша учётная запись.

#!/bin/sh
# ZRAZU: обновление SSL-сертификата в DirectAdmin по личной ссылке. Запускается cron раз в сутки.
# Ключ остаётся у вас; в панель сертификат идёт, только если он подходит к ключу.
set -eu
PATH=/usr/local/bin:/usr/bin:/bin
PULL="https://api.zrazu.com/ssl-pull/zsp_ВАШ_ТОКЕН"
DOMAIN="example.com"
PANEL="https://server.example.com:2222"
DIR="$HOME/.zrazu"
KEY="$DIR/key.pem"
# Имя и ключ входа — в файле, а не в строке запуска: так их не видно другим пользователям сервера.
LOGIN="$DIR/login"

umask 077
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
TAG=""
[ -f "$DIR/$DOMAIN.etag" ] && TAG=$(cat "$DIR/$DOMAIN.etag")
CODE=$(curl -fsS --retry 3 --max-time 60 -o "$WORK/full.pem" -D "$WORK/hdr" -w '%{http_code}' -H "If-None-Match: $TAG" "$PULL/fullchain.pem")
if [ "$CODE" = "304" ]; then echo "ZRAZU: сертификат не изменился"; exit 0; fi
grep -q "BEGIN CERTIFICATE" "$WORK/full.pem" || { echo "ZRAZU: по ссылке не сертификат" >&2; exit 1; }
# Первый сертификат в файле — сайта, остальное — цепочка.
awk -v d="$WORK" '/BEGIN CERTIFICATE/{n++} n==1{print > (d "/cert.pem")} n>1{print > (d "/chain.pem")}' "$WORK/full.pem"
if [ "$(openssl x509 -in "$WORK/cert.pem" -noout -pubkey | openssl sha256)" != "$(openssl pkey -in "$KEY" -pubout | openssl sha256)" ]; then
  echo "ZRAZU: сертификат выпущен на другой ключ — ничего не меняю, положите новый ключ в $KEY" >&2
  exit 1
fi
openssl x509 -in "$WORK/cert.pem" -noout -checkend 0 >/dev/null || { echo "ZRAZU: сертификат уже не действует" >&2; exit 1; }
# Панель принимает ключ и сертификат одним полем: сначала ключ, под ним сертификат.
cat "$KEY" "$WORK/cert.pem" > "$WORK/paste.pem"
api() {
  OUT=$(curl -sS --max-time 60 -K "$LOGIN" "$PANEL/CMD_API_SSL" -d "domain=$DOMAIN" -d action=save "$@") || true
  case "$OUT" in
    error=0*) ;;
    *) echo "ZRAZU: панель не приняла: $(printf '%s' "$OUT" | head -c 300)" >&2; exit 1 ;;
  esac
}
api -d type=paste -d request=no --data-urlencode "certificate@$WORK/paste.pem"
if [ -s "$WORK/chain.pem" ]; then
  api -d type=cacert -d active=yes --data-urlencode "cacert@$WORK/chain.pem"
fi
# Метка — только после того, как панель приняла: не получилось — следующий запуск попробует снова.
sed -n 's/^[Ee][Tt][Aa][Gg]: *//p' "$WORK/hdr" | tr -d '\r' > "$DIR/$DOMAIN.etag"
echo "ZRAZU: новый сертификат установлен, действует до $(openssl x509 -in "$WORK/cert.pem" -noout -enddate | cut -d= -f2)"

Скрипт ничего не меняет, если сертификат не изменился, если по ссылке не сертификат, если сертификат не подходит к вашему ключу или уже не действует. Панель отвечает error=0, когда приняла сертификат; любой другой ответ — ошибка, и на следующий день скрипт попробует снова.

Шаг 4. Расписание

Advanced Features → Cron Jobs → Create Cron Job: Minute 17, Hour 4, остальные поля — *, команда:

/bin/sh $HOME/.zrazu/zrazu-ssl.sh >> $HOME/.zrazu/log.txt 2>&1

Минуту можно взять любую — лучше не ровно 00. Чтобы проверить сразу, временно поставьте время на две минуты вперёд, а после запуска откройте .zrazu/log.txt: должно быть «новый сертификат установлен».

Свой сервер Linux вместо cron хостинга — тот же скрипт с HOME на свой каталог, строка crontab -e: 17 4 * * * /bin/sh $HOME/.zrazu/zrazu-ssl.sh >> $HOME/.zrazu/log.txt 2>&1, а в Allowed IPs ключа — IP этого сервера.

Проверка

Откройте https://zrazu.com/tools/ssl/example.com/ — должен быть сертификат ZRAZU с новой датой окончания.

Чтобы хостинг не заменил наш сертификат

Если для домена включён бесплатный сертификат (Free & automatic certificate from Let's Encrypt в SSL Certificates), панель сама продлевает его и может заменить наш. Скрипт ставит сертификат как вставленный вручную, и панель перестаёт продлевать бесплатный; проверьте после первого запуска, что в SSL Certificates выбран вставленный сертификат.

Если не получилось

Это новая инструкция: на живой панели мы её ещё не проверили. Не сработало — напишите нам в кабинете (Обращения) или на почту с текстом из .zrazu/log.txt, исправим инструкцию в тот же день.

  • «панель не приняла» и в ответе HTML со словами Cannot Execute Your Request — ключ входа не имеет права на CMD_API_SSL или запуск идёт с IP вне Allowed IPs. Проверьте шаг 2.
  • error=1 и text=… — панель объяснила причину в text: чаще всего домен записан не так, как в панели (без *. и без www).
  • curl: (60) или SSL certificate problem — в PANEL адрес, на который у панели другой сертификат. Возьмите адрес точно из строки браузера, когда заходите в панель.
  • openssl: not found — на этом хостинге нет openssl; запускайте скрипт со своего сервера Linux.
  • curl: (22) … 404 — ссылка отключена или заменена: создайте новую в карточке сертификата и замените строку PULL.

Эта страница в Markdown