Разрешить процесс без полномочий root связываться с портами 80 и 443?

104

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

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

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

jww
источник
1
См. Serverfault.com/questions/268099 и stackoverflow.com/questions/413807 . Короткий ответ: нет.
Сами Лэйн
10
Длинный ответ «да», поэтому короткий ответ тоже должен быть «да».
BT
4
Краткий ответ - да.
Джейсон С

Ответы:

163

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

Джейсон С
источник
Это действительно нужно rwxразрешение?
Мэтт
Чтобы отменить операцию в варианте 1, вы могли бы запустить команду снова, используя -pintead of +eip?
eugene1832
5
Помните, что при использовании setcap, если вы перезаписываете исполняемый файл, которому вы предоставляете привилегии (например, выполните перестройку), он теряет статус привилегированного порта, и вы должны снова дать ему привилегии: |
rogerdpack
1
Что-то, с чем мне пришлось возиться; Я пытался запустить службу sysv, которая запускает исполняемый файл ruby, использующий ruby. Вы должны дать setcapразрешение на версию- конкретного рубинового исполняемого файла , например/usr/bin/ruby1.9.1
Christian Рондо
3
У меня есть сомнения, что chmod777 byportфайлов - лучшая идея. Я видел , давая permisions в диапазоне от 500до 744. Я бы остановился на самом строгом, который работает для вас.
Пере
28

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

В мире 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службами -style "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 были разработаны для того, чтобы сделать это совместно. Команды структурно очень похожи на команды daemontoolsUCSPI-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этот раз, но они делают то же самое, и код высокого риска, который выполняет чтение, обработку и запись вещей, отправленных по сети ненадежными клиентами, все еще находится в отдельной программе.

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

JdeBP
источник
2
Спасибо за комплимент. Это большая коллекция конкретных советов. +1.
Дейл Хэгглунд
11

У меня довольно другой подход. Я хотел использовать порт 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

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

нуб
источник
6
+1 за нестандартное мышление
Ричард Уайзман
4

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

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

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

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

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

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

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

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

Дейл Хагглунд
источник
То, что вы предлагаете, не считается лучшей практикой. Вы можете взглянуть на inetd, который может прослушивать привилегированный сокет и затем передавать этот сокет непривилегированной программе.
Дейл Хагглунд,
3

Самое простое решение: удалить все привилегированные порты в 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 с учетной записью без полномочий root)

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

soleuu
источник
Это умно. Одна маленькая гнида: конфигурация открывает 80 и 443, но она также открывает все остальные порты. Расслабляющие разрешения для других портов могут быть нежелательны.
jww