Привязать сокет tcp ниже 1000 с некорневым пользователь, использующий Systemd, java и Ubuntu 16.04

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

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

Моя тестовая среда довольно просто. У меня есть следующая программа на 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[12148 sizescat /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: 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. Я думаю, это более безопасно.

0
задан 14 June 2018 в 15:27
2 ответа

Фактически вы не используете сокет, предоставляемый systemd, в вашем демоне. Вместо создания нового ServerSocket используйте System.inheritedChannel () и проверьте, является ли он ServerSocketChannel . Если да, вы унаследовали сокет от systemd и можете начать с него соединения accept () , в противном случае вы все равно можете создать новый ServerSocket или ServerSocketChannel (e .g. для целей разработки). Обратите внимание, что для этого необходимо установить 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} ).

3
ответ дан 4 December 2019 в 12:17

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

`sudo setcap 'cap_net_bind_service=+ep' /usr/lib/jvm/oracle-java8-jdk-amd64/bin/java`
0
ответ дан 4 December 2019 в 12:17

Теги

Похожие вопросы