Як запустити сервер на порт 80 як звичайний користувач в Linux?


311

Я довгий час розглядав рішення про рішення, але не зміг знайти відповіді.

Я перебуваю на Ubuntu Linux і хочу запустити сервер на порт 80, але через механізм захисту Ubuntu я отримую таку помилку:

java.net.BindException: В дозволі відмовлено: 80

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


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


4
Ви не можете. Порти нижче 1024 є привілейованими, і лише root може відкривати розетки для прослуховування на них. Відповідне, що потрібно зробити, - це скинути дозволи після відкриття.
Falcon Momot

2
"Анонімні" - користувачів у світі пройшли навчання для пошуку певних послуг у певних портах. У деяких випадках його стандартизовано. Наприклад, HTTP на порт 80 і HTTPS на порт 443. Його різновид важко змінити користувачів та стандарти світу.

1
@FalconMomot Якщо порти нижче 1024 мають пільги, чому сервер Apache не вимагає ввести пароль, щоб отримати привілеї root на порт 80, і як вони це роблять? Я думаю, що запитуючий прагне знати, як робити такі речі (будь то технічно використовується як корінь чи ні). Сміливо скажіть мені, чи не помиляюся, Діпак Міттал.
Shule

Відповіді:


355

Коротка відповідь: не можна. Порти нижче 1024 можна відкрити лише корінь. Відповідно до коментаря - добре, ви можете, використовуючи CAP_NET_BIND_SERVICE , але такий підхід, застосований до java bin, змусить запускати будь-яку програму java з цим налаштуванням, що небажано, якщо не загрожує безпеці.

Відповідь довга: ви можете перенаправити з'єднання через порт 80 на інший порт, який ви можете відкрити як звичайний користувач.

Запустити як корінь:

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

Оскільки пристрої зворотного зв'язку (наприклад, localhost) не використовують правила прероутування, якщо вам потрібно використовувати localhost тощо, також додайте це правило ( спасибі @Francesco ):

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

ПРИМІТКА. Вищевказане рішення не дуже підходить для багатокористувацьких систем, оскільки будь-який користувач може відкрити порт 8080 (або будь-який інший високий порт, який ви вирішили використовувати), тим самим перехоплюючи трафік. (Кредити CesarB ).

РЕДАКТУВАТИ: відповідно до питання коментаря - щоб видалити вищевказане правило:

# iptables -t nat --line-numbers -n -L

Це виведе щось подібне:

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:8080 redir ports 8088
2    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:80 redir ports 8080

Правило, яке вас цікавить, - nr. 2, щоб видалити його:

# iptables -t nat -D PREROUTING 2

13
@Sunny: оновлення не для найшвидшого пістолета на заході, а для найкращих відповідей. Ваш - найкращий поки що (я лише згадав iptables; ви фактично надали повний командний рядок). Єдине, що у мене є у вас, - це застереження про те, що інші користувачі також можуть прив’язатись до порту 8080.
CesarB

3
Зауважте, що це не працює з IPv6.
Еммануель Бург

3
Чи може хтось пояснити, як я можу видалити це правило пізніше, коли хочу? Після запуску він працює, але це, мабуть, не "правило", оскільки воно не з’являється, коли я це роблю sudo iptables --list. Я знаю, що таке iptables, але це я ніколи не використовував.
Кодування

4
Дякую за вашу відповідь ... щоразу, коли я перезавантажую Ubuntu, це правило зникає, і мені доведеться запустити його ще раз. Чи є спосіб зберегти його назавжди?
Кодерджі

4
@Coderji: перегляньте розділ «зберегти» у документації спільноти: help.ubuntu.com/community/IptablesHowTo
Сонячний

79

Використовуйте authbind .

Він навіть працює з Java, якщо ввімкнути стек Java лише для IPv4. Я використовую:

authbind --deep $JAVA_HOME/bin/java -Djava.net.preferIPv4Stack=true …

4
Якщо сервер є Tomcat, ви можете використовувати автоматичне зв’язування автоматично, встановивши AUTHBIND=yesв / etc / default / tomcat6
Emmanuel Bourg

Я не міг змусити це працювати на сервері Ubuntu за замовчуванням пакетом Java ...
Ешлі,

Я ні, будь-яке відоме рішення?
позначка

10
Зауважте, що ви повинні налаштувати, authbindщоб насправді це допустити. На головній сторінці "/ etc / authbind / byport / port перевіряється. Якщо цей файл доступний для виконання користувачеві, що викликає, відповідно до доступу (2), тоді прив'язка до порту дозволена." , Наприклад , для порту 80, sudo touch /etc/authbind/byport/80 ; sudo chmod 777 /etc/authbind/byport/80. Початкова установка програми authbindзазвичай не має попередньо налаштованих авторизацій.
Джейсон C

4
О, nonononononono, ніколи ніколи не chmod 777 нічого!
Йоганнес

57

