82

Можно ли настроить параметр ядра, чтобы позволить программе пользовательского интерфейса связываться с портами 80 и 443?

Причина, по которой я спрашиваю, заключается в том, что глупо разрешать привилегированному процессу открывать сокет и слушать. Все, что открывает сокет и прослушивает, представляет собой высокий риск, и приложения с высоким риском не должны запускаться от имени пользователя root.

Я бы предпочел попытаться выяснить, какой непривилегированный процесс прослушивает порт 80, а не пытаться удалить вредоносное ПО, проникшее с правами root.

4 ответа4

126

Я не уверен, на что ссылаются другие ответы и комментарии здесь. Это возможно довольно легко. Существует два варианта, каждый из которых позволяет получить доступ к портам с низким номером без необходимости поднять процесс до уровня root:

Вариант 1. Используйте CAP_NET_BIND_SERVICE для предоставления доступа к порту с низким номером для процесса:

При этом вы можете предоставить постоянный доступ к конкретному двоичному файлу для привязки к портам с низким номером с помощью команды setcap :

sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary

Для получения дополнительной информации о части e/i/p см. cap_from_text.

После этого /path/to/binary сможет связываться с портами с низким номером. Обратите внимание, что вы должны использовать setcap для самого двоичного файла , а не символическую ссылку.

Вариант 2. Используйте authbind для предоставления одноразового доступа с более точным контролем пользователя / группы / порта:

Инструмент authbind (man-страница) существует именно для этого.

  1. Установите authbind используя ваш любимый менеджер пакетов.

  2. Настройте его для предоставления доступа к соответствующим портам, например, для разрешения 80 и 443 от всех пользователей и групп:

    sudo touch /etc/authbind/byport/80
    sudo touch /etc/authbind/byport/443
    sudo chmod 777 /etc/authbind/byport/80
    sudo chmod 777 /etc/authbind/byport/443
    
  3. Теперь выполните вашу команду через authbind (при желании можно указать --deep или другие аргументы, см. Справочную страницу):

    authbind --deep /path/to/binary command line args
    

    Например

    authbind --deep java -jar SomeServer.jar
    

Есть и плюсы и минусы обоих вышеперечисленных. Вариант 1 предоставляет доверие к двоичному файлу, но не обеспечивает контроль доступа к каждому порту. Вариант 2 предоставляет доверие пользователю / группе и обеспечивает контроль доступа к каждому порту, но AFAIK поддерживает только IPv4.

25

Дейл Хэгглунд на месте. Так что я просто собираюсь сказать то же самое, но по-другому, с некоторыми особенностями и примерами. ☺

В мире 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 в качестве суперпользователя. и сервисный процесс, который он порождает, выполняется без привилегий суперпользователя.

5

У меня довольно другой подход. Я хотел использовать порт 80 для сервера node.js. Мне не удалось это сделать, поскольку Node.js был установлен для пользователя, не являющегося пользователем sudo. Я пытался использовать символические ссылки, но у меня это не сработало.

Затем я узнал, что могу перенаправлять соединения с одного порта на другой порт. Поэтому я запустил сервер на порту 3000 и настроил переадресацию порта с порта 80 на порт 3000.

Эта ссылка предоставляет фактические команды, которые могут быть использованы для этого. Вот команды -

локальный / шлейфу

sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 3000

внешний

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000

Я использовал вторую команду, и она сработала для меня. Так что я думаю, что это промежуточная точка для того, чтобы не позволить пользовательскому процессу напрямую обращаться к нижним портам, а предоставлять им доступ с помощью переадресации портов.

4

Ваши инстинкты совершенно верны: плохая идея - запускать большую сложную программу от имени пользователя root, потому что их сложность затрудняет доверие.

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

Стандартный подход к разрешению этого очевидного противоречия - разделение привилегий. Основная идея состоит в том, чтобы разделить вашу программу на две (или более) части, каждая из которых выполняет четко определенную часть общего приложения и взаимодействует посредством простых ограниченных интерфейсов.

В приведенном вами примере вы хотите разделить вашу программу на две части. Тот, который запускается от имени root, открывается и привязывается к привилегированному сокету, а затем каким-то образом передает его другой части, которая работает как обычный пользователь.

Эти два основных способа достижения этого разделения.

  1. Единственная программа, которая запускается от имени пользователя root. Самое первое, что он делает, - это создает необходимый сокет максимально простым и ограниченным способом. Затем он отбрасывает привилегии, то есть он превращается в процесс обычного режима пользователя и выполняет всю другую работу. Правильно отбросить привилегии сложно, поэтому, пожалуйста, найдите время, чтобы изучить правильный способ сделать это.

  2. Пара программ, которые обмениваются данными через пару сокетов, созданную родительским процессом. Программа непривилегированного драйвера получает начальные аргументы и, возможно, выполняет некоторую базовую проверку аргументов. Он создает пару подключенных сокетов с помощью socketpair(), а затем разветвляет и исполняет две другие программы, которые будут выполнять реальную работу, и обмениваются данными через пару сокетов. Один из них является привилегированным и создаст сокет сервера, а также любые другие привилегированные операции, а другой будет выполнять более сложное и, следовательно, менее надежное выполнение приложения.

[1] http://en.m.wikipedia.org/wiki/Privilege_separation

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .