Дейл Хэгглунд на месте. Так что я просто собираюсь сказать то же самое, но по-другому, с некоторыми особенностями и примерами. ☺
В мире Unix и Linux нужно сделать следующее:
- иметь небольшую, простую, легко проверяемую программу, которая работает от имени суперпользователя и связывает сокет прослушивания;
- иметь другую маленькую, простую, легко проверяемую программу, которая отбрасывает привилегии, порожденные первой программой;
- чтобы основная часть службы работала в отдельной третьей программе под учетной записью не-суперпользователя и цепью, загруженной второй программой, ожидая простого наследования дескриптора открытого файла для сокета.
Вы неправильно поняли, где находится высокий риск. Высокий риск заключается в чтении из сети и воздействии на то, что читается, а не в простых действиях по открытию сокета, привязке его к порту и вызову listen()
. Это часть службы, которая осуществляет фактическое общение с высоким риском. Открываемые части, bind()
и listen()
и даже (в некоторой степени) accepts()
часть не представляют высокого риска и могут выполняться под эгидой суперпользователя. Они не используют и не обрабатывают (за исключением исходных IP-адресов в случае accept()
) данные, которые находятся под контролем ненадежных незнакомцев в сети.
Есть много способов сделать это.
inetd
Как говорит Дейл Хагглунд, старый inetd
"сетевого суперсервера" делает это. Учетная запись, под которой запускается сервисный процесс, является одним из столбцов в inetd.conf
. Он не разделяет прослушивающую часть и часть удаления привилегий на две отдельные программы, маленькие и легко проверяемые, но он разделяет основной код службы на отдельную программу, exec()
созданную в процессе службы, который порождается с помощью дескриптор открытого файла для сокета.
Сложность аудита - не такая уж большая проблема, поскольку нужно проверять только одну программу. Основная проблема inetd
заключается не столько в аудите, сколько в том, что он не обеспечивает простого детального управления сервисом во время выполнения по сравнению с более поздними инструментами.
UCSPI-TCP и daemontools
Daniel J. Бернштейна UCSPI-TCP и DaemonTools пакеты были разработаны , чтобы сделать это в сочетании. В качестве альтернативы можно использовать практически эквивалентный набор инструментов Брюса Гюнтера на бис .
Программа для открытия дескриптора файла сокета и привязки к привилегированному локальному порту - tcpserver
, из UCSPI-TCP. Это делает listen()
и accept()
.
Затем tcpserver
запускает либо служебную программу, которая сама отбрасывает привилегии root (поскольку обслуживаемый протокол включает в себя запуск в качестве суперпользователя, а затем "вход в систему", как, например, в случае с демоном FTP или SSH), либо setuidgid
который это отдельная небольшая и легко проверяемая программа, которая только отбрасывает привилегии и затем загружает их в цепочку непосредственно в служебную программу (ни одна из частей которой, таким образом, никогда не будет работать с привилегиями суперпользователя, как, например, в случае с qmail-smtpd
).
Таким образом, сценарий run
службы может быть, например, таким (для dummyidentd для предоставления нулевой службы IDENT):
#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl
перекус
Мой пакет Nosh предназначен для этого. У него есть небольшая утилита setuidgid
, как и у других. Одно небольшое отличие состоит в том, что его можно использовать со службами в стиле systemd
"LISTEN_FDS", а также со службами UCSPI-TCP, поэтому традиционная программа tcpserver
заменяется двумя отдельными программами: tcp-socket-listen
и tcp-socket-accept
.
Опять же, одноразовые утилиты порождают и загружают друг друга. Одна интересная особенность дизайна заключается в том, что можно отказаться от привилегий суперпользователя после listen()
но даже до accept()
. Вот скрипт run
для qmail-smtpd
который действительно делает именно это:
#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'
Программы, которые работают под эгидой суперпользователя - это небольшие сервисно-независимые инструменты загрузки fdmove
, clearenv
, envdir
, softlimit
, tcp-socket-listen
и setuidgid
. К моменту запуска sh
, сокет открыт и привязан к порту smtp
, и у процесса больше нет привилегий суперпользователя.
S6, S6-сети и Execline
Пакеты Laurent Bercot s6 и s6-network были разработаны для того, чтобы сделать это совместно. Команды структурно очень схожи с daemontools
и UCSPI-TCP.
Сценарии run
будут почти такими же, за исключением замены s6-tcpserver
на tcpserver
и s6-setuidgid
на setuidgid
. Тем не менее, можно также использовать набор инструментов execline М. Bercot в то же время.
Вот пример службы FTP, слегка измененной по сравнению с оригинальной Уэйн Маршалл, которая использует execline, s6, s6-network и программу сервера FTP из publicfile:
#!/command/execlineb -PW
multisubstitute {
define CONLIMIT 41
define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp
s6-softlimit -o25 -d250000
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21
ftpd ${FTP_ARCHIVE}
ipsvd
Ipsvd Gerrit Pape - это еще один набор инструментов, который работает по тем же принципам, что и ucspi-tcp и s6-network. На этот раз инструментами являются chpst
и tcpsvd
, но они делают то же самое, и код высокого риска, который выполняет чтение, обработку и запись вещей, отправленных по сети ненадежными клиентами, все еще находится в отдельной программе.
Вот пример М. Pape запуска fnord
в сценарии run
:
#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord
systemd
systemd
, новая система управления службами и инициализации, которую можно найти в некоторых дистрибутивах Linux, предназначена для того, чтобы делать то, что может делать inetd
. Тем не менее, он не использует набор небольших автономных программ. К сожалению, приходится проверять systemd
полностью.
С помощью systemd
создаются файлы конфигурации для определения сокета, который слушает systemd
, и запускается служба, которая запускает systemd
. Файл "модуля" службы имеет настройки, которые позволяют значительно контролировать процесс службы, в том числе от имени пользователя, с которым он работает.
Когда этот пользователь настроен как не суперпользователь, systemd
выполняет всю работу по открытию сокета, привязке его к порту и вызову listen()
(и, если требуется, accept()
) в процессе № 1 в качестве суперпользователя. и сервисный процесс, который он порождает, выполняется без привилегий суперпользователя.