Using Systemd as a Socket Server for a Shell Command

In the past I’ve run socat as a daemon to run a shell command and send the output to a socket. It works well, but I wanted to try the systemd way of doing what “xinetd” has done in the past: Run a command and send the output to a socket.

A quick word on my use case: I’m using headscale, but at the moment we are in a transition phase and I will be having much of the use by users going to our old firewalls that previously were VPN concentrators. Previously we had different VPNs, and hence different networks, for Operations and Development. With headscale I’m using ACLs to control access, but I need a way to tell the firewalls what IPs belong to what groups of users.

I wrote a script that converts headscale ACL groups and node IPs into “ipset” commands. So the firewall can check “is the source headscale-ops” and pass it to the existing “operations allowed” rules. But to get this script to the firewalls, I decided to set up a port that the firewalls can connect to and get the rules.

It took me quite a while to get the exact right set of configs. In this example I called my service “headscale-ipset”.

You need a “/etc/systemd/system/headscale-ipset.socket” file:

[Unit]
Description=Socket server to display ipset for headscale groups

[Socket]
ListenStream=8080
Accept=yes

[Install]
WantedBy=sockets.target

And a “/etc/systemd/system/headscale-ipset@.service” file:

[Unit]
Description=Headscale IPSet Socket Server
After=network.target

[Service]
Type=simple
Restart=always
RestartSec=1
ExecStart=/usr/bin/cat /usr/local/lib/10-headscale-rules
StandardOutput=socket

[Install]
WantedBy=multi-user.target

After adding these files, run a “systemctl daemon-reload” and “systemctl start headscale-ipset.socket” and you should then be able to telnet to port 8080 and have the contents of “/usr/local/lib/10-headscale-rules” spit out.