SSL/TLS VPN with stunnel

From the stunnel manual page:

The stunnel program is designed to work as SSL encryption wrapper between remote clients and local (inetd-startable) or remote servers. The concept is that by having non-SSL aware daemons running on your system you can easily set them up to communicate with clients over secure SSL channels.

stunnel can be used to add SSL functionality to commonly used inetd daemons like POP-2, POP-3, and IMAP servers, to standalone daemons like NNTP, SMTP and HTTP, and in tunneling PPP over network sockets without changes to the source code.

I will use stunnel to encapsulate PPP frames into the SSL tunnel, in order to create an SSL VPN between two peers. Both peers will have two network interfaces each:

  • a eth0, or eth0-like interface, which is the native, physical, non-tunneled network interface, used by each peer to directly reach the other via a WAN, insecure connection, like the Internet or a Wireless network.

    Traffic sent directly between peers through eth0 is assumed to be sent in the clear, without confidentiality, authentication or integrity.

  • a ppp0 network interface which acts as a Layer 2 interface, using PPP framing to tunnel traffic between both peers over an insecure network connection. PPP frames will get wrapped inside an SSL/TLS session between both peers and delivered via the real eth0 interface.

    Thus, traffic sent to the ppp0 interface will get wrapped inside a PPP frame, then transported over an SSL/TLS tunnel through the eth0 insecure interface to the other peer.

    SSL/TLS adds confidentiality, authentication and integrity by means of an SSL/TLS session.

Configuring the server

The configuration file for the stunnel server will be stored in the /root/stunnel-server.conf file:

# The CA certificate file CAfile = /root/cacert.pem # The server public key certificate cert = /root/server.pem # The server private key key = /root/server.key # Name of server PID file pid = /root/server.pid # Verify peer certificate verify = 2 # The directory where all the certificates can be found # Only used when verify = 3 # CApath = /root # Some debugging stuff debug = 7 # output = /root/server.log # Use it for client mode client = no foreground = yes # Protocol Specific options # We need to specify DES-CBC3-SHA since there are some AES # ciphers that, when used with RSA, can't be decoded by ssldump ciphers = DES-CBC3-SHA:IDEA-CBC-MD5 # Service-level configuration [vpn] # incoming port number accept = 9871 # argv[0] for the PPP server exec = /usr/bin/pppd # argv[0], argv[1], ... argv[n] for the PPP server execargs = /usr/sbin/pppd debug noauth \ noaccomp noccp nodeflate nopcomp novj novjccomp \ 192.168.1.1:192.168.1.2 # Use a pty, since the PPP server will write PPP frames to a pty pty = yes

Some of the options used in this configuration file are described in the next paragraphs:

  • CAfile, cert and key point to the location of the PEM-encoded CA certificate file, PEM-encoded server public certificate and PEM-encoded server private key, respectively.
  • verify defines the level of peer certificate verification:
    1. verify peer certificate, if present
    2. always verify peer certificate
    3. verify peer with locally installed certificate

    If no verify option is supplied, no peer certificate verification is performed.

  • client = no tells stunnel this side will act as the server.
  • foreground =yes requests stunnel not to fork off and log to stderr instead of using syslog.
  • ciphers = DES-CBC3-SHA:IDEA-CBC-MD5 configures the ciphers preferred list for the server when negotiating a cipher and MAC protocol with the client.

    I have chosen DES-CBC3-SHA in first place and IDEA-CBC-MD5 in second place cause if no cipher list is supplied, OpenSSL tends to select RSA-AES-256-SHA which ssldump is, at the time of this writing, is unable to decode.

  • accept defines the port stunnel will listen to.
  • exec specifies the program to execute in order to set up the PPP server used to encapsulate traffic inside the SSL/TLS tunnel.

    In this case, we use /usr/sbin/pppd.

  • execargs defines the arguments, starting at argv[0], that will be supplied to the command specified by the exec option (in this case, /usr/sbin/pppd):
    • debug, enables debugging information
    • noauth, disables any form of PPP authentication
    • noccp novj novjccomp noaccomp, disables any form of compression.

      noaccomp disables address/control compression in both directions.
      noccp disables CCP (Compression Control Protocol) negotiation.
      nodeflate disables Deflate compression support.
      nopcomp disables protocol field compression negotiation, in both the receive and the transmit direction.
      novj disables Van Jacobson style TCP/IP header compression in both the transmit and receive direction.
      novjccomp disables the connection-ID compression option in the Van Jacobson style TCP/IP header compression.

      This will be useful when using ssldump to decode traffic between the client and the server.

    • 192.168.1.1:192.168.1.2, tells the PPP server the remote peer ppp0 interface will get assigned IP 192.168.1.2, while the server, local ppp0 interface will get assigned IP 192.168.1.1.
  • pty = yes tells stunnel to allocate a pseudo-terminal used for the exec program.

    stunnel will get traffic from the remote peer through the SSL/TLS tunnel, will decrypt it using the private key and the resulting PPP frame will be written to this pseudo-tty so the PPP server can process it and send it back to the TCP/IP stack for further processing.

    Any time the local machine sends traffic to the ppp0 interface, the PPP server capture it and encapsulate it into a PPP frame that will get written to the pseudo-tty. stunnel will see that PPP frame, will send it to the remote peer trough the SSL/TLS tunnel.

