Files
han-app/architectory/arch-06-service-hosting-security.md
T

42 KiB
Raw Blame History

arch-06. Стандарт безопасности размещения сервисов

Общие границы системы и прикладная безопасность — в arch-01-system-architecture.md. Docker Compose, nginx и сети контейнеров — в arch-03-docker-compose-blueprint.md. Настройки и секреты — в arch-04-settings-and-content.md. Процесс разработки и Definition of Done — в arch-05-agent-development-process.md.

Назначение

Документ задаёт обязательный минимальный стандарт размещения сервисов HAN на виртуальных машинах, подготовки VM и production-деплоя.

Главная цель — ограничить последствия компрометации отдельного сервиса: захват процесса или контейнера не должен автоматически давать доступ к host OS, Docker daemon, соседним сервисам, чужим секретам или всей private network.

Стандарт применяется к:

  • VM с публичной точкой входа;
  • внутренним VM в private network;
  • VM без постоянного доступа в интернет, включая SigNoz;
  • пользователям deploy, admin и tunnel;
  • systemd-юнитам, Docker Compose и deployment-артефактам.

Если конкретный сервис не может выполнить требование, отклонение должно быть явно описано в его спецификации: причина, риск, компенсирующая мера, владелец и срок пересмотра. Молчаливое ослабление требований запрещено.

Модель угроз и границы доверия

Базовое допущение: атакующий может добиться выполнения кода внутри одного прикладного контейнера.

После этого он не должен получить:

  • доступ к Docker socket или Docker API;
  • root на host OS;
  • возможность менять compose-файлы, systemd-юниты, deployment-скрипты или sudoers;
  • секреты сервисов, которые не нужны скомпрометированному процессу;
  • произвольный доступ к PostgreSQL, S3 и другим VM;
  • возможность публиковать новый host port или подключать host directories;
  • постоянный канал управления через неограниченный исходящий трафик.

Изоляция строится несколькими независимыми слоями: IAM и секреты, Unix-права, systemd/sudo, настройки контейнера, Docker networks, host firewall и cloud security groups. Один слой не считается заменой остальных.

Классы VM

VM с постоянным egress

VM имеет только утверждённые исходящие направления, необходимые сервисам: Selectel Secrets Manager, S3, внешние API, package/image registry и DNS/NTP по принятой схеме.

Постоянный egress не означает unrestricted internet access. Направления и назначение фиксируются в deployment inventory; лишние правила удаляются.

Private/no-egress VM

В steady state VM:

  • не принимает соединения из интернета;
  • не имеет общего выхода в интернет;
  • принимает только явно разрешённый трафик из private network;
  • администрируется через утверждённую private точку входа: bastion/основную VM или VPN.

SigNoz относится к этому классу, если его UI, OTLP и SSH доступны только из private network.

Каноническая классификация ВМ2 Processing

ВМ2 с message-safety и bitrix-syncсамостоятельная service VM с минимальным public ingress и постоянным ограниченным egress:

  • собственный public DNS/IP или dedicated LB направляет 80/443 только на nginx ВМ2;
  • public 80 обслуживает только ACME challenge/HTTPS redirect;
  • public 443 разрешает только exact /bitrix/sync/webhook/contact и /bitrix/sync/webhook/alert; остальные paths закрыты;
  • private ingress 8443/tcp разрешён только от security group ВМ1 и утверждённого ops path для Message Safety/internal API;
  • ни один public запрос ВМ2 не проходит через nginx ВМ1;
  • freshclam имеет egress только к утверждённым источникам сигнатур;
  • bitrix-sync имеет HTTPS egress только к утверждённому порталу Bitrix24;
  • Safety worker имеет доступ только к managed PostgreSQL, S3-quarantine и доверенному DNS resolver;
  • локальный OTEL Collector имеет private egress к SigNoz;
  • registry, package repositories и OS updates открываются только в bootstrap/controlled maintenance window.

ВМ2 использует отдельный cloud IAM principal. han-secrets получает только секреты сервисов ВМ2 и материализует раздельные root-owned файлы на tmpfs; общий secret bundle с ВМ1 запрещён. На ВМ2 один root Compose project и отдельный root-owned systemd deployment unit.

Для схемы message_safety разделяются DB roles: API/worker runtime читает active/исторические config_versions, но не создаёт и не активирует их; migration/config-admin role используется только controlled job и имеет право version activation. Config не содержит secrets, endpoint topology или MOCK flags.

Public и private ingress ВМ2 завершаются разными server blocks одного nginx без общего fallback. Public block использует сертификат доверенного CA и до proxy ограничивает Contact/alert webhook version-controlled source IP CIDR allow-list. Штатный робот передаёт отдельный receiver token в query и application/x-www-form-urlencoded body; query/body исключаются из logs/traces, а upstream проверяет token и document/entity/domain/member fields. Server-to-server ingress 8443 использует внутренний CA и service token вторым слоем. mTLS не обязателен для MVP.

Lifecycle private/no-egress VM

Этот lifecycle применяется к SigNoz и иным полностью private VM. Для ВМ2 обязательный public webhook ingress 80/443 после bootstrap не удаляется; вместо этого проверяются exact route и source IP CIDR allow-list, а SSH и все прочие public ports закрываются. Новый IP не добавляется автоматически: всплеск восстановлений Contact инкрементальной reconciliation инициирует проверку rejected-IP telemetry и controlled review allow-list.

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

flowchart LR
    Bootstrap[Bootstrap_phase]
    Verify[Verify_services_and_private_links]
    Lockdown[Steady_state_lockdown]
    Bootstrap -->|"temporary public SSH and package egress"| Verify
    Verify -->|"remove public ingress and egress"| Lockdown

Фаза bootstrap

На ограниченное время разрешаются:

  • SSH из утверждённого trusted ops CIDR, а не из 0.0.0.0/0 - управляется через группу безопасности облачного провайдера;
  • egress, необходимый для обновлений ОС, установки пакетов и получения pinned images/artifacts;
  • доступ deploy и admin в пределах правил этого документа.

До перехода в lockdown необходимо:

  1. установить обновления и минимальный набор пакетов;
  2. установить и проверить host firewall и fail2ban;
  3. развернуть сервисы и секреты;
  4. проверить health/readiness;
  5. проверить требуемые private-соединения в обоих направлениях;
  6. подтвердить альтернативный private путь администрирования;
  7. сохранить rollback-инструкцию и inventory разрешённых соединений.

Фаза lockdown

После проверки:

  • public IP удаляется, если он больше не нужен;
  • публичный SSH и любой иной internet ingress удаляются из cloud security group;
  • host firewall принимает административный и прикладной трафик только из утверждённых private CIDR/SG;
  • общий internet egress закрывается на cloud и host-уровне;
  • временные bootstrap credentials, правила, installer-файлы и package caches удаляются, если они больше не нужны;
  • с внешней сети проверяется недоступность SSH и сервисных портов;
  • с VM проверяется запрет неразрешённого egress;
  • из private network повторно проверяются SSH и обязательные service flows.

Раскатка private/no-egress VM не завершена, пока lockdown и обе группы проверок не зафиксированы в deployment checklist.

Повторное открытие

Временное открытие ingress/egress после lockdown — break-glass операция:

  1. фиксируются причина, исполнитель, окно работ и необходимые destination/ports;
  2. правило ограничивается trusted CIDR и минимальным сроком;
  3. после работ правила удаляются;
  4. повторяются проверки lockdown;
  5. факт закрытия фиксируется в runbook/журнале изменений.

Постоянно оставлять bootstrap-доступ «для будущих обновлений» запрещено.

Пользователи host OS

deploy

Используется для штатного деплоя. Пользователь:

  • не входит в группы docker, root и другие root-equivalent группы;
  • не имеет общего sudo, shell root и sudoedit;
  • не меняет compose-файлы, systemd-юниты, deployment-скрипты и конфигурацию секретов;
  • может записывать только в выделенный incoming/staging-каталог;
  • может запускать только заранее утверждённые операции над конкретными systemd-юнитами;
  • на ВМ2 может запускать пять exact-argument вариантов root-owned Message Safety mode helper;
  • читает только логи своего стека, без доступа к секретам других сервисов.

