SIRINVIDEO CCTV Документация администратора и пользователя На сайт 2026.08

TLS и сертификаты

Режимы http, self-signed, own-cert и ACME для Plane; у ноды HTTPS работает сразу — как оставить его, взять сертификат Plane или выпустить свой.

TLS в системе два независимых: свой у Plane (админка и API) и свой у ноды (видео и её API). Браузер обращается к обоим напрямую, поэтому у каждого должен быть рабочий сертификат. У ноды он есть с первого запуска — самоподписанный, на её собственное имя; ниже разобрано, как оставить его или заменить на доверенный.

Смешанное содержимое ломает видео

Если админка открыта по https://, а нода отдаёт видео по http://, браузер заблокирует поток: страница загрузится, а плеер останется пустым. Правило простое — обе стороны по HTTPS либо обе по HTTP. У ноды HTTPS работает с первого запуска, поэтому так получается только если ноду намеренно завели по http:// — например за прокси, который сам терминирует TLS.

Режимы доступа Plane#

Режим выбирается при установке и хранится в /etc/hastreamer-cctv/plane.toml в секции [tls]. Названия в установщике и значения в файле различаются — соответствие такое:

Ответ установщику [tls] mode Слушатель public_url Обязательные ключи Когда применять
http off обычный HTTP http:// Изолированная сеть, TLS нигде не нужен
— (только правкой файла) external обычный HTTP https:// Впереди реверс-прокси, который сам терминирует TLS
own-cert pem HTTPS https:// cert, key Сертификат уже куплен или выдан корпоративным центром
self-signed self_signed HTTPS https:// hosts Быстрый старт и тест в локальной сети
acme acme HTTPS https:// hosts, желательно acme_contact Публичный домен — рекомендуемый режим

Порт по умолчанию: 8080 для http, 443 для всех режимов с TLS.

Схема public_url обязана соответствовать режиму

Plane проверяет это при старте и отказывается запускаться при несоответствии:

tls.mode=off requires an http:// public_url
tls.mode=pem requires cert and key paths
tls.mode=self_signed requires at least one hostname
tls.hosts must include the public_url hostname "cctv.example.com"

Отдельно запомните разницу off и external: за реверс-прокси нужен именно external — он отдаёт обычный HTTP, но объявляет https:// во всех ссылках, редиректах и обращениях нод. С off такой public_url будет отвергнут.

Все параметры [tls] читаются один раз при старте. Любое изменение вступает в силу только после sudo systemctl restart hastreamer-cctv-plane.

Переключение self-signed → ACME#

Типовой сценарий: сначала подняли Plane с самоподписанным сертификатом, потом получили домен. Переход поддерживается и не требует переустановки.

Сначала убедитесь, что выполнены оба условия ACME:

getent hosts cctv.example.com          # должен вернуть внешний IP этого сервера
curl -s https://api.ipify.org; echo    # фактический внешний IP сервера — адреса должны совпасть
sudo ss -tlnp | grep -w ':80 '         # порт 80 должен быть свободен (кроме самого Plane)

Правьте plane.toml точечно — в файле лежит пароль базы, пересоздавать его нельзя:

sudo sed -i 's/^mode = "self_signed"/mode = "acme"/' /etc/hastreamer-cctv/plane.toml
grep -q '^acme_contact' /etc/hastreamer-cctv/plane.toml || \
  sudo tee -a /etc/hastreamer-cctv/plane.toml >/dev/null <<'EOF'
acme_contact = "admin@example.com"
acme_listen = "0.0.0.0:80"
EOF
sudo systemctl restart hastreamer-cctv-plane
Сертификат действительно выпущен
sudo journalctl -u hastreamer-cctv-plane -b | grep -iE "issuing via ACME|issuance.*succeeded"

Ожидаемые строки:

persisted plane identity is self-signed, not CA-issued — issuing via ACME
ACME plane identity issuance/renewal succeeded and was activated

И проверка снаружи — издатель должен быть настоящим центром сертификации:

echo | openssl s_client -connect cctv.example.com:443 -servername cctv.example.com 2>/dev/null \
  | openssl x509 -noout -issuer -dates

