SSH Tunnel

Simple SSH Tunnel Service for systemd

Here is a simple example of starting an SSH tunnel on startup with systemd.

There is a repository for this service here that makes for an easy install.

The code looks long and scary, but it is simpler than it seems, and all files have been commented throughout to explain the steps along the way.

First, let’s create the service file:

nano ./ssh-tunnel-service.service
[Unit]
Description=SSH Tunnel Background Service
# Wait for the internet to come online
After=network.target network-online.target

[Service]
# Fork this service into a background process
Type=forking

# Always restart and try after > 2 seconds to avoid 'Too Fast' error
Restart=always
RestartSec=3

[Install]
# Always connect so the tunnel launches at startup
WantedBy=multi-user.target

Now for our install bash script:

nano ./install.sh
#!/bin/bash

# Check for root
if [ "$(id -u)" != "0" ]; then
   echo "This script must be run as root" 1>&2
   exit 1
fi

# Validate Service Name argument
if [ "$#" -ne 1 ]
then
  echo "Please provide a service name."
  exit 1
fi

# Set the Service Name from the provided argument
SERVICE_NAME=$1

# Get the conf settings
read -p "SSH Private Key File (full path): "  SSH_PRIVATE_KEY
read -p "SSH Port: "  SSH_PORT
read -p "SSH Tunnel Username: "  SSH_TUNNEL_USERNAME
read -p "SSH Tunnel Host: "  SSH_TUNNEL_HOST

# Remove the current conf
rm "$(pwd)/$SERVICE_NAME.conf"

# Create a new conf
touch "$(pwd)/$SERVICE_NAME.conf"

# Write the settings to the conf file
echo "[Service]" >> "$(pwd)/$SERVICE_NAME.conf"
echo "ExecStart=/bin/sh -c \"/usr/bin/ssh -N -D $SSH_PORT -o StrictHostKeyChecking=no -i $SSH_PRIVATE_KEY $SSH_TUNNEL_USERNAME@$SSH_TUNNEL_HOST &\"" >> "$(pwd)/$SERVICE_NAME.conf"
echo "ExecStop=/bin/kill \$(pgrep -f $SSH_TUNNEL_USERNAME@$SSH_TUNNEL_HOST)" >> "$(pwd)/$SERVICE_NAME.conf"

# Copy the service
cp "$(pwd)/ssh-tunnel-service.service" /etc/systemd/system/$SERVICE_NAME.service

# Create the conf directory
if [ ! -d /etc/systemd/system/$SERVICE_NAME.service.d ]
  then
    mkdir /etc/systemd/system/$SERVICE_NAME.service.d
fi

# Copy the conf file
cp "$(pwd)/$SERVICE_NAME.conf" /etc/systemd/system/$SERVICE_NAME.service.d/$SERVICE_NAME.conf

# Reload the systemd daemon
systemctl daemon-reload

# Enable the service
systemctl enable $SERVICE_NAME

# Start the service
systemctl start $SERVICE_NAME

Now an uninstall file:

nano ./uninstall.sh
#!/bin/bash

# Check for root
if [ "$(id -u)" != "0" ]; then
   echo "This script must be run as root" 1>&2
   exit 1
fi

# Validate Service Name argument
if [ "$#" -ne 1 ]
then
  echo "Please provide a service name."
  exit 1
fi

# Set the Service Name from the provided argument
SERVICE_NAME=$1

# Stop the service
systemctl stop $SERVICE_NAME.service

# Disable the service
systemctl disable $SERVICE_NAME.service

# Delete the service
rm /etc/systemd/system/$SERVICE_NAME.service

# Delete the conf directory
if [ -d /etc/systemd/system/$SERVICE_NAME.service.d ]
  then
    rm -R /etc/systemd/system/$SERVICE_NAME.service.d
fi

Ok, now that everything has been created, let’s install:

chmod +x ./install.sh
./install.sh SERVICE_NAME

Replace SERVICE_NAME with the name of the service.

This will ask for credentials and SSH settings, generate the conf file and register the service.

Restart your computer and it should have launched correctly.

Since it can be tricky to debug systemd, here is a simple command to check if a process exists:

echo $(pgrep -f SSH_TUNNEL_HOST)

Make sure to replace SSH_TUNNEL_HOST with your host.

It should output a Process Id (PID) of the service process running.

If it doesn’t, you can check the status of the service with this command:

sudo systemctl status SERVICE_NAME

Replace SERVICE_NAME with the name of the service.

To uninstall the services and clean up after ourselves, just run the uninstall script:

chmod +x ./uninstall.sh
./uninstall.sh SERVICE_NAME

Replace SERVICE_NAME with the name of the service.

Leave a Reply

%d bloggers like this: