tunnel home hosted service to public with ssh
SSH is a really powerful tool and has a lot of funtionalites built in that can solve quite complex use case, one of this is tunnelling,
you can easily create a tunnel with a command ssh user@host -L 127.0.0.1:4000:127.0.0.1:3000, this for example allow you to open a local port (4000) that map
a service listening on "127.0.0.1" (3000) on the "host". A more interesting use case is actually the opposite ssh user@host -R 127.0.0.1:4000:127.0.0.1:3000
in this case you can expose a local only service (3000) as a service on the "host" on port 4000, you could do also *:4000 to accept any incoming connections on the "host" but may need some configuration on the ssh server.
So ssh can be used for expose a local service not reachable on the wider internet on the internet using a public host with just an ssh server running, so let's go through how do that, but i will do it using as well a http server on the public host to have better/safer control of what go through and for ssl.
Let's do it in few steps:
- configure a user on the public host to be used by the tunnel
- configure a service in the private host to establish the ssh tunnel
- configure a http server to expose publicly the private service
Configure the user
first thing create the user on the public facing host for handle the tunnel, better to do a specific user to limit risks.
sudo adduser user_for_tunnel
after creating the user let's make sure that do not support login by password, for do that let's edit /etc/ssh/ssh_config and add at the bottom:
Match User user_for_tunnel
PasswordAuthentication no
Done this we can generate a new ssh key with ssh-keygen and copy the public key to our public host.
scp new_key.pub user@host:~/
Done this we can put the public key in the ~/.ssh/authorized_key of the user_for_tunnel.
# make sure ssh folder exist
sudo mkdir /home/user_for_tunnel/.ssh/
# copy the key
cat new_key.pub | sudo tee -a /home/user_for_tunnel/.ssh/authorized_keys
# make sure the files have the right permission
sudo chmod 600 /home/user_for_tunnel/.ssh/authorized_keys
# make sure the files have the right owner
sudo chown -R user_for_tunnel:user_for_tunnel /home/user_for_tunnel/.ssh/
test the connection with
ssh -i new_key user_for_tunnel@tglman.com
Configure the tunnel
Let's configure a service to handle the tunnel in the private host, so at first let's create a configuration directory for the keys and copy the private key.
sudo mkdir /etc/remote_tunnel/
sudo cp new_key /etc/remote_tunnel/
# make sure that only the user of the service can read it (I'm going for root)
sudo chmod 400 /etc/remote_tunnel/new_key
Then let's create a service that use ssh to tunnel, I will put it in /etc/systemd/system/ssh-tunnel.service this will map the local port to the server port
[Unit]
Description=SSH tunnel to tglman.com
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/ssh -N user_for_tunnel@tglman.com \
-o StrictHostKeyChecking=accept-new -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 \
-R 127.0.0.1:4000:127.0.0.1:4000 -i /etc/remote_tunnel/new_key
RestartSec=10
Restart=on-success
RestartForceExitStatus=255
[Install]
WantedBy=multi-user.target
To notice the option: -o StrictHostKeyChecking=accept-new this is necessary to accept the ssh key of your public host, maybe you may want to avoid this option
to do that you need to login once by hand with the same user or just copy the key to knwon_hosts by hand.
Done this let's just enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable ssh-tunnel
sudo systemctl start ssh-tunnel
Configure the proxy
Finally, we can edit hour http service to make our service public, in my case I'm using my own server vacuna, so I add a subdomain and a proxy to connect to the service using localhost
host {
name "homeservice.tglman.com"
proxy "/" {
url "http://127.0.0.1:4000"
}
}
As a reference I got inspiration for the systemd tunnel service from here: https://gist.github.com/olbat/0e4879825b01239d8d6064d239e0723b
That's all!
Ko-fi