Членство в группе docker считается эквивалентом root и запрещено.

admin

Break-glass пользователь для восстановления:

  • не используется для штатного деплоя;
  • имеет персональные SSH-ключи, а не общий ключ команды;
  • доступен только из trusted ops network/VPN, а для private VM — только через private path после lockdown;
  • расширенные sudo-права выдаются осознанно и аудируются;
  • ключи хранятся отдельно от deploy credentials и регулярно пересматриваются.

Доступ через cloud console/recovery mode также считается break-glass и должен быть ограничен ролями облачного проекта.

tunnel

Отдельный пользователь основной/bastion VM для доступа к PostgreSQL и другим private endpoints:

  • не имеет sudo;
  • не входит в deployment-группы;
  • не получает доступ к секретам приложения;
  • разрешает только local TCP forwarding;
  • имеет allow-list конкретных host:port через PermitOpen;
  • использует login shell /usr/sbin/nologin (после отдельной проверки, что forwarding-only соединение работает);
  • не разрешает agent forwarding, X11 forwarding и TTY;
  • ключ ограничивается теми же возможностями в authorized_keys.

Произвольный SOCKS proxy и forwarding на неутверждённые адреса запрещены. Настройка должна быть проверена отдельной SSH-сессией до отключения старого пути.

Root

  • PermitRootLogin no;
  • пароль root заблокирован (passwd -l root) как дополнительная мера;
  • штатные операции выполняются через именных пользователей;
  • прямой root допускается только механизмом recovery провайдера при инциденте.

SSH baseline

Минимальные настройки production VM:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowAgentForwarding no
X11Forwarding no
AllowUsers deploy admin tunnel

Дополнительно:

  • SSH для Private/no-egress VM разрешается cloud SG и host firewall только из trusted ops CIDR/VPN/private network;
  • ключи пользователей индивидуальны; общий приватный ключ запрещён;
  • устаревшие алгоритмы и пустые пароли запрещены;
  • fail2ban включается на VM, где SSH хотя бы временно доступен из интернета;
  • AllowTcpForwarding no задаётся по умолчанию, а исключение local — только в Match User tunnel;
  • после изменения выполняется проверка конфигурации sshd и вход во второй независимой сессии;
  • текущую рабочую сессию не закрывают до успешной проверки нового доступа.

AllowUsers должен содержать только реально созданные учётные записи. Неиспользуемая роль не создаётся «на будущее».

Права deploy и production-деплой

Управление только через systemd

deploy не запускает docker, docker compose или произвольные root-скрипты через sudo. Docker Compose запускается root-owned systemd-юнитом или root-owned deployment helper с фиксированным интерфейсом.

Sudoers хранится только в /etc/sudoers.d/deploy и проверяется через visudo. /etc/sudoers напрямую не редактируется.

Разрешения перечисляют полные команды и конкретные unit names без wildcard. Принципиальный пример:

Cmnd_Alias HAN_STATUS = /usr/bin/systemctl --no-pager status han-stack.service
Cmnd_Alias HAN_DEPLOY = /usr/bin/systemctl start han-deploy.service, \
                        /usr/bin/systemctl restart han-stack.service
Cmnd_Alias HAN_LOGS = /usr/bin/journalctl --no-pager -u han-stack.service
Cmnd_Alias HAN_SAFETY_MODE = /usr/local/sbin/han-message-safety-mode standard, \
                            /usr/local/sbin/han-message-safety-mode mock --text-free true --file-free true, \
                            /usr/local/sbin/han-message-safety-mode mock --text-free true --file-free false, \
                            /usr/local/sbin/han-message-safety-mode mock --text-free false --file-free true, \
                            /usr/local/sbin/han-message-safety-mode mock --text-free false --file-free false
deploy ALL=(root) NOPASSWD: HAN_STATUS, HAN_DEPLOY, HAN_LOGS, HAN_SAFETY_MODE

