3

I have multiple raspberry pi's running Arch Linux (No GUI) in which I need to access. These pi's are behind firewalls in each unique location. Currently I use openvpn to connect to these but the costs of that system is expensive per license. I use the access server from them.

As a result, I am trying to design and setup a system that allows me the ability to login to my VPN server (vps) and run a command to search for a specific name (OfficeDevice1991) such as: customcommandsearch "OfficeDevice1991" and then it then returns the IP address of the machine or something I can use to SSH into with. I am also looking for the ability to run a command to list every active connected device. It lists back the IP, name and perhaps how long it's been active for.

For this goal, I of course need to create something that includes the name of the device (in this case OfficeDevice1991) and then that pi will be able to hook into my vps public server. From the public server, I can log in and do a search of every device connected to it and return information required to ssh into.

I have been looking into reverse SSH and so far I got one of my test pi's connected and accessible from my vps using the following commands:

PI:

ssh -fN -R 12345:localhost:22 -i /publickeyfile useraccount@ip //Pi's command to connect to vpn

VPS:

ssh -p 12345 useraccount@localhost //command for vpn to connect to pi

This works great, but using this method, if I was to implement it, I would run into a few issues:

  1. I would need to set up unique unused ports
  2. Some way to keep these ports / tunnels opened
  3. I need to come up with a system to identify each device. I can log each port to a name in like a text file locally? It would be beneficial to be able to include that in the ssh setup for each device if possible. I would still need to make sure the ports I use are not being used by any other programs or any device already there.

What I don't want to have to do

  1. Check what ports are free to use for each RPI

  2. Have to manually edit .ssh/config to add a name to represent each port assigned to RPI from part 1 above.

I am writing this for information/assistance as to what to do for my goal.

Could anyone provide me with a suitable solution?

  • Why are you being charged to use OpenVPN? There should be no cost unless you've taken out a support contract or you're using someone's server implementation as an endpoint. Neither seems necessary from your description here. – roaima Apr 11 '18 at 21:52
  • Hi, i am using openvpn access server? By default you get 2 concurrent free users. After that you need to pay for additional users (concurrents) It states its 10 licences for 1 year 150USD. I havnt paid but i have over 100 units not online due to this cost, i have 2 devices currently using openvpn (Since the 2 is free). – irishwill200 Apr 12 '18 at 8:19
  • 1
    I deleted my solution: it relies on the ability to remove stale remote unix sockets but -o StreamLocalBindUnlink=yes removes only local sockets (while the man page says "for local or remote port forwarding") it doesn't work with a remote socket. sorry for all the trouble – A.B Apr 12 '18 at 10:52
  • 1
    @A.B Looking for a solution to remove socket i found: unix.stackexchange.com/questions/427189/… Here is a solution that works with StreamLocalBindUnlink=yes. I followed and your solution now works a charm! I just need to make sure the connection stays live! Might use autossh for that. Need to test a few things of course as reading it states it overrides previous session or something? Thanks though! Will keep posting as i go along. – irishwill200 Apr 12 '18 at 12:58
  • nice information (I +1ed it). Ok it's on the sshd side to allow it then. I'll put back my solution and update it – A.B Apr 12 '18 at 14:42
1

