Я хотел попробовать активацию сокета с помощью 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. Я думаю, это более безопасно.
Фактически вы не используете сокет, предоставляемый 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}
).
Используйте setcap
, чтобы разрешить самому двоичному файлу Java возможность связываться с привилегированными портами без необходимости работать как root
:
`sudo setcap 'cap_net_bind_service=+ep' /usr/lib/jvm/oracle-java8-jdk-amd64/bin/java`