Launching the server

To make things easier, create a shell-script called launch-server.sh with the following lines:

#!/bin/sh /usr/sbin/stunnel /root/stunnel.conf

Configuring the client

The configuration file for the stunnel client will be stored in the /root/stunnel-client.conf file:

# The CA certificate file CAfile = /root/cacert.pem # The client public key certificate cert = /root/client.pem # The client private key key = /root/client.key # Name of client PID file pid = /root/client.pid # Verify peer certificate verify = 2 # The directory where all the certificates can be found # Only used when verify = 3 # CApath = /root # Some debugging stuff debug = 7 #output = /root/client.log # Use it for client mode client = yes foreground = yes # Service-level configuration # host and port where stunnel server peer is listening connect = stunnel-server:9871

Some of the options used in this configuration file are described in the next paragraphs:

  • CAfile, cert and key point to the location of the PEM-encoded CA certificate file, PEM-encoded client public certificate and PEM-encoded client private key, respectively.
  • verify defines the level of peer certificate verification:
    1. verify peer certificate, if present
    2. always verify peer certificate
    3. verify peer with locally installed certificate

    If no verify option is supplied, no peer certificate verification is performed.

  • client = yes tells stunnel this side will act as the client.
  • foreground =yes requests stunnel not to fork off and log to stderr instead of using syslog.
  • connect is used when client = yes and tells stunnel which host and port to connect to.

Launching the client

To make things easier, create a shell-script called launch-client.sh with the following lines:

#!/bin/sh /usr/sbin/pppd noauth debug dump passive noaccomp noccp \ nodeflate nopcomp novj novjccomp nodetach \ pty "/usr/sbin/stunnel /root/stunnel.conf"

This will launch the PPP client, pppd, using an SSL/TLS tunnel supplied by stunnel as the encapsulating Layer 2 tunneling protocol.

  • noccp novj novjccomp noaccomp, disables any form of compression.

    noaccomp disables address/control compression in both directions.
    noccp disables CCP (Compression Control Protocol) negotiation.
    nodeflate disables Deflate compression support.
    nopcomp disables protocol field compression negotiation, in both the receive and the transmit direction.
    novj disables Van Jacobson style TCP/IP header compression in both the transmit and receive direction.
    novjccomp disables the connection-ID compression option in the Van Jacobson style TCP/IP header compression.

    This will be useful when using ssldump to decode traffic between the client and the server.

  • debug, enables debugging information
  • noauth, disables any form of PPP authentication
  • passive, enables the passive option in the LCP.

    With this option, pppd will attempt to initiate a connection. If no reply is received from the peer, pppd will then just wait passively for a valid LCP packet from the peer, instead of exiting, as it would do without this option.

  • nodetach, tells pppd to not detach from the controlling terminal. Useful to keep pppd in the foreground, so it can get stopped with a SIGINT signal.
  • pty, specifies the command script used to communicate rather than a specific terminal device. pppd will allocate itself a pseudo-tty master/slave pair and use the slave as its terminal device. The script will be run in a child process with the pseudo-tty master as its standard input and output.

    The command script is just the invocation to stunnel, in order to encapsulate all the PPP frames generated by pppd inside the SSL/TLS tunnel.

Testing connectivity

After launching the client, a new ppp0 interface should be automatically configured with the following parameters:

ppp0      Link encap:Point-to-Point Protocol
          inet addr:192.168.1.2  P-t-P:192.168.1.1  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:3 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:3
          RX bytes:30 (30.0 b)  TX bytes:30 (30.0 b)

A simply ping 192.168.1.1 command executed from the client should suffice to trigger traffic through the SSL/TLS tunnel and test connectivity.

One Response to “SSL/TLS VPN with stunnel”

  1. [...] Setup a PPP VPN through STunnel: http://felipe-alfaro.org/blog/2005/11/18/ssltls-vpn-with-stunnel/ [...]

Leave a Reply