Дозволити некореневий процес прив'язуватися до портів 80 та 443?


104

Чи можна настроїти параметр ядра, щоб дозволити програмі userland прив'язуватися до портів 80 і 443?

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

Я набагато скоріше спробую розібратися, який непривілейований процес прослуховується на порту 80, а не намагатися видалити зловмисне програмне забезпечення, яке загрожує кореневими привілеями.


1
Див serverfault.com/questions/268099 і stackoverflow.com/questions/413807 . Коротка відповідь - ні.
Самі Лайн

10
Довга відповідь тхо - так, тому тип короткої відповіді повинен бути і так.
BT

4
Коротка відповідь - так.
Джейсон C

Відповіді:


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

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

    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, яка запускає виконуваний рубін, який використовує рубін. Потрібно надати setcapдозвіл на виконаний для рубіну рубін , наприклад/usr/bin/ruby1.9.1
Christian Rondeau

3
У мене є сумніви, що chmodз 777 byportфайлів найкраща ідея. Я бачив надання дозволів в межах від 500до 744. Я б дотримувався самого обмежувального, який працює для вас.
Пер

28

Дейл Хагглунд на місці. Тож я просто хочу сказати те саме, але по-іншому, з деякою специфікою та прикладами. ☺

Правильне в світі Unix та Linux:

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

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

Існує багато способів зробити це.

inetd

Як каже Дейл Хагглунд, старий "мережевий суперсервер" inetdробить це. Обліковий запис, під яким запускається сервісний процес, є одним із стовпців в inetd.conf. Він не розділяє прослуховувальну частину та частину привілеїв, що випадають, на дві окремі програми, невелику і легко піддається аудиту, але він відокремлює основний код послуги на окрему програму, exec()редагувану в процесі обслуговування, який породжується дескриптором відкритого файлу для розетки.

Складність аудиту не є великою проблемою, оскільки потрібно лише перевірити одну програму. inetdОсновна проблема полягає не в тому, щоб зробити аудит настільки великим, але він полягає в тому, що він не забезпечує простий дрібнозернистий сервіс контролю часу виконання, порівняно з останніми інструментами.

UCSPI-TCP і демон

Пакети 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

ніш

Мій пакет з нішами призначений для цього. Він має невелику 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 М. Беркота .

Ось приклад послуги FTP, злегка модифікованої з оригіналу Wayne Marshall , яка використовує execline, s6, s6-мережу та програму сервера 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

Gerrit Папа ipsvd ще один набір інструментів , який проходить по тій же схемі, UCSPI-ТСР і s6-мереж. Інструменти є 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, нова система нагляду та обслуговування init, яку можна знайти в деяких дистрибутивах Linux, покликана робити все, що inetdможе зробити . Однак він не використовує набір невеликих автономних програм. На systemdжаль, доводиться проводити аудит у повному обсязі.

За допомогою systemdодного створюються файли конфігурації для визначення сокету, який systemdслухає, і служби, яка systemdзапускається. У файлі "блок" служби є налаштування, які дозволяють здійснювати великий контроль над процесом обслуговування, включаючи того, якого користувача він працює.

Якщо цей користувач налаштований як непідрядник, systemdвиконує всю роботу над відкриттям сокета, прив'язкою його до порту та викликом listen()(і, якщо потрібно accept()), в процесі №1 як суперпользователь, і сервісний процес, який він ікру працює без пільг суперпользователя.


2
Спасибі за комплімент. Це чудова колекція конкретних порад. +1.
Дейл Хагглунд

11

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

Потім я зрозумів, що я можу пересилати з'єднання з одного порту на інший порт. Тому я запустив сервер на порт 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

зовнішній

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

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


6
+1 за роздуми над полем
Річард Вісман

4

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

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

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

У наведеному прикладі ви хочете розділити свою програму на дві частини. Той, який працює як 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 з некореневим обліковим записом)

Тепер будьте обережні щодо безпеки, оскільки всі користувачі можуть прив’язати всі порти!


Це розумно. Один невеликий ніт: конфігурація відкриває 80 і 443, але також відкриває всі інші порти. Розслаблення прав на інші порти може не бажати.
jww
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.