DigraphDocs Docker Infrastructure Graph

Docs / Эксплуатация

Секреты, бэкапы, алерты

Штатные операции: доставка секретов в контейнеры, резервное копирование и восстановление, уведомления об инцидентах, SSO/TLS и плановая ротация ключа.

Секреты

Секрет — это именованный JSON (поле content), зашифрованный AES-256-GCM. Ключ шифрования (data key) свой у каждой записи; при заданном SECRET_MASTER_KEY он оборачивается мастер-ключом.

Привязка и режимы

  • Привязка — список имён контейнеров через запятую; сравнение точное, по имени контейнера (не сервиса compose).
  • Режим file — файл по пути file_path или /run/secrets/<project>.json.
  • Режим env — /etc/profile.d/<project>.sh с export KEY="value" и source в rc-файлы; влияет только на новые shell-сессии.

Когда выполняется доставка

  1. На событие container.start — всем привязанным секретам.
  2. При старте приложения и переподключении к Docker — reconcile запущенных контейнеров (дебаунс 30 секунд).
  3. Вручную — кнопка в строке секрета, POST /api/secrets/{id}/push.
  4. Автоматически после сохранения секрета, если включены push и exec.

Проверка доставки

Перед запуском команды Digraph сверяет целевой путь с картой mount'ов контейнера. Побеждает самый длинный mount, поэтому read-only mount перекрывает writable-родителя. Недоступный путь отклоняется сразу, без exec:

GET /api/secrets/targets
# [{"name":"vuln-scanner","read_only":true,
#   "mounts":[{"path":"/cache","kind":"volume","rw":true},
#             {"path":"/tmp","kind":"tmpfs","rw":true}]}]

POST /api/secrets/1/push
# {"results":[{"container":"vuln-scanner","status":"pushed","path":"/cache/demo.json"}]}

Если команда всё же выполнилась с ошибкой, в results[] будет status: "error" и текст из контейнера — например, Read-only file system.

Ограничения

  • Нужен /bin/sh: distroless и scratch не поддерживаются.
  • Read-only rootfs: используйте tmpfs или writable mount; иначе push отклоняется.
  • Контейнер с USER без прав на каталог: укажите writable путь или tmpfs с нужным uid.
  • Файл живёт в writable layer: пересоздание контейнера его стирает; авто-push выполнится на старте.
  • Нет истории версий: сохранение перезаписывает содержимое.

Бэкапы и восстановление

Архив содержит SQLite (VACUUM INTO) и каталог compose-проектов, плюс манифест с размерами и SHA-256 каждого файла.

ПараметрГдеЗначение
Расписание/#/settings и /#/backups1–168 часов
Ретенциятам же1–50 архивов
КаталогBACKUP_DIR/data/backups
RestoreBACKUP_RESTORE_ENABLED=trueпо умолчанию выключен

Restore двухфазный: архив проверяется (манифест, размеры, SHA-256, отсутствие path traversal, лимит распаковки 4 GiB) и ставится в очередь; подмена базы и проектов происходит на следующем старте.

# поставить локальный архив в очередь
curl -X POST localhost:9090/api/backup/files/<name>/restore \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"confirm":"RESTORE"}'

docker compose restart docker-stat   # применить на старте

Bootstrap-секреты в архив не входят. AUTH_TOKEN, JWT_SIGNING_KEY и SECRET_MASTER_KEY лежат в зашифрованном override-файле, а PORTAL_TOKEN — в .env. Храните их отдельно; в манифест пишется только отпечаток.

Обновление версии

Образы версионированы: обновление — смена VERSION в .env, без пересборки. Sidecar'ы обновляются вместе с контуром; миграции SQLite применяются при старте.

cd deploy/contour
sed -i 's/^VERSION=.*/VERSION=v0.2.0/' .env
docker compose -f docker-compose.yml -f docker-compose.contour.override.yml pull
docker compose -f docker-compose.yml -f docker-compose.contour.override.yml up -d

cd ../portal
sed -i 's/^VERSION=.*/VERSION=v0.2.0/' .env
docker compose -f docker-compose.yml -f docker-compose.portal.override.yml pull
docker compose -f docker-compose.yml -f docker-compose.portal.override.yml up -d

Откат — вернуть предыдущий VERSION и повторить. Перед обновлением сделайте архив в /#/backups.

Алерты и вебхуки

Контур

Фоновый evaluator работает без открытого браузера. Правила: unhealthy, stopped, restarting, CPU/RAM хоста, устаревший сбор. Есть grace-период, состояние и retry-очередь в SQLite, HMAC-SHA256-подпись запросов.

  • Пороги и grace — в /#/settings → Alerts.
  • Пересылка Docker/API-событий настраивается по типам (container, network, volume, image, api).
  • История переходов и статистика доставок доступны в настройках.

Портал

Watchdog опрашивает /api/ready всех контуров и ведёт статусы online / degraded / offline. Каналы уведомлений: generic webhook, Slack incoming webhook, PagerDuty Events v2, SMTP email. Настройка и тест — в /#/settings портала.

SSO и TLS

Портал

OIDC настраивается в /#/settings → Portal → Single sign-on: issuer, client ID, secret, redirect URL, scopes, allowlist по email/домену и текст кнопки. Секрет шифруется и не возвращается в браузер.

  • Redirect: https://<домен>/api/portal/auth/oidc/callback.
  • Пароль портала (PORTAL_TOKEN) остаётся вторым входом.
  • После сохранения клиент OIDC пересоздаётся без рестарта.

TLS на контуре

Сертификаты монтируются в контейнер, пути — внутри его файловой системы. Добавьте в свой override:

services:
  docker-stat:
    volumes:
      - ./certs:/etc/digraph/certs:ro
    environment:
      TLS_CERT_FILE: /etc/digraph/certs/cert.pem
      TLS_KEY_FILE: /etc/digraph/certs/key.pem
      TLS_REDIRECT_ADDR: ":8080"      # HTTP → 308 HTTPS
      TLS_PUBLIC_HOST: digraph.example:8443

Ротация мастер-ключа

  1. /#/settings → Security → Rotate master key, подтверждение словом ROTATE.
  2. Сервер пере-оборачивает все data key секретов и AI-ключ в одной транзакции и показывает новый ключ один раз.
  3. Передайте ключ в контейнер как явную переменную окружения — она перекрывает значение из зашифрованного конфига:
    services:
      docker-stat:
        environment:
          SECRET_MASTER_KEY: "<новый ключ>"
  4. Перезапустите контур: без нового ключа при старте будет предупреждение и [decrypt failed] в ответах.

Env-оверрайд — временное решение. Чтобы вернуться к одному конфигу, перевыпустите override в генераторе с тем же паролем и уберите переменную из compose; при этом сменятся все токены, поэтому токен контура в портале придётся обновить (генератор покажет новый AUTH_TOKEN).