Контракт между приложением и окружением
Наши приложения используют множество внешних зависимостей — библиотек, пакетов, модулей. Между тем тестируется только разрабатываемый код, давая нам определённую уверенность в его надёжности. Но что там с надёжностью используемых зависимостей? По большому счёту, только наша вера.

Конечно, я слукавил. В рамках обычного тестирования косвенно покрываются и внешние зависимости. Однако при этом речь идёт только об их функциональном тестировании, ведь нам доступен только публичный API библиотек, а реализация скрыта. И это нормально, в этом суть инкапсуляции.
Но как в таких условиях понять, что на самом деле производится за кулисами? Где взять уверенность, что вызываемый код делает только то, что нужно, и не более; не лезет туда, куда не просили; не использует больше ресурсов, чем предполагалось и т.д. Например, вызывая функцию Math.Max(x, y), мы наивно или интуитивно предполагаем, что примерно делается на уровне реализации. Ясно, что это простой пример, но если подумать, то значительная часть нашего кода основана на таких предположениях.
Во что это выливается на практике? Например, в аморфные Dockerfile’s, в которых границы дозволенного для приложения либо совсем не определены, либо урезаны до предела. В первом случае, чтобы ничего не сломать у приложения; во втором, наоборот, чтобы ничего не сломать в инфраструктуре. Чаще и справедливо идут по второму сценарию, выстраивая барьеры в виде различного рода изоляции, лимитов и т.п. Но если изоляция и лимитирование выполнены некорректно, это приводит к нестабильному или непредсказуемому поведению приложения, его многочасовой отладке и тестированию. Например, так возникает неожиданный OOM Killer, CPU throttling, VM pauses, временная недоступность, скачки/замедления/ускорения времени и т.п. Иногда всё это смешивается воедино, и с трудом понимаешь, что же пошло не так.
И тут я подумал вот о чём. Кто лучше всех знает код? Разработчик. Только он лучше других может рассказать, что требуется приложению от операционного окружения, в котором оно работает. Может рассказать, но не может гарантировать, что так работает на самом деле, поскольку взаимодействие с окружением часто отдаётся на откуп внешним зависимостям. Но что, если бы был механизм предоставления подобных гарантий?
Например, если разрабатывается web-приложение, которое для обработки входящих запросов должно прослушивать 8080 порт и только его, то почему бы не объявить это явно? Разработчик это знает, следовательно, он мог бы это требование к окружению выразить в виде обычного кода. Аналогично, если приложению не нужен доступ на запись в файловую систему.
К счастью, такие механизмы есть, они доступны из коробки и являются частью возможностей ядра Linux. К сожалению, об этом мало кто знает, а тех, кто об этом говорит, ещё меньше. Одним из ярких представителей таких инструментов является проект Landlock. Посмотрите, как лаконично он позволяет объявить контракт между приложением и ОС (пример на Go).
err := landlock.V5.BestEffort().Restrict(
landlock.RODirs("/usr", "/bin"),
landlock.RWDirs("/tmp"),
landlock.BindTCP(8080),
landlock.ConnectTCP(5432),
)
По большому счёту, это нефункциональные требования, выраженные кодом и обеспечиваемые ОС. Границы выгружаются из головы разработчика и определяются в явном виде. Это значит, что разработчик, как и должно быть, обладая необходимыми знаниями об устройстве приложения, полноценно включается в выстраивание модели безопасности.
Такая самоизоляция позволяет уменьшить поверхность возможной атаки и на корню избавиться от возможных zero-day-уязвимостей, которые могут (транзитивно) заехать через подключаемые внешние зависимости. При этом подход идеален для MSA, т.к. границы функциональности каждого микросервиса определены достаточно чётко.
С данной темой я выступил для студентов в минувший четверг. На самом деле необычные ощущения — вернуться в универ, ведь я не преподавал уже 8 лет. На мероприятие меня позвали спонтанно, за день до начала. Однако материал у меня уже был, поэтому надеюсь, что получилось хорошо. Сам себя я фоткать не мог, поэтому пока только единственная фотка со мной.
Понравилась статья?
Посмею напомнить, что у меня есть Telegram-канал Архитектоника в ИТ, где я публикую материал на похожие темы примерно раз в неделю. Подписчики меня мотивируют, но ещё больше мотивируют живые дискуссии, ведь именно в них рождается истина. Поэтому подписывайтесь на канал и будем оставаться на связи! ;-)
Статьи из той же категории: