Чи є спосіб для некореневих процесів прив’язатись до "привілейованих" портів в Linux?


389

Дуже прикро мати таке обмеження на моєму вікні розробки, коли ніколи не буде інших користувачів, крім мене.

Я знаю про стандартні способи вирішення , але жоден з них не робить саме те, що я хочу:

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

Чи є якась проста sysctlзмінна, яка дозволяє некореневим процесам прив’язуватися до "привілейованих" портів (портів менше 1024) в Linux, або мені просто не пощастило?

EDIT: У деяких випадках ви можете використовувати можливості для цього.


5
На мій досвід, однією з причин цього є написання веб-сервера, а не використання Apache (або lighttpd).
S.Lott

Я додав речі про setcap до своєї відповіді майже однаковому stackoverflow.com/questions/277991/…
Пол Томблін

15
Тому що в цьому випадку я використовував IPv6, тому деякі "звичайні" способи вирішення (authbind та iptables REDIRECT) не працювали для мене.
Джейсон Крейтон


1
Є кілька способів зробити це. Див. Дозволити некореневий процес прив'язуватися до портів 80 та 443? на Super User.
jww

Відповіді:


392

Гаразд, дякую людям, які вказали на систему та CAP_NET_BIND_SERVICEможливості. Якщо у вас недавнє ядро, це дійсно можна використовувати для запуску послуги як некореневого, але прив'язування низьких портів. Коротка відповідь полягає в тому, що ви робите:

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.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

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 kernel v3.8.
Крейг МакКуїн

31

Стандартний спосіб полягає в тому, щоб зробити їх "налаштованими", щоб вони запустилися як root, а потім вони скидають цей привілей на root, як тільки вони зв'язалися з портом, але перш ніж вони почнуть приймати з'єднання з ним. Ви можете побачити хороші приклади цього у вихідному коді для Apache та INN. Мені кажуть, що Лайттпд - ще один хороший приклад.

Інший приклад - Postfix, який використовує декілька демонів, які спілкуються через труби, і лише один або два з них (які роблять дуже мало, крім прийняття або випромінювання байтів) запускаються як root, а решта виконується з меншою привілеєю.


1
Цікаво, що це не працює в останніх версіях Linux (можливо, просто Ubuntu) без набору CAP_SETUID. Тож якщо вам потрібна налаштування, вам все одно доведеться встановлювати цю можливість.
Метт

Видалення кореневих приватних файлів - це правильний спосіб зробити це. Хоча не потрібно встановлювати setuid, якщо у вас є init / upstart / systemd, запускайте послугу.
Майкл Хемптон

2
Це складно, якщо програма написана інтерпретованою мовою або інтерпретатором байт-коду, такими як C # (Mono), Java, Python. ( По- видимому Perl зробив це з допомогою binfmt_miscі її прапор «C», я не впевнений , про інших.)
Крейг Макквін

дуже мало, за винятком байтів, це не дуже мало.
Райан

Якщо встановити бінарний параметр, він запуститься як кореневий процес. Однак питання задає питання, як це досягти, не запускаючи свій бінарний файл як кореневий процес. Це рішення є відповіддю на інше запитання, а саме: "Як непривілейований користувач може прив'язати процес до привілейованого порту", але це не питання, поставлене, ІМХО?
Майкл Бір

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. Це здається мені справді німою ідеєю. Можливо, тут я щось пропускаю ...
jww

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
Багстер,

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чи інші аргументи, див. Сторінку man):

      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, ви повинні застосувати її до JVA JVM

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

Очевидно, що тоді означає, що будь-яка програма Java може зв'язати системні порти.
Dito для моно / .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.

Це вже все.
Складіть ядро ​​та встановіть його.
Перезавантажте.
Готово - цей дурний ліміт GONE, і він також працює для сценаріїв.

Ось як ви компілюєте ядро:

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-ненависниця, чи інструкції з компіляції ядра не працювали?
Стефан Стейгер

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

