I wanted to try socket activation with Systemd and Java on my ubuntu server 16.04. My idea is to make my program able to open directly a standard socket number with a user that is not root.
I currently use the iptable NAT rules, but I wanted to put it away after reading the article of liquidat and this one from Pid Eins,
My test environement is pretty simple. I've got the following java program :
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(); } } } I can call it though the following command line : java -cp target/bindTCP-1.0-SNAPSHOT.jar com.test.bindTCP.App 60606
It echoes all commands sent though tcp, it's able to serve only one client at time. I tested with "nc" and it work properly on socket upper than 1000.
To set my systemd deamon, I wrote the following config files :
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 Then I reload my systemctl config : sudo systemctl daemon-reload
I restart rsyslog deamon : sudo systemctl restart rsyslog
And then, I try to start my own service sudo systemctl start bindTCP
But it doen't work, in my log (/var/log/bindTCP.log), I found the following java stacktrace
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) Any idea how to setup my service properly ?
EDIT: A solution based on authbind is working. The one proposed with setcap 'cap_net_bind_service=+ep' doesn't change anything. Clearly systemd is able to deal with this issue, so I'm still looking for a solution 100% based on Systemd. I'm thinking is more secure.