OpenVPN as a Forking TCP Server Which Can Service Multiple Clients Over a Single TCP Port

WARNING: this article is very outdated and refers to obsolete version of OpenVPN. This article is kept here purely for historical reference.

See the OpenVPN 2.0 release notes.

Also:

Here at work, we wanted to implement a VPN solution for Roadwarriors who have to connect to the main office. OpenVPN looked like a nice solution, as it also works with Windows and can tunnel through an https proxy.

One problem though, was the inability to use a constant port number on the server side.

The --inetd option seemed very promising, but the man-page explicitly states that this option cannot be used for multiple connections, although there seemed to be no reason. (The only reason I found mentioned was that there was no config file templating mechanism, which obviously isn't really a problem).

A test later I found, that the code was written for inetd with "wait=yes" in mind, but that was quickly changed (see attached patch).

The attached patch (merged with OpenVPN as of 1.6-beta5) adds a new option --inetd nowait which makes OpenVPN work with a connected socket on stdin, like (x)inetd does with wait=no, or even netcat with the -e option.

Of course, this still runs one OpenVPN process per client. You still need one tap device per client, and configure bridging between them.

This also only works with SSL/TLS and tap devices, because of the single config file shared between all server processes.

The server config file in our case is easy:

# OpenVPN multiple-client-server
inetd nowait
proto tcp-server

# 10.254.0.1 is our local VPN endpoint (office).
dev tap
ifconfig 10.254.0.1 255.255.255.0

# Our up script will establish routes once the VPN is alive.
ifconfig-noexec
up /etc/openvpn/vpn-server.up

# We are SSL/TLS Server
tls-server
dh /etc/openvpn/dh2048.pem
ca /etc/openvpn/my-ca.crt
crl-verify /etc/openvpn/my-ca.crl
cert /etc/openvpn/server.crt
key /etc/openvpn/server.key

The only magic thing is the ifconfig-noexec. Of course, we can't ifconfig each of the tap interfaces with the same IP address. But we don't need to, as we are using bridging.

My Bridging setup script (run once at bootup):

# load Bridging-Module
modprobe bridge

# configure bridge
brctl addbr br0
brctl stp br0 off
brctl setfd br0 0

# our private vpn network
ifconfig br0 10.254.0.1 netmask 0xffffff00 broadcast 10.254.0.255

The per-client setup script (vpn-server.up):

# add interface to bridge and activate
brctl addif br0 $1
ifconfig $1 up

And for completeness sake, here is the xinetd config file for the OpenVPN server:

service openvpn_1
{
        type               = UNLISTED
        port               = 443
        socket_type        = stream
        protocol           = tcp
        wait               = no
        user               = root
        server             = /usr/sbin/openvpn
        server_args        = --config /etc/openvpn/vpn-server.conf
        disable            = no
}

And that's it. It works for me. If you have any questions or comments, contact me, I'd be happy to hear from you.

CU, Stefan Sec Zehl

The necessary code changes are very small. Only socket_listen_accept needs to be changed. The accept() and openvpn_close_socket() calls need to be removed, because the connection on stdin is already the connection we want. The second thing is, we have to use getpeername to find the IP and Port of our remote peer.

All the other changes in the attached patch deal with adding a new command-line option, and propagating that new option down to the correct function.

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9