The SSH protocol and SSH Applications - Part 6

SSH and SSH Applications from a Security Gateway Perspective
Part 6: TCP Tunnels via SSH from a Gateway Perspective
After examining several SSH applications in the previous articles, let’s return to a powerful and fundamental capability built into the SSH protocol itself: tunneling.
Tunneling allows SSH to transport arbitrary TCP connections within an encrypted SSH session. While this is a flexible and secure mechanism from a cryptographic standpoint, it also introduces major security risks if left unchecked.
Forward TCP Tunneling

Setting up a forward TCP tunnel
OpenSSH refers to this capability as port forwarding or local forwarding, typically configured using the -L
parameter in the ssh command, like in this example:
ssh -L 8080:target-server.com:80 user@ssh-server.org
This instructs the SSH client to:
- Establish an SSH connection to ssh-server.org (typically includes starting a remote shell session as described in Part 2).
- Open a local listener on TCP port 8080.
The flowchart illustrates what happens when a new TCP connection arrives on the listener port:

TCP Forward Tunnel message flow
- New connection arrives on port 8080
- The SSH client opens a new channel.
- The SSH_MSG_CHANNEL_OPEN message has type direct-tcpip.
- Additional parameters of this message include the target host and target port.
(In our example target-server.com:80) - The SSH server resolves the hostname and connects to the target server. Upon success it sends an SSH_MSG_CHANNEL_OPEN_CONFIRMATION message.
- Client and server exchange SSH_MSG_CHANNEL_DATA messages containing the TCP data they receive from the user and from the target server. From this point on, data flows:
- From the local TCP connection → SSH client → SSH tunnel → SSH server → remote TCP destination.
- And back again in the reverse direction.
Each tunneled connection gets its own SSH channel, allowing for multiple parallel tunnels within the same SSH connection
This enables use cases like:
curl http://target-server.com:8080/ --resolve target-server.com:8080:127.0.0.1
In this case, the curl
tool connects to localhost port 8080 but the request is tunneled securely to the actual destination. Users with access to the local hosts file can also modify the DNS resolution for target-server.com pointing to localhost and have their browser connect through the tunnel to the destination server.
Security Concern
From a network security standpoint, this tunnel is opaque. Firewalls and IDS systems can only see:
- The initial SSH connection to
ssh-server.org
. - Not the true destination to
target-server.com
nor the application protocol (HTTP, SMTP, etc.) flowing inside. - Additional parameters of this message include the target host and target port.
(In our example target-server.com:80)
Users can use this technique to:
- Bypass destination filtering
- Access services that are otherwise blocked
- Tunnel unauthorized applications
Jump Hosts as Forwarding SSH Tunnels
SSH jump hosts use the same forwarding tunnel technique - but in a slightly different way: The jump host acts as an intermediary and the client opens a direct-tcpip channel to the final SSH server via the jump host, and then initiates a second SSH session over this channel.
In this case:
- The client is running two SSH sessions concurrently:
- One to the jump host
- One to the final target server
- The client does not open a listener port, but manages the two sessions and internally.
- From the jump host’s perspective, it's just relaying TCP traffic. It cannot inspect the contents of the nested SSH session.
This setup is common in enterprise environments where access to production servers is strictly controlled although we now understand that normal jump hosts can only control access but cannot control the content.
When developing our SSH Gateway, we not only solved that security concern but also discovered the opportunity to simply insert itself into the communication path as a virtual jump host.
We’ll cover more on jump host use cases in a future article.
Reverse TCP Tunneling

Setting up a reverse TCP tunnel
Now let’s look at the reverse of what we’ve described so far: In a reverse tunnel, the SSH connection is still initiated from the client - but new TCP connections are flowing from the server back into the client’s internal network.
Here’s an example how to initialize such a reverse tunnel:
ssh -R 80:192.168.1.11:80 user@ssh-server.org
This creates a listener on port 80 on the remote server, but traffic to that port is forwarded via the SSH tunnel to a machine in the client’s internal network (192.168.1.11)
Step-by-step as illustrated:

TCP Reverse Tunnel message flow
- After authentication, the client sends a SSH_MSG_GLOBAL_REQUEST message with type tcpip-forward, asking the server to open a listener on port 80.
- The server starts the listener port and responds with SSH_MSG_REQUEST_SUCCESS.
- The client may also open a standard remote shell channel (typically unused except to keep the connection alive).
- When the server accepts a new connection on the listener port, it starts a new channel with type forwarded-tcpip.
Important: Unlike other channels we discovered so far, this channel is opened by the server not by the client! - The client connects to the specified internal address (192.168.1.11:80) and confirms the channel.
- Data flows between the remote user and the internal address by having the SSH client and server exchange SSH_MSG_CHANNEL_DATA messages.
Security Concern
This effectively punches a hole through the firewall:
- From a firewall’s perspective, there’s a legitimate outgoing SSH connection:
- But that SSH connection now enables incoming TCP connections into the internal network
- All without needing to open or allow any inbound ports on the client side
Why Tunnels Are a Challenge for Security Gateways
As we've seen:
- Forward tunnels can be used to circumvent outbound restrictions
- Reverse tunnels can allow remote access to internal systems - even if inbound connections are blocked
A normal stateful firewall is blind to what’s happening inside the SSH connection unless it blocks SSH entirely!
What an SSH Security Gateway Needs to Do
A modern SSH Security Gateway must:
- Offer real SSH proxy functionality to inspect SSH message payloads
- Analyze messages with types direct-tcpip, tcpip-forward, and forwarded-tcpip.
- Identify the target host, port, and direction of each tunneled connection.
- Match tunneling behavior against security policy.
- Ideally apply application-level filtering proxies to inspect the tunneled protocols and applications.
Some next-generation firewalls claim SSH inspection capabilities, but many only perform superficial checks or require to weaken the end-to-end security of the SSH connections.
At Cloud Fellows, we designed our SSH Gateway to:
- Integrate fully into modern SSE platforms to combine SSH with other application proxies for an integrated security policy.
- Retain full SSH security (recommended algorithms, dual-authentication including private key authentication, agent support).
- Apply policy to tunneled data.
Final Thoughts
SSH tunnels are powerful and flexible - but if left unmonitored, they can bypass even well-designed network security controls.
Similar security concerns exist with the other SSH applications that we have discussed in this blog article series.
But with the right gateway in place, enterprises can retain all the benefits of SSH while enforcing robust usage and content policies across all SSH Applications and Tunneled Connections.
The articles in this series
- The SSH Core Protocol and the SSH Remote Command
- SSH Remote Shell from a Gateway Perspective
- SCP from a Gateway Perspective
- SFTP from a Gateway Perspective
- Git via SSH from a Gateway Perspective
- TCP Tunnels via SSH from a Gateway Perspective [this article]