From 793966ed64c5a6884e7f1a3d5491a7be4b3eea5f Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Mon, 15 Mar 2021 22:31:02 +0300 Subject: [PATCH] =?utf8?q?=D0=9F=D0=BE=D0=B7=D0=BD=D0=B0=D0=BA=D0=BE=D0=BC?= =?utf8?q?=D0=B8=D0=BB=D1=81=D1=8F=20=D1=81=20Capsicum,=20kqueue,=20libnv,?= =?utf8?q?=20privsep=20=D0=B8=20privdrop?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit https://en.wikipedia.org/wiki/Capsicum_(Unix) https://en.wikipedia.org/wiki/Kqueue https://oshogbo.vexillium.org/blog/42/ https://utcc.utoronto.ca/~cks/space/blog/solaris/SolarisNvpairLibrary Реализовал программу с честным (как мне кажется) полноценным privsep-ом, когда она fork-ается, разные процессы занимаются разными вещами и имеют доступ к разным ресурсам. Общаются между собой по Unix-сокету. Умеют делать chroot, сбрасывать привилегии root-а после этого. Capsicum включают, в том числе и устанавливая ограничения на каждый файловый дескриптор выборочно. Закрывают всё лишнее и ненужное (stdin/out/err всякие). С Capsicum работать по сути тривиально, но архитектура программы выстраивается вокруг использования файловых дескрипторов. Благо, в отличии от уродского Linux, в FreeBSD даже процессы можно представлять в виде файлов (pdfork()). Так как системные вызовы типа waitpid() уже нельзя использовать в Capsicum окружении, то нужно делать pdfork(), чтобы получить файл. В man-е присутствует pdwait4(), однако его нет в исходном коде, кроме как с пометками "ещё не реализовано". Подсмотрел как с этим живут capsicum-изированные программы в самой ОС. Оказалось что просто используют kqueue. Пришлось впервые и с ним поработать. Думал будет сложно. Но... под рукой был ровно один только man kqueue и через считанные минуты я полностью реализовал код и ожидания события когда в Unix сокете что-то будет для чтения и когда процесс завершит свою работу, оповещая об этом через process descriptor файл. Я очень очень удивлён как просто работать с kqueue и как много он умеет. Можно даже просто таймер поставить -- что я часто делал в Go языке в select-ах. А по сокету мне нужно гонять разношёрстные данные. Точнее, не то чтобы нужно, а хотелось бы. Как вариант можно открыть несколько сокетов и ошибки и события отправлять по одним, а полезную нагрузку по другим (как в FTP отдельные TCP соединения для данных и команд). Но попробовал libnv библиотеку. У меня никогда не возникло бы мысли о том, что разнотипизированные данные, где могут быть и вложенные словари и массивы, могут хотя бы в теории быть просты в использовании в Си или Go. libnv библиотека супер проста! Даже ошибку абсолютно штатно можно проверять только во время сериализации. Даже в цикле накапливать и доделывать (append) данные спокойно. Читать всё аналогично просто. Сам код является схемой. Очень эффективна по использованию памяти: можно даже брать значения из nv-пар и они сразу же будут очищать из nv-структур, оставляя заботу о free() на самом пользователе. И добавлять значения в nv-структуры можно тоже сразу же их очищая. Да я на Go такой простой работы не встречал наверное нигде. Одно удовольствие работать с данными в таком виде. Причём она и endianness блюдёт и можно спокойно сериализовывать всё на диск. И куча типов данных: null, bool, number, строки, списки вложенных nv структур, файловый дескриптор, бинарь, массивы bool/number/строк/дескрипторов. Но корни libnv растут ещё из Sun Microsystems и Solaris. В ZFS nvpairs используются всюду и везде. libnv это функциональный аналог nvpairs. В Solaris оно и вне ZFS встречается. Причём реализация Sun сериализует в XDR формате всё. Который мне уже и так давно нравился, а недавно я прочувствовал его крутость с 32-х битным выравниваем всего и вся -- это существенно упрощает работу с ним на системах которым не нравится невыровненный доступ к памяти. Ну и пускай что bool занимает всё равно 32-бита, но зато какой простой код получается для работы со всем этим! Бегло поглядел на libnv и там уже не XDR, а что-то своё, без этой alignment красоты. XDR я на практике ведь в NNCP использую. А nvpairs сериализованные данные хранятся прямо на диске в ZFS структурах и без всякой схемы можно выводить их содержимое. Дальше всё аналогичное я хочу проделать в экосистеме GNU/Linux. Мне кажется это будет настоящий ад. Seccomp вроде бы мне достаточен в его strict режиме. Например после входа в Capsicum в FreeBSD я дополнительно ограничиваю read/write/kqueue возможности на оставшиеся сокеты -- а в seccomp strict режиме они сразу же будут только в read/write. Но вопросы сериализации остаются и... как мне waitpid то там сделать? С ходу не знаю, если в strict. Плюс kqueue там нет. Буду готовиться к страданиям. -- 2.50.0