Skip to content

Instantly share code, notes, and snippets.

@johnsimcall
Last active October 22, 2022 07:22
Show Gist options
  • Save johnsimcall/61a2c6d05899dd1da4d68614c5faf263 to your computer and use it in GitHub Desktop.
Save johnsimcall/61a2c6d05899dd1da4d68614c5faf263 to your computer and use it in GitHub Desktop.
systemd unit file for establishing an SSH tunnel to a remote host that runs (or can reach) a proxy service
Host example-bastion
HostName example-bastion.fqdn.com
IdentityFile /root/.ssh/id_ed25519 # -i
User john # -l
ExitOnForwardFailure yes
ServerAliveInterval 10
SessionType none # -N
RequestTTY no # -T
GatewayPorts yes # -g
LocalForward 3129 outside.proxy.com:3128 # -L
[Unit]
Description=Setup a secure tunnel to %i
After=network-online.target
[Service]
ExecStart=/usr/bin/autossh -M 0 -F /etc/ssh/ssh-tunnel-%i.config %i
RestartSec=5
Restart=always
[Install]
WantedBy=multi-user.target
@johnsimcall
Copy link
Author

I created this as an alternative to running a proxy behind a proxy (e.g. squid w/ cache_peer configuration)

autossh needs to be installed because asking systemd to run /usr/bin/ssh directly is denied by SELinux

In addition to creating these two files, please also make sure that the remote host (example-bastion) has had its HostKey accepted into your known_hosts file:

  1. sudo systemctl edit --force --full ssh-tunnel@.service
  2. sudo -e /etc/ssh/ssh-tunnel-outside-bastion.config
  3. sudo dnf --enable-repo epel install autossh
  4. ssh john@example-bastion.fqdn.com #to confirm its host key is known
  5. sudo systemctl enable --now ssh-tunnel@outside-bastion

Open the firewall if you would like other systems to be able to use this systems (-g) port forward

  1. sudo firewall-cmd --permanent --add-port 3129/tcp
  2. sudo firewall-cmd --reload

@johnsimcall
Copy link
Author

All of this systemd trickery is the equivalent of just running

$ ssh -L 3129:outside.proxy.com:3128 outside.bastion.com

Here are some other options to consider that can put the ssh command into the background (-f) and expose the forwarded port(s) to other hosts in the local network (g)

 -L | start listening on 3129 locally, then
    | forward anything received there to port 3128 on outside.proxy (via outside.bastion)
 -g | allow other systems to use the port forwarding
 -N | don't execute any commands on outside.bastion, just setup the port forwarding
 -f | after prompting for password, fork into the background and return the shell prompt
 -n | don't allow password input, instead connect stdin to /dev/null
 -T | don't allocate a pseudo-terminal

 -o "ExitOnForwardFailure yes" | fails if cannot setup port forwarding
                               | doesn't fail if the port forward successfully goes to a blackhole
                               | e.g. sshd sets "DisableForwarding yes" or "AllowTcpForwarding no"
 -o "ServerAliveInterval 10"   | if the connection is quiet, send a heartbeat every 10 seconds
 -o "ServerAliveCountMax 3"    | exit when this many heartbeats are unacknowledged

If you also have control over the remote sshd daemon, you might consider applying this:

 -o "ClientAliveInterval 10"   | if the client is quiet, send a heartbeat every 10 seconds

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment