top of page
  • Linkedin

Encrypting TCP Traffic Without Changing Your App using "stunnel"

In modern systems, security is no longer optional. Many legacy or lightweight applications still communicate over plain TCP without encryption. This is where stunnel becomes extremely useful.


This article explains:

  • What stunnel is

  • How it works internally

  • Two common real-world architectures with clear examples

  • Common mistakes to avoid


What is stunnel?

stunnel is a lightweight proxy that adds TLS/SSL encryption to TCP-based applications.

It acts as a secure tunnel between a client and a server, encrypting data on one side and decrypting it on the other. The applications themselves do not need to understand TLS.


Key points:

  • stunnel works at the transport layer (TCP)

  • It does not understand application protocols (MySQL, Redis, ClickHouse, SMTP, etc.)

  • It only handles TLS + TCP


In simple terms:

stunnel allows you to run unencrypted applications securely over an encrypted channel.


Most commonly used stunnel options

  1. client

    Determines which side of the TLS handshake stunnel participates in.

    • client = yes → stunnel initiates a TLS connection

    • client = no  → stunnel accepts a TLS connection


    TLS is directional. One side must act as the client and one as the server. stunnel does not auto-detect this.

  2. accept

    Defines where stunnel listens locally for incoming TCP connections.

    • Can bind to:

      • A specific IP (127.0.0.1)

      • All interfaces (0.0.0.0)

    • Port must be free and accessible

    This is the port your application connects to, not the backend. Binding to 127.0.0.1 is safer for local-only usage.

  3. connect

    Specifies the remote destination where stunnel forwards traffic to. stunnel is a TCP forwarder, not a load balancer. One service → one destination.

    connect can point to:

    • Local service

    • Remote server

    • TLS or non-TLS endpoint (depends on client mode)

  4. cert

    Specifies the public certificate used during TLS handshake. Always include the full chain if clients don’t trust intermediates.

    • Mandatory when stunnel:

      • Accepts TLS (client = no)

      • Performs mutual TLS (mTLS)

    Acceptable file format

    • PEM encoded

    • Can include certificate chain


  5. key

    Specifies the private key corresponding to the certificate. If omitted, stunnel assumes the key is inside the cert file.

    Key file Restrict permissions should be: chmod 600 server.key

  6. CAfile

    Defines the trusted Certificate Authority bundle used for verification. Without this, TLS encryption works but identity verification does not. It is recommended to use cert & key OR CAfile. Not both at same time.

    It is used for

    • Verifying remote server certificates

    • Verifying client certificates (mTLS)


  7. CApath

    Specifies a directory containing multiple trusted CA certificates. It is used in large environments or if there are system-wide CA trust stores presents.

    Key Difference:

    CAfile: single file

    CApath: directory of hashed certs


  8. verifyChain

    Enforces full certificate chain validation. It prevents man-in-the-middle attacks. Never disable in production environment. It ensures certificate is signed by a trusted CA & rejects self-signed or incomplete chains


  9. checkHost

    Validates the hostname against the certificate’s CN or SAN. Even a valid certificate is useless if it belongs to a different host.

    If connecting to: connect = db.example.com:443

    You must set:

    checkHost = db.example.com

  10. sslVersion

    Restricts which TLS protocol versions stunnel will use. It is not mandatory but recommended to use because older protocols are vulnerable.

    Recommended values

    • TLSv1.2 (widely supported)

    • TLSv1.3 (modern, faster)

    Avoid TLSv1.0 and TLSv1.1 entirely.

  11. ciphers

    Controls which encryption algorithms are allowed. Weak ciphers reduce encryption strength even if TLS is enabled.

    Recommended cipher

    ciphers = HIGH:!aNULL:!MD5:!RC4


  12. foreground

    Controls whether stunnel runs as a daemon or in foreground. It should be no in production.

    Use cases

    • yes: Debugging, containers

    • no: Traditional servers


  13. debug and output

    Controls logging level and log destination.

    debug = 7

    output = /var/log/stunnel.log

    Acceptable debug values

Level

Description

0

Errors only

5

Normal

7

