Ultimate Guide to Tunneling in Linux
There are cases where your machine does not have access to the destination server, or you don’t want to expose the destination server to the internet. In these cases, you can use a relay server to tunnel the traffic to the destination server.
A relay server simply forwards the incoming traffic to the destination server and forwards the response back to the client.
For the sake of this article, we want to relay openvpn traffic from machine A to machine B.
All the examples in this article are for TCP traffic thus they operate at the transport layer.
iptables
iptables is a powerful tool that allows you to configure the IP packet filter rules of the Linux kernel firewall.
First we need to enable ip forwarding
|
|
Then we need to add the following rules
|
|
The first rule tells iptables to forward the incoming traffic on port 8443 to machine B on port 443.
The second rule masquerade the IP address of the incoming packets with the IP address of the outgoing network interface of the relay server. so the client will see the relay server IP address as the source IP address.
iptables rules are not persistent, so you need to save them to make them persistent using a tool like iptables-persistent
on Ubuntu/Debian.
|
|
This is a simple soltuion with low overhead, but it’s not very flexible.
socat
Another great program that can be used to tunnel traffic is socat
.
|
|
|
|
Let’s break down the command:
This first part tells socat on which host and port to listen, the second part specifies where to forward the traffic.
It listens on port 8443 tcp and forwards the traffic to machine B on port 443 tcp.
The fork
option create a new process for each connection by calling fork()
, so that multiple connections can be handled.
The reuseaddr
option allows other sockets to bind to an address to which another socket has already bound.
it’s enabled by default on most systems so It’s not necessary to specify it.
The -dd
option enables debugging output.
SSH
We want to use SSH Local Port Forwarding feature to tunnel the traffic.
|
|
This command simply forwards traffic from port 8443 (on all interfaces meaning 0.0.0.0) on the system that the command is executed (machine A) to port 443 on 127.0.0.1 on machine B. it’s the address that the traffic will be forwarded to which is the openvpn server running on machine B.
Note: you can omit 0.0.0.0
since it’s the default value.
let’s break down the command:
-L
option specifies the port forwarding
-C
option enables compression
-N
option tells ssh not to execute a remote command.
In order to send keep-alive packets to the server to prevent the connection from being closed by some firewalls because of connection inactivity, you can use the following options:
ServerAliveInterval
sets a timeout interval in seconds after which if no data has been received from the server, ssh will send a message through the encrypted channel to request a response from the server.ServerAliveCountMax
sets the number of server alive messages which may be sent without ssh receiving any messages back from the server.
so the command becomes:
|
|
There’s another great program called autossh
that can be used to automatically restart the ssh connection if it fails.
|
|
autossh
comes with a monitoring feature but we disabled it with the -M 0
option. you can experiment with it to see if it fits your needs.
The -f
option tells autossh to run in the background.
stunnel
This progmram adds TLS encryption to the TCP traffic.
|
|
Then we need create a certificate to encrypt the traffic. this command creates a private key and a self-signed certificate:
|
|
-new
create a new certificate request.
-x509
create a self-signed certificate.
-days
specifies the number of days the certificate is valid.
-noenc
tells openssl not to encrypt the private key.
-out
specifies the output file for the certificate.
-keyout
specifies the output file for the private key.
Combine the key and the certificate into a single file as stunnel requires it:
|
|
Now we need to setup a client and server. the client is machine A and the server is machine B. client sends the traffic to the server and the server forwards it to the openvpn service running on port 443.
We need to create a configuration file for machine A /etc/stunnel/stunnel.conf
as client
|
|
accept
is the port that stunnel listens on. connect
is where the traffic is forwarded to.
On machine B which recieves the traffic and forwards it to the openvpn server running on port 443
|
|
|
|
HAProxy
HAProxy is a very fast and reliable solution offering high availability, load balancing, and proxying for TCP and HTTP-based applications. here we will use it as a forward proxy.
HAProxy has two main sections: frontend
and backend
. the frontend recieves the traffic and based on some rules, forwards it to the backend.
Install HAProxy:
|
|
Let’s define our frontend and backend in the configuration file /etc/haproxy/haproxy.cfg
frontend default
log stdout format raw local0 info # log to stdout
mode tcp
bind :8443
default_backend mybackend
backend mybackend
mode tcp
server server1 machine_b:443
server1
is a tag that HAProxy uses to identify the server. you can use any name you want.
We could also encrypt this communication between the two machines using SSL
Let’s create a self-signed certificate:
|
|
Combine the key and the certificate into a single file as HAProxy requires it:
|
|
On machine A
frontend default
log stdout format raw local0 info
mode tcp
bind :8443
default_backend mybackend
backend mybackend
mode tcp
server server1 machine_b:8443 ssl crt /etc/haproxy/haproxy.pem verify none
On machine B, the traffic is recived on port 8443 and be forwarded to the openvpn server running on port 443.
frontend default
log stdout format raw local0 info
mode tcp
bind :8443 ssl crt /etc/haproxy/haproxy.pem verify none
default_backend mybackend
backend mybackend
mode tcp
server server1 127.0.0.1:443
Note that only the communication between the HAProxy instances is encrypted.
The verify none
option tells haproxy not to verify the certificate of the destination server. you could add the certificate to the operating system trust store so you don’t have to use this option. run the following commands on both machines:
|
|
Restart the HAProxy service:
|
|
Conclusion
We have seen different ways to tunnel traffic in Linux. Each method has its own use case and advantages.
iptables, socat are simple and have low overhead, but they are not very flexible and they don’t provide encryption.
stunnel, SSH and HAProxy provide encryption between relay and destination server and are more flexible but they have more overhead.
You can choose the method that fits your needs.