Есть ли способ для некорневых процессов связываться с «привилегированными» портами в Linux?

389

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

Мне известны стандартные обходные пути , но никто из них не делает именно то, что я хочу:

  1. authbind (версия в тестировании Debian, 1.0, поддерживает только IPv4)
  2. Использование цели iptables REDIRECT для перенаправления низкого порта на высокий порт (таблица «nat» еще не реализована для ip6tables, версии iptables IPv6)
  3. sudo (Запуск от имени root - это то, чего я пытаюсь избежать)
  4. SELinux (или аналогичный). (Это всего лишь мой блок разработчика, я не хочу вводить много дополнительной сложности.)

Есть ли какая-нибудь простая sysctlпеременная, позволяющая процессам без полномочий root связываться с «привилегированными» портами (портами менее 1024) в Linux, или мне просто не повезло?

РЕДАКТИРОВАТЬ: В некоторых случаях вы можете использовать возможности, чтобы сделать это.

Джейсон Крейтон
источник
5
По моему опыту, одна из причин для этого - написать веб-сервер, а не использовать Apache (или lighttpd).
S.Lott
Я добавил материал setcap в свой ответ на почти идентичный stackoverflow.com/questions/277991/…
Paul Tomblin
15
Потому что в этом случае я использовал IPv6, поэтому некоторые из «обычных» обходных путей (authbind и iptables REDIRECT) не работали для меня.
Джейсон Крейтон
1
serverfault.com/questions/112795/…
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
1
Есть несколько способов сделать это. См. Разрешение процесса без полномочий root связываться с портами 80 и 443? на Супер пользователя.
jww

Ответы:

392

Хорошо, спасибо людям, которые указали на возможности системы и CAP_NET_BIND_SERVICEвозможности. Если у вас последнее ядро, это действительно возможно использовать для запуска службы без полномочий root, но для привязки низких портов. Короткий ответ: вы делаете:

setcap 'cap_net_bind_service=+ep' /path/to/program

И затем в любое время programвыполняется после этого он будет иметь CAP_NET_BIND_SERVICEвозможность. setcapнаходится в пакете Debian libcap2-bin.

Теперь о предостережениях:

  1. Вам понадобится как минимум ядро ​​2.6.24
  2. Это не будет работать, если ваш файл является скриптом. (т. е. использует строку #! для запуска интерпретатора). В этом случае, насколько я понимаю, вам придется применить эту возможность к самому исполняемому файлу интерпретатора, что, конечно, является кошмаром безопасности, поскольку любая программа, использующая этот интерпретатор, будет иметь такую ​​возможность. Я не смог найти какой-либо простой и простой способ обойти эту проблему.
  3. Linux отключит LD_LIBRARY_PATH для любого пользователя programс повышенными привилегиями, например setcapили suid. Так что, если вы programиспользуете его самостоятельно .../lib/, вам может понадобиться другой вариант, например, переадресация портов.

Ресурсы:

Примечание: RHEL впервые добавил это в v6 .

Джейсон Крейтон
источник
3
Частичный обходной путь для сценариев: создайте копию интерпретатора (например, bash), предоставьте ей возможности, но ограничьте доступ к копии тем пользователям, которые в ней нуждаются. Конечно, этим пользователям нужно доверять, но они все равно могут изменить сценарий.
Эрих Кицмюллер
3
Помимо вышеупомянутого пакета debian (двоичного), сайт разработчика - friedhoff.org/posixfilecaps.html связанные документы / презентации / и т. Д.
RandomNickName42
1
на suse SLES 11.1 мне пришлось добавить параметр ядра file_caps = 1 в grub menu.lst, чтобы это работало.
Шей
2
Да @ C.Ross, так как его нужно будет применить, /usr/bin/javaа затем открыть возможность для любого Java-приложения, работающего в системе. Слишком плохие возможности не могут быть установлены для каждого пользователя.
Joeytwiddle
5
Сохраняется ли настройка setcap при перезагрузках; если нет, то есть ли стандартное место для установки этого правила, чтобы оно запускалось при запуске системы? Помогает ли /etc/security/capability.confDebian / Ubuntu?
Joeytwiddle
34

Вы можете сделать перенаправление порта. Это то, что я делаю для сервера политики Silverlight, работающего на Linux

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300
FlappySocks
источник
5
К сожалению, это будет работать только для маршрутизируемых соединений, т.е. не с локальной машины.
zbyszek
@joeytwiddle Я думаю, что это в сценарии, который запускает (запускает) ваш сервер, но я могу ошибаться. Я также хотел бы знать: P
Камило Мартин
@CamiloMartin Еще раз, документация Debian, которая была связана с вопросом, рекомендует поместить его в сценарий брандмауэра или создать его по адресу /etc/network/if-up.d/firewall.
joeytwiddle
8
@zbyszek Это может работать для локальных подключений к компьютеру с дополнительным правилом iptables: stackoverflow.com/a/31795603/1356953
00500005
В старых ядрах кажется, что это не поддерживается для IPv6 . Но, видимо, это поддерживается в ip6tables v1.4.18 и ядре Linux v3.8.
Крейг МакКуин,
31

Стандартный способ состоит в том, чтобы сделать их «setuid», чтобы они запускались как root, а затем они отбрасывают эту привилегию root, как только они привязаны к порту, но до того, как они начнут принимать подключения к нему. Вы можете увидеть хорошие примеры этого в исходном коде для Apache и INN. Мне сказали, что Lighttpd - еще один хороший пример.

Другим примером является Postfix, который использует несколько демонов, которые обмениваются данными по каналам, и только один или два из них (которые делают очень мало, за исключением принимаемых или отправляемых байтов) работают как root, а остальные работают с более низкими привилегиями.

Пол Томблин
источник
1
Интересно, что это не работает в последних версиях Linux (может быть, просто Ubuntu) без установленного CAP_SETUID. Поэтому, если вам нужен setuid, вам все равно придется установить эту возможность.
Мэтт
Удаление корневых привилегий - правильный способ сделать это. Хотя нет необходимости устанавливать setuid, если у вас есть init / upstart / systemd, запускающий службу.
Майкл Хэмптон
2
Это сложно, если программа написана на интерпретируемом языке или интерпретаторе байт-кода, таком как C # (Mono), Java, Python. (Очевидно, Perl сделал это с помощью binfmt_miscсвоего флага 'C'; я не уверен насчет других.)
Крейг МакКуин,
очень мало, кроме испускаемых байтов, это не очень мало.
Райан
Если вы установите двоичный setuid, он будет запущен как корневой процесс. Вопрос, однако, заключается в том, как выполнить это без запуска вашего двоичного файла в качестве корневого процесса. Это решение является ответом на другой вопрос, а именно: «Как может непривилегированный пользователь связать процесс с привилегированным портом», но это не вопрос, ИМХО?
Майкл Бир
24

Или исправьте свое ядро ​​и уберите проверку.

(Вариант последней инстанции, не рекомендуется).

В net/ipv4/af_inet.c, удалите две строки, которые читают

      if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
              goto out;

и ядро ​​больше не будет проверять привилегированные порты.

Джошуа
источник
22
Очень плохая идея по множеству причин.
Адам Лассек
24
Это плохая идея. Но на самом деле будет работать. И конечно заставил меня смеяться.
Ото Брглез
23
Конечно, это плохая идея. Вот почему я сказал в крайнем случае. Смысл открытого исходного кода в том, что если он не работает так, как вы хотите, вы можете изменить его.
Джошуа
18
Я не уверен, почему вы были отклонены. Авторы вредоносных программ не заботятся о том, какой порт они слушают, и они более чем рады открыть порт больше 1024. Мне кажется, что все порты должны требовать привилегий, или никакие порты не должны требовать привилегий. А требование root для открытия порта ниже 1024 означает, что у вас есть приложение с высоким риском, работающее от имени root. Это кажется мне глупой идеей. Возможно, я что-то здесь
упускаю
9
В свое время порт <1024 использовался в протоколах UNIX - UNIX, чтобы доказать, что код, запущенный на другом конце, работает от имени пользователя root. Это работало достаточно хорошо для набора серверов UNIX с общей безопасностью.
Джошуа
22

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

sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N

Это имеет преимущество работы с серверами сценариев и является очень простым.

Габриэль Берт
источник
1
Мой личный фаворит. Очень легко включается и выключается и не требует никаких общесистемных изменений. Отличная идея!
Дан Пассаро,
Это потрясающе. Безусловно самый простой ответ.
michaelsnowden
1
Я ожидал бы, что это будет умеренно дорого и не будет хорошей идеей в любой ситуации, связанной с производительностью, но я не могу быть уверен без тестирования. Шифрование SSH стоит ненулевую сумму.
lahwran
3
Это может быть сделано с netcat без ssh:sudo nc -l 80 | nc localhost 3000
Bugster
1
Вы шифруете + дешифруете только для перемещения данных на другой порт на той же машине? Кроме того, это не работает для трафика UDP.
Даниэль Ф,
19

Обновление 2017:

Использовать authbind


Гораздо лучше, чем CAP_NET_BIND_SERVICE или пользовательское ядро.

  • CAP_NET_BIND_SERVICE предоставляет доверие к двоичному файлу, но не обеспечивает контроль над доступом через порт.
  • Authbind предоставляет доверие пользователю / группе и обеспечивает контроль доступа к каждому порту, а также поддерживает IPv4 и IPv6 (поддержка IPv6 была добавлена ​​в последнее время).

    1. Установка: apt-get install 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
      

В качестве продолжения сказочной рекомендации Джошуа (= не рекомендуется, если вы не знаете, что делаете) взломать ядро:

Я впервые опубликовал это здесь .

Просто. С нормальным или старым ядром у вас нет.
Как отмечают другие, iptables может переадресовывать порт.
Как также отметили другие, CAP_NET_BIND_SERVICE также может выполнять эту работу.
Конечно, CAP_NET_BIND_SERVICE потерпит неудачу, если вы запустите свою программу из скрипта, если вы не установите ограничение на интерпретатор оболочки, что бессмысленно, вы также можете запустить свой сервис как root ...
например, для Java вы должны применить его в JAVA JVM

sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java

Очевидно, это означает, что любая Java-программа может связывать системные порты.
Дито для моно / .NET.

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

Вы просто скачиваете исходный код для самого последнего ядра (или того же, что у вас есть). После этого вы идете к:

/usr/src/linux-<version_number>/include/net/sock.h:

Там вы ищете эту строку

/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK       1024

и изменить его на

#define PROT_SOCK 0

если вы не хотите иметь небезопасную ситуацию с ssh, измените ее следующим образом: #define PROT_SOCK 24

Как правило, я бы использовал самый низкий параметр, который вам нужен, например, 79 для http или 24 при использовании SMTP на порту 25.

Это уже все.
Скомпилируйте ядро ​​и установите его.
Перезагрузка.
Закончено - этот глупый предел ушел, и это также работает для сценариев.

Вот как вы собираете ядро:

https://help.ubuntu.com/community/Kernel/Compile

# You can get the kernel-source via package linux-source, no manual download required
apt-get install linux-source fakeroot

mkdir ~/src
cd ~/src
tar xjvf /usr/src/linux-source-<version>.tar.bz2
cd linux-source-<version>

# Apply the changes to PROT_SOCK define in /include/net/sock.h

# Copy the kernel config file you are currently using
cp -vi /boot/config-`uname -r` .config

# Install ncurses libary, if you want to run menuconfig
apt-get install libncurses5 libncurses5-dev

# Run menuconfig (optional)
make menuconfig

# Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
export CONCURRENCY_LEVEL=3
# Now compile the custom kernel
fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers

# And wait a long long time

cd ..

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

Стефан Штайгер
источник
Есть ли причина для понижения? Root-hater, или инструкции по компиляции ядра не сработали?
Стефан Штайгер,
4
Модификация ядра делает будущие обновления гораздо более болезненными. Я бы не стал этого делать, потому что знаю, что буду лениться регулярно обновлять ядро. хорошо, что это возможно в теории, но это не жизнеспособный вариант во многих обстоятельствах.
kritzikratzi
Возможно, более безопасный способ сделать это - изменить chmod 777 / etc / authbind / byport / 80 на chmod 544 / etc / authbind / byport / 23, чем вход в систему chown: group / etc / authbind / byport / 23
Jérôme B
18

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

Идеальным решением, ИМХО, должна быть возможность создания оболочки с наследуемым CAP_NET_BIND_SERVICE набором.

Вот несколько запутанный способ сделать это:

sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
     --caps='cap_net_bind_service+pei' -- \
     YOUR_COMMAND_GOES_HERE"

capshутилиту можно найти в пакете libcap2-bin в дистрибутивах Debian / Ubuntu. Вот что происходит:

  • sgизменяет эффективный идентификатор группы на идентификатор пользователя демона. Это необходимо, потому что capshоставляет GID без изменений, и мы определенно не хотим этого.
  • Устанавливает бит «сохранить возможности при изменении UID».
  • Изменяет UID на $DAEMONUSER
  • Удаляет все заглавные буквы (на данный момент все заглавные буквы все еще присутствуют из-за --keep=1), кроме наследуемыхcap_net_bind_service
  • Выполняет вашу команду ('-' является разделителем)

Результатом является процесс с указанными пользователем и группой и cap_net_bind_serviceпривилегиями.

В качестве примера, строка из ejabberdскрипта запуска:

sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"
Cyberax
источник
И это на самом деле не работает. Заглавные буквы не сохраняются по переключателю идентификатора пользователя. Это будет работать, если вы хотите работать с правами суперпользователя, но все возможности будут удалены.
Cyberax
Если я попробую это, я получу «не могу выполнить двоичный файл». На самом деле, я не capshмогу ничего сделать, кроме «не могу выполнить двоичный файл» при использовании опций --или ==. Интересно, что мне не хватает.
Крейг МакКуин
@CraigMcQueen, все после --передается /bin/bash, так что вы можете попробовать -c 'your command'. Увы, я, похоже, испытываю ту же проблему, что и @Cyberax, потому что при попытке получить разрешение «мне отказано» bind.
Амир
Для этого , чтобы работать без установки возможностей файлов (или работают как корень), вам нужно будет использовать возможности внешней среды , как описано в этом ответе Unix.SE . Вы можете также использовать --gidвместо sg(или --userкакие наборы --uid, --gidи --groups): sudo capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' --keep=1 --user="$service_user" --addamb=cap_net_bind_service -- -c 'exec $service $service_args'
Kevinoid
18

По какой-то причине никто не упомянул о снижении sysctl net.ipv4.ip_unprivileged_port_start до необходимого вам значения. Пример: нам нужно привязать наше приложение к порту 443.

sysctl net.ipv4.ip_unprivileged_port_start=443

Некоторые могут сказать, что существует потенциальная проблема безопасности: непривилегированные пользователи теперь могут связываться с другими привилегированными портами (444-1024). Но вы можете легко решить эту проблему с помощью iptables, заблокировав другие порты:

iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP

Сравнение с другими методами. Этот способ:

  • с некоторой точки зрения (IMO) даже более безопасен, чем установка CAP_NET_BIND_SERVICE / setuid, поскольку приложение вообще не устанавливает setuid, даже частично (возможности на самом деле есть). Например, чтобы поймать coredump приложения с поддержкой возможностей, вам нужно изменить sysctl fs.suid_dumpable (что приводит к другим потенциальным проблемам безопасности). Также, когда установлен CAP / suid, каталог / proc / PID принадлежит root, поэтому ваш пользователь без полномочий root не будет иметь полной информации / контроля над запущенным процессом, например, пользователь не сможет (в общем случае) определить, какие соединения принадлежат приложению, через / proc / PID / fd / (netstat -aptn | grep PID).
  • имеет недостаток безопасности: в то время как ваше приложение (или любое приложение, которое использует порты 443-1024) по какой-то причине не работает, другое приложение может занять порт. Но эта проблема также может быть применена к CAP / suid (в случае, если вы установите его на интерпретаторе, например, java / nodejs) и iptables-redirect. Используйте метод systemd-socket для исключения этой проблемы. Используйте метод authbind, чтобы разрешить только специальную привязку пользователя.
  • не требует установки CAP / suid при каждом развертывании новой версии приложения.
  • не требует поддержки / модификации приложения, как метод systemd-socket.
  • не требует перестройки ядра (если работающая версия поддерживает этот параметр sysctl)
  • не делает LD_PRELOAD как метод authbind / privbind, это может потенциально повлиять на производительность, безопасность, поведение (не так ли? не проверял). В остальном authbind действительно гибкий и безопасный метод.
  • переопределяет метод iptables REDIRECT / DNAT, поскольку он не требует трансляции адресов, отслеживания состояния соединения и т. д. Это заметно только в системах с высокой нагрузкой.

В зависимости от ситуации я бы выбрал sysctl, CAP, authbind и iptables-redirect. И это здорово, что у нас так много вариантов.

Уруша
источник
2
Спасибо за отличный ответ! Похоже, что эта функциональность впервые появилась в Linux 4.11 в апреле 2017 года , поэтому ее не было в 2009 году, когда я впервые задал этот вопрос. Я также провел быстрый тест, и он, кажется, также работает для IPV6, хотя «ipv4» находится в имени sysctl.
Джейсон Крейтон
15

Две другие простые возможности:

Существует старое (немодное) решение для «демона, который привязывается к низкому порту и передает управление вашему демону». Это называется inetd (или xinetd). Минусы:

  • ваш демон должен общаться по stdin / stdout (если вы не управляете демоном - если у вас нет источника - тогда это, возможно, showtopper, хотя некоторые службы могут иметь флаг совместимости с inetd)
  • новый процесс демона разветвляется для каждого соединения
  • это одно дополнительное звено в цепочке

Плюсы:

  • доступно на любом старом UNIX
  • как только ваш системный администратор настроил конфигурацию, вы можете приступить к разработке (когда вы пересобираете своего демона, можете ли вы потерять возможности setcap? И тогда вам придется вернуться к своему администратору), пожалуйста, сэр .. . ")
  • демон не должен беспокоиться об этом сетевом материале, он просто должен поговорить на stdin / stdout
  • можно настроить для запуска вашего демона от имени пользователя без полномочий root, по запросу

Другая альтернатива: взломанный прокси (netcat или даже что-то более устойчивое ) с привилегированного порта на произвольный порт с большим номером, где вы можете запустить целевой демон. (Очевидно, что Netcat - это не производственное решение, а «просто мой компьютер разработчика», верно?). Таким образом, вы можете продолжать использовать версию вашего сервера с поддержкой сети, для запуска прокси-сервера (при загрузке) потребуется только root / sudo, не полагаясь на сложные / потенциально хрупкие возможности.

Мартин Карпентер
источник
1
Хорошее предложение. По какой-то причине я даже не думал об использовании inetd. За исключением того, что рассматриваемая служба основана на UDP, поэтому она немного сложнее, чем службы TCP. У меня есть решение, работающее сейчас с setcap, но я должен подумать об этом.
Джейсон Крейтон
Что касается второго варианта, вы, вероятно, даже можете запустить прокси, используя inetd ... (Но IPTables, вероятно, снижает накладные расходы ...)
Герт ван ден Берг
14

Мой «стандартный обходной путь» использует socat в качестве перенаправителя пространства пользователя:

socat tcp6-listen:80,fork tcp6:8080

Остерегайтесь, что это не будет масштабироваться, разветвление дорого, но так работает socat.

астрономический
источник
13

Linux поддерживает возможности для поддержки более детальных разрешений, чем просто «это приложение запускается с правами root». Одной из таких возможностей CAP_NET_BIND_SERVICEявляется привязка к привилегированному порту (<1024).

К сожалению, я не знаю, как использовать это для запуска приложения без полномочий root, при этом предоставляя его CAP_NET_BIND_SERVICE(возможно, используя setcap, но для этого обязательно найдется решение).

Йоахим Зауэр
источник
Не будет работать для скриптов или отдельных программ JVM / mono.
Стефан Штайгер
13

Я знаю, что это старый вопрос, но теперь с последними (> = 4.3) ядрами, наконец, есть хороший ответ на это - окружающие возможности.

Быстрый ответ - взять из git копию последней (пока не выпущенной) версии libcap и скомпилировать ее. Скопируйте полученный progs/capshбинарный файл куда-нибудь ( /usr/local/binэто хороший выбор). Затем, с правами root, запустите вашу программу с

/usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
    --inh='cap_net_bind_service' --addamb='cap_net_bind_service' \ 
    -- -c 'your-program'

По порядку мы

  • Объявив, что когда мы переключаем пользователей, мы хотим сохранить наши текущие наборы возможностей
  • Переключение пользователя и группы на «ваше-имя-службы-пользователя»
  • Добавление cap_net_bind_serviceвозможности к унаследованным и окружающим наборам
  • Форкинг bash -c 'your-command'(поскольку capshавтоматически запускает bash с аргументами после --)

Здесь много всего происходит под капотом.

Во-первых, мы работаем от имени пользователя root, поэтому по умолчанию мы получаем полный набор возможностей. В это входит возможность переключения uid и gid с помощью setuidи setgidсистемных вызовов. Однако, обычно, когда программа делает это, она теряет свой набор возможностей - это так, что старый способ удаления root с помощью setuidвсе еще работает. --keep=1Флаг указывает capshвыдавать prctl(PR_SET_KEEPCAPS)системный вызов, который отключает капания возможностей при смене пользователя. Фактическая смена пользователей capshпроисходит с --userфлагом, который запускается setuidи setgid.

Следующая проблема, которую нам нужно решить, - это как настроить способности таким образом, чтобы это продолжалось после того, как мы стали execнашими детьми. Система возможностей всегда имела «унаследованный» набор возможностей, который является «набором возможностей, сохраняемых в execve (2)» [ abilities (7) ]. Хотя это звучит так, как будто это решает нашу проблему (просто установите cap_net_bind_serviceвозможность наследоваться, верно?), На самом деле это применимо только к привилегированным процессам - и наш процесс больше не является привилегированным, потому что мы уже изменили пользователя (с --userфлагом).

Новый набор возможностей окружения работает вокруг этой проблемы - это «набор возможностей, которые сохраняются в execve (2) программы, которая не является привилегированной». Включив cap_net_bind_serviceокружение, когда capshнаша серверная программа exec будет наследовать эту возможность, она сможет привязывать слушателей к низким портам.

Если вам интересно узнать больше, страница руководства по возможностям объясняет это очень подробно. Запуск capshчерез straceэто тоже очень познавательно!

К.Я. Цанакцидис
источник
--addambФлаг был введен с libcap 2,26. В моей системе было 2.25, и мне пришлось строить из исходного кода.
ngreen
12

TLDR: для «ответа» (как я его вижу), перейдите к части >> TLDR << в этом ответе.

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

Опять же, я рассмотрел все ответы здесь. Я попробовал некоторые из них (и решил не пробовать другие, потому что мне просто не нравились решения). Я думал , что решение было использовать systemdс его Capabilities=и CapabilitiesBindingSet=настроек. После некоторой борьбы с этим я обнаружил, что это не решение, потому что:

Возможности предназначены для ограничения корневых процессов!

Как мудро заявил OP, всегда лучше избегать этого (для всех ваших демонов, если это возможно!).

Вы не можете использовать опции, связанные с возможностями, в файлах модулей User=и Group=в них systemd, потому что возможности ВСЕГДА сбрасываются при execevвызове (или любой другой функции). Другими словами, когда systemdразветвляется и отбрасывает перманент, возможности сбрасываются. Обойти это невозможно, и вся эта логика привязки в ядре основана на uid = 0, а не на возможностях. Это означает, что маловероятно, что Возможности когда-либо будут правильным ответом на этот вопрос (по крайней мере, в ближайшее время). Кстати, setcapкак уже упоминали другие, это не решение проблемы. Это не сработало для меня, оно не работает хорошо со скриптами, и они в любом случае сбрасываются при изменении файла.

В своей скудной защите я утверждал (в комментарии, который я сейчас удалил), что предложение iptables Джеймса (о котором также упоминает ФП) было «вторым лучшим решением». :-П

>> << TLDR

Решение состоит в том, чтобы объединить systemdс iptablesкомандами на лету , как это ( взято из DNSChain ):

[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service

[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc

# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps

[Install]
WantedBy=multi-user.target

Здесь мы выполняем следующее:

  • Демон слушает на 5333, но соединения успешно принимаются на 53 благодаря iptables
  • Мы можем включить команды в сам файл модуля и таким образом избавить людей от головной боли. systemdочищает правила брандмауэра для нас, убирая их, когда демон не работает.
  • Мы никогда не запускаемся с правами root и делаем повышение привилегий невозможным (по крайней мере, systemdутверждаем), предположительно, даже если демон скомпрометирован и установлен uid=0.

iptablesк сожалению, это все еще довольно уродливая и сложная в использовании утилита. Если демон , например, слушает eth0:0вместо eth0, например, команды немного отличаются .

Грег Слепак
источник
2
Никто не должен использовать псевдонимы в старом стиле, как сейчас eth0:0, если у них нет действительно древнего дистрибутива Linux. Они устарели в течение многих лет и в конечном итоге уйдут.
Майкл Хэмптон
1
Я думаю, что вы имеете в виду OpenVZ. (SolusVM - это панель управления.) И да, OpenVZ делает много неправильных вещей, сетевая работа - лишь одна из них.
Майкл Хэмптон
1
Нет, я имею в виду под SolusVM. Из / etc / network / interfaces:# Generated by SolusVM
Грег Слепак
1
Все еще не OpenVZ ... По крайней мере, я не использую его, как и мой VPS.
Грег Слепак
7
Спасибо, что указали на возможности системы systemd. Однако нет необходимости в сложных iptables, когда systemd запускает двоичный файл напрямую (не в сценарии). Настройка AmbientCapabilities=CAP_NET_BIND_SERVICEотлично работала для меня в сочетании с User=.
Рууд
11

systemd - это замена sysvinit, у которой есть возможность запустить демон с определенными возможностями. Параметры Возможности =, CapabilityBoundingSet = в man-странице systemd.exec (5) .

Zbyszek
источник
2
Раньше я рекомендовал этот ответ, но, попробовав, теперь нет. Смотрите мой ответ для альтернативы, которая все еще использует systemd.
Грег Слепак
10

Перенаправление портов имело для нас наибольшее значение, но мы столкнулись с проблемой, когда наше приложение решало бы локально URL-адрес, который также необходимо перенаправить; (это означает, что ты shindig ).

Это также позволит вам быть перенаправлены при доступе к URL на локальном компьютере.

iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -A OUTPUT -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
00500005
источник
9

Современный Linux поддерживает /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.

Джин Маккалли
источник
8

При использовании systemd вам просто нужно немного изменить свой сервис, чтобы принимать предварительно активированные сокеты.

Позже вы можете использовать systemd socket для активации .

Никаких возможностей, iptables или других хитростей не требуется.

Это содержимое соответствующих системных файлов из этого примера простого Python http-сервера

файл httpd-true.service

[Unit]
Description=Httpd true 

[Service]
ExecStart=/usr/local/bin/httpd-true
User=subsonic

PrivateTmp=yes

файл httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target
j123b567
источник
7

При запуске:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080

Затем вы можете привязать к порту, к которому вы перешли.

Джеймс
источник
действительно --to-portсуществует? man iptablesтолько упоминания --to-ports(множественное число).
Абдул
я заметил некоторые различия, и я прыгал вокруг
Джеймс
Посмотрите этот ответ, чтобы узнать, как это сочетать с systemd.
Грег Слепак
3

Используйте утилиту privbind : она позволяет непривилегированному приложению связываться с зарезервированными портами.

Александр Давыдов
источник
3

Существует также «путь DJB». Вы можете использовать этот метод для запуска вашего процесса с правами root на любом порту в tcpserver, тогда он передаст управление процессом пользователю, которого вы укажете, сразу после запуска процесса.

#!/bin/sh

UID=$(id -u username)
GID=$(id -g username)
exec tcpserver -u "${UID}" -g "${GID}" -RHl0 0 port /path/to/binary &

Для получения дополнительной информации см .: http://thedjbway.b0llix.net/daemontools/uidgid.html

mti2935
источник
1

Поскольку OP - это просто разработка / тестирование, могут оказаться полезными менее чем гладкие решения:

setcap может использоваться в интерпретаторе сценария для предоставления возможностей сценариям. Если setcaps для двоичного файла глобального интерпретатора неприемлемы, сделайте локальную копию двоичного файла (любой пользователь может) и получите root для setcap для этой копии. Python2 (по крайней мере) правильно работает с локальной копией интерпретатора в вашем дереве разработки скриптов. Нет необходимости в suid, чтобы пользователь root мог контролировать, к каким возможностям пользователи имеют доступ.

Если вам нужно отслеживать общесистемные обновления интерпретатора, используйте скрипт оболочки, как показано ниже, чтобы запустить ваш скрипт:

#!/bin/sh
#
#  Watch for updates to the Python2 interpreter

PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7

cmp $PRG_ORIG $PRG || {
    echo ""
    echo "***** $PRG_ORIG has been updated *****"
    echo "Run the following commands to refresh $PRG:"
    echo ""
    echo "    $ cp $PRG_ORIG $PRG"
    echo "    # setcap cap_net_raw+ep $PRG"
    echo ""
    exit
}

./$PRG $*
duanev
источник
1

Я пробовал метод iptables PREROUTING REDIRECT. В старых ядрах этот тип правил не поддерживался для IPv6. . Но, по-видимому, теперь он поддерживается в ip6tables v1.4.18 и ядре Linux v3.8.

Я также обнаружил, что PREROUTING REDIRECT не работает для соединений, инициированных в машине. Для работы с соединениями с локального компьютера также добавьте правило OUTPUT - смотрите, что перенаправление порта iptables не работает для localhost . Например, что-то вроде:

iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

Я также обнаружил, что PREROUTING REDIRECT также влияет на перенаправленные пакеты . То есть, если машина также пересылает пакеты между интерфейсами (например, если она действует как точка доступа Wi-Fi, подключенная к сети Ethernet), тогда правило iptables также будет перехватывать подключения подключенных клиентов к адресатам Интернета и перенаправлять их на машина. Это не то, что я хотел - я только хотел перенаправить соединения, которые были направлены на саму машину. Я обнаружил, что могу сделать так, чтобы это влияло только на пакеты, адресованные коробке, добавив -m addrtype --dst-type LOCAL. Например, что-то вроде:

iptables -A PREROUTING -t nat -p tcp --dport 80 -m addrtype --dst-type LOCAL -j REDIRECT --to-port 8080

Еще одна возможность - использовать переадресацию TCP-портов. Например, используя socat:

socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080

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

Крейг МакКуин
источник
0

Ответ на 2015 / сентябрь:

ip6tables теперь поддерживает IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Вам понадобится ядро ​​3.7+

Доказательство:

[09:09:23] root@X:~ ip6tables -t nat -vnL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:80 redir ports 8080
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:443 redir ports 1443

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination
HVNSweeting
источник