Можливості файлів не є ідеальними, оскільки вони можуть зламатися після оновлення пакета.

Ідеальним рішенням, IMHO, має бути здатність створити оболонку із спадковим 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змінює ефективний ідентифікатор групи на той, хто користується демоном. Це необхідно, тому що capshGID залишається незмінним, і ми точно не хочемо цього.
  • Встановлює біт "зберігати можливості при зміні 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"

І це насправді НЕ працює. Кришки не зберігаються через перемикач ідентифікатора користувача. Він буде працювати, якщо ви хочете запустити як root, але з усіма відкинутими можливостями.
Кіберак

Якщо я спробую це, я отримаю "не можу виконати бінарний файл". Насправді я не можу 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, оскільки програма взагалі не налаштована, навіть частково (можливости є). Наприклад, для того, щоб зловити корумпований додаток із підтримкою можливостей, вам потрібно буде змінити sysctl fs.suid_dumpable (що призводить до виникнення інших можливих проблем із безпекою) Також, коли встановлено CAP / suid, каталог / proc / PID належить root ваш некорінний користувач не буде мати повну інформацію / контроль запущеного процесу, наприклад, користувач не зможе (у звичайному випадку) визначити, які з'єднання належать додатку через / proc / PID / fd / (netstat -aptn | grep ПІД).
  • має недолік у безпеці: хоча ваша програма (або будь-яка програма, що використовує порти 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 (якщо ви не керуєте демоном - якщо у вас немає джерела - то це, можливо, шоустоппер, хоча деякі служби можуть мати прапор сумісності з inetd)
  • новий процес демон демонструється для кожного з'єднання
  • це одна додаткова ланка в ланцюжку

Плюси:

  • доступний на будь-якому старому UNIX
  • Після того, як ваш sysadmin налаштує конфігурацію, ви можете зайнятися своєю розробкою (коли ви переобладнаєте демон, ви можете втратити можливості setcap? І тоді вам доведеться повернутися до свого адміністратора ", будь ласка, сер .. . ")
  • daemon не повинен турбуватися про те, що стосується мереж, просто потрібно поговорити на stdin / stdout
  • може налаштувати виконання вашого демона як не-root користувача, як вимагається

Інша альтернатива: зламаний проксі (netcat або навіть щось більш надійне ) від привілейованого порту до якогось довільного порту з великим номером, де можна запустити цільовий демон. (Netcat, очевидно, не є виробничим рішенням, а "просто моя скринька", правда?). Таким чином, ви можете продовжувати використовувати мережеву версію свого сервера, для запуску проксі (під час завантаження) знадобиться тільки root / sudo, не покладаючись на складні / потенційно тендітні можливості.


1
Гарна пропозиція. Я чомусь навіть не думав використовувати inetd. За винятком того, що мова, про яку йдеться, базується на UDP, тому вона є дещо складнішою, ніж служби TCP. У мене є рішення, що зараз працює з setcap, але мені доведеться подумати над цим.
Джейсон Крейтон

Для другого варіанту, ви можете, навіть, запустити проксі-сервер за допомогою inetd ... (Але IPTables, ймовірно, нижній накладні витрати ...)
Gert van den Berg

14

Мій "стандартний спосіб вирішення" використовує socat як переспрямовувач простору користувача:

socat tcp6-listen:80,fork tcp6:8080

Будьте уважні, що це не буде масштабувати, роздрібнення дороге, але це так, як працює socat.


13

Linux підтримує можливості для підтримки більш дрібних дозволів, ніж просто "ця програма запускається як root". Одна з таких можливостей полягає CAP_NET_BIND_SERVICEв прив'язці до привілейованого порту (<1024).

На жаль, я не знаю, як це запустити, щоб запустити додаток як некореневий, але надаючи його CAP_NET_BIND_SERVICE(можливо, використовуючи setcap, але для цього, мабуть , існує рішення).


Не працюватимуть для сценаріїв або окремих програм JVM / mono.
Стефан Штайгер

13

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

Швидка відповідь полягає в тому, щоб схопити копію останньої (поки що не вийшла) версії libcap з git і скласти її. Скопіюйте отриманий 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автоматично починає башти з аргументами після --)

