Весь предыдущий код, который я написал для ядра не имеет смысла, если не будет реального применения в userspace-окружении. Как можно дагадаться из всей идеи, основное применение — это apache. У него достаточно гибкая архитектура, которая позволила мне написать весь функционал для работы с ugidctl в виде модуля. Название соответствующее — mod_ugidctl
Аналогично модулю ядра, я упаковал его в rpm’ки для el6/el7
CentOS6 / RHEL6 (x86_64 only)
1 2 3 |
# cd /etc/yum.repos.d # wget https://ibuffed.com/pub/repo/el6/ibuffed-com.repo # yum install mod_ugidctl |
CentOS7 / RHEL7
1 2 3 |
# cd /etc/yum.repos.d # wget https://ibuffed.com/pub/repo/el7/ibuffed-com.repo # yum install mod_ugidctl |
Теперь можно делать в конфигурации apache вот так:
1 2 3 4 5 6 7 8 9 10 11 12 |
<VirtualHost *:80> ServerName host1.example.com ServerAdmin webmaster1@example.com ServerUserGroup user1 group1 DocumentRoot /var/www/host1 </VirtualHost> <VirtualHost *:80> ServerName host2.example.com ServerAdmin webmaster2@example.com ServerUserGroup user2 group2 DocumentRoot /var/www/host2 </VirtualHost> |
А на файловой системе вот так:
1 2 3 4 5 6 |
# ls -la /var/www total 16 drwxr-xr-x 4 root root 4096 Oct 27 16:10 . drwxr-xr-x. 21 root root 4096 Oct 26 01:13 .. drwxr-x--- 2 user1 group1 4096 Oct 27 16:10 host1 drwxr-x--- 2 user1 group2 4096 Oct 27 16:10 host2 |
Список процессов при работе будет выглядель примерно так:
1 2 3 4 5 6 7 8 9 10 11 |
# ps -AH -o pid,comm,user,group,command | awk '$2 ~ /httpd|COMMAND/ { print }' PID COMMAND USER GROUP COMMAND 1314 httpd root root /usr/sbin/httpd -DFOREGROUND 14216 httpd apache apache /usr/sbin/httpd -DFOREGROUND 14230 httpd user1 group1 /usr/sbin/httpd -DFOREGROUND 14298 httpd user1 group1 /usr/sbin/httpd -DFOREGROUND 14301 httpd apache apache /usr/sbin/httpd -DFOREGROUND 14302 httpd user2 group2 /usr/sbin/httpd -DFOREGROUND 14303 httpd apache apache /usr/sbin/httpd -DFOREGROUND 14304 httpd apache apache /usr/sbin/httpd -DFOREGROUND 14305 httpd apache apache /usr/sbin/httpd -DFOREGROUND |
В этом примере pid’ы 14230, 14298 и 14302 обрабатывают запросы соответствующих хостов.
Совместимость
Модуль спроектирован для работы с MPM prefork. Не запускайте его для других MPM.
Безопасность
Модуль открывает /dev/ugidctl и конфигурирует его из основного процесса, который работает под root’ом. Открытый файловый дескриптор оставляет всем своим дочерним процессам, которые непосредственно обрабатывают запросы. Также во время конфигурации основной процесс получает от модуля ядра случайный ключ для доступа к функциям UGIDCTL_SETUID / UGIDCTL_SETGID / UGIDCTL_SETGROUPS. Этот ключ хранится в памяти как основного процесса, так и всех дочерних. Теоретически, во время обработки запроса зловредный код может найти этот ключ и начать «гулять» по всем uid’ам и gid’ам, которые описаны в файле конфигурации apache. На практике достать этот ключ так-же сложно, как и найти загруженные приватные ключи модуля mod_ssl.
Отдельное внимание надо уделить многопоточности. В linux у каждой нити собственные права. Поэтому, glibc, например, для совместимости с POSIX, использует достаточно сложный алгоритм при вызове setuid(2) / setgid(2) / setgroups(2), который гарантирует всем нитям процесса работу с одинаковыми правами. Мой модуль практически напрямую пользуется системными вызовами в обход glibc, поэтому меняется пользователь только основной нити. Из этого следует, что если во время обработки запроса будет запущена новая нить и не остановлена перед его завершением, она может достаться другому пользователю «как есть», т.е. с правами процесса, которые он имел во время её запуска. Следует избегать использования многопоточных приложений при обработке запросов.
Масштабируемость
Масштабируется аналогично простому apache2. После обработки запроса процесс не убивает себя, а возвращает себе привилегии основного пользователя и ждет нового запроса. Единственное ограничение — встроенные лимиты на количество пользователей и групп в модуле ядра. При написании модуля я выбрал их достаточно большими — чуть больше 1-го миллиона uid’ов и gid’ов (1048576).