Tuesday, September 16, 2008

Stunnel

I mentioned stunnel in my Netcat post before. Stunnel is short for "universal SSL tunnel". It is a great utility for securing TCP/IP and HTTP(s) connections when an application doesn't have the ability (or doesn't want to deal with) secure transport layer security.

http://www.stunnel.org/

I use stunnel frequently when I want to trap an HTTPS request then replay it to another server. I can't just trap the HTTPS data as it is encrypted. Therefore, I modify my client to use HTTP, trap the plain-text HTTP request, then use stunnel to do the HTTPS for me.

Stunnel is available for immediate download for *nix, Cygwin and a native Windows port.

Configuring Stunnel
Stunnel 4.x is configured via a conf file which is specified as the main parameter on the command line (stunnel 3.x uses cmd-line options to configure it.)
$ stunnel my.conf
The configuration file stunnel uses is broken into two main parts -- Global Options and Service-Level Options.

Global Options dictate how stunnel behaves such as forked or not, logging location, logging levels, etc. Common Global options are:
  • debug = 0-7, where 0=[emergency], 7=[debug] . The default is 5, [notice]
  • foreground = yes|no. This dictates whether or not stunnel will fork the process into the background or stay in the foreground.
  • output = somefile. This is where output goes. /dev/stdout indicates to just output to STDOUT.
  • pid = somefile. If present, the name of the file to write the background process' pid.
  • taskbar=yes|no. Win32 only -- shows a taskbar icon you can use to control the running instance.
Service-Level options are for individual protocols such as https, imap, etc, and control the way the forwarding/proxying behave. Common service-level options are:
  • accept=port #. This is what port incoming connections will be accepted on. Only a single value can be given, but you are free to create multiple services as the following incomplete example shows:
    [https1]
    accept=9000
    connect=someserver:90

    [https2]
    accept=9001
    connect=someserver:91
  • cert=somecert.pem. This specifies where your certificate resides. In client mode (client=yes), this cert will be used for 2-way SSL.
  • connect=[host:]port. Where the backend resides.
  • key=keyfile.pem. This is the private key to be used for serving up SSL connections.
These are the only options we'll use in the examples below. RTFM for the other options. :)

Examples
1) Proxying Plain Text HTTP client Traffic to HTTPS Server
I use this feature a lot to debug client-side HTTP issues and to see the exact HTTP message on-the-wire. Basically, this is handy to do HTTP from your client, but convert to HTTPS before hitting the server.

All this entails is doing "pseudo-https" with the following stunnel configuration:
$ cat https.conf
foreground = yes
output = /dev/stdout
debug = 7

[psuedo-https]
accept = 9443
connect = localhost:443
client = yes
Then, your client can hit http://localhost:9443 which will be proxied to localhost:443 over SSL.

2) Creating an HTTPS Listener which Proxies to non-HTTP server
Requirement -- PEM-encoded file private key with signed certificate. The private key should not have a password on it. Both the private key AND the cert should be in the PEM-encoded file.

The stunnel conf looks like this to proxy incoming HTTPS requests to your local JBoss/Jetty/Tomcat service:
$ cat accept-https.conf
debug=7
output=/dev/stdout
foreground=yes

[stunnel]
cert=MyKeyFileWithCert.pem
accept=443
connect=8080
When you start stunnel with "stunnel accept-https.conf", you can test it with:
$curl --insecure https://localhost
Note that the "--insecure" option may be needed if the stunnel.pem file contains a cert signed by a non-trusted certificate authority. Likewise, in IE or Firefox, you'll need to add a security exception in order to test.

3) Load Balancing Incoming Connections
If multiple "connect" options are given for a service, then a round-robin algorithm is used to load-balance the back-end requests.

The following configuration will load balance incoming HTTP connects on port 80 to HTTPS ports 9080 and 9081.
$ cat loadbalance.conf
foreground=yes
output = /dev/stdout
debug = 7

[load-balance]
client=yes
accept = 80
connect = localhost:9080
connect = localhost:9081

No comments: