2014-02-02 05:48:05 +0000 2014-02-02 05:48:05 +0000
117
117

Позволить не корневой процесс привязки к портам 80 и 443?

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

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

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

Ответы (5)

176
176
176
2015-03-21 21:12:41 +0000

Я не уверен, на что здесь ссылаются другие ответы и комментарии. Это довольно просто. Есть два варианта, оба из которых позволяют получить доступ к малочисленным портам без необходимости поднимать процесс до уровня 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 page ).

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

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

  3. Теперь выполните команду через authbind (опционально указывая --deep или другие аргументы, смотрите страницу man):


Есть плюсы и минусы в обоих случаях. Вариант 1 предоставляет доверие binary, но не контролирует доступ к каждому порту. Вариант 2 предоставляет доверие user/group и обеспечивает контроль над доступом к порту, но более старые версии поддерживают только IPv4 (так как я изначально писал об этом, были выпущены более новые версии с поддержкой IPv6).

29
29
29
2014-02-02 16:21:39 +0000

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

Правильно в мире Unix и Linux:

  • иметь маленькую, простую, легко прослушиваемую программу, которая работает как суперпользователь и связывает прослушивающий сокет;
  • иметь другую маленькую, простую, легко прослушиваемую программу, которая бросает привилегии, порожденные первой программой;
  • иметь мясо сервиса, в отдельной третьей программе, запущенной под не-суперпользовательским аккаунтом и цепочкой, загруженной второй программой, ожидая просто унаследовать открытый файловый дескриптор для сокета.

Вы имеете неверное представление о том, где высок риск. Высокий риск заключается в чтении из сети и воздействии на то, что читается, а не в простых действиях по открытию сокета, привязке его к порту и вызове listen(). Это та часть сервиса, которая выполняет фактическое взаимодействие, которое является высоким риском. Части, которые открываются, bind(), и listen(), и даже (в какой-то степени) часть, которая accepts(), не является частью высокого риска и может быть запущена под эгидой суперпользователя. Они не используют и не действуют на данные (за исключением IP-адресов источника в случае accept()), которые находятся под контролем недоверенных посторонних лиц в сети.

Есть много способов сделать это.

inetd

Как говорит Дейл Хагглунд, старый “сетевой суперсервер” inetd делает это. Учетная запись, под которой запущен сервисный процесс, является одной из колонок в inetd.conf. Она не разделяет прослушивающую часть и выпадающую часть привилегий на две отдельные программы, маленькие и легко прослушиваемые, но она разделяет основной сервисный код на отдельную программу, exec()ed в сервисном процессе, который она порождает с открытым файловым дескриптором для сокета.

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

UCSPI-TCP и даемонструменты

Пакеты Дэниела Дж. Бернштейна UCSPI-TCP и daemontools были разработаны для этого совместно. В качестве альтернативы можно использовать во многом эквивалентный daemontools-encore инструментарий Брюса Гюнтера.

Программа для открытия дескриптора файла сокета и привязки к привилегированному локальному порту - tcpserver , из UCSPI-TCP. Она делает и listen() и accept().

tcpserver затем порождает либо служебную программу, которая сама отказывается от привилегий root (потому что обслуживаемый протокол включает в себя запуск от имени суперпользователя, а затем “вход в систему”, как в случае, например, FTP или SSH-демон) или setuidgid , которая является автономной маленькой и легко проверяемой программой, которая только сбрасывает привилегии, а затем загружает их в собственно служебную программу (ни одна из частей которой, таким образом, никогда не работает с привилегиями суперпользователя, как в случае, скажем, qmail-smtpd ). Скрипт

Сервис run будет, например (этот скрипт для dummyidentd для предоставления сервиса нулевого IDENT):

nosh

My nosh package предназначен для этого. У него есть небольшая утилита setuidgid, как и у других. Небольшое отличие состоит в том, что ее можно использовать как с сервисами в стиле systemd “LISTEN_FDS”, так и с сервисами UCSPI-TCP, поэтому традиционная программа tcpserver заменяется двумя отдельными программами: tcp-socket-listen и tcp-socket-accept.

Опять же, одноцелевые утилиты порождают и цепную загрузку друг друга. Одна интересная причуда конструкции заключается в том, что можно отказаться от привилегий суперпользователя после listen(), но даже до accept(). Вот скрипт run для qmail-smtpd, который действительно делает именно это:

#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl

Программы, которые работают под эгидой суперпользователя - это маленькие инструменты служебной диагностики и загрузки цепочек fdmove, clearenv, envdir, softlimit, tcp-socket-listen, setuidgid, и sh. К моменту запуска smtp сокет открыт и привязан к порту daemontools, и процесс больше не имеет привилегий суперпользователя. Пакеты

s6, s6-networking и execline

Laurent Bercot s6 и s6-networking были разработаны для этого совместно. Команды структурно очень похожи на команды run и UCSPI-TCP. Сценарии

s6-tcpserver будут во многом одинаковыми, за исключением замены tcpserver на s6-setuidgid и setuidgid на chpst. Тем не менее, можно было бы также использовать одновременно и инструментарий М. Беркота execline .

Вот пример службы FTP, слегка модифицированной из Уэйна Маршалла , которая использует execline, s6, s6-networking, и программу сервера FTP из publicfile :

#!/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 \
'

ipsvd

Gerrit Pape ipsvd является еще одним набором инструментов, который работает по тем же строкам, что и ucspi-tcp и s6-сети. На этот раз инструменты tcpsvd и fnord, но они делают то же самое, и высокорискованный код, который делает чтение, обработку и написание вещей, отправляемых по сети посредством недоверенные клиенты все еще находятся в отдельной программе.

Вот пример M. Pape’s example выполнения run в скрипте systemd:

#!/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}

systemd

inetd , новая система служебного контроля и init, которая есть в некоторых дистрибутивах Linux, предназначена для того, чтобы сделать то, что может сделать systemd . Однако, она не использует набор маленьких самодостаточных программ. Приходится, к сожалению, проводить аудит systemd в полном объеме.

С помощью systemd создаются конфигурационные файлы для определения сокета, который прослушивает systemd, и сервиса, который запускается systemd. Файл “unit” службы имеет настройки, которые позволяют контролировать процесс работы службы, включая то, каким пользователем она запускается.

С таким пользователем, установленным как не суперпользователь, listen() выполняет всю работу по открытию сокета, привязке его к порту и вызову accept() (и, если требуется, 0x6&) в процессе #1 в качестве суперпользователя, а порожденный им сервисный процесс выполняется без привилегий суперпользователя.

17
17
17
2018-06-27 07:00:56 +0000

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

Тогда я узнал, что могу переадресовывать соединения с одного порта на другой. Поэтому я запустил сервер на порту 3000 и установил переадресацию с порта 80 на порт 3000. Эта ссылка предоставляет действительные команды, которые можно использовать для этого. Вот команды -

localhost/loopback

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

external

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

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

4
4
4
2014-02-02 06:49:22 +0000

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

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

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

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

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

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

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

[1] _COPY11_separation

3
3
3
2019-09-13 07:38:46 +0000

Самое простое решение : удалить все привилегированные порты на linux

Работает на ubuntu/debian :

#save configuration permanently
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
#apply conf
sysctl --system

(хорошо работает для VirtualBox с не корневой учетной записью)

Теперь, будьте осторожны с безопасностью, потому что все пользователи могут связать все порты !

Похожие вопросы

6
10
5
15
6