Stunnel is one of those tools I consider quietly essential — it takes any plain, unencrypted TCP-based service and wraps it in a proper SSL/TLS tunnel without requiring the underlying application to natively support encryption at all. I use it both defensively (protecting legacy services during a client’s remediation window) and during authorized testing (setting up encrypted channels for tooling that doesn’t natively support TLS).
What Stunnel Is and How It Works
Stunnel is a proxy that sits in front of (or behind) a TCP service and handles the SSL/TLS handshake and encryption/decryption on its behalf. It operates in one of two primary modes:
- Server mode — Stunnel listens for incoming encrypted connections, decrypts them, and forwards the plaintext traffic to a local (or remote) unencrypted service, such as an old POP3 or SMTP daemon that has no built-in TLS support.
- Client mode — Stunnel accepts local plaintext connections from an application and encrypts them before sending them out to a remote TLS-enabled endpoint (which could be another Stunnel instance, or any standard TLS service).
Internally, it’s built on OpenSSL, so it inherits mature, well-tested cipher suite support, certificate validation, and protocol version control. Configuration is handled through a straightforward INI-style config file, with each tunnel defined as its own named section specifying accept/connect addresses and certificate details.
Installing Stunnel
On Debian/Ubuntu/Kali:
sudo apt update
sudo apt install -y stunnel4
Verify:
stunnel -version
Enable the service to actually start (on Debian-based systems it’s disabled by default until configured):
sudo systemctl enable stunnel4
Basic Configuration and Syntax
Stunnel is driven by a config file, typically at /etc/stunnel/stunnel.conf:
; Server mode example - wrap a plaintext service (e.g. a legacy telnet-based service on 2323)
[legacy-service]
accept = 0.0.0.0:9999
connect = 127.0.0.1:2323
cert = /etc/stunnel/stunnel.pem
; Client mode example - encrypt outbound traffic to a remote TLS-enabled Stunnel server
[client-tunnel]
client = yes
accept = 127.0.0.1:8080
connect = remote-server.lab.local:9999
Generating a self-signed certificate for lab/testing use:
openssl req -new -x509 -days 365 -nodes \
-out /etc/stunnel/stunnel.pem \
-keyout /etc/stunnel/stunnel.pem
Starting Stunnel with a specific config:
sudo stunnel /etc/stunnel/stunnel.conf
Real Example (Authorized Lab Environment)
Server side (wrapping a plaintext service running on local port 2323):
$ cat /etc/stunnel/stunnel.conf
[legacy]
accept = 9999 connect = 2323 cert = /etc/stunnel/stunnel.pem $ sudo stunnel /etc/stunnel/stunnel.conf
Client side, connecting through the encrypted tunnel:
$ cat client.conf
[legacy-client]
client = yes accept = 127.0.0.1:2323 connect = labserver.local:9999 $ stunnel client.conf $ telnet 127.0.0.1 2323
That final telnet command now rides entirely inside an SSL/TLS tunnel — the plaintext-looking Telnet session is actually encrypted end-to-end between the two Stunnel instances.
Real-World Use Cases
Protecting legacy services during a remediation window — after a penetration test flags an unencrypted service (old mail protocols, legacy database ports) that can’t be immediately replaced, Stunnel provides an interim encryption layer while a proper long-term fix is scheduled.
Securing custom tooling in authorized red team infrastructure — wrapping command-and-control or file transfer traffic used during an authorized engagement in TLS, so traffic doesn’t traverse client networks in plaintext even when the tool itself lacks native TLS support.
VPN alternative for simple point-to-point encrypted links — setting up a lightweight encrypted tunnel between two hosts for a specific service without deploying a full VPN solution.
Integration with Other Tools
- udptunnel — since Stunnel is TCP-only, I pair it with udptunnel when the traffic that needs encrypting is UDP-based (tunnel UDP into TCP first, then wrap that TCP stream in Stunnel).
- sslh — used together when a single exposed port needs to multiplex both TLS-wrapped traffic and other protocols (see the sslh article) — sslh handles the port sharing, Stunnel handles the actual TLS termination.
- Nagios/monitoring tools — commonly placed in front of legacy monitoring agents that don’t support TLS natively, to secure the monitoring data in transit.
Performance and Troubleshooting
- Certificate/key mismatches are the most common startup failure — always verify with
openssl x509 -in stunnel.pem -text -nooutthat the certificate loads correctly before assuming a networking issue. - Use
-fdand foreground/debug options (stunnel -fdor settingdebug = 7in the config) to get verbose logging when a handshake is failing. - A mistake I’ve made: forgetting
client = yeson the client-side config, which causes Stunnel to instead try to run in server mode and fail to establish the intended outbound tunnel.
Best Practices
- Use properly issued certificates (not self-signed) for anything beyond an isolated lab, and enable certificate verification (
verify = 2) rather than leaving connections unauthenticated. - Restrict
acceptbindings to specific interfaces rather than0.0.0.0where possible, to avoid unintentionally exposing a tunnel endpoint to unintended networks. - Keep Stunnel and its underlying OpenSSL library updated, since it directly handles security-critical TLS negotiation.
FAQ
Does Stunnel replace a VPN? For simple point-to-point service encryption, yes; for full network-level tunneling of many services and routing, a proper VPN solution is generally more appropriate.
Can Stunnel wrap UDP services? Not directly — Stunnel is TCP-only; UDP traffic needs to be converted to TCP first (e.g., with udptunnel) before Stunnel can wrap it.
Is Stunnel still relevant given how many services support native TLS now? Yes, especially for legacy systems, internal tooling, and custom protocols that were never built with TLS support, plus authorized security testing scenarios needing quick ad hoc encrypted channels.
Summary
Stunnel remains one of the simplest, most reliable ways to bolt proper SSL/TLS encryption onto a service that wasn’t built with it — whether that’s protecting a legacy production system during a remediation window or securing custom tooling used during an authorized security engagement.
References
- Official project site: https://www.stunnel.org
- GitHub mirror: https://github.com/mtrojnar/stunnel
- Man page:
man stunnel