Setting up an encrypted SOCKS proxy using Dante and stunnel

Overview

Why Dante?

In the previous post, I talked about why we might need a SOCKS proxy at all, and how we can properly setup a secure one using only stunnel.

That approach is fine and all, but it still suffers from some limitations. The most important of which are:

  • UDP relaying is not supported.
  • Advanced SOCKS options like BIND or UDPASSOCIATE is not available.
  • Only suited for personal use and should not be shared with untrusted clients.

Comparing to the stunnel limited SOCKS functionality, Dante (which is one of the most popular SOCKS server available), comes with pretty much every functionality one can imagine out of a SOCKS server.

From advanced authentication and access control, to server chaining, traffic monitoring and even bandwidth control1, Dante has got them all.

Dante and SOCKS encryption

While it might be okay to use a non-encrypted SOCKS proxy in you local network, it is definitely not a good idea to do so over the internet.

For this, RFC 1961 added GSS-API authentication protocol for SOCKS Version 5. GSS-API provides integrity, authentication and confidentiality. Dante of course completely supports GSS-API authentication and encryption.

But GSS-API (which is typically used with Kerberos), could be overkill for a simple setup. It might actually not make much sense to set one up, only later to be used for SOCKS authentication and encryption. Not to mention that as far as I can tell, the older version of SOCKS (namely Version 4), can not be secured by this mean at all.

In this post, I’m going to show you how to combine the power of Dante and stunnel together, and get the best out of both worlds.

For this setup I’ll be using Ubuntu 18.04 Server, but any other decent distro should do just fine.

Installing Dante

First thing first, you need to install Dante. In Debian-based distros, we use apt:

apt-get update && apt-get install dante-server

In Debian-based distros, Dante is split into two packages:

dante-server, which is the SOCKS server

dante-client, which is used to “socksify” client programs

For this guide you only need dante-server.

While installing the dante-server, you may encounter an error message like this:

Setting up dante-server (1.4.2+dfsg-2build1) ...
Job for danted.service failed because the control process exited with error code.
.
.
May 23 18:08:57 dante-test danted[1689]: May 23 18:08:57 (1558634937.202673) danted[1689]: warning: checkconfig(): no socks authentication methods enabled.  This means all socks requests will be blocked after negotiation.  Perhaps this is not intended?
May 23 18:08:57 dante-test danted[1689]: May 23 18:08:57 (1558634937.202773) danted[1689]: error: checkconfig(): no internal address given for server to listen for clients on
May 23 18:08:57 dante-test danted[1689]: May 23 18:08:57 (1558634937.202798) danted[1689]: alert: mother[1/1]: shutting down
May 23 18:08:57 dante-test systemd[1]: danted.service: Control process exited, code=exited status=1
May 23 18:08:57 dante-test systemd[1]: danted.service: Failed with result 'exit-code'.
May 23 18:08:57 dante-test systemd[1]: Failed to start SOCKS (v4 and v5) proxy daemon (danted).
Please edit the Dante server config file /etc/danted.conf and specify at least the following directives: internal external

Don’t worry about it for now. This happens because the installation script tries to run the recently-installed Dante daemon, but Dante config file lacks some required parameters, and so it errors out. We will fix this soon enough.

Setting up Dante

The Dante server process (which is called danted), reads its config file from /etc/danted.conf .

Dante server is an advanced SOCKS server. It has a wide variety of different config options. Dante documentation contains all the little details that you might need to fully set it up and take advantage of its full potential.

But for this relatively simple setup, we just need to adjust couple of different settings and we’ll be good to go:

Default settings

There are also couple of default settings in the danted.conf file which we will leave as they are. At the time of writing, these are the following:

# the server will log both via syslog, to stdout and to /var/log/sockd.log
#logoutput: syslog stdout /var/log/sockd.log
logoutput: stderr

# when doing something that can require privilege, it will use the
# userid:
user.privileged: proxy
  
# when running as usual, it will use the unprivileged userid of:
user.unprivileged: nobody
  
# If you are not using libwrap, no need for the below line, so leave
# it commented.
# If you compiled with libwrap support, what userid should it use
# when executing your libwrap commands?  "libwrap".
#user.libwrap: libwrap
user.libwrap: nobody

clientmethod

Since all connections to the Dante server will be initiated by the stunnel and from the localhost, using clientmethod doesn’t make much sense and it should be set to none:

# methods for client-rules.
clientmethod: none

socksmethod

In this setup, stunnel will be handling clients authentication, so we set socksmethod to none as well:

# methods for socks-rules.
socksmethod: none

While in this article we will primarily rely on stunnel for both server and client authentication, it is also possible to use socksmethod in conjunction with stunnel (For example using the username method to match against the system password file).

You may go for such approach if you want to rely on stunnel for server authentication and traffic encryption only. In such case, socksmethod could be used to authenticate clients on the SOCKS level.

Furthermore, since stunnel will be protecting the underlying SOCKS traffic, if implemented correctly, such approach would be immune to eavesdrop.

internal

The address and port that the Dante server will listen on.

Since we only want to connect to the Dante server through local stunnel service, we set its binding address to 127.0.0.1. The port that we’ll be using here, is merely a local one that will be shared between Dante and stunnel. To be more precise, It is NOT the port facing the public (That will be done in stunnel config). So we’ll be using some random >1024 port like 9898:

# The server will bind to the localhost, port 9898
# It will only accept connections going to that address/port.
internal: 127.0.0.1 port = 9898

external

Dante sever requires us to also specify the address for the outgoing connections. This is particularly useful if you have a server with multiple external addresses. If your server does not use a static address however, you may use the interface name here instead (in our case, eth0):

# all outgoing connections from the server will use the IP address
# of eth0 interface
external: eth0

Access rules

These are the rules required for giving stunnel proper access to Dante server as well as further securing it:

After getting familiar with these rules, you can adjust them to fit your needs.
# Allow localhost (stunnel) connections
client pass {
        from: 127.0.0.1/32 to: 127.0.0.1/32
}

# Block and log the rest of connection attempts
client block {
        from: 0.0.0.0/0 to: 0.0.0.0/0
        log: connect error
}

# Blocking clients access to the localhost services
socks block {
        from: 0.0.0.0/0 to: lo
        log: connect error
}

# Allow clients access to the outside - tcp 80 using "connect" method
socks pass {
        from: 127.0.0.1/32 to: 0.0.0.0/0 port = 80
        command: connect
        protocol: tcp
}
 
# Allow clients access to the outside - tcp 443 using "connect" method
socks pass {
        from: 127.0.0.1/32 to: 0.0.0.0/0 port = 443
        command: connect
        protocol: tcp
}

# Block and log all other clients attempts
socks block {
        from: 0.0.0.0/0 to: 0.0.0.0/0
        log: connect error
}

Testing Dante

Now Dante server should be ready. Restart the service and this time you should get no error:

sudo systemctl restart danted.service

At this point and before going any further, it is a good idea to ensure our setup is actually working. We can test that by issuing a simple curl command on the server:

curl --proxy 'socks5h://127.0.0.1:9898' 'https://api.ipify.org/'

If everything goes fine, This command should return the server’s public IP address.

Installing and setting up stunnel

I have a whole article on how to install and configure stunnel on Ubuntu, so I’m not going to repeat myself here.

After installing stunnel, little needs to be done to make the whole thing work together2:

Server side

Here’s a stunnel.conf sample file for the server:

setuid = stunnel4
setgid = stunnel4

pid = /var/run/stunnel4/stunnel.pid
output = /var/log/stunnel4/stunnel.log

; https://www.stunnel.org/faq.html
socket = l:TCP_NODELAY=1

debug = 4

[dante-server]
accept = 1081
connect = 127.0.0.1:9898
PSKsecrets = /etc/stunnel/stunnel.secrets

In the above example, we assumed clients are connecting to the server on port 1081, and also using PSK secret keys (IDENTITY:KEY) stored in /etc/stunnel/stunnel.secrets .

Client side

It goes without saying that Dante server is only needed on the server and you do not need to install it on clients. The only program that clients need, is stunnel.

Here’s a stunnel.conf sample file for the clients:

setuid = stunnel4
setgid = stunnel4

pid = /var/run/stunnel4/stunnel.pid
output = /var/log/stunnel4/stunnel.log

; https://www.stunnel.org/faq.html
socket = r:TCP_NODELAY=1

debug = 4

[socks]
client = yes
accept = 127.0.0.1:1080
connect = <your-server-ip>:1081
PSKsecrets = /etc/stunnel/stunnel.secrets

stunnel would listen on localhost port 1080 (The standard SOCKS port), and then encrypts and forwards your traffic to the server. /etc/stunnel/stunnel.secrets should contain a shared IDENTITY:KEY with the server.

Final Testing

You now should be able to connect to the stunnel server using your client (which may even be an Android Phone!).

Any program that supports SOCKS, should be able to work with such setup (including pretty much all decent browsers).

You may use this curl command on the client side to check your overall SOCKS setup:

curl --proxy 'socks5h://127.0.0.1:1080' 'https://api.ipify.org/'

If everything goes fine, this should print out the same IP address as the one you’ve got in the Testing Dante section.

Troubleshooting

There couple of things that you can do to track down any potential issues:

Dante

For Dante, you can activate logging of each client connection. You do so by adding the log: option in the Access rules section. You can even enable the debug mode if needed. See Dante documentation (or man danted.conf) for more information.

You may also decide to stop the danted.service and manually run the danted process in the foreground to see the log output (or you may use journalctl -xeu danted.service for that).

stunnel

For stunnel, you can also increase the debug level and check its log file.

Again, you may also stop stunnel4.service service and make it run in the foreground to see the log output3.


  1. Dante’s Bandwidth control module, is a separate commercial product. ↩︎

  2. Don’t forget that after adjusting stunnel config files, you need to restart the stunnel4.service (Again, everything you need to know about how to install and setup stunnel, is provided Here. Please read it carefully). ↩︎

  3. Just don’t forget to remove the foreground option before running it as a service again. ↩︎

Hamy
Hamy
a sysadmin in the wind
comments powered by Disqus

Related