Фактические пути сверяются через command -v; разрешается только необходимый набор. Нельзя разрешать:

  • systemctl *, journalctl *, wildcard в unit name;
  • systemctl status/journalctl с интерактивным pager (он может дать shell escape под root);
  • shell, editor, package manager, cp, mv, chmod, chown;
  • произвольный путь к compose-файлу или environment-файлу;
  • команды с параметрами, позволяющими подменить unit, working directory, image или mount.

han-message-safety-mode — исключение с конечным exact-argument allow-list, а не произвольный root-script. Он принадлежит root:root, недоступен deploy на запись, не принимает paths/commands/env expansion, атомарно меняет только root:han-message-safety 0640 /etc/han-chat/message-safety-mode.env; dedicated host group имеет GID 10001, совпадающий с primary GID non-root контейнера. Helper валидирует конфигурацию и выполняет только фиксированную Message Safety API recreate/restart operation внутри root Compose project. MOCK не имеет автоматического срока действия; выключение — отдельная явная команда standard. Все вызовы и old/new mode аудируются.

Ownership deployment-файлов

Root-owned и недоступны deploy на запись:

  • /etc/systemd/system/han-*.service;
  • production compose-файлы;
  • deploy/helper scripts;
  • /etc/han-chat/message-safety-mode.env;
  • /etc/sudoers.d/deploy;
  • secret mappings и credentials;
  • active release manifest.

deploy может загружать артефакты только в отдельный каталог, например /var/lib/han-deploy/incoming, без права менять его parent. Активация выполняется фиксированным root-owned процессом после проверок:

  • артефакт относится к ожидаемому проекту и версии;
  • digest/signature соответствует approved release;
  • отсутствуют symlink/path traversal;
  • compose config прошёл валидацию;
  • image reference pinned по version/digest;
  • миграции и rollout соответствуют release manifest.

Если такого валидатора пока нет, compose/unit changes выполняет admin, а deploy ограничивается запуском уже подготовленного релиза. Выдавать deploy запись в production compose — не допустимая замена автоматизации.

Shell-артефакты (*.sh, entrypoint, hooks и helpers) обязаны поставляться с LF line endings. Репозиторий фиксирует это через .gitattributes, а release preflight проверяет отсутствие CRLF до активации. Ошибка вида cannot execute: required file not found при существующем executable-файле считается признаком некорректного shebang/line endings, а не основанием менять права или запускать файл через обходной интерпретатор.

Изменение прав deploy

Новая доработка не получает дополнительные права автоматически. В change request указываются:

  1. требуемая операция и конкретный systemd unit;
  2. почему существующего интерфейса недостаточно;
  3. полный executable path и фиксированные аргументы;
  4. какие root-owned файлы читает или меняет операция;
  5. возможность command/path/argument injection;
  6. тест негативных сценариев;
  7. способ отзыва права и rollback.

Изменение:

  • проходит review владельца инфраструктуры/безопасности;
  • вносится отдельным файлом в /etc/sudoers.d;
  • проверяется visudo;
  • сначала проверяется в production-like среде;
  • отражается в этом документе и deployment runbook;
  • после rollout подтверждается через sudo -l, что лишних прав нет.

Wildcard, временный NOPASSWD: ALL и включение в docker group запрещены даже как «временное» решение.

Секреты

Общие требования

  • секреты не коммитятся и не хранятся в обычном .env;
  • .env содержит только несекретную конфигурацию и ссылки/имена secret files;
  • контейнер получает только необходимые ему секреты;
  • общий файл со всеми секретами стека не монтируется во все контейнеры;
  • секреты не передаются в command line, build args, image layers и логи;
  • runtime secret files доступны только root и целевому process UID/GID;
  • ротация не требует выдачи сервису доступа к чужим секретам;
  • приложение не выступает сетевым прокси секретов для других VM.

Значения uid, gid и mode в Compose file secrets нельзя считать security boundary: Docker Compose при bind-backed secret может их игнорировать. Фактические owner/mode задаются host-side materializer'ом и проверяются через stat и негативный тест от постороннего UID. Предупреждение Compose об игнорировании этих атрибутов не подавляется и не трактуется как подтверждение прав.

