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.
