Friday, 22 January 2016

Port forwarding with systemd

I wanted to run a daemon on a remote shell server and connect to it remotely.
Specifically it's a quassel core which I want to run inside the vpn.

Problem 1: The shell server, quite sensibly, doesn't allow external
connections.

Problem 2: The shell server, less sensibly, doesn't permit ssh local port
forwarding. I'll demonstrate why this doesn't make a great deal of sense on a
shell server (spoiler: you can do it anyway).

Assumption: When appropriately connected and authenticated, I can ssh into the
shell server without entering a password.

My first thought was obviously ssh port forwarding:
$ ssh -L 5555:localhost:5555 shell-server.example.com -Nf
But no:
channel 3: open failed: administratively prohibited: open failed
Boo! nc is available on the shell server, though, and if it wasn't I'd just
copy it there:
$ cat > ~/bin/quassel-proxy.sh <<EOF
#!/bin/sh
exec ssh shell-server.example.com -- nc localhost 5555
EOF 
$ nc -l 5555 --keep-open -e ~/bin/quassel-proxy.sh
This runs a local nc process which acts like inetd. When it gets a connection
on 5555 it runs quassel-proxy.sh, which sshes to the shell server and runs nc
remotely to connect to my daemon on remote localhost port 5555. Now, I can
point my quassel client at localhost 5555 and it will create the ssh
connections automatically. Which is nice.

But wait! We can do better than that. I'd still need to remember to fire up nc
when I log in to my workstation, and that's way too much like hard work.
systemd to the rescue!

On an appropriately configured workstation (my Fedora 23 workstation does this
by default), systemd will run both a system daemon, and user daemons for any
users with active sessions. systemd also does the inetd thing, so I can just
write my own systemd unit for it:
$ mkdir -p ~/.config/systemd/user 
$ cat > ~/.config/systemd/user/quassel.socket <<EOF
[Unit]
Description=Proxy quassel connections

[Socket]
ListenStream=127.0.0.1:5555
Accept=yes

[Install]
WantedBy=default.target
EOF
$ cat > ~/.config/systemd/user/quassel@.service <<EOF
[Unit]
Description=Proxy Quassel connections

[Service]
ExecStart=-/usr/bin/ssh shell-server.example.com -- nc localhost 5555
StandardInput=socket
EOF
$ systemd --user daemon-reload
$ systemd --user enable quassel.socket
$ systemd --user start quassel.socket
And that's it! Whenever I'm logged in to my workstation, connected to the vpn,
and appropriately authenticated, connections to localhost 5555 will be
automagically proxied over ssh to my quassel core daemon.

No comments:

Post a Comment