Структурированные секреты валидируются до старта потребителя. Для PEM это означает проверку парсинга certificate/private key, отсутствие повторного base64 или литеральных \n, соответствие public key и запрет зашифрованного private key, если сервис не поддерживает non-interactive passphrase.

VM с egress: han-secrets

На VM с утверждённым доступом к Selectel:

  • один host-side han-secrets запускается через systemd до старта стека;
  • используется отдельный IAM principal на VM/контур;
  • IAM разрешает чтение только секретов сервисов этой VM;
  • контейнеры не получают cloud IAM credentials и сами не обращаются в Secrets Manager;
  • materialized secrets размещаются в /run/han-chat/secrets на tmpfs;
  • ошибка получения обязательного секрета блокирует rollout (fail closed);
  • автоматический fallback с Selectel на локальный production-файл запрещён.

Использование единой реализации han-secrets на нескольких VM допустимо; общая IAM-учётная запись и общий набор секретов — нет.

Private/no-egress VM

Cloud sync не требуется. admin во время bootstrap:

  • получает минимальный набор секретов по защищённому каналу;
  • размещает их в root-owned каталоге вне репозитория;
  • задаёт каталогам 0700, файлам 0400 или более узкие ACL для целевого UID;
  • по возможности передаёт их процессу как systemd credentials или read-only secret files;
  • удаляет временную копию и историю команд;
  • фиксирует fingerprint/version секрета без его значения.

Допускается han-secrets в локальном file-режиме как единый loader, но он не должен создавать egress или зависимость от основной app-VM. Для ротации используется повторная контролируемая provisioning-процедура.

DB roles и migration boundary

Runtime, migration и config-admin роли разделяются для каждого сервиса. Runtime-role не получает DDL, ownership схемы или право менять immutable configuration. Migration-role монтируется только в controlled job и не передаётся runtime-контейнерам.

Cross-schema migration не получает постоянный broad access. Если ей нужно однократно перенести legacy data:

  1. владелец исходной схемы или DB administrator выдаёт именованной migration-role минимальные временные USAGE на schema и SELECT на конкретную таблицу;
  2. migration копирует данные и fail-closed проверяет полноту переноса;
  3. владелец/администратор отзывает временные права после успешного commit.

Migration-role не выполняет REVOKE, ALTER или DROP на объекте чужого owner. Такие contract-операции принадлежат owner migration исходного сервиса либо отдельной административной процедуре. Проверку существования объекта нельзя реализовывать как «нет доступа — значит объекта нет»: permission error должен блокировать rollout, иначе legacy data может быть молча пропущена.

Alembic graph обязан сохранять известные ранее выданные revision IDs, включая no-op baseline revisions. Удаление revision из нового image при наличии её в alembic_version запрещено; совместимость обеспечивается bridge/no-op revision, а не ручным stamp или правкой production DB.

Права на каталоги

Базовая модель:

Путь Владелец / режим Назначение
/opt/han-chat/releases/<version> root:root, 0755/файлы 0644 immutable release
/opt/han-chat/current root:root active release link; меняет только deployment helper/admin
/var/lib/han-deploy/incoming deploy:deploy, 0750 загрузка неактивированных артефактов
/etc/han root:root, 0750 или строже конфигурация и secret mappings
/run/han-chat/secrets root:root, 0700 runtime secrets на tmpfs
/var/lib/han-chat/public-tls root:han-nginx-tls, 0750; key/cert 0640 минимальный TLS staging для non-root edge
/var/lib/han-chat/acme root:root, 0755 ACME webroot без private key
/var/lib/han-chat/<service> UID сервиса, минимальные права service state
/var/log/han-chat root/service group, без world-read host-side логи при необходимости

Требования:

  • world-writable каталоги в deployment path запрещены;
  • setuid/setgid binaries не добавляются без обоснования;
  • сервис не получает write к каталогу с executable/config, если ему нужен только state;
  • bind mounts задаются read-only, кроме явно выделенных state/upload paths;
  • backup-файлы и дампы получают не менее строгие права, чем исходные данные;
  • symlinks из writable каталога не используются привилегированным helper без безопасной проверки.

