This is an old revision of the document!
SSH Tunneling
There are many ways to provide access to the internal server from outside. Let's briefly summarize them:
- You have no CG-NAT: get a public IP and port-forward to your internal server (very rare today)
- Get a VPN with port-forwarding (require a third party service)
- Setup a tunnel to an external server
I will show you the third case, because it's the solution that gives you the biggest opportunities. A VPN is also nice, but there are not many VPN supporting port-forwarding and they also tend to be expensive. Renting a VPS on the other hand, can be very cheap and since you only need it to get into your services (outgoing bandwidth is not affected) you don't even need some high tier network package with it.
On Local Host
Create a new user called tunnel. Leave tunnel home folder on root partition (/home/tunnel) to avoid tunnel failure if partitions don't mount. You need also to create an SSH key, and prepare the tunnels config folder /home/tunnel/tunnels and log folder /home/tunnel/logs:
> useradd -m tunnel > su - tunnel > ssh-keygen > mkdir tunnels > mkdir logs
Add tunnels configurations like this under /home/tunnel/tunnels/main:
- main
REMOTE_SERVER=99.99.99.99 # your remote server IP - don't use name, use IP REMOTE_SERVER_SSH_PORT=22 # your remote server SSH port HOME_SERVER_REMOTE_SSH_PORT=5022 # the port your local server SSH will be accessible from remote SSH_IDENTITY=# optional path to private ssh key, leave empty for default REMOTE_USER=tunnel # remote user to login as REMOTE_TO_HOME=\ "127.0.0.1:8080:127.0.0.1:80 "\ "127.0.0.1:8443:127.0.0.1:8443 "\ "0.0.0.0:6022:10.70.43.99:22 "\ "0.0.0.0:12112:127.0.0.1:12112 " # list of ports forwarded from remote to local HOME_TO_REMOTE= # list of ports forwarded from local to remote
Remember to “ssh” manually at least once each host to accept host keys!
You can create as many tunnels as you like. Note that SSH port will always be forwarded anyway, so you want one file for each remote host.
Now create this script under /home/tunnel/tunnel.sh:
#!/bin/bash function log() { echo $(date) - $@ >> $LOG } Add this to .ssh/config for keepalive: <code> Host * ServerAliveInterval 60
cd $HOME TUNNELS=$(ls tunnels)
TUNNEL_PIDS= for i in $TUNNELS do
# Spin a shell for each tunnel ( NAME=$i CONFIG=tunnels/$NAME LOG=logs/tunnel-$NAME
log "Starting operations for tunnel '$NAME'..."
# Iterate forever while true do # Spin up a sub-shell to ensure we reload the config properly each iteration (log "Reading configuration from '$CONFIG'..." source $CONFIG
log "Testing if remote server '$REMOTE_SERVER' is reachable..." if ping -c 10 -W 5 $REMOTE_SERVER &> /dev/null then log "Remote server '$REMOTE_SERVER' is reachable!"
LOGIN_IDENTITY= test ! -z $SSH_IDENTITY && LOGIN_IDENTITY="-i $SSH_IDENTITY" LOGIN_AS=$USER test ! -z $REMOTE_USER && LOGIN_AS="-l $REMOTE_USER"
REMOTES="-R0.0.0.0:$HOME_SERVER_REMOTE_SSH_PORT:127.0.0.1:22" # SSH port we always export LOCALS= for i in $REMOTE_TO_HOME; do REMOTES="$REMOTES -R$i"; done for i in $HOME_TO_REMOTE; do LOCALS="$LOCALS -L$i"; done
COMMAND="ssh $LOCALS $REMOTES $LOGIN_IDENTITY $LOGIN_IDENTITY $LOGIN_AS -p $REMOTE_SERVER_SSH_PORT $REMOTE_SERVER -nNT"
log "Run: '$COMMAND'..." $COMMAND &>> $LOG & ssh_pid=$!
# Wait a bit to ensure command is running... sleep 1
# until SSH returns, check network, because SSH might hang for a long time. while ps -p $ssh_pid &> /dev/null do if ping -c 2 -W 2 $REMOTE_SERVER &> /dev/null then sleep 30 else # Network is down? forcing ssh to stop log "WATCHDOG: Detected '$REMOTE_SERVER' is NOT reachable! Killing command." kill -9 $ssh_pid while ps -p $ssh_pid; do sleep 1; done fi done
# get return code wait $ssh_pid log "Command returned code '$?'. Retrying..." sleep 10 # after disconnection, wait a bit before retrying else log "It seems that '$REMOTE_SERVER' is not reachable. Wait and retry..." sleep 5 # wait a bit before retry ping fi ) # close iteration shell done )& # close tunnel shell TUNNEL_PIDS="$TUNNEL_PIDS "$!
done
wait </code>
and set it as executable:
> chmod +x /home/tunnel/tunnel.sh
Now create the startup script /etc/local.d/99-tunnels.start:
- 99-tunnels.start
#!/bin/bash start-stop-daemon -b -m -p /var/run/tunnel.pid -n tunnel -u tunnel /home/tunnel/tunnel.sh
Share tunnel's /home/tunnel/.ssh/id_rsa.pub with remote host, paste it inside authorized_hosts file of remote tunnel user.