Keep an Eye on SSH Forwarding!


OpenSSH is a wonderful tool box. The main purpose is to establish encrypted connections (SSH means Secure SHell) on a remote UNIX machine and, once authenticated, to spawn a shell to perform remote administration. Running on port 22 (default), the client (ssh) and the server (sshd) exchange encrypted information (what you type and the result of your command). I’ll not review the long list of options available with SSH but let’s focus on a particular feature: tunneling.

By default, sshd (the server) has the flag AllowTcpForwarding turned on (I won’t start a debate here about this default setting). “TCP Forwarding” allows you to encapsulate any other protocol (based on TCP of course) inside an already established SSH connection. It’s very useful to increase the security of any unsecured protocol exchanging data in clear text (example: to check a mailbox via the POP3 or IMAP protocol). TCP Forwarding is also a common way to “hide” your activity on the network. Here is an example:

# ssh -f -N -L 1100:localhost:110 -f's password: 
# telnet localhost 1100
Connected to localhost.
Escape character is '^]'.
+OK Solid POP3 server ready
+OK session ended
Connection closed by foreign host.

If you want to read more about tunnels, check the following tutorial.

But the ssh client has a much more interesting feature: dynamic port forwarding. When you connect to the remote host and specify a”-D ” argument, the remote ssh server acts as a SOCKS proxy! Example:

# ssh -f -N -D 9001

Starting from now, all applications compatible with SOCKS proxies can use the proxy running on! Here is an example on FireFox:

Click to enlarge
Click to enlarge

Configured like this, your FireFox will send all HTTP traffic though the remote server via the SSH session. The server will connect to the final website and send the HTTP requests. Really nice! But there are some security concerns:

  • The UNIX server will generate a lot of traffic from the Internet. There is a risk of high resources consumption (bandwidth and/or CPU).
  • As connections will originate from the UNIX server itself, the logged IP address on remote services will be the one of the UNIX server. There is a risk in case of abuse (hidden IP address).
  • By default, the SSH daemon permits all protocols to be forwarded. Some users may abuse your security policy by encapsulating unexpected protocols (Instant Messenging is a good example).

Here follows some steps to use the SSH tunnel in a safe way. First of all, if you don’t really need this feature, disable it! In /etc/ssh/sshd_config, set AllowTcpForwarding to off and restart the sshd process.


By default, the SSH daemon does not log the sessions established via a tunnel. To show them, you need to run the sshd in debug mode (-d). This is not acceptable in an operational environment. Here is a quick patch to log all outgoing sessions initiated by the sshd with a mapping to the UID (UserID). In serverloop.c, patch the function server_request_direct_tcpip() like this:

<  // BEGIN PATCH TunnelLogging
<  uid_t who;
<  // BEGIN PATCH TunnelLogging
<  // debug("server_request_direct_tcpip: originator %s port %d, target %s port %d",
<  who = getuid();
<    logit("Tunnel: %s:%d -> %s:%d UID(%d)",
<      originator, originator_port, target, target_port, who);
>  debug("server_request_direct_tcpip: originator %s port %d, target %s port %d",
>      originator, originator_port, target, target_port);

For each new TCP session, the following line will be sent to Syslog:

Feb 27 08:03:08 honey sshd[9060]: Tunnel: -> UID(2349)

The patch will allow to correlate who connected and from which IP address.

Restricting the allowed ports

By default, sshd allow to forward TCP sessions to any ports. You can restrict them to specific hosts and/or ports via the PermitOpen parameter (available since release 4.4):

PermitOpen host:port
PermitOpen IPv4_addr:port
PermitOpen [IPv6_addr]:port

Another alternative is to use the local firewall – iptables – to restrict connection initiated by the UNIX server.

Restricting the allowed users or groups

Now that hosts and ports are restricted, it can be useful to restrict who can use the port forwarding feature. Back to the sshd_config man page, let’s have a look at the Match keyword:

Introduces a conditional block. If all of the criteria on the Match line are satisfied, the keywords on the following lines override those set in the global section of the config file, until either another Match line or the end of the file. The arguments to Match are one or more criteria-pattern pairs. The available criteria are User, Group, Host, and Address. Only a subset of keywords may be used on the lines following a Match keyword. Available keywords are AllowTcpForwarding, Banner, ForceCommand, GatewayPorts, GSSApiAuthentication, KbdInteractiveAuthentication, KerberosAuthentication, PasswordAuthentication, PermitOpen, RhostsRSAAuthentication, RSAAuthentication, X11DisplayOffset, X11Forwarding, and X11UseLocalHost.

Here are some example. First let’s restrict the users who are allowed to forward TCP sessions:

AllowTcpForwarding no
Match User john,andy,ted
AllowTcpForwarding yes

Or better, allow specific ports per user groups:

AllowTcpForwarding no
Match Group admins
AllowTcpForwarding yes
Match User john,andy,ted
AllowTcpForwarding yes

With this configuration, administrators will be able to open unrestricted connections, specific users will be able to open an IMAP session to a single server and all remaining users won’t be allowed to create tunnels.

Restricting the server Internet connectivity

Finally, restrict the Internet connectivity of your server! Even if you don’t allow TCP Forwarding, it’s a good idea. A server should never have a full direct Internet connectivity. Close everything and open connectivity depending on the needs (example: download some patches via HTTP(S) from specific servers).


  1. To log the login instead of the UID I use this patch that differs slightly from your :

    — ./serverloop.c 2017-01-23 15:24:04.226976530 +0100
    +++ ./serverloop.c 2017-01-23 14:55:51.433139600 +0100
    @@ -448,6 +448,9 @@
    /* XXX fine grained permissions */
    if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 &&
    !no_port_forwarding_flag && !options.disable_forwarding) {
    + logit(“Tunnel: %s:%d -> %s:%d (%s)”,
    + originator, originator_port, target, target_port,
    + cuserid(NULL));
    c = channel_connect_to_port(target, target_port,
    “direct-tcpip”, “direct-tcpip”);
    } else {

  2. Any terminal based access program, any version.
    root or non-root.

    The point is that if you can send keystrokes (stdin) and see command output (stdout) you can tunnel using that.

  3. Andy, you mentioned that if users use ssh (or even telnet) then you cannot stop them tunnelling. Is that true for any user or only those with root access can tunnel? thanks

Leave a Reply

Your email address will not be published. Required fields are marked *