Обратный переход выполняется так же: mode = "self_signed" и перезапуск. Строки acme_* можно оставить — в режиме self_signed они не используются.

Требования ACME и продление#

Проверка Let's Encrypt (HTTP-01) устроена одинаково для Plane и для ноды:

  1. Домен — публичный и резолвится на этот хост. IP-адреса и wildcard-имена не подходят: ACME HTTP-01 requires DNS hostnames, not IP literals.
  2. Порт 80 доступен из интернета — на него приходит запрос http://cctv.example.com/.well-known/acme-challenge/<токен>.
  3. Проверка отвечает на отдельном обычном HTTP-слушателе (acme_listen, по умолчанию 0.0.0.0:80). Он обязан отличаться от основного listen, и порт 80 не должен занимать другой веб-сервер.
  4. Разрешён исходящий HTTPS к центру сертификации.

Продление полностью автоматическое и не настраивается:

Что Значение
Продление начинается за 30 суток до истечения
Периодичность проверки каждые 12 часов
Повтор после неудачи раз в час
Порт 80 нужен и после установки

Продление использует ту же проверку, что и первый выпуск. Если после установки закрыть порт 80, сертификат перестанет обновляться, и примерно через два месяца сайт станет недоступен по HTTPS.

Plane не падает из-за сертификата

Если выпустить сертификат не удалось, Plane всё равно поднимется — на временном самоподписанном сертификате (браузер предупредит) и продолжит попытки в фоне. В API это видно как temporary_self_signed: true. Как только DNS и порт 80 исправлены, настоящий сертификат подставляется автоматически, без перезапуска сервиса.

Свой сертификат Plane (own-cert)#

[tls]
mode = "pem"
cert = "/etc/hastreamer-cctv/plane-cert.pem"
key  = "/etc/hastreamer-cctv/plane-key.pem"

cert — цепочка в PEM: сначала сертификат сервера, затем промежуточные. key — приватный ключ. Список hosts в этом режиме не нужен: имена берутся из самого сертификата.

# права на ключ — только root
sudo chown root:root /etc/hastreamer-cctv/plane-key.pem
sudo chmod 600 /etc/hastreamer-cctv/plane-key.pem

# проверка, что ключ и сертификат — пара (обе строки должны совпасть)
openssl x509 -noout -pubkey -in /etc/hastreamer-cctv/plane-cert.pem | openssl sha256
openssl pkey -pubout      -in /etc/hastreamer-cctv/plane-key.pem  | openssl sha256
Продление в этом режиме — ваша задача

Автоматики нет. После замены файлов обязательно перезапустите сервис: sudo systemctl restart hastreamer-cctv-plane. Заведите напоминание за 30 суток до истечения — проверить срок можно командой openssl x509 -noout -enddate -in /etc/hastreamer-cctv/plane-cert.pem.

Plane за реверс-прокси (external)#

Минимальная конфигурация: Plane слушает только петлевой интерфейс, TLS делает прокси.

listen = "127.0.0.1:8080"
public_url = "https://cctv.example.com"
database_url = "postgres://hastreamer_cctv:ПАРОЛЬ@127.0.0.1:5432/cctv"

[tls]
mode = "external"

Настройте прокси так, чтобы он перезаписывал (а не дополнял) заголовок X-Forwarded-For: Plane берёт из него IP клиента для аудита и ограничения частоты запросов. Привязка listen к 127.0.0.1 гарантирует, что напрямую, в обход прокси, до Plane не достучаться.

TLS ноды#

Нода отдаёт видео браузеру напрямую, поэтому сертификат ей нужен собственный. Он работает сразу после установки: стартовая конфигурация, которую выдаёт Plane, уже содержит TLS-слушатель и самоподписанную пару, выпущенную на настоящее имя ноды — то, что стоит в её адресе управления.

http 80;                     # обычный слушатель: проверка ACME и редирект на HTTPS
https 443;                   # видео, плеер и API ноды поверх TLS
rtsp 8554;
cores 0;
log_level info;

tls {
  self_signed_names node.example.com;   # самоподписанная пара на имя ноды
}

Прежней двухэтапной установки — «сначала обычный http, потом руками перевести на https» — больше нет. Схема эффективного адреса управления (Control URL, а если он пуст — Public URL) решает всё один раз, при создании ноды:

Эффективный адрес управления Слушатели ноды Собственный TLS
https://node.example.com http 80; + https 443; да, самоподписанный на имя ноды
https://cctv.example.com:9443 http 9080; + https 9443; да; так ставится нода рядом с Plane, которому принадлежат 80 и 443
http://node.example.com:8090 http 8090; нет — TLS терминирует прокси впереди
Ноду со своим TLS нельзя опрашивать по http://

Такая нода перенаправляет любой обычный HTTP-запрос на HTTPS, а при перенаправлении теряется заголовок авторизации: Plane получает 401, управление молча перестаёт работать, и нода уходит в offline, хотя видео раздаёт. Именно поэтому адрес управления задаётся по https:// с самого начала.

Если нода уже заведена с http://, приведите оба её адреса к https://: Ноды → нода → Сеть и TLS → Адреса ноды → Изменить. Дополнительный эффект: конфигурация с логинами и паролями камер уходит на ноду в шифрованном виде, а не открытым текстом.

Самоподписанный сертификат браузер доверенным не считает — при первом заходе на страницу с видео он предупредит. Дальше есть три пути.

Вариант Когда подходит Что делать
Оставить самоподписанный закрытый контур, операторы работают в той же сети и один раз принимают исключение ничего
Взять сертификат Plane нода стоит на одном хосте с Plane (совмещённая установка) переключить владельца сертификата в админке
Выпустить свой по ACME нода на отдельном сервере с публичным доменным именем добавить блок certificate { acme; }

Сертификат Plane (нода на одном хосте с Plane)#

Совмещённой ноде ACME недоступен: проверка Let's Encrypt приходит строго на порт 80, а его на этом хосте занял Plane. Отдельный сертификат ей и не нужен — она берёт сертификат Plane.

В админке: Ноды → нода → Сеть и TLS → Управлять TLS, поле «Владелец сертификата» → «Сертификат Plane · тот же хост». Локальный синхронизатор копирует сертификат Plane в снимок ноды и подхватывает его продления сам, без перезапуска и без передачи приватного ключа на другие хосты. Состояние видно тут же: «Сертификаты Plane и Node совпадают».

Совмещённый установщик (installers/hastreamer-cctv-all/install.sh) делает это переключение сам — ставить и настраивать ничего не требуется. Подробности о вариантах владельца — Ноды и флот.

Доверие берётся у Plane

Нода отдаёт ровно тот сертификат, который отдаёт Plane. Если Plane работает в режиме acme, доверенным становится и адрес ноды; если Plane самоподписанный, предупреждение браузера останется и на ноде — лечится переводом Plane на acme.

Свой сертификат по ACME (отдельный сервер ноды)#

Вариант для ноды на собственном хосте с публичным именем. Отредактируйте /etc/hastreamer-cctv-node/node.confтолько верхнюю, системную часть файла, выше управляемой Plane секции — и добавьте блок certificate:

http 80;                     # сюда приходит проверка HTTP-01
https 443;                   # видео отдаётся по HTTPS
rtsp 8554;
cores 0;
log_level info;

tls {
  self_signed_names node.example.com;   # запасная пара: работает, пока ACME не выпустил
}

certificate le {             # имя блока произвольное
  hosts node.example.com;    # только конкретные имена, без wildcard
  acme;                      # выпуск и продление автоматически
}

Условия выпуска на ноде — те же четыре, что выше, плюс два специфических:

  • Должен быть включён хотя бы один TLS-слушатель (https, rtsps). Без него задача выпуска вообще не стартует — сертификат некуда ставить. В стартовой конфигурации отдельной ноды он уже есть.
  • Проверка HTTP-01 отвечает на http-слушателе ноды, но центр сертификации всегда стучится на порт 80. У отдельной ноды в стартовой конфигурации стоит http 80; — это и нужно. Если слушатель перенесён на другой порт (http 9080; у совмещённой ноды, http 8090; за прокси), проверка не дойдёт: верните http 80; либо пробросьте внешний 80 на порт слушателя.
sudo systemctl reload hastreamer-cctv-node
sudo journalctl -u hastreamer-cctv-node -f | grep -i acme
# ACME certificate "le" issuance attempt started for [node.example.com]
# acme: issued/renewed cert(s) hot-swapped into the SNI store

Выпуск обычно занимает 30–90 секунд: фоновая задача сканирует конфигурацию раз в минуту. Готовый сертификат подставляется «на лету», без перезапуска и без разрыва сессий зрителей. Продление начинается за 30 суток; после неудачи интервал повторов растёт с 15 минут до 6 часов, поэтому запаса времени достаточно.

Тестовый центр сертификации для отладки

Пока настраиваете новый хост, используйте тестовый каталог — его лимиты гораздо мягче:

acme_directory https://acme-staging-v02.api.letsencrypt.org/directory;

Сертификаты оттуда браузер не признаёт — это нормально, они нужны только чтобы убедиться, что проверка проходит. Затем уберите строку и перезапустите ноду (acme_directory применяется только при рестарте).

Готовый сертификат ноды#

Если сертификат уже есть, укажите файлы вместо acme:

certificate node-public {
  hosts node.example.com;
  cert  certs/node-public/fullchain.pem;   # абсолютный путь или относительно хранилища TLS
  key   certs/node-public/privkey.pem;
  ca    certs/node-public/chain.pem;       # необязательно
}

Хранилище TLS ноды — /var/lib/hastreamer-cctv-node/tls-state; относительные пути считаются от него. Одновременно указать автоматический выпуск и готовые файлы нельзя — конфигурация будет отвергнута:

certificate "node-public": `acme` and explicit `cert`/`key` are mutually exclusive

Проверка#

# Plane: издатель и сроки действия
echo | openssl s_client -connect cctv.example.com:443 -servername cctv.example.com 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates

# Нода: то же самое
echo | openssl s_client -connect node.example.com:443 -servername node.example.com 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates

# цепочка валидна целиком (verify=0 — успех)
curl -sS -o /dev/null -w '%{http_code} verify=%{ssl_verify_result}\n' https://cctv.example.com/admin/

Для самоподписанного сертификата verify будет отличен от нуля — это ожидаемо и не является ошибкой.

Диагностика#

Симптом Вероятная причина Что сделать
ACME … issuance failed в журнале Порт 80 закрыт снаружи или DNS не указывает на этот хост Проверьте getent hosts и межсетевой экран; Plane продолжит попытки сам
Браузер предупреждает о сертификате Plane Режим self_signed либо временный сертификат после неудачного выпуска Перейдите на acme (см. выше) и убедитесь, что выпуск удался
Нода недоступна в админке, хотя раздаёт видео Адрес управления http:// у ноды со своим TLS: при редиректе теряется авторизация Приведите оба адреса ноды к https://TLS ноды
Плеер пустой, в консоли браузера — mixed content Админка по HTTPS, нода по HTTP Выпустите сертификат ноде и переведите public_url на https://
Нода не запускается после правки node.conf Ошибка синтаксиса — конфигурация не проходит проверку journalctl -u hastreamer-cctv-node -n 50: сообщение содержит номер строки
Сертификат ноды не выпускается Нет TLS-слушателя, wildcard в hosts или порт 80 не приходит на http-слушатель Задайте https, укажите конкретное имя, пробросьте порт 80
Address already in use при старте Plane Порт 80 или 443 занят другим веб-сервером Освободите порт либо перейдите на режим external и поставьте прокси впереди
Проверяйте правку ноды перезагрузкой конфигурации, а не перезапуском

sudo systemctl reload hastreamer-cctv-node проверяет файл и применяет изменения «на лету». При ошибке нода продолжит работать на прежней конфигурации и напишет в журнал config reload REJECTED: … (running config unchanged). Перезапуск же при ошибке в файле уронит сервис в цикл рестартов. Перезапуск нужен только для смены портов и параметров тюнинга — см. node.conf.