Назад | Перейти на главную страницу

Свяжите сокет tcp ниже 1000 с пользователем без полномочий root, используя Systemd, java и Ubuntu 16.04

Я хотел попробовать активацию сокета с помощью Systemd и Java на моем сервере ubuntu 16.04. Моя идея состоит в том, чтобы моя программа могла напрямую открывать стандартный номер сокета с пользователем, который не является root.

В настоящее время я использую правила iptable NAT, но я хотел убрать их после прочтения статья о ликвидации и этот из Пид Эйнс,

Моя тестовая среда довольно проста. У меня есть следующая Java-программа:

package com.test.bindTCP;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class App 
{

private static void acceptConnection(ServerSocket serverSocket) {
    while (true) {
        try (
                Socket clientSocket = serverSocket.accept();
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        ) {
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                out.println(inputLine);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public static void main( String[] args )
{
    int portNumber = Integer.parseInt(args[0]);
    try (
        ServerSocket serverSocket = new ServerSocket(portNumber);
    ) {
        acceptConnection(serverSocket);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

Я могу вызвать это через следующую командную строку: java -cp target/bindTCP-1.0-SNAPSHOT.jar com.test.bindTCP.App 60606

Он повторяет все команды, отправленные через tcp, он может одновременно обслуживать только одного клиента. Я тестировал "nc", и он правильно работает на сокетах выше 1000.

Чтобы установить мой systemd deamon, я написал следующие файлы конфигурации:

cat /lib/systemd/system/bindTCP.socket

[Unit]
Description=bindTCP Java 23

[Socket]
ListenStream=23

cat /lib/systemd/system/bindTCP.service

[Unit]
Description=bindTCP service
Requires=bindTCP.socket
After=syslog.target
After=network.target

[Service]
User=mylogin
Group=mygroup

ExecStart=/usr/lib/jvm/oracle-java8-jdk-amd64/bin/java  -cp /home/mylogin/bindTCP-1.0-SNAPSHOT.jar com.test.bindTCP.App 23
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=bindTCP
Restart=always


[Install]
WantedBy=multi-user.target

cat /etc/rsyslog.d/bindTCP.conf

$template bindTCPlog,"%msg%\n"
if $programname == 'bindTCP' then /var/log/bindTCP.log;bindTCPlog
if $programname == 'bindTCP' then stop

Затем я перезагружаю конфигурацию systemctl: sudo systemctl daemon-reload

Я перезапускаю rsyslog deamon: sudo systemctl restart rsyslog

А потом я пытаюсь запустить свой собственный сервис sudo systemctl start bindTCP

Но это не работает, в моем журнале (/var/log/bindTCP.log), Я нашел следующую трассировку стека java

 java.net.BindException: Permission denied (Bind failed)
    at java.net.PlainSocketImpl.socketBind(Native Method)
    at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387)
    at java.net.ServerSocket.bind(ServerSocket.java:375)
    at java.net.ServerSocket.<init>(ServerSocket.java:237)
    at java.net.ServerSocket.<init>(ServerSocket.java:128)
    at com.test.bindTCP.App.main(App.java:38)  

Есть идеи, как правильно настроить мою службу?

РЕДАКТИРОВАТЬ: Решение на основе authbind работает. Предлагаемый с помощью setcap 'cap_net_bind_service = + ep' ничего не меняет. Очевидно, что systemd может справиться с этой проблемой, поэтому я все еще ищу решение, на 100% основанное на Systemd. Думаю, безопаснее.

Фактически вы не используете сокет, предоставленный systemd, в вашем демоне. Вместо создания нового ServerSocket, используйте System.inheritedChannel() и проверьте, является ли это ServerSocketChannel. Если да, вы унаследовали сокет от systemd и можете начать accept() соединения на этом, иначе вы все еще можете создать новый ServerSocket или ServerSocketChannel (например, в целях разработки). Обратите внимание, что для этого вам необходимо установить StandardInput=socket в служебном модуле: Java ожидает, что унаследованный канал будет дескриптором файла 0 (стиль inetd), но systemd по умолчанию добавляет сокеты, начиная с дескриптора файла 3.

Кроме того, вы можете добавить это в свой служебный файл (в Service раздел):

AmbientCapabilities=CAP_NET_BIND_SERVICE

Это позволит Java самой выделять зарезервированные номера портов. Тем не менее, использование блока розеток определенно является лучшим выбором. (Если вы хотите использовать AmbientCapabilities, вам придется отключить модуль сокета, потому что systemd и ваша служба не могут одновременно связываться с одним и тем же портом. Наверное, поэтому setcap предложение не сработало.)


Боковое примечание: поскольку ваши файлы модулей управляются вами, системным администратором, а не менеджером пакетов, они принадлежат /etc, не в /lib (то есть, /etc/systemd/system/bindTCP.{service,socket}).

Использовать setcap чтобы позволить самому двоичному файлу java способность для привязки к привилегированным портам, без необходимости работать как root:

`sudo setcap 'cap_net_bind_service=+ep' /usr/lib/jvm/oracle-java8-jdk-amd64/bin/java`