Тут багато чого відбувається під капотом.

По-перше, ми працюємо як root, тому за замовчуванням ми отримуємо повний набір можливостей. До цього входить можливість перемикати uid & gid з setuidта setgidsyscalls. Однак зазвичай, коли програма робить це, вона втрачає свій набір можливостей - це так, що старий спосіб скидання коріння setuidвсе ще працює. --keep=1Прапор вказує capshвидавати prctl(PR_SET_KEEPCAPS)системний виклик, який відключає капання можливостей якщо змінився основний користувач. Фактична зміна користувачів capshвідбувається за допомогою --userпрапора, який працює setuidі setgid.

Наступною проблемою, яку нам потрібно вирішити, є те, як налаштувати можливості таким чином, щоб продовжувати нас після execнаших дітей. Система можливостей завжди мала "успадкований" набір можливостей, який є "набором можливостей, збережених через execve (2)" [ sposobnosti (7) ]. Хоча це звучить так, як це вирішує нашу проблему (просто встановіть cap_net_bind_serviceможливість успадкованої, правда?), Це насправді стосується лише привілейованих процесів - і наш процес більше не привілейований, тому що ми вже змінили користувача (з --userпрапором).

Новий набір можливостей для оточуючого середовища вирішує цю проблему - це "набір можливостей, які зберігаються через execve (2) програми, яка не має пільги". Встановивши cap_net_bind_serviceзовнішній набір, коли програма capshexec є нашою серверною програмою, наша програма успадкує цю можливість і зможе прив’язати слухачів до низьких портів.

Якщо вам цікаво дізнатись більше, сторінка керівництва з можливостями пояснює це дуже докладно. Запуск capshчерез straceце теж дуже пізнавально!


--addambПрапор був введений з libcap 2,26. Моя система мала 2,25, і мені довелося будувати з джерела.
ngreen

12

TLDR: Для "відповіді" (як я бачу), перейдіть до частини >> TLDR << у цій відповіді.

Гаразд, я зрозумів (справді цього разу) відповідь на це питання, і моя відповідь - це також спосіб вибачення за просування іншої відповіді (і тут, і на Twitter), яку я вважав "найкращою" ", але, спробувавши це, виявив, що я помилявся з цим. Дізнайтеся на моїй помилці, діти: не рекламуйте щось, поки ви насправді не спробували самі!

Знову я переглянув усі відповіді тут. Я спробував деякі з них (і вирішив не пробувати інші, тому що мені просто не сподобалися рішення). Я подумав, що рішення полягає у використанні systemdз його Capabilities=та CapabilitiesBindingSet=налаштуваннями. Після деякої боротьби з цим я виявив, що це не рішення, оскільки:

Можливості призначені для обмеження кореневих процесів!

Як мудро сказано в ОП, завжди краще цього уникати (для всіх ваших демонів, якщо це можливо!).

Ви не можете використовувати параметри, пов’язані з можливостями, User=і Group=в systemdодиничних файлах, тому що можливості ВИНАГО скидаються під час викликуexecev (або будь-якої функції). Іншими словами, коли systemdвилки і опускають її завивки, можливості скидаються. Цього не обійтися, і вся ця логіка прив'язки в ядрі є базовою навколо uid = 0, а не можливостей. Це означає, що навряд чи здібності коли-небудь стануть правильною відповіддю на це питання (принаймні, незабаром). До речі,setcap як уже згадували інші, це не є рішенням. Це не спрацювало для мене, воно не працює добре зі сценаріями, і вони скидаються в будь-якому разі, коли файл змінюється.

На мій мізерний захист я заявив (у коментарі, який я зараз видалив), що пропозиція iptables Джеймса (про яку також згадує ОП) була "другим найкращим рішенням". :-P

>> 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очищає правила брандмауера для нас, переконуючись видалити їх, коли демон не працює.
  • Ми ніколи не запускаємося як корінь, і робимо неможливим ескалацію привілеїв (принаймні, systemdпретензії на це), нібито навіть якщо демон демонструється компромісом і встановлюється uid=0.

iptablesдосі, на жаль, досить потворна та складна у використанні утиліта. Якщо демон демонструє прослуховування eth0:0замість eth0, наприклад, команди дещо відрізняються .


2
Ніхто не повинен використовувати псевдоніми старого стилю, як eth0:0більше, якщо вони не мають справді стародавнього дистрибутиву Linux. Вони роками застаріли і згодом зникнуть.
Майкл Хемптон

1
Я думаю, ти маєш на увазі OpenVZ. (SolusVM - це панель управління.) І так, OpenVZ робить багато речей не так, мережа є лише однією з них.
Майкл Хемптон

1
Ні, я маю на увазі під SolusVM. З / і т.д. / мережі / інтерфейси:# Generated by SolusVM
Грег Слєпак

1
Ще не OpenVZ ... Принаймні, я не користуюся ним, а також не моє VPS.
Грег Слєпак

7
Дякуємо, що вказали на особливості системи systemd. Однак немає необхідності в складних iptables, коли systemd запускає бінарний файл безпосередньо (а не скрипт). Установка AmbientCapabilities=CAP_NET_BIND_SERVICEпрацювала прекрасно для мене в поєднанні з User=.
Рууд

11

systemd - це заміна sysvinit, яка має можливість запустити демон з певними можливостями. Опції Capability =, CapabilityBoundingSet = на сторінці systemd.exec (5) .


2
Я раніше рекомендував цю відповідь, але після того, як спробував її, тепер ні. Дивіться мою відповідь щодо альтернативи, яка все ще використовується systemd.
Грег Слєпак

10

Перенаправлення портів мало для нас сенс, але ми зіткнулися з проблемою, коли наша програма дозволить вирішити URL-адресу на місцевому рівні, яке також потрібно переадресувати; (це означає, що ви шиндиг ).

Це також дозволить перенаправлятись під час доступу до 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


8

З системою просто потрібно трохи змінити послугу, щоб прийняти попередньо активовані сокети.

Пізніше можна використовувати активовану системну розетку .

Ніяких можливостей, iptables та інших хитрощів не потрібно.

Це вміст відповідних системних файлів із цього прикладу простого http-сервера python

Файл 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

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

Існує також "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


1

Оскільки ОП - це лише розробка / тестування, менше корисних рішень може бути корисним:

setcap може бути використаний в інтерпретаторі сценарію для надання можливостей скриптам. Якщо setcaps на глобальному бінарному інтерпретаторі неприйнятний, зробіть локальну копію двійкового файлу (будь-який користувач може) та отримайте root для setcap на цій копії. Python2 (принаймні) працює належним чином з локальною копією інтерпретатора у вашому дереві розробки сценаріїв. Суїд не потрібен, щоб користувач 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 $*

1

Я спробував iptables ПЕРЕДАЧАЙТЕ ПОСЛІДНИЙ метод. У старих ядрах здається, що цей тип правил не підтримувався для IPv6 . Але, мабуть, зараз він підтримується в ip6tables v1.4.18 та Linux kernel v3.8.

Я також виявив, що PREROUTING REDIRECT не працює для з'єднань, ініційованих всередині машини. Для роботи з підключеннями з локальної машини також додайте правило OUTPUT - див. Перенаправлення iptables port не працює для localhost . Наприклад, щось на зразок:

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

Я також виявив, що ПЕРЕДАЧА ПЕРЕДАЧ також впливає на переслані пакети . Тобто, якщо машина також пересилає пакети між інтерфейсами (наприклад, якщо вона діє як точка доступу 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.