Перейти к содержанию

Безопасность

Стенд открыт в интернет и содержит кнопки, которые ломают сервис. Поэтому защита здесь не декоративная.

Узел

Ansible приводит узел в известное состояние: файрвол ufw пропускает только 22, 80 и 443 порты, fail2ban блокирует перебор пароля по SSH, вход по паролю и вход пользователем root отключены, обновления безопасности устанавливаются автоматически.

Файл доступа к кластеру k3s создаётся с правами 0640 вместо стандартных 0644: читать его может только владелец и группа. Секреты в базе состояния кластера шифруются (--secrets-encryption), иначе они лежали бы там открытым текстом.

Транспорт

Всё внешнее общение идёт по TLS. Сертификаты выпускает cert-manager через Let's Encrypt по проверке HTTP-01; обновление автоматическое, за тридцать дней до истечения. Отдельное правило оповещения следит за тем, чтобы автоматика действительно сработала.

Traefik перенаправляет весь HTTP на HTTPS и добавляет заголовки:

headers:
  stsSeconds: 31536000
  stsIncludeSubdomains: true
  contentTypeNosniff: true
  browserXssFilter: true
  referrerPolicy: strict-origin-when-cross-origin
  frameDeny: true

Ограничение частоты — 40 запросов в секунду со всплеском до 80 на один адрес. Стенд рассчитан на посетителей, а не на исследователей его пропускной способности.

Контейнеры

Одинаковые правила для всех подов, без исключений:

  • образ собирается многоэтапно, в итоговый слой попадает только среда исполнения Java и собранное приложение — ни компилятора, ни исходников;
  • процесс работает не под root, а под непривилегированным пользователем;
  • файловая система корня только для чтения, а изменяемые каталоги подключены отдельными томами; исключение — база данных, которой нужен доступ на запись к своему каталогу;
  • все дополнительные возможности ядра сброшены, повышение прав запрещено, профиль seccomp — стандартный для среды исполнения;
  • у каждого контейнера есть запрос и предел по памяти и процессору: под с утечкой уходит в перезапуск, а не забирает узел целиком.

Интерфейс раздаёт непривилегированный Nginx на порту 8080: чтобы слушать 80-й, контейнеру понадобилась бы дополнительная возможность ядра, а смысла в этом нет.

Секреты

Пароли Grafana и PostgreSQL генерирует Terraform (random_password) и кладёт в секреты Kubernetes. В репозитории их нет и быть не может: файл состояния Terraform, где они хранятся, лежит в секрете кластера, а не в репозитории, и внесён в список исключений .gitignore вместе со всеми *.tfstate*.

Пароль администратора Grafana достаётся командой:

kubectl -n observability get secret grafana-admin \
  -o jsonpath='{.data.admin-password}' | base64 -d

Публичный доступ и пульт

Дашборды открыты анонимно в роли наблюдателя: без этого стенд нельзя было бы показать по ссылке. Роль наблюдателя не даёт править дашборды и заводить источники данных, а перед Grafana стоит ограничение частоты запросов.

Пульт со сценариями отказов открыт всем, и это самая спорная часть стенда. Ограничения, которые делают её приемлемой:

  • частота запусков ограничена двенадцатью в минуту на весь стенд;
  • каждый сценарий выключается сам не позже чем через десять минут;
  • утечка памяти ограничена сверху 512 МБ — под уйдёт в перезапуск по своему лимиту, не задев соседей;
  • сценарии влияют только на пространство имён приложения; ни Prometheus, ни Grafana, ни база их не чувствуют;
  • сценарии можно выключить целиком одним параметром конфигурации.

Самое плохое, что может сделать посетитель, — уронить поды приложения. Они поднимутся сами, и это будет видно на дашборде: собственно, ради этого стенд и сделан.

Чего здесь нет

Честный список того, что нужно в промышленной среде и чего на стенде нет:

  • сетевые политики между пространствами имён — на одном узле с k3s они требуют замены сетевого слоя, а показать было бы нечего;
  • внешнее хранилище секретов (Vault, внешний оператор секретов) — ради двух паролей это лишний сервис на 300 МБ памяти;
  • подпись образов и проверка подписи при запуске — правильная практика, которая на одноузловом стенде без реестра сводится к ритуалу;
  • резервное копирование базы — обращения жителей синтетические и пересоздаются за минуту.

Каждый пункт — не забытый, а взвешенный. Именно поэтому он записан.