Skip to content

Recipe: Forward insecure TCP-based protocol

Some protocols are hard to use securely and privately. This is often caused by a lack of a standardized and secure encryption layer, incomplete/broken implementations or local deployment hurdles.

Such protocols can be tunneled though SSH to add the desired properties. A prime example (which we will use in this chapter) is the VNC protocol.

Example scenario

Users want to access the machine lab01 which is running some VNC server as well as an OpenSSH server.

Generate dedicated port forwarding ssh key

OpenSSH CLI client

Create a dedicated ssh key for connection tunneling on your local machine (see man ssh-keygen for all options):

ssh-keygen -C 'ssh port forwarding only (you full name or email address)' \
  -t ed25519 -f ~/.ssh/portforwarding

PuTTY(gen)

Run PuTTYGen. Pick a sensible key type (we recommend EdDSA with the Ed25519 curve). Press Generate to create a new key.

PuTTYgen

Edit the key comment. Save the private key using the Save private key button. Copy the public key in OpenSSH format from the textbox and save it in a textfile.

PuTTYgen

Load the private key into PuTTY’s ssh agent Pageant (simply doubleclick the secret key file on Windows). Create a link in the Startup folder to load the key on every login.

Serverside Setup

We will consider two variations:

  1. Enforced: Usage of SSH tunneling is mandatory and (if possible) enforced by system administration. Setup requires administrator level access to the server.
  2. Voluntary: Usage of SSH tunneling is not enforced but desired by the users themselved. This only requires user access to the server.

Pick the variant that matches your environment.

Enforced

To prevent (accidential) insecure access to the VNC server bind its socket only to the loopback interface. Please refer to your VNC servers documentation for details. You may check this by listing all listening sockets and filtering for those of interest (subsitute ss with netstat on legacy systems):

ss -tlpn | grep vnc
LISTEN 0    32    127.0.0.1:5900       *:*    users:(("x11vnc",pid=.....,fd=4))
LISTEN 0    32    [::]:5900         [::]:*    users:(("x11vnc",pid=.....,fd=5))

Socket values of 127.0.0.1, ::1, [::1], and localhost indicate local listeners only. Public IP addresses and hostnames, *, :: or [::] indicate a potentially publicly accessible service.

Another option to prevent remote access is to disable external access using the local firewall. Please refer to your distributions documentation for details.

Create a dedicated system user called portforwarding:

# CentOS/RHEL/Fedora:
adduser --comment "port forwarding only" --system --shell /usr/bin/false portforwarding
# Debian/Ubuntu:
adduser --gecos   "port forwarding only" --system --shell /usr/bin/false portforwarding

Create ~portforwarding/.ssh/authorized_keys file with the correct permissions:

mkdir -p ~portforwarding/.ssh
touch ~portforwarding/.ssh/authorized_keys
chown 700 ~portforwarding/.ssh
chown -R portforwarding: ~portforwarding/.ssh
chmod 600 ~portforwarding/.ssh/authorized_keys

Add public keys of every user that needs VNC access to ~portforwarding/.ssh/authorized_keys.

Edit /etc/ssh/sshd_config and add the following section:

Match User portforwarding
    AllowAgentForwarding no
    AllowTcpForwarding local
    AuthenticationMethods publickey
    Compression no
    ForceCommand /usr/bin/sleep 36500d
    LogLevel VERBOSE
    PasswordAuthentication no
    PermitOpen localhost:*
    PermitOpen 127.0.0.1:*
    PermitOpen [::1]:*
    PermitTTY no
    X11Forwarding no

See man sshd_config for details and explanations. Restart sshd.

Voluntary

(This assumes that the user has a local account on lab01).

Log into lab01 and paste your port forwarding public key into ~/.ssh/authorized_keys. Prepend this string (don’t miss the single space at the end):

restrict,no-pty,port-forwarding,permitopen=localhost:*,permitopen=127.0.0.1:*,permitopen=[::1]:*,command="/usr/bin/sleep 36500d" 

(See the sshd-manpage under “authorized_keys file format” for details).

Clientside setup

The setup on the client is the same for both variants.

OpenSSH CLI Client

All examples will forward TCP port 5900 from the remote side (lab01) to port 5900 locally. Edit according to your local use case.

Add these two snippets to your local SSH client configuration in ~/.ssh/config.

The first one is a generic template for all tunnel setups.

# generic tunnel settings
Host tunnel_*
    ExitOnForwardFailure yes
    IdentityFile         ~/.ssh/portforwarding
    User                 portforwarding
    RequestTTY           no

This snippet describes a concrete host.

Host tunnel_lab01
    Hostname     lab01.enter-fqhn-here
    LocalForward 127.0.0.1:5900

Simply open a tunnel with

ssh -v tunnel_lab01

TODO: systemd-unit

PuTTY

PuTTY PuTTY PuTTY PuTTY PuTTY