Here's a solution using OpenSSH >= 6.7 + socat:

  1. OpenSSH >= 6.7 can use Unix domain socket forwarding

    That means the reverse tunnel endpoint will be an UNIX listening socket instead of a traditional TCP listening socket. You can then manage more easily the flotilla of RPIs with an easy naming scheme: the socket's name will be the RPI's chosen (and fixed) name, like OfficeDevice1991. It could be even be an unique property from the RPI as long as it's a valid filename (since unix socket names adhere to filename conventions). For example its hostname, the MAC address of its ethernet or wifi card ...

    SSH can handle unix sockets for tunnels, not for connecting itself. It will need the help of a ProxyCommand to be able to work as unix-socket client. socat can handle many kind of connections, including unix sockets.

    UPDATE:
    There is also a specific issue to handle: the unix socket file is not deleted on clean exit, nor would it have been be deleted anyway for example after a crash. This require the option StreamLocalBindUnlink=yes. I didn't find initially that, as the name perhaps imply, this option must be set on the node creating the unix socket. So in the end it's set on the client with a local forward (-L) or else on the server (in sshd_config) with a remote forward (-R). OP found it there. This solution uses a remote forward.

    Configuration on VPS:

    mkdir /rpi-access
    

    (as root) edit the sshd_config file (/etc/ssh/sshd_config). It requires this additional option:

    StreamLocalBindUnlink yes
    

    Depending on default options it might also require AllowStreamLocalForwarding yes

    UPDATE2:
    Also set in sshd_config the parameters ClientAliveInterval and ClientAliveCountMax, thus allowing to detect a disconnect in a reasonable time, eg:

    ClientAliveInterval 300
    ClientAliveCountMax 2
    

    Stale ssh connections should then be detected earlier on the VPS (~10mn with the example), and the corresponding sshd process will then exit.

    Usage on RPI:

    ssh -fN -R /rpi-access/OfficeDevice1991:localhost:22 -i /privatekeyfile useraccount@ip
    

    In a config file this would be similar to this:

    Host ip
    User useraccount
    RemoteForward /rpi-access/OfficeDevice1991:localhost:22
    IdentityFile /privatekeyfile
    

    Repeating it again: the StreamLocalBindUnlink yes set on sshd on VPS side option is important: the socket just created is not removed, even upon normal exit. This option ensures that the socket is removed if it exists before use, thus allowing to be reused for further reconnections. This also means one can't consider the mere presence of the socket as meaning the RPI is connected (but see later).

    Now this allows to do on VPS:

    ssh -o 'ProxyCommand=socat UNIX:/rpi-access/%h -' rpiuseraccount@OfficeDevice1991
    

    As a configuration file, considering for example RPIs have all a name starting with OfficeDevice:

    Host OfficeDevice*
        User rpiuseraccount
        ProxyCommand socat UNIX:/rpi-access/%h -
    
  2. To keep the link, just use a loop

    The RPI can run a loop reconnecting ssh to the VPS whenever the connections ends. For this it musn't use the background mode (no -f). A keepalive mechanism should also be used. TCPKeepAlive (system level) or ServerAliveInterval (application level) are available. I think TCPKeepAlive is useful only on the server (the side receiving the connection), so let's rather use ServerAliveInterval.

    Its value (as well as ServerAliveCountMax) should probably be adapted depending on various criteria: a firewall dropping inactive connections after a certain time, the wished recovery delay, not generating useless traffic, ... let's say 300s here.

    OfficeDevice1991 RPI:

    #!/bin/sh
    while : ; do
        ssh  -N -o ConnectTimeout=30 -o ServerAliveInterval=300 -R /rpi-access/OfficeDevice1991:localhost:22 -i /privatekeyfile useraccount@ip
        sleep 5 # avoid flood/DDoS in case of really unexpected issues
    done
    

    Even if the remote side didn't yet detect the previous connectivity failure, and for a few more time has the old ssh connection still running, StreamLocalBindUnlink yes will anyway forcefully refresh the unix socket to the new connection.

  3. it's already handled by 1.

    There's no customcommandsearch needed. With the right settings set in 1. just using ssh OfficeDevice1991 will connect to OfficeDevice1991.

    If needed on the VPS, as root user only, this command:

    fuser /rpi-access/*
    

    can show which RPIs are currently connected (of course except those that recently lost the connection before detection). It won't show the stale unix socket files because there's no process tied to them.

  • For the loop section in keeping the client always connected to the VPS even after network issues, would it be better to us autossh here instead? If not, what do you suggest in doing with the provided code? Create a simple cron on startup then run the code or? Thanks. – irishwill200 Apr 12 '18 at 15:44
  • I'm certain autossh is better than a simple shell loop. But having no experience with it, I can't give a solution using it. For the provided code, the best would probably to have it run from a systemd service (again I'm not very experimented at systemd service configuration so won't attempt it). Running it from a cron might require an other wrapper checking it's not running, or running it only at boot (@reboot) – A.B Apr 12 '18 at 15:53
  • Currently i have it on a @reboot. I may run it off systemd or give autossh a shot! I have experience around systemd etc but autossh is new to me! I love involving myself with new things so i may give that a shot first. Thanks! – irishwill200 Apr 12 '18 at 15:57
  • 1
    Added a ClientAliveInterval parameter on VPS which should fix those sshd staying around (and a ConnectTimeout on RPIs in the loop, should avoid lengthy timeouts before retries) – A.B May 22 '18 at 19:26
  • 1
    correction made – A.B May 23 '18 at 21:14
1

This is a couple of different takes on the situation. I've omitted some of the detail here because there's nothing stopping you asking a further Question for specifics if you try either of these routes.

1. Replace OpenVPN Access Server

If cost of OpenVPN Access Server is a prohibitive issue, consider deploying your own OpenVPN server. The software is free to use; you'd just need a server endpoint (a VPS or equivalent if you have nothing locally) and the skill-set to set it up. Many tutorials are available for that, so you don't need to start from ground zero.

Once you've got this set up you can use DNS or /etc/hosts to define host names such as your OfficeDevice1991 to point to the appropriate VPN endpoint address on your server.

2. Use autossh instead of OpenVPN

Once you have your own endpoint you can use something like autossh on each of the Pi systems to ssh to a "well known" endpoint on boot (a VPS or equivalent if you have nothing locally).

Carry a reverse tunnel (ssh -R) across each, such that each Pi presents a different port number of your server that maps back to its own ssh port 22.

Judicious use of entries in ~/.ssh/config can allow you to run ssh OfficeDevice1991 and have it map automatically to something like ssh -p 12345 localhost, which in turn would correspond to a normal ssh connection to the Pi representing OfficeDevice1991.


Answering your set of questions you've added in your edit,

who or finger would give you each RPi's connected time.

  1. Ports 49152 to 65535 are yours
  2. That's what autossh does
  3. Hostnames

and

  1. As #1 above
  2. A short for x ... do ... done loop could autogenerate the file for you. I wouldn't want to create 1000 entries manually either.
  • autossh is the way to go. I'm running a dozen or so reverse SSH tunnels using this method. Some of them in fact chained because the originating network allows no direct connection to the outside world. – 0xC0000022L Apr 12 '18 at 9:12
  • With the autossh solution, does this mean i need to manually assign ports and make sure these ports are not in use for each device i connect? If i have 1000 RPIs this also means i need to make sure that these 1000 ports i assign are all free before use? I would then need to go and config ssh with names that hooks to these ports. EG: RPI1 has port 1001 so i can type ssh RPI1 and in my config it does the rest. I would then need to append 1000 records to config file for this? Am i wrong or if not.. Please say theres a better solution. Also, how can i get a list of active devices? – irishwill200 Apr 12 '18 at 12:02
  • From my understanding this doesnt exactly give me an answer to my 3 issues listed in my question. – irishwill200 Apr 12 '18 at 12:04
  • @irishwill200 answer updated to address your six questions – roaima Apr 12 '18 at 15:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.