Encrypted SOCKS Proxy

Stunnel can carry SOCKS 4, SOCKS 4a, or SOCKS 5 requests inside TLS. This configuration is sometimes called a SOCKS VPN, but it is an encrypted proxy rather than a full IP-layer VPN.

Each proxied TCP connection gets its own TLS connection:

application -> local SOCKS listener -> TLS -> stunnel SOCKS server -> destination

The TLS tunnel hides the SOCKS request and application traffic from observers between the client and the stunnel server. Local DNS queries or other side channels may still disclose the destination. The server sees the destination and forwards plaintext traffic beyond the TLS tunnel unless the application uses its own end-to-end encryption, such as HTTPS or SSH.

Scope and Limitations

Stunnel's SOCKS service supports:

It does not provide:

The SOCKS USERID field is ignored. Authenticate access at the TLS layer with a unique PSK or a client certificate.

For most deployments, configure applications to use an explicit local SOCKS 5 proxy. This mode works without firewall redirection or administrative privileges and is available on every platform supported by the relevant stunnel build.

Recommended PSK Configuration

PSK is convenient for a closed set of clients. Give every client a distinct random key so one compromised credential can be revoked without replacing all clients.

Generate a 32-byte hexadecimal key for each client:

openssl rand -hex 32

The server secrets file contains every permitted identity:

laptop:8f3f26f0d65b8f8c1d690de3f9fdd0e2d1c261573f7557406f068c3f8a355f78
phone:54f47f783aeb9ba534e85ad0c6f50ee343e7be79eecfc338de67a28a66734a33

Each client file contains only that client's entry. Protect both files:

chmod 600 /etc/stunnel/socks-server.psk
chmod 600 /etc/stunnel/socks-client.psk

Transfer secrets through an authenticated confidential channel and keep them out of source control, command histories, and logs.

Server

foreground = yes
debug = info

[encrypted-socks]
accept = 0.0.0.0:9080
protocol = socks
PSKsecrets = /etc/stunnel/socks-server.psk
sslVersionMin = TLSv1.3

Use accept = :::9080 for IPv6. Remove foreground = yes when a service manager runs stunnel.

A SOCKS server chooses its backend from each SOCKS request, so this service intentionally has no connect option.

Client

foreground = yes
debug = info

[encrypted-socks]
client = yes
accept = 127.0.0.1:1080
connect = proxy.example.com:9080
PSKsecrets = /etc/stunnel/socks-client.psk
PSKidentity = laptop
sslVersionMin = TLSv1.3

Binding to 127.0.0.1 prevents other hosts from using the local plaintext SOCKS listener. Add a separate ::1:1080 listener only when local IPv6 access is needed and supported by the application.

The explicit client section intentionally omits protocol = socks: the application creates the SOCKS request, and the client-side stunnel carries it unchanged inside TLS. The server-side protocol = socks handler parses the request and selects the destination.

Current stunnel and OpenSSL releases can negotiate TLS 1.3 PSK without a legacy ciphers = PSK setting. A deployment that requires TLS 1.2 needs a separately reviewed cipher policy.

Testing the Explicit Proxy

Configure an application for SOCKS 5 at 127.0.0.1:1080. Ensure that it sends hostnames to the proxy rather than resolving them locally.

With curl:

curl --socks5-hostname 127.0.0.1:1080 https://www.example.com/

The --socks5-hostname form performs DNS resolution on the SOCKS server. In contrast, --socks5 normally resolves the hostname on the client and may expose DNS queries outside the tunnel.

SOCKS 4a can also carry a hostname to the server:

curl --socks4a 127.0.0.1:1080 https://www.example.com/

During initial testing, keep foreground = yes and debug = info. After confirming authentication and forwarding, return logging to its normal notice level.

Certificate-Based Authentication

Certificates are preferable when the deployment already has a PKI or uses hardware-backed keys.

Server with mutual TLS

[encrypted-socks]
accept = 0.0.0.0:9080
protocol = socks
cert = /etc/stunnel/server-fullchain.pem
key = /etc/stunnel/private/server.key
verifyChain = yes
CAfile = /etc/stunnel/client-ca.pem
sslVersionMin = TLSv1.2

verifyChain = yes requires clients to present a certificate issued by the configured client CA.

Client

[encrypted-socks]
client = yes
accept = 127.0.0.1:1080
connect = proxy.example.com:9080
cert = /etc/stunnel/client-fullchain.pem
key = /etc/stunnel/private/client.key
verifyChain = yes
CAfile = /etc/stunnel/server-ca.pem
checkHost = proxy.example.com
sni = proxy.example.com
sslVersionMin = TLSv1.2

Use a distinct certificate and private key for every client. See the authentication guide for certificate issuance, pinning, revocation, providers, and rotation.

DNS Privacy

An encrypted SOCKS connection protects DNS only when the application sends a hostname through SOCKS 4a or SOCKS 5. Applications that call the local resolver first still generate local DNS traffic.

For explicit proxy mode, enable the application's “proxy DNS,” “remote DNS,” or equivalent option. For software that cannot proxy DNS, configure a separate authenticated encrypted DNS solution appropriate for the operating system and network. Stunnel's SOCKS transport does not intercept UDP DNS automatically.

Transparent TCP interception occurs after the application has usually resolved the destination, so it does not by itself prevent DNS leaks.

Server Security

A publicly reachable SOCKS service without TLS client authentication is an open proxy. Require PSK or mutual TLS before exposing it outside a trusted network.

The SOCKS server can make outbound connections on behalf of authenticated clients. Treat that capability as privileged access:

Stunnel rejects SOCKS requests for its own IPv4 and IPv6 loopback addresses, but this is not a complete egress policy. Other private, local, or infrastructure addresses require firewall controls.

The proxy server is not an anonymity service. It can associate authenticated clients with destinations, and destination services see the proxy server's address.

Transparent TCP Interception on Linux

Transparent mode redirects selected local TCP connections to stunnel and derives the original destination from the redirected socket. It is more complex and less portable than explicit SOCKS configuration.

Use a dedicated unprivileged account for stunnel and a different account for applications whose traffic will be redirected. Firewall administration requires root, but stunnel itself does not need to run as root merely to accept an unprivileged loopback port.

Stunnel client

Keep the explicit listener for applications that support SOCKS and add separate transparent listeners:

[encrypted-socks]
client = yes
accept = 127.0.0.1:1080
connect = proxy.example.com:9080
PSKsecrets = /etc/stunnel/socks-client.psk
PSKidentity = laptop
sslVersionMin = TLSv1.3

[transparent-socks-ipv4]
client = yes
accept = 127.0.0.1:9051
connect = proxy.example.com:9080
protocol = socks
PSKsecrets = /etc/stunnel/socks-client.psk
PSKidentity = laptop
sslVersionMin = TLSv1.3

[transparent-socks-ipv6]
client = yes
accept = ::1:9052
connect = proxy.example.com:9080
protocol = socks
PSKsecrets = /etc/stunnel/socks-client.psk
PSKidentity = laptop
sslVersionMin = TLSv1.3

In client mode, protocol = socks builds a SOCKS 5 CONNECT request from the original destination preserved by the firewall redirect.

Illustrative nftables policy

The following example redirects TCP connections created by one local user. It deliberately bypasses local networks and the proxy server itself. Replace all numeric user IDs and proxy addresses before loading it.

table inet stunnel_socks {
    define stunnel_uid = 995
    define tunneled_uid = 1000
    define proxy_ipv4 = 192.0.2.10
    define proxy_ipv6 = 2001:db8::10

    chain output {
        type nat hook output priority dstnat; policy accept;

        # Never redirect stunnel's own connection to the remote proxy.
        meta skuid $stunnel_uid return

        # Keep loopback, local, multicast, and the proxy endpoint outside
        # the tunnel. Adjust these exclusions to the local security policy.
        ip daddr {
            0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8,
            169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16,
            224.0.0.0/4, 240.0.0.0/4, $proxy_ipv4
        } return
        ip6 daddr {
            ::/128, ::1/128, fc00::/7, fe80::/10, ff00::/8,
            $proxy_ipv6
        } return

        meta skuid $tunneled_uid meta nfproto ipv4 \
            meta l4proto tcp redirect to :9051
        meta skuid $tunneled_uid meta nfproto ipv6 \
            meta l4proto tcp redirect to :9052
    }
}

This is a policy example, not a drop-in firewall script. Integrate equivalent rules into the system's firewall manager, verify nftables syntax on the target release, and keep an administrative session outside the affected user account while testing. Exclude every address to which the proxy hostname may resolve. An incorrect catch-all redirect can break networking or create a proxy loop.

Destinations returned by the exclusion rules connect directly rather than through the proxy. If local policy requires them to be blocked, enforce that in the firewall instead of using these bypass rules.

The example affects only locally generated TCP connections for one user. Gateway traffic requires a separate forwarding, prerouting, routing, and security design and is intentionally not covered here.

Transparent mode does not carry UDP, including QUIC and most conventional DNS traffic. Applications may need QUIC disabled so they fall back to TCP.

Troubleshooting

TLS authentication fails

Confirm that the client and server use the same PSK identity and key, or diagnose certificate validation as described in the FAQ. Check file ownership and permissions before increasing the log level.

The explicit SOCKS listener works, but transparent mode does not

Verify that:

Hostnames work with one application but leak DNS with another

The applications are using different SOCKS DNS behavior. Select remote hostname resolution explicitly or use a separate encrypted DNS resolver.

The TLS tunnel succeeds, but a destination is unreachable

Check DNS and outbound firewall policy on the SOCKS server, then test whether the server can reach the destination directly. The stunnel SOCKS implementation does not report every backend connection failure perfectly to all SOCKS clients, so server logs are important.

Operational Checklist

Our supporters:
Go to the top