Hardening контейнеров

Для каждого production-контейнера обязательна оценка и, где применимо, конфигурация:

services:
  service:
    user: "10001:10001"
    read_only: true
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    tmpfs:
      - /tmp:rw,noexec,nosuid,nodev

Базовые правила:

  • процесс запускается непривилегированным UID/GID;
  • root в контейнере допускается только с документированным обоснованием;
  • privileged: true запрещён;
  • network_mode: host, pid: host, ipc: host и userns_mode: host запрещены;
  • Docker socket/API не монтируется;
  • Linux capabilities удаляются все, затем точечно возвращаются необходимые;
  • root filesystem read-only; writable paths — отдельные volume/tmpfs;
  • mount host paths минимален и read-only;
  • default seccomp сохраняется; AppArmor/аналог провайдера включается, если доступен;
  • задаются CPU/memory/PID limits, restart policy и healthcheck;
  • сервис подключается только к необходимым Docker networks;
  • внешний ports: разрешён только утверждённой edge-точке;
  • image использует pinned version/digest, проходит vulnerability scan и не содержит package managers/compilers без необходимости;
  • секреты не копируются в image и не доступны healthcheck-команде.

Если контейнер не может работать с read_only, в спецификации перечисляются конкретные writable paths. Полное отключение read_only без анализа запрещено.

Проверка совместимости image с hardening

Non-root UID сам по себе недостаточен. Для каждого pinned digest до rollout составляется inventory всех путей, куда пишет entrypoint и процесс: runtime/socket, cache/temp, generated config, logs и persistent state. Каждый путь получает отдельный volume/tmpfs с минимальным размером и явными uid/gid/mode; writable root filesystem, запуск root или возврат capabilities не используются как универсальный workaround.

Проверяется не только основной binary, но и image entrypoint. Если vendor image имеет отдельный unprivileged entrypoint, при принудительном user используется именно он. Root entrypoint, который делает mkdir/chown, несовместим с non-root + read_only, даже если сам daemon способен работать без root.

Изменение image digest повторяет эти проверки: tag/version, entrypoint, writable-path inventory, healthcheck semantics и фактический UID/GID считаются частью security contract образа.

TLS для non-root edge

Root-only дерево ACME/Certbot не монтируется целиком в non-root nginx и не делается world-readable. Host-side root hook атомарно копирует только fullchain.pem и privkey.pem в выделенный staging-каталог с группой han-nginx-tls (канонический GID 11001); nginx получает этот каталог read-only. Renewal hook сначала обновляет staged files, затем выполняет полный config test и только после успеха отправляет reload. Права и соответствие certificate/key проверяются preflight.

Daemon и updater как разные security-профили

Если один vendor image используется для daemon и updater, им задаются разные сети, mounts и health semantics. Проверенный паттерн ClamAV:

  • clamd не имеет signature-CDN egress, читает signatures read-only и имеет healthcheck реального daemon socket;
  • freshclam один получает ограниченный egress и write к signatures;
  • оба используют vendor init-unprivileged и только выделенные writable /run/clamav, /var/log/clamav и /tmp;
  • updater запускается как постоянный foreground daemon, чтобы restart policy не превращала успешный one-shot exit в download loop/rate limit;
  • унаследованный healthcheck, проверяющий отсутствующий в updater-контейнере daemon, отключается; updater контролируется по Up, restart count, логам и возрасту сигнатур.

Ошибки read-only file system устраняются точечным writable mount. Запрещено лечить их глобальным read_only: false, root, privileged или broad capability.

Сетевые ограничения

Cloud и host

Используются одновременно:

  1. cloud security groups — граница между internet/VPC/managed services;
  2. host firewall (UFW/nftables/iptables) — защита VM;
  3. DOCKER-USER — защита от обхода UFW опубликованными Docker ports;
  4. Docker networks — разделение сервисов внутри VM.

Default policy для ingress — deny. Разрешение задаёт source, destination, protocol, port и назначение. Правила «вся private network на все порты» запрещены.

Cloud SG для managed PostgreSQL разрешает TLS-подключения только от VM/SG сервисов, которым нужна соответствующая схема. PostgreSQL, Redis, OTLP receivers, admin UI и internal API не публикуются в интернет.

Egress

  • сервис без внешней интеграции не подключается к сети egress;
  • внешние destination/ports фиксируются в inventory;
  • DNS/NTP и package/image registry учитываются отдельно;
  • временный bootstrap egress удаляется при lockdown;
  • отсутствие технической возможности фильтровать по FQDN компенсируется NAT/proxy/provider firewall и мониторингом исходящих соединений.

Fail2ban

Fail2ban обязателен для SSH, временно или постоянно доступного из интернета. Он дополняет allow-list trusted CIDR и key-only auth, а не заменяет их.

Для VM без публичного ingress в steady state fail2ban можно оставить включённым, но основная защита — отсутствие внешнего маршрута и закрытые SG/firewall.

Минимизация host OS

  • используется поддерживаемый минимальный образ ОС;
  • пакеты устанавливаются из доверенных репозиториев с проверкой подписи;
  • компиляторы, отладчики, сетевые утилиты и installer dependencies не остаются без эксплуатационной необходимости;
  • отключаются неиспользуемые daemon/socket units;
  • автоматические security updates или утверждённое patch window обязательны;
  • kernel и container runtime регулярно обновляются;
  • удаление пакетов выполняется по утверждённому allow-list, а не слепым autoremove;
  • старые images/releases удаляются только после сохранения необходимого rollback window;
  • cleanup не удаляет active image, последний рабочий релиз, forensic data или backup.

Для no-egress VM обновление выполняется в контролируемое окно через временный ограниченный egress либо проверенные offline packages/images. После обновления повторяется lockdown.

Логи, аудит и инциденты

Аудируются:

  • входы deploy, admin, tunnel;
  • sudo-вызовы и systemd deployment actions;
  • изменение SG/firewall/SSH/sudoers;
  • получение и ротация секретов без записи значений;
  • открытие и закрытие break-glass доступа;
  • версия/digest развернутого релиза.

Секреты, токены, содержимое credentials и полные PII в логи не попадают.

При подтверждённой компрометации контейнера:

  1. изолировать VM/контейнер сетевыми средствами;
  2. не использовать скомпрометированную VM как доверенную точку восстановления;
  3. ротировать доступные контейнеру секреты и service tokens;
  4. проверить соседние сервисы по разрешённым network flows;
  5. сохранить необходимые snapshot/log evidence;
  6. пересоздать VM из доверенного образа вместо ручной «очистки», если затронут host;
  7. задокументировать причину выхода за границу изоляции, если он произошёл.

Definition of Done для новой VM или сервиса

  • определён класс VM: egress или private/no-egress;
  • составлена матрица ingress/egress;
  • созданы отдельные OS users и IAM principal;
  • root/password SSH отключены после проверки key access;
  • deploy не состоит в docker и имеет только конкретные systemd-команды;
  • production-файлы root-owned и недоступны deploy на запись;
  • каждый контейнер проверен по hardening baseline;
  • для каждого image digest проверены entrypoint, UID/GID и полный inventory writable paths;
  • секреты разделены по сервисам/VM и отсутствуют в обычном .env;
  • bind-backed secrets и staged TLS проверены по фактическим owner/mode и содержимому, а не только по декларации Compose;
  • runtime/migration/config-admin DB roles разделены, временные cross-schema grants выданы и отозваны владельцем;
  • healthcheck проверяет процесс, реально присутствующий в контейнере, а updater freshness контролируется отдельным сигналом;
  • внутренние ports недоступны извне;
  • backup/restore и rollback проверены в объёме релиза;
  • для private/no-egress VM завершён и зафиксирован lockdown;
  • проверена недоступность внешних портов и неразрешённого egress;
  • отклонения имеют владельца, компенсирующую меру и срок пересмотра.