Very verbose

  1. pid

    Writes the process ID to a file. it is required by init systems. it helps monitoring and restarts

    Example

    pid = /var/run/stunnel.pid


  2. TIMEOUTclose

    Controls how long stunnel waits before closing connections. It avoids broken pipes and hanging sockets in long-lived connections. It prevents half-closed TCP connections. 0 disables timeout


This is all about commonly used options in stunnel configuration. Now, Let's understand the stunnel by taking couple of practical scenarios.


Scenario 1:  Client -> stunnel -> stunnel -> Plain Server

Client connecting to non TLS server using stunnel
 Client -> stunnel -> stunnel -> Plain Server


Use case

  • The backend server does not support TLS

  • You want encrypted traffic over the network

  • TLS should terminate close to the server


Typical examples:

  • Legacy Redis deployments

  • Legacy MySQL deployments

  • Custom internal TCP services


Architecture

Architecture of Client connecting to non TLS server using stunnel
Architecture of  Client -> stunnel -> stunnel -> Plain Server

How it works

  1. The client establishes a TLS connection to the first stunnel.

  2. This stunnel decrypts the traffic.

  3. Data is transmitted securely between the two stunnel instances.

  4. The second stunnel decrypts the data.

  5. The backend server receives plain TCP traffic.


The server never sees TLS at all.


Example: Securing Redis without native SSL

Client-side stunnel

client = yes
accept = 127.0.0.1:3360
connect = mysql-server.example.com:3360

cert = server.pem
key  = server.key

Server-side stunnel

client = no
accept = 0.0.0.0:3360
connect = mysql-server.example.com:3306

CAfile = ca.pem
cert = client.pem
key  = client.key

Client connection

mysql -h 127.0.0.1 -p 3360

Key takeaway

  • TLS exists only between the two stunnel instances

  • Backend remains unchanged and simple


Scenario 2: Client -> stunnel -> Secure Server


Non TLS Client connecting to TLS server using stunnel
Client -> stunnel -> Secure Server

Use case

  • The backend server already supports TLS

  • The client cannot or should not manage TLS

  • stunnel acts as a TLS client proxy


Common examples:

  • ClickHouse HTTPS port

  • PostgreSQL with SSL

  • Secure APIs

  • SMTP over SSL


Architecture


Architecture of Non TLS Client connecting to TLS server using stunnel
Architecture of Client -> stunnel -> Secure Server


  1. The client sends plain TCP traffic to stunnel.

  2. stunnel initiates a TLS handshake with the server.

  3. Traffic is encrypted before reaching the server.

  4. Responses are decrypted and sent back to the client.


Only one TLS layer exists in this design.


Example: ClickHouse with SSL

ClickHouse server configuration

<!-- <https_port>9000</https_port> -->
<https_port>9440</https_port>

stunnel configuration

client = yes
accept = 127.0.0.1:9000
connect = clickhouse.example.com:9440

CAfile = ca.pem
verifyChain = yes
checkHost = clickhouse.example.com

Client connection

clickhouse-client --host 127.0.0.1 --port 9000

(No --secure flag is needed because stunnel handles TLS.)


Key takeaway

  • TLS is handled by stunnel

  • Client remains simple and unmodified


Common mistake: Double TLS

Do not enable TLS in both stunnel and the client/server


Common mistake of stunnel configuration
Client (TLS) -> stunnel (TLS) -> Server (TLS)

This leads to errors such as:

  • packet length too long

  • record layer failure


Why?

TLS traffic wrapped inside another TLS layer appears as corrupted data.

Summary

Architecture

TLS Termination

Recommended

Client -> stunnel -> stunnel -> Plain server

Between stunnels

✅ Yes

Client -> stunnel -> TLS server

stunnel <--> server

✅ Yes

Client -> TLS server directly

Client <--> server

✅ Yes

TLS everywhere

Multiple layers

❌ No


Final Takeaways,

stunnel is a powerful and simple solution for securing TCP traffic when applications cannot handle TLS themselves. The key principle to remember is: Only one component should terminate TLS.

When used correctly, stunnel provides strong encryption, flexibility, and minimal application changes.

 
 
 

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page