Якщо ваша система підтримує це, можливо, ви можете використовувати можливості. Ознайомтеся з можливостями людини, потрібна вам CAP_NET_BIND_SERVICE.

На новіших Debian / Ubuntu можна запустити:

sudo apt-get install libcap2-bin 
sudo setcap 'cap_net_bind_service=+ep' /path/to/program

це працює для nodejs: setcap 'cap_net_bind_service = + ep' / usr / bin / nodejs
JasonS

5
Це. Цікаво, чому ця відповідь не має більшої підтримки. Набагато простіше, ніж параметр iptables imho.
Домінік Р

5
це правильна і найефективніша відповідь, всі інші відповіді викликають хіт ефективності або просто нечіткий / небезпечний.
OneOfOne

41

Ще одне рішення - зробити ваш додаток налаштованим так, щоб він міг зв’язуватися з портом 80. Як корінь, виконайте наступне

chown root ./myapp
chmod +S ./myapp

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

Оновлення: Як видно з цього питання , виявляється, що ядра Linux починаючи з 2.6.24 мають нову можливість, яка дозволяє вам позначити виконуваний файл (але, звичайно, не сценарій) як " CAP_NET_BIND_SERVICE" здатність. Якщо ви встановите пакет debian "libcap2-bin", ви можете зробити це, видавши команду

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

6
Це те саме, що працювати з root, якщо додаток не знає, як скинути привілеї.
CesarB

14
Це небезпечно. Це означає, що будь-який запит працюватиме як root. Це з тієї причини, що навіть apache починає як root прив'язувати, а потім передає привілеї іншому користувачеві.
Сонячно

9
Пол: iptables не настільки небезпечний, тому що навіть якщо програма буде порушена, він не піддасть систему атакам, принаймні, не з привілеями root. Запуск програми як root - це вже інша історія.
Сонячно

1
Небезпека для iptables полягає лише в тому випадку, якщо це багатокористувацька система, як сказав CesarB, оскільки кожен може прив’язатись до 8080 і перехопити трафік.
Сонячно

1
хаха, люблю, як у прийнятій відповіді є -15
Еван Теран

40

Я просто використовую Nginx попереду. Він також може працювати на localhost.

  • apt-get install nginx

.. або ..

  • pkg_add -r nginx

.. або що коли-небудь підходить для вашої ОС.

Все, що вам потрібно в nginx.conf, якщо він працює на localhost, це:

сервер {
        слухати 80;
        ім'я сервера some.domain.org;
        Місцезнаходження / {
            proxy_set_header Хост $ хост;
            proxy_set_header X-Real-IP $ remote_addr;
            proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:8081;
        }
}

Мені дуже подобається таке рішення.
thomasfedb

1
Це (одне із) запропонованих рішень від самих JFrog
stolsvik

36

Підхід, запропонований Сонячним та CesarB:

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

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

Розглянемо наступний сценарій, коли це може бути проблемою.

Скажімо, у нас є сервер, який приймає HTTP-з'єднання на порт 8080 і HTTPS-з'єднання на порт 8181.

Ми використовуємо iptables для встановлення таких переспрямувань:

80  ---> 8080
443 ---> 8181

Тепер, припустимо, наш сервер вирішує перенаправити користувача зі сторінки HTTP на сторінку HTTPS. Якщо ми ретельно не перепишемо відповідь, вона буде переспрямована на https://host:8181/. У цей момент ми накручуємо:

  • Деякі користувачі додають закладки до https://host:8181/URL-адреси, і нам потрібно буде підтримувати цю URL-адресу, щоб не порушувати їх закладки.
  • Інші користувачі не зможуть підключитися, оскільки їх проксі-сервери не підтримують нестандартні порти SSL.

Я використовую такий підхід:

iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -p tcp --dport 443 -j MARK --set-mark 1
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8181
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -m mark --mark 1 -j ACCEPT
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8181 -m mark --mark 1 -j ACCEPT

У поєднанні з правилом REJECT за замовчуванням у ланцюзі INPUT цей підхід заважає користувачам безпосередньо підключатися до портів 8080, 8181


1
Це добре, але чому б не просто прив’язати демон до localhost: 8080 замість 0,0.0.0:8080? Я хочу це зробити, але для цього мені потрібні iptables.
Амала

Це працює, дійсно круто.
xtian

29

Традиційно в Unix, лише корінь може зв'язуватися з низькими портами (<1024).

Найпростіший спосіб обійти це - запустити ваш сервер на високому порту (наприклад, 8080) і використовувати просте правило iptables для пересилання з'єднань з порту 80 на порт 8080. Зауважте, що при цьому ви втрачаєте додатковий захист від низькі порти; будь-який користувач на вашій машині може прив’язати до порту 8080.


23

Якщо ваша система підтримує це, можливо, ви можете використовувати можливості. Бачите man capabilities, той, який вам потрібен, був би CAP_NET_BIND_SERVICE. Ні, я ніколи їх не використовував і не знаю, чи справді вони працюють :-)


