Use stunnel with certificate auth to secure any TCP connection

In some cases, we need to secure some custom TCP communications which NOT supported TLS/SSL protocols.  Use stunnel with self-signed certificates, we can secure the connection and authorise  the identities of the server/clients.  And it can also hide the connection characteristics in the TLS wrapper, which has a legally standard TLS cert-handshake process. For further privacy concerns, we may need extra characteristics eliminate method to avoid the side-channel attack,  for example, statistic characteristics of TLS-handshake in another TLS connection which implied a TLS proxy.


Case: hide a ssr connection in a stunnel TLS channel with cert-auth.

I set up it on a centos-7 VPS machine. for better obfuscation, it is strongly recommend to buy a domain name, any kind is OK, as cheap as possible, but don’t forget to get a domain name privacy-protect to hide your information.

First, set a sub-domain of this domain name( like and resolve it to your VPS. This sub-domain is better to be named less sensitive for any outside observers, just look like an auto-sync scheme or other self control system. Here I set up an example like “” and resolve to the IP address.


1, Set up ssr-server

Set up a ssr-server, and DO NOT use the 443 or 80 port. choose the “auth_chain-a” protocol for better obfuscation. (Because it run in the TLS channel, there is no need for encrypt and obfs-setting, choose “none” and “plain”)

 You can use a one-click install script to do this, and remember this results like: 

Your Server IP :
Your Server Port : 8888
Your Password : 12345678
Your Protocol : auth_chain_a
Your obfs : plain
Your Encryption Method: none

(If you don’t want to use the ss-r directly or use weak password, you’d better close the port on for better security)


2, Set up Stunnel on server

Install stunnel

yum install stunnel 



Create Self-signed certificates 

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/stunnel/server.key -out /etc/stunnel/server.crt


The cert info  “Common Name” must be the sub-domain of the VPS address, like “”,the others can be anything. Now you have a cert for  “”, which looks like the company issued to identify their own file server. Because this cert can be seen as plain text in the TLS-handshake process, It may need to be less strange.

 merge these two files to one server.pem (private key above, cert below) 


If you need server to check the client’s cert, make a cert for client too.

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/stunnel/client.key -out /etc/stunnel/client.crt

You can set anything in this cert.

 merge these two files to one client.pem (private key above, cert below) 

and DO NOT use these pem files to check server/client because it contain private keys. Use server’s cert file (server.crt) for client to verify the server and use the client’s cert for server. Save the server.crt to server-check.pem and the client.crt to client-check.pem. 


Save these files on the server’s /etc/pki/tls/certs/ , like:



Edit the conf file in /etc/stunnel/stunnel.conf

// serverside conf
cert = /etc/pki/tls/certs/serverside.pem
key = /etc/pki/tls/certs/serverside.pem
client = no
; Allow only TLS, thus avoiding SSL
sslVersion = TLSv1
;chroot = /var/run/stunnel
setuid = root
setgid = root
pid = /
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1

[service load]
accept = 443
connect = 8888
TIMEOUTclose = 10

verify = 4
CAfile = /etc/pki/tls/certs/clent-check.pem


The “connect = 8888” is to connect the ssr’s port on local (, and the “accept = 443″ is to accept TLS connection from client on WAN ( ,”verify = 4” is to check the client’s cert.


Check the Iptables or firewalld settings to allow input on port 443.


Setup the stunnel as a service:  

Edit a service script  “stunnel.service”  in  /etc/systemd/system/


ExecStart=/bin/stunnel /etc/stunnel/stunnel.conf
ExecStop=kill -9 $(pgrep stunnel)
ExecStatus=pgrep stunnel



Start the service with systemctl:

systemctl start stunnel

Check status :

systemctl status stunnel


When the stunnel service is running, the server-side’s conf is OK.


3, Set up the stunnel client (Windows)

Install the windows-client, and edit the conf file stunnel.conf. (Put the pem files “client.pem” and “server-check.pem” along with the conf file in the same dir.

sslVersion = TLSv1
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
[remote tls]
client = yes
accept =
connect =
verify = 4
cert = client.pem
key = client.pem
CAfile = server-check.pem


The “accept=” is to accept TCP connection locally on port 12345, which is for ssr-client to send its traffic via stunnel.  “connect =” is to connect remote server’s port 443.


4,Set up the ssr-client

Set up the ssr client(windows, for example) like this:


Server IP :   (connect to local stunnel port)
Server Port : 12345 (connect to local stunnel port)
Password : 12345678
Protocol : auth_chain_a
obfs : plain
Encryption Method: none


and set the ssr’s listening port on, to use it, connect proxy on (socks5 or other).


If it is working, the ssr’s TCP traffic is tunnelling in the TLS wrappers. 


browser—-socks5:—-ssr—-connect—–stunnel—–connect remote:443—–remote server connect—-remote ssr—-Internet.