OpenVPN library purely in OCaml
OpenVPN creates secure point-to-point or site-to-site connections in routed or bridged configurations and remote access facilities. It uses TLS to establish a (mutually) authenticated connection, over which material to derive the symmetric keys for packet encryption is exchanged.
The goal of this project is to provide:
- A pure library implementing the protocol logic, and the OpenVPN config file format to enable interoperabilty and a smooth transition for existing deployments.
- A MirageOS unikernel that acts as an OpenVPN client.
Our goal is not to implement the complete protocol, but rather a small useful subset with modern crypto and the latest key exchange methods, without deprecated or redundant features (embodying the philosophy of nqsb-tls). An initial draft of the network setup is depicted in the diagram below:
Since OpenVPN is not detailed in a protocol specificaton specified, apart from comments in the header files, we have written a specification document in Markdown, still work in progress:
Our OpenVPN configuration parser can be tested with an OpenVPN configuration file:
Included in this repository is a unix program that will connect to an
OpenVPN server, open a
tun interface, and tunnel packets between
Unix client on Linux
There are two ways to open
- Using a dynamically allocated interface (
dev tun). In order to dynamically allocate a
tuninterface, the process will need privileges to do so. Either by running the client as
rootor with the
CAP_NET_ADMINprivilege. You would then add
dev tunto your configuration file.
- Using a preallocated interface (
dev tunX) This is the recommend configuration. To allocate such an interface for
tun5you can use this command:You would then add
sudo ip tuntap add mode tun user MYUSERNAME name tun5
dev tun5to your configuration file.
dune build # Bestowing the binary with CAP_NET_ADMIN if using dynamic tun allocation: sudo setcap cap_net_admin=ep ./_build/default/app/openvpn_client_lwt.exe ./_build/default/app/openvpn_client_lwt.exe -v MY-CONFIG-FILE.CONF
OpenVPN-compatible config parser
Our goal has been to implement a usable subset (as found in various real-world configuration files available to us during the implementation phase).
As far as possible we have strived to derive a representation that does not
permit ambiguity or conflicting options to be present in the parsed config.
type 'a k declaration in
openvpn_config.mli for more
This does not mean that conflicting options cannot be accepted from an on-disk
configuration file, but rather that such conflicts are explicitly handled in
the parser code (specifically in the
A notable difference from OpenVPN configuration parser is that we treat relative
paths in a configuration file to be relative to the configuration file
location, and not relative to the current working directory. OpenVPN supports
--cd argument, which we do not.
Supported config directives
Here is a list of configuration directives supported by this parser.
NB: These are supported by the parser, not necessarily supported by the unikernel or client executables. TODO document that subset somewhere, and maybe check upon initialization that we understand all the options given?
Directives that call for an external file to be read (or can be
supplied inline with the
auth-nocache auth-user-pass FILE-PATH ca FILE-PATH cert FILE-PATH connection FILE-PATH key FILE-PATH pkcs12 FILE-PATH tls-auth FILE-PATH tls-auth FILE-PATH 0 tls-auth FILE-PATH 1 # the 0/1 here is the keydirection: 0 for CN_OUTGOING; 1 for CN_INCOMING
Other supported directives:
nobind bind lport PORT local HOSTNAME local IP cipher CIPHER comp-lzo dev null dev tun dev tap dev tunNUMBER dev tapNUMBER dhcp-option disable-nbt dhcp-option domain DOMAIN dhcp-option ntp IP dhcp-option dns IP hand-window SECONDS tran-window SECONDS ping SECONDS # Send a control channel ping after SECONDS of inactivity. # Defaults to 0, which means no pings will be sent. ping-exit SECONDS ping-restart SECONDS mssfix SIZE link-mtu SIZE tun-mtu SIZE float ifconfig-nowarn mute-replay-warnings passtos persist-key persist-tun remote-random auth-retry nointeract proto tcp proto tcp-server proto tcp-client proto tcp4 proto tcp4-server proto tcp4-client proto tcp6 proto tcp6-server proto tcp6-client proto udp proto udp4 proto udp6 pull client tls-client tls-server remote-cert-tls server remote-cert-tls client reneg-bytes BYTES reneg-pkts PACKET-COUNT reneg-sec SECONDS # renegotiate data channel key after N items > sent-items + received-items replay-window LOW-SECONDS HIGH-SECONDS connect-retry LOW-SECONDS HIGH-SECONDS keepalive LOW-SECONDS HIGH-SECONDS connect-retry-max unlimited connect-retry-max TIMES # Configures the maximum amount of times to retry each remote/connection # before giving up. Defaults to "unlimited". connect-timeout SECONDS # a.k.a. --server-poll-timeout SECONDS # defaults to: connect-timeout 120 resolv-retry infinite resolv-retry SECONDS # defaults to: resolv-retry infinite route-delay N-SECONDS W-SECONDS # TODO describe these route NETWORK [NETMASK [GATEWAY [METRIC]]] # specification of network/netmask/gateway/metric is implemented. # NETWORK: "net_gateway" or "remote_host" or or "vpn_gateway" or an IP address. # NETMASK: "default" or CIDR format ("A.B.C.D/PREFIX") # GATEWAY: "default" or an IP address. # METRIC: may be "default" or an integer between 0 and 255, inclusively. route-gateway default route-gateway dhcp route-gateway IP route-metric METRIC # METRIC: may be "default" or an integer between 0 and 255, inclusively. tls-timeout SECONDS # resend control data packets after not receivin an ACK for SECONDS # TODO should this only apply to UDP? tls-version-min 1.1 tls-version-min 1.2 tls-version-min 1.3 tls-version-min 1.1 or-highest tls-version-min 1.2 or-highest tls-version-min 1.3 or-highest topology net30 topology p2p topology subnet verb LEVEL
The following directives are ignored. Either because they were not deemed useful, or because we have not had a use for them yet:
inactive ip-win32 rcvbuf redirect-gateway remote-cert-ku rport sndbuf socket-flags up # string containing shell command to run ? dh # TODO path to a PEM file port # TODO influences Bind and Remote socks-proxy dhcp-option # DHCP options not listed in "Supported directives" above are ignored. engine ENGINE-NAME # select OpenSSL hw crypto backend; we don't use OpenSSL and so this doesn't # make sense to parse.
allow-recursive-routing ping-timer-rem secret FILE # static key