Support non-root operation using ocproxy
cover Support non-root operation using ocproxy
Sometimes it is desirable to establish VPN connections without setting up a kernel tun/tap device. Possible use cases include:
- Routing traffic from different applications through different VPNs.
- Connecting to multiple VPNs (clients, sites, ...) that have overlapping IP ranges.
- Connecting to multiple VPNs that each advertise their own default route and/or DNS settings.
- Situations in which direct access to the VPN from all processes/UIDs is not necessarily wanted, impairs performance, or presents an unacceptable risk of intrusion or data leakage.
- Multiuser systems or container-based VPSes.
- Other situations in which the openvpn user or program is not trusted to reconfigure a tun/tap device.
For a long time, OpenConnect has had the ability to run as non-root[1], redirecting VPN traffic to/from a helper program over a socketpair. ocproxy[2] was written to take advantage of this interface. Now I am submitting changes to allow openvpn to be used in a similar manner. Sample usage:
openvpn --config foo.ovpn \
--script-security 2 \
--dev "|/usr/bin/ocproxy -L 2222:unix-host:22 -D 11080"The -D and -L flags work just like their equivalents on ssh, enabling port forwarding and a SOCKS5 proxy on the specified local sockets. "--dev-type tun" is assumed if unspecified. The IP configuration is passed into ocproxy through "vpnc-script"-compatible environment variables.
This was tested on Linux with IPv4.
[1] http://www.infradead.org/openconnect/nonroot.html
[2] http://repo.or.cz/w/ocproxy.git
Kevin Cernekee (3):
Add S_NOWAIT and S_SETPGRP flags to openvpn_execve Move MTU field into struct tuntap
Add "pipe to external program" device
doc/openvpn.8 | 29 +++++++++++++-- src/openvpn/init.c | 17 ++++++--- src/openvpn/misc.c | 9 ++++- src/openvpn/misc.h | 6 ++-- src/openvpn/tun.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/openvpn/tun.h | 4 ++- 6 files changed, 149 insertions(+), 18 deletions(-)
Signature · 1 line
1.7.9.5
PATCH 1/3 Add S_NOWAIT and S_SETPGRP flags to openvpn_execve
This is to let openvpn_execve be used to create a process that runs in the background, and return its PID so that its process group can be nuked on exit.
--- src/openvpn/misc.c | 9 ++++++++- src/openvpn/misc.h | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-)
src/openvpn/misc.c +8 -1
@@ -308,6 +308,11 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i pid =fork ();if (pid == (pid_t )0 )/* child side */ { +if (flags &S_SETPGRP ) + { +if (setpgid (0 ,getpid ()) ==-1 ) +msg (M_WARN |M_ERRNO ,"openvpn_execve: setpgid failed" ); + }execve (cmd, argv, envp);exit (127 ); }
@@ -315,7 +320,9 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned imsg (M_ERR ,"openvpn_execve: unable to fork" ); else /* parent side */ { -if (waitpid (pid, &ret,0 ) != pid) +if (flags &S_NOWAIT ) + ret = (int )pid; +else if (waitpid (pid, &ret,0 ) != pid) ret =-1 ; } }
src/openvpn/misc.h +4 -2
@@ -86,8 +86,10 @@ void write_pid (const struct pid_state *state);void warn_if_group_others_accessible (const char * filename);/* system flags */ -#define S_SCRIPT (1<<0) -#define S_FATAL (1<<1) +#define S_SCRIPT (1<<0) +#define S_FATAL (1<<1) +#define S_NOWAIT (1<<2) +#define S_SETPGRP (1<<3)const char *system_error_message (int ,struct gc_arena *gc);
Signature · 1 line
1.7.9.5
PATCH 2/3 Move MTU field into struct tuntap
In reply to Kevin Cernekeeopen_tun() will need to access this value, so we'll store it alongside the IP/netmask addresses.
--- src/openvpn/init.c | 5 +++-- src/openvpn/tun.c | 2 +- src/openvpn/tun.h | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-)
src/openvpn/init.c +3 -2
@@ -1448,6 +1448,7 @@ do_open_tun (struct context *c)do_init_route_ipv6_list (&c->options, c->c1.route_ipv6_list, false, c->c2.es);/* do ifconfig */ + c->c1.tuntap->mtu =TUN_MTU_SIZE (&c->c2.frame);if (!c->options.ifconfig_noexec &&ifconfig_order () ==IFCONFIG_BEFORE_TUN_OPEN ) {
@@ -1457,7 +1458,7 @@ do_open_tun (struct context *c) c->options.dev_type, c->options.dev_node, &gc); -do_ifconfig (c->c1.tuntap, guess,TUN_MTU_SIZE (&c->c2.frame), c->c2.es); +do_ifconfig (c->c1.tuntap, guess, c->c2.es); }/* possibly add routes */
@@ -1482,7 +1483,7 @@ do_open_tun (struct context *c)if (!c->options.ifconfig_noexec &&ifconfig_order () ==IFCONFIG_AFTER_TUN_OPEN ) { -do_ifconfig (c->c1.tuntap, c->c1.tuntap->actual_name,TUN_MTU_SIZE (&c->c2.frame), c->c2.es); +do_ifconfig (c->c1.tuntap, c->c1.tuntap->actual_name, c->c2.es); }/* run the up script */
src/openvpn/tun.c +1 -1
@@ -631,10 +631,10 @@ void delete_route_connected_v6_net(struct tuntap * tt,void do_ifconfig (struct tuntap *tt,const char *actual,/* actual device name */ -int tun_mtu,const struct env_set *es) {struct gc_arena gc =gc_new (); +int tun_mtu = tt->mtu;if (tt->did_ifconfig_setup) {
src/openvpn/tun.h +1 -1
@@ -150,6 +150,7 @@ struct tuntapin_addr_t local;in_addr_t remote_netmask;in_addr_t broadcast; +int mtu;struct in6_addr local_ipv6;struct in6_addr remote_ipv6;
@@ -243,7 +244,6 @@ void init_tun_post (struct tuntap *tt,void do_ifconfig (struct tuntap *tt,const char *actual,/* actual device name */ -int tun_mtu,const struct env_set *es);bool is_dev_type (const char *dev,const char *dev_type,const char *match_type);
Signature · 1 line
1.7.9.5
PATCH 3/3 Add "pipe to external program" device
In reply to Kevin CernekeeAllow the tunneled traffic to be handled by an external program rather than by a real tun/tap kernel device. This allows non-root users to connect to a VPN through a userland TCP/IP stack.
--- doc/openvpn.8 | 29 +++++++++++++-- src/openvpn/init.c | 12 +++++-- src/openvpn/tun.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/openvpn/tun.h | 2 ++ 4 files changed, 132 insertions(+), 11 deletions(-)
doc/openvpn.8 +27 -2
@@ -709,17 +709,42 @@ peers which will be initiating connections by using the option. .\"********************************************************* .TP -.B \-\-dev tunX | tapX | null +.B \-\-dev tunX | tapX | null | <program> TUN/TAP virtual network device ( .B X can be omitted for a dynamic device.) +.B tunX +or +.B tapX +is the name of a network interface that openvpn will create. + +.B null +is a special value used for testing only. It will not pass traffic. + +.B <program> +is the full path to an executable, such as +.B |/usr/bin/ocproxy, +which will pass traffic to and from openvpn over a socketpair. This is an +alternative to using a tun/tap interface to pass traffic to and from the +OS kernel; unlike tun/tap it does not require any special privileges. The +path must start with a `|' (pipe) character. This works with either +.B \-\-dev-type tun +or +.B \-\-dev-type tap. +If left unspecified, it will default to tun. + See examples section below for an example on setting up a TUN device. You must use either tun devices on both ends of the connection or tap devices on both ends. You cannot mix them, as they -represent different underlying network layers. +represent different underlying network layers. Similarly, endpoints using +device type +.B null +will only interoperate with other +.B null +endpoints. .B tun devices encapsulate IPv4 or IPv6 (OSI Layer 3) while
src/openvpn/init.c +9 -3
@@ -1326,7 +1326,9 @@ do_route (const struct options *options,const struct plugin_list *plugins, struct env_set *es) { - if (!options->route_noexec && ( route_list || route_ipv6_list ) ) + if (!options->route_noexec && + !tt->is_pipe && + ( route_list || route_ipv6_list ) ) {add_routes (route_list ,route_ipv6_list ,tt ,ROUTE_OPTION_FLAGS (options ),es );setenv_int (es,"redirect_gateway" ,route_did_redirect_default_gateway (route_list));
@@ -1450,6 +1452,7 @@ do_open_tun (struct context *c)/* do ifconfig */ c->c1.tuntap->mtu =TUN_MTU_SIZE (&c->c2.frame);if (!c->options.ifconfig_noexec + && !c->c1.tuntap->is_pipe &&ifconfig_order () ==IFCONFIG_BEFORE_TUN_OPEN ) {/* guess actual tun/tap unit number that will be returned
@@ -1462,7 +1465,8 @@ do_open_tun (struct context *c) }/* possibly add routes */ -if (route_order () ==ROUTE_BEFORE_TUN ) { +if (route_order () ==ROUTE_BEFORE_TUN + && !c->c1.tuntap->is_pipe) {/* Ignore route_delay, would cause ROUTE_BEFORE_TUN to be ignored */ do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list, c->c1.tuntap, c->plugins, c->c2.es);
@@ -1476,11 +1480,13 @@ do_open_tun (struct context *c) c->c1.tuntap);/* set the hardware address */ -if (c->options.lladdr) +if (c->options.lladdr + && !c->c1.tuntap->is_pipe)set_lladdr (c->c1.tuntap->actual_name, c->options.lladdr, c->c2.es);/* do ifconfig */ if (!c->options.ifconfig_noexec + && !c->c1.tuntap->is_pipe &&ifconfig_order () ==IFCONFIG_AFTER_TUN_OPEN ) {do_ifconfig (c->c1.tuntap, c->c1.tuntap->actual_name, c->c2.es);
src/openvpn/tun.c +94 -6
149 lines not on this page · raw message
src/openvpn/tun.h +2 -0
@@ -131,6 +131,8 @@ struct tuntap# define TUNNEL_TOPOLOGY (tt) ((tt) ? ((tt)->topology) : TOP_UNDEF)int topology;/* one of the TOP_x values */ +bool is_pipe; +pid_t pipe_pid;bool did_ifconfig_setup;bool did_ifconfig_ipv6_setup;bool did_ifconfig;
Signature · 1 line
1.7.9.5
Hi,
Quoted 11 lines · Kevin Cernekee
Sometimes it is desirable to establish VPN connections without setting up a kernel tun/tap device. Possible use cases include:
- Routing traffic from different applications through different VPNs.
- Connecting to multiple VPNs (clients, sites, ...) that have overlapping IP ranges.
- Connecting to multiple VPNs that each advertise their own default route and/or DNS settings.
Quoted 8 lines
- Situations in which direct access to the VPN from all processes/UIDs is not necessarily wanted, impairs performance, or presents an unacceptable risk of intrusion or data leakage.
- Multiuser systems or container-based VPSes.
- Other situations in which the openvpn user or program is not trusted to reconfigure a tun/tap device.
I can see that you might want that, but I do not think we want this in OpenVPN.
It brings in a fair bit of extra code complexity, more arcane options, and code that needs to maintained and tested across all supported platforms.
I think the goal can in most cases be achieved using SSH port forwarding - or OpenConnect, or a myriad of other "tunnel this file descriptor for me" tools. OpenVPN will do tun/tap, and do that well.
gert
Signature · 4 lines
USENET is *not* the non-clickable part of WWW!
//www.muc.de/~gert/
Gert Doering - Munich, Germany [email protected]...
fax: +49-89-35655025 [email protected]...Quoted 3 lines · Gert Doering
I think the goal can in most cases be achieved using SSH port forwarding - or OpenConnect, or a myriad of other "tunnel this file descriptor for me" tools. OpenVPN will do tun/tap, and do that well.
The problem with all of these alternatives (at least in my case) is that the other end is already running OpenVPN, not one of the other tools. Right now, there aren't a lot of good ways to connect to somebody's OpenVPN service without running as root. Prior to this work I had been using separate VMs.
Are there any changes you could suggest that would make this feature more palatable?
Quoted 4 lines · Kevin Cernekee
Quoted 3 lines · Gert Doering
I think the goal can in most cases be achieved using SSH port forwarding - or OpenConnect, or a myriad of other "tunnel this file descriptor for me" tools. OpenVPN will do tun/tap, and do that well.
Quoted 10 lines
The problem with all of these alternatives (at least in my case) is that the other end is already running OpenVPN, not one of the other tools. Right now, there aren't a lot of good ways to connect to somebody's OpenVPN service without running as root. Prior to this work I had been using separate VMs.
Are there any changes you could suggest that would make this feature more palatable?
You could look at the TARGET_ANDROID. That uses the management interface and fds over unix socket to achieve something similar.
Arne
Quoted 2 lines · Arne Schwabe
You could look at the
TARGET_ANDROID. That uses the management interface and fds over unix socket to achieve something similar.
Do you think it would be feasible to enable TARGET_ANDROID by default in the Linux OpenVPN builds, and change the logic so that if the user passes e.g. "--android" it will accept commands over the management socket instead of using the standard CLI?
This would not work with ocproxy as-is, but I could write a wrapper program.
Let me know if you think it's worth putting together a patch set.
BTW, here is an example of how my current patch works with an existing OpenVPN service:
$ ./src/openvpn/openvpn --config /tmp/vpnbook/vpnbook-us1-udp25000.ovpn \
--verb 0 --script-security 2 \
--dev "|/usr/bin/ocproxy -D 12345" &
Sun Apr 13 08:46:44 2014 WARNING: file '/tmp/vpnbook/user.txt' is
group or others accessible
Sun Apr 13 08:46:44 2014 WARNING: No server certificate verification
method has been enabled. See http://openvpn.net/howto.html#mitm for
more info.
Sun Apr 13 08:46:44 2014 WARNING: this configuration may cache
passwords in memory -- use the auth-nocache option to prevent this$ SOCKS5_SERVER=127.0.0.1:12345 socksify links -source ipchicken.com |
grep -A1 Address
Address:
198.7.62.204 </font></td>Using port forwarding with "ocproxy -L", socksify, or FoxyProxy, individual connections / applications / URLs can be seamlessly forwarded over different VPN links. Or, if I omit the --dev option, OpenVPN will fall back to standard "create tunX" behavior.
Quoted 7 lines · Kevin Cernekee
Quoted 2 lines · Arne Schwabe
You could look at the
TARGET_ANDROID. That uses the management interface and fds over unix socket to achieve something similar.Do you think it would be feasible to enable
TARGET_ANDROIDby default in the Linux OpenVPN builds, and change the logic so that if the user passes e.g. "--android" it will accept commands over the management socket instead of using the standard CLI?
I am sure how invasive a patch that allows --android would be.
Arne
Hi
Quoted 12 lines · Kevin Cernekee
Quoted 3 lines · Arne Schwabe
You could look at the
TARGET_ANDROID. That uses the management interface and fds over unix socket to achieve something similar.Do you think it would be feasible to enable
TARGET_ANDROIDby default in the Linux OpenVPN builds, and change the logic so that if the user passes e.g. "--android" it will accept commands over the management socket instead of using the standard CLI?
I'd vote against enabling TARGET_ANDROID by default - there's a reason the code is split into
TARGET_LINUX
TARGET_FREEBSD
TARGET_SOLARIS
etc and I'd say you're asking for trouble if you enable two TARGETs on one platform.
Quoted 3 lines
This would not work with ocproxy as-is, but I could write a wrapper program.
I do like the idea of not needing root access to run openvpn - esp windows users could benefit from this, as they're not always allowed to install the tap-win adapter. Then again, it goes against the UNIX/Linux philosophy that each tool (e.g. openvpn) should do one thing and it should do it well. By adding patches like this left and right we're steering away from that philosophy.
cheers,
JJK
Quoted 1 line · Jan Just Keijser
I'd vote against enabling
TARGET_ANDROIDby default
This would not be my first choice either, but it would allow non-root processes to set up a fully functional VPN connection on Linux hosts without a special build.
If I'm going to have to make a special build anyway, I'll just apply my three patches locally. It's working for me now and I posted packages in my Ubuntu PPA. But that is somewhat inconvenient to maintain, and it doesn't benefit other OpenVPN users (almost all of whom are using binaries built from the official sources).
Quoted 6 lines
there's a reason the code is split into
TARGET_LINUXTARGET_FREEBSDTARGET_SOLARIS
etc and I'd say you're asking for trouble if you enable two TARGETs on one platform.
Yes, the way the code is currently written, enabling more than one TARGET_* option is a problem.
But, stepping back a bit, does Android really need its own TARGET? Or can it be treated as a special operating mode on a standard TARGET_LINUX build? Any Linux host, Android or PC, should be able to pass its traffic over either tunX or over a file descriptor passed in from outside. It would be nice if this choice was available at runtime.
Quoted 3 lines
I do like the idea of not needing root access to run openvpn - esp windows users could benefit from this, as they're not always allowed to install the tap-win adapter.
There are also a number of cases where it is useful to have fine-grained control over which applications use the VPN connection, e.g.
https://pay.reddit.com/r/techsupport/comments/22x2oo/routing_clients_through_vpn_connected_through/
I don't know if my solution is necessarily the best option for this user, but it's something to consider at least...
Quoted 4 lines
Then again, it goes against the UNIX/Linux philosophy that each tool (e.g. openvpn) should do one thing and it should do it well. By adding patches like this left and right we're steering away from that philosophy.
We could always split {route,tun,lladdr}.c into a separate program and invoke it with "openvpn --dev '|tunhelper'". :-)
Quoted 6 lines · Jan Just Keijser
I do like the idea of not needing root access to run openvpn - esp windows users could benefit from this, as they're not always allowed to install the tap-win adapter. Then again, it goes against the UNIX/Linux philosophy that each tool (e.g. openvpn) should do one thing and it should do it well. By adding patches like this left and right we're steering away from that philosophy.
I'm inclined to agree about the philosophy, but I think it's a design decision made long ago in OpenVPN, and I wouldn't necessarily advocate changing it.
One way of applying the UNIX philosophy in this context is to suggest that the "one thing" that a VPN tool should do well is the transfer of packets between an encrypted network link and a local one.
If you take a look at vpnc and openconnect, you'll see that the nasty details of how you *configure* a network connection for every platform under the sun was considered to be outside the scope. It was farmed out to a separate tool, vpnc-script. Which allows us to *share* that vpnc-script between the two clients rather than reproducing the effort.
If you look at the situation from that point of view, then yes — adding "patches like this left and right" is definitely steering away from the UNIX philosophy. But the "patches like this" could be seen to include all the existing support for configuring specific platforms such as Linux, *BSD, Solaris, Windows, OSX, Android, etc. :)
But there are good reasons why openvpn does this stuff for itself, and I don't think it's very practical to change it. Kevin's patches are in line with the existing design.
We have a lot of configuration cruft in openvpn, and fundamentally what it does is give the core code a file descriptor that it can shove packets in and out of. If Kevin offers a relatively clean way for that file descriptor to be a UNIX dgram socket which we handed to a spawned child process, instead of a handle to the kernel's /dev/net/tun device, I don't see why we'd consider that a violation of the philosophy.
Signature · 2 lines
David Woodhouse Open Source Technology Centre [email protected]... Intel Corporation