Skip to content

Security: onixus/Ferrum

SECURITY.md

Политика безопасности FERRUM

FERRUM — enforcement plane: он решает, какой pod создастся и какой процесс переживёт syscall. Дефект в нём — это не «упало», а «разрешило». Поэтому сообщать о таком надо не в публичный issue.

Куда сообщать

Единственный канал — private vulnerability reporting GitHub: https://github.com/onixus/Ferrum/security/advisories/new.

Кнопка появляется у любого, кто видит репозиторий; переписка идёт в приватном advisory, куда не имеет доступа никто, кроме сопровождающих и вас.

Другого канала нет, и это сказано явно, чтобы его не пришлось выдумывать:

  • почтового адреса для сообщений об уязвимостях у проекта нет. Ни одного адреса в этом дереве не написано, и адрес, найденный в git log или в профиле GitHub, каналом раскрытия не является — он никем не читается как канал и ничем не подтверждён;
  • PGP-ключа у проекта нет. Ни ключа, ни отпечатка здесь не опубликовано. Отпечаток, который вам предъявят от имени FERRUM где-то ещё, — не наш;
  • баунти нет, оплаты за сообщения нет, и обещать её этот файл не будет.

Публичный issue, PR с исправлением уязвимости и сообщение в любой чат — это раскрытие. Если уязвимость уже раскрыта публично, всё равно откройте advisory: дальше нужен как минимум фиксированный релиз.

Что приложить: версию (git-тег или commit SHA), затронутый компонент (ferrum-admission, ferrum-agent, ferrum-controller, ferrum-compiler, ferrum-crypto, ferrum-cli), включённые cargo-фичи (attach, apiserver — в сборке по умолчанию их нет, и без этой строки половина сообщений невоспроизводима), версию ядра и архитектуру, если дефект в датапейсе, и минимальный воспроизводимый случай: политику YAML, манифест pod, запись датапейса.

Окно ответа

Проект ведёт один человек, и сроки написаны исходя из этого, а не из шаблона:

Шаг Срок
Подтверждение получения 5 рабочих дней
Первичная оценка: воспроизведено или нет, в модели угроз или вне её 14 дней
Исправление или названная дата исправления 90 дней с подтверждения
Публичное раскрытие после релиза с исправлением, по согласованию

Если ответа нет 14 дней — считайте, что канал не сработал, и раскрывайте публично. Тишина сопровождающего не должна быть вашей проблемой.

Сроки — обязательство приложить усилия, а не SLA: коммерческой поддержки у проекта нет.

Какие версии поддерживаются

Тегированных релизов пока нет ни одного. Пока это так, поддерживается ровно одна вещь — текущий main; сообщение против него принимается всегда.

С появлением первого тега поддерживается последняя минорная линия, и не больше: 0.x — версии, где ломающее изменение приезжает в минорной, а бэкпортировать исправления в предыдущие минорные некому.

Версия Поддержка
main да
0.1.x да, с момента выхода v0.1.0
всё, что раньше нет; таких версий не существует

Что здесь считается уязвимостью

Продукт про то, что enforcement нельзя обойти незаметно, и подпись нельзя подделать. Уязвимость — всё, что ломает одно из этих двух.

Обход enforcement:

  • pod, который проходит admission вопреки применённой политике: неподписанный образ, privileged, привязка к cluster-admin, нарушение PSS restricted;
  • процесс, который переживает правило Kill/Isolate, совпавшее с ним;
  • совпадение по правилу, которое молча не наступает: путь, комм или syscall, который датапейс видит иначе, чем ядро, и потому не матчится. Гонка на чтении пути (double-fetch на sys_enter) в базовой форме — известное ограничение, см. ниже; уязвимость — конкретный сценарий, где она даёт обход конкретного поставляемого правила;
  • вылет из scope: PolicyException, действующее шире своего scope или после expiresAt; allow, побеждающий deny.

Подделка доверия:

  • принятый bundle с неверной, отсутствующей или чужой подписью; любой путь, на котором проверка подписи возвращает Ok, не проверив;
  • смешение доменов подписи: Ed25519-seed bundle (BUNDLE_SIGNATURE_CONTEXT), принятый как TLS-ключ (KEY_BIND_MSG), или наоборот;
  • подмена метки: событие, приписанное чужому pod через cgroup→pod-индекс;
  • фальшивый control plane, чей bundle агент принимает.

Отказ в безопасную сторону, которого не случилось:

  • fail-open вместо last-known-good при недоступном CP;
  • деградация, о которой узел не сообщил: enforcement, не работающий без Degraded=true и без записи в статусе, — это уязвимость, а не «известное поведение». Молчаливое «работает» хуже честного «не работает»;
  • потеря события без учёта в events_dropped_total.

Поставка: подделываемая цепочка от git-тега до образа в ghcr.io/onixus/* — подпись cosign, которую проходит образ, собранный не этим воркфлоу; SBOM, описывающий не то, что внутри.

Что вне модели угроз

Модель угроз — RFC-02, раздел C. Этот раздел не список отговорок: это то, от чего продукт не защищает, и сообщение об этом будет закрыто как «вне модели» без исправления. Лучше прочитать это здесь, чем узнать после установки.

  • Root на ноде enforcement не побеждает. Так и записано в RFC-02. Атакующий с root в host namespace снимает eBPF-программы, останавливает агента, правит last-known-good на диске. Контрмеры, которые это осложняют (LSM на pin path, self-watch вне процесса), в этом дереве не реализованы — это записано и в RFC-02, и в границе. Сообщение «под root я выгрузил агента» — воспроизведение известного факта.
  • Компрометация kubelet и apiserver. Агент — вторая цель после kubelet, а не защита kubelet. Кто владеет apiserver, владеет и объектами политики.
  • Гонка на чтении пути (TOCTOU) как класс. Путь читается из user memory на sys_enter; это double-fetch по построению tracepoint-режима. Известно, записано в ROADMAP как п. 3, закрывается переходом на BPF LSM. Нечитаемая страница закрыта fail-closed (path_unknown утверждает совпадение), подменённая — нет. Конкретный обход конкретного правила интересен; сам факт double-fetch — нет.
  • Detect-then-kill — не prevent. Датапейс исполняет SIGKILL из userspace после sys_enter. К моменту сигнала execve уже произошёл. Это архитектура текущего режима, а не дефект в нём.
  • Доступность кластера от fail-closed admission. failurePolicy=Fail при упавшем webhook останавливает создание pod в scope. Это выбранное поведение: fail-open в admission — то, от чего продукт написан. HA-профиль и break-glass — ROADMAP, фаза 1.
  • Сборка без датапейса. По умолчанию фичи attach и apiserver выключены. «Runtime enforcement не сработал» на такой сборке — не дефект: датапейса в ней нет. Продуктовая комбинация — attach,apiserver.
  • Ядра, на которых ничего не мерялось. Матрица ядер не определена (ROADMAP, п. 7). Незацепленный syscall, о котором attach честно сообщил, — свойство ядра, а не уязвимость. Уязвимость — если он сообщил зацепленным то, что не зацепил.
  • Компоненты, ещё не работавшие в живом кластере. Ни один компонент не проверялся против настоящего apiserver; что исполнено, а что нет — только docs/MVP-1-BOUNDARY.md. Ненаписанная интеграция — не уязвимость в написанной.
  • Уязвимости в зависимостях, о которых уже знает cargo-audit: они видны стадией Security: supply chain и заводятся обычным issue. Уязвимость — та, которую advisory-база ещё не знает, или та, которую этот код делает достижимой вопреки advisory.
  • Чужая инфраструктура: GitHub, ghcr.io, Sigstore, реестр образов получателя. Сообщайте им.

Если сомневаетесь, вне модели ваша находка или нет, — открывайте advisory. Разобрать лишнее сообщение дешевле, чем пропустить обход.

Чего этот файл не обещает

Заявлений о безопасности продукта здесь нет. Что исполнено, а что принято на веру, — docs/MVP-1-BOUNDARY.md, и это единственный документ, который держится гейтом. Наличие политики раскрытия — не свидетельство, что раскрывать будет нечего.

There aren't any published security advisories