1
вони працюють, я використовую CAP_NET_RAW для запуску таких інструментів, як tcpdump, як звичайний користувач.
Джастін

12

Використовуйте зворотний проксі (nginx, apache + mod_proxy) або кешування зворотного проксі (Squid, Varnish) перед вашими серверами додатків!

За допомогою зворотного проксі ви можете досягти багатьох цікавих речей, таких як:

  • Збалансування навантаження
  • Перезапуск серверів додатків з користувачами, які отримують уявну сторінку помилок
  • Прискоріть роботу з кешем
  • Тонкі налаштування, які ви зазвичай робите з реверсійним проксі, а не з сервером додатків

6

Ви можете скористатися програмою redir:

sudo redir --lport=80 --laddr=192.168.0.101 --cport 9990 --caddr=127.0.0.1

4

Використовуйте судо.

Налаштуйте sudo так, щоб звичайний користувач міг виконувати відповідні команди:

/etc/init.d/httpd start

Або

apachectl stop

Або

/usr/local/apache2/apachectl restart

Або

/myapp/myappbin start

(Або будь-яку іншу команду / скрипт, яку ви використовуєте для запуску / зупинки конкретного веб-сервера / програми)


8
У цьому ж питанні - погана практика запуску служби як root.
Кевін Панько

4

відповідь Sunny є правильною, але у вас можуть виникнути додаткові проблеми, оскільки інтерфейс циклу не використовує таблицю PREROUTING,

тому правила iptables, які потрібно додати, є два:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8080

3

У Linux у вас є ще два варіанти:

Обидва розширення до ядра Linux дозволяють надати права доступу на дуже тонкому рівні. Це дозволить вам дозволити цьому процесу відкрити порт 80, але він не успадкував би жодне з інших кореневих прав.

З того, що я чув, безпека у користуванні набагато простіша у використанні, але SELinux є більш захищеною.


2

Одне рішення - використовувати iptables для виконання PAT на пакетах для порту 80. Ви можете використовувати це, наприклад, для маршрутизації пакетів до локального порту 8080. Будьте впевнені і відрегулюйте вихідні пакети назад до порту 80.

На мій досвід, функції дрібнозернистих дозволів Linux не збираються в стандартні ядра через проблеми безпеки.


2

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

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


2

Коли у мене є різні веб-сервісні програми (сценарії python, Tomcat Engine, ...), які я не хочу запускати як root, зазвичай налаштовую веб-сервер apache перед ними. Apache слухає порт 80, а tomcat слухає 8080.

У налаштуваннях apache: s:

ProxyPass /webapp http://localhost:8080/webapp
ProxyPassReverse /webapp http://localhost:8080/webapp

Докладнішу інформацію див. У документації про mod-proxy: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html


1
Недолік цього рішення - втрата вхідного IP в журналах Tomcat.
Еммануель Бург

2

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


1

Деякі хост-системи не дозволяють використовувати модуль NAT, "iptables" не вирішує проблему в цьому випадку.

Як щодо xinetd?

У моєму випадку (ubuntu 10.04)

# apt-get install xinetd
# touch /etc/xinetd.d/my_redirect
# vim /etc/xinetd.d/my_redirect

Вставте конфігурацію:

service my_redirector_80
{
 disable = no
 socket_type = stream
 protocol = tcp
 user = root
 wait = no
 port = 80
 redirect = localhost 8080
 type = UNLISTED
}

Тоді:

# service xinetd restart

http://docs.codehaus.org/display/JETTY/port80 пояснює краще.


1
Це в основному еквівалентно розміщенню зворотного проксі-сервера перед сервером, але він досить неефективний порівняно з виділеними рішеннями, такими як HAproxy, Pound або Varnish, які розуміють протокол HTTP і можуть додавати корисні заголовки, як X-Forwarded-For.
Еммануель Бург

-1

На відміну від цього, FreeBSD і Solaris (хто пам'ятає це ?) Дозволяють вам це зробити (прив’язати до низьких портів) без ескалації привілеїв (тобто, використовуючи програми для переходу на root). Оскільки ви вказали Linux, я просто розміщую це як повідомлення іншим, яке може знайти це питання.


-1

Некромантування.

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


Будь-хто, будь ласка, пояснить події? Ядро-компіляція не працює? Або це лише тому, що згодом ви більше не можете робити оновлення ядра з звичайних сховищ?
Квандарі

Я спробував це рішення ( sudo setcap cap_net_bind_service=+ep /path-to-java/java), але я завжди отримую, java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directoryякщо у Java встановлені кришки. дивіться також першу частину unix.stackexchange.com/a/16670/55508
Даніель Олдер

1
Вирішено попередню помилку, додавши <javahome>/lib/amd64/jli/до ld.so.confі працюєsudo ldconfig
Даніель вільха
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.