Coder Social home page Coder Social logo

vpn-ws's Introduction

vpn-ws

A VPN system over websockets

This is the client/server implementation of a layer-2 software switch able to route packets over websockets connections.

The daemon is meant to be run behind nginx, apache, the uWSGI http router or a HTTP/HTTPS proxy able to speak the uwsgi protocol and to manage websockets connections

How it works

A client creates a tap (ethernet-like) local device and connects to a websocket server (preferably over HTTPS). Once the websocket handshake is done, every packet received from the tuntap will be forwarded to the websocket server, and every websocket packet received from the server will be forwarded to the tuntap device.

The server side of the stack can act as a simple switch (no access to the network, only connected nodes can communicate), a bridge (a tuntap device is created in the server itself that can forward packets to the main network stack) a simpe router/gateway (give access to each node to specific networks without allowing communication between nodes) or whatever you can think of. (The server is voluntary low-level to allow all the paradigms supported by the network stack).

Authentication/Authorization and Security

Authentication and Authorization is delegated to the proxy. We believe that battle-tested webservers (like nginx and apache) cover basically every authentication and security need, so there is no need to reimplement them.

By default only HTTPS access (eventually with client certificate authentication) should be allowed, but plain-http mode is permitted for easy debugging.

Virtualhosting

A vpn-ws client is required to send the Host header during the handshake and "should" support SNI. In this way virtualhosting can be easily managed by the proxy server.

Installation from sources

note: see below for binary packages

You need gnu make and a c compiler (clang, gcc, and mingw-gcc are supported).

The server has no external dependancies, while the client requires openssl (except for OSX and Windows where their native ssl/tls implementation is used)

Just run (remember to use 'gmake' on FreeBSD instead of 'make')

make

after having cloned the repository. If all goes well you will end with a binary named vpn-ws (the server) and another named vpn-ws-client (the client)

You can eventually build server or client selectively with

make vpn-ws
make vpn-ws-client

You can build a static binary version too of the server (where supported) with:

make vpn-ws-static

the resulting binary (vpn-ws) will have no library dependancies.

Binary packages

updated to [20141121]

Running the server

by default the server binary takes a single argument, the name of the socket to bind (the one to which the proxy will connect to):

./vpn-ws /run/vpn.sock

will bind to /run/vpn.sock

Now you only need to configure your webserver/proxy to route requests to /run/vpn.sock using the uwsgi protocol (see below)

Nginx

Nginx will be your "shield", managing the authentication/authorization phase. HTTPS + basicauth is strongly suggested, but best setup would be HTTPS + certificates authentication. You can run with plain HTTP and without auth, but please, do not do it, unless for testing ;)

You need to choose the location for which nginx will forward requests to the vpn-ws server:

(we use /vpn)

location /vpn {
  include uwsgi_params;
  uwsgi_pass unix:/run/vpn.sock;
}

this a setup without authentication, a better one (with basicauth) could be:

location /vpn {
  include uwsgi_params;
  uwsgi_pass unix:/run/vpn.sock;
  auth_basic "VPN";
  auth_basic_user_file /etc/nginx/.htpasswd;
}

where /etc/nginx/.htpasswd will be the file containing credentials (you can use the htpasswd tool to generate them)

The Official Client

The official client (vpn-ws-client) is a command line tool (written in C). Its syntax is pretty simple:

vpn-ws-client <tap> <server>

where 'tap' is a (platform-dependent) tap device path, and 'server' is the url of the nginx /vpn path (in the ws://|wss:// form)

Before using the client, you need to ensure you have some form of tun/tap implementation. Linux, FreeBSD and OpenBSD already have it out-of the box.

For OSX you need to install

http://sourceforge.net/projects/tuntaposx/files/tuntap/20141104

while on Windows (ensure to select utils too, when running the installer)

http://swupdate.openvpn.org/community/releases/tap-windows-9.9.2_3.exe

The client must be run as root/sudo (as it requires to create a network interface [TODO: drop privileges after having created the interface).

On linux (you can name devices as you want):

./vpn-ws-client vpn-ws0 wss://foo:[email protected]/vpn

On OSX (you have a fixed number of /dev/tapN devices you can use)

./vpn-ws-client /dev/tap0 wss://foo:[email protected]/vpn

On FreeBSD (you need to create the interface to access the device):

ifconfig tap0 create
./vpn-ws-client /dev/tap0 wss://foo:[email protected]/vpn

On windows (you need to create a tap device via the provided utility and assign it a name, like 'foobar')

./vpn-ws-client foobar wss://foo:[email protected]/vpn

Once your client is connected you can assign it an ip address (or make a dhcp request if one of the connected nodes has a running dhcp server)

The mode we are using now is the simple "switch" one, where nodes simply communicates between them like in a lan.

Server tap and Bridge mode

By default the server acts a simple switch, routing packets to connected peers based on the advertised mac address.

In addition to this mode you can give the vpn-ws server a virtual device too (with its mac address) to build complex setup.

To add a device to the vpn-ws server:

./vpn-ws --tuntap vpn0 /run/vpn.sock

the argument of tuntap is platform dependent (the same rules of clients apply).

The 'vpn0' interface is considered like connected nodes, so once you give it an ip address it will join the switch.

One of the use case you may want to follow is briding the vpn with your physical network (in the server). For building it you need the server to forward packets without a matching connected peers to the tuntap device. This is the bridge mode. To enable it add --bridge to the server command line:

./vpn-ws --bridge --tuntap vpn0 /run/vpn.sock

Now you can add 'vpn0' to a pre-existing network bridge:

# linux example
brctl addbr br0
brctl addif br0 eth0
brctl addif br0 vpn0

Client bridge-mode

This mode allows a client to act as a bridge giving access to its whole network to the vpn (and it clients).

Just add --bridge to the client command line and attach the tuntap device to a bridge.

The main problem is that you still need a route to the vpn server, so the best approach would be having two network interfaces on the client (one for the connection with the server, and the other for the physical bridge).

On linux, you can use the macvlan interface (it is basically a copy of a physical interface with a different mac address):

ip link add link eth0 name virt0 type macvlan
ifconfig virt0 0.0.0.0 promisc up
brctl addif br0 virt0
# add vpn-ws tuntap device to the bridge
ifconfig vpn17 0.0.0.0 promisc up
brctl addif br0 vpn17

The --exec trick

Both the server and client take an optional argument named '--exec '. This option will instruct the server/client to execute a command soon after the tuntap device is created.

As an example you may want to call ifconfig upon connection:

vpn-ws-client --exec "ifconfig vpn17 192.168.173.17 netmask 255.255.255.0" vpn17 wss://example.com/

or to add your server to a tuntap to an already existent bridge:

vpn-ws --exec "brctl addif br0 vpn0" --bridge --tuntap vpn0 /run/vpn.sock

You can chain multiple commands with ;

vpn-ws --exec "brctl addif br0 vpn0; ifconfig br0 192.168.173.30" --bridge --tuntap vpn0 /run/vpn.sock

Required permissions

The server, when no tuntap device is created, does not require specific permissions. If bound to a unix socket, it will give the 666 permission to the scket itself, in this way nginx (or whatever proxy you are using) will be able to connect to it.

If the server needs to create a tap device, root permissions are required. By the way you can drop privileges soon after the device is created (and the --exec option is eventually executed) with the --uid ang --gid options:

vpn-ws --tuntap vpn0 --uid www-data --gid www-data /run/vpn.sock

The client instead requires privileged operations (future releases may allow dropping privileges in the client too)

Client-certificate authentication

Your client can supply a certificate for authenticating to the server.

On OpenSSL-based clients (Linux, FreeBSD) you need a key file and a certificate in pem format:

vpn-ws-client --key foobar.key --crt foobar.crt vpn0 wss://example.com/vpn

On OSX you need to import a .p12 file (or whatever format it support) to the login keychain, then you need to specify the name of the certificate/identity via the --crt option (no --key is involved):

vpn-ws-client --crt "My certificate" /dev/tap0 wss://example.com/vpn

The JSON Control interface

The uwsgi protocol supports a raw form of channel selections using 2 bytes of its header. Thos bytes are called "modifiers". By setting the modifier1 to '1' (by default modifiers are set to 0) you will tell the vpn-ws server to show the JSON control interface. This is a simple way for monitoring the server and for kicking out clients.

When connectin to modifier1, a json blob with the data of all connected clients is shown. Passing a specific QUERY_STRING you can issue commands (currently only killing peers is implemented)

location /vpn {
  include uwsgi_params;
  uwsgi_pass unix:/run/vpn.sock;
  auth_basic "VPN";
  auth_basic_user_file /etc/nginx/.htpasswd;
}

location /vpn_admin {
  include uwsgi_params;
  uwsgi_modifier1 1;
  uwsgi_pass unix:/run/vpn.sock;
  auth_basic "VPN ADMIN";
  auth_basic_user_file /etc/nginx/.htpasswd;
}

You can now connect to /vpn_admin to see a json representation of connected clients. Each peer has an id. you can kick-out that peer/client adding a query string to the bar:

/vpn_admin?kill=n

where n is the id of the specific client.

If needed, more commands could be added in the future.

Example Clients

In the clients/ directory there are a bunch of clients you can run on your nodes or you can use as a base for developing more advanced ones.

Clients must run as root/sudo as they need to create/interact with tuntap devices

  • vpn_linux_tornado.py - a linux-only client based on tornado and ws4py
sudo pip install tornado ws4py python-pytun
sudo python clients/vpn_linux_tornado.py ws://your_server/
  • vpn.pl - more-or-less platform independent perl client (works with OSX and FreeBSD)
sudo cpanm AnyEvent::WebSocket::Client
sudo perl clients/vpn.pl /dev/tap0 ws://your_server/

As the official client you need to ensure a tuntap device implementation is available on the system

then (after having connected to the vpn server) you can assign the ip to it

Remember that we are at layer-2, so if you place a dhcp server on one of those nodes it will work as expected.

Multicast and Broadcast

They are both supported, (yes bonjour, mdns, samba will work !).

You can eventually turn off them selectively adding

  • --no-broadcast
  • --no-multicast

to the server command line

Tutorials

https://github.com/unbit/vpn-ws/blob/master/tutorials/ubuntu_trusty_nginx_bridge_client_certificates.md

Support

https://groups.google.com/d/forum/vpn-ws

(or drop a mail to info at unbit dot it for commercial support)

Twitter

(mainly for announces)

@unbit

Status/TODO/Working on

The server on windows is still a work in progress

The client on windows has no support for SSL/TLS

Grant support for NetBSD, and DragonflyBSD

Investigate solaris/smartos/omnios support

vpn-ws's People

Contributors

mobiliodevelopment avatar orgads avatar rdeioris avatar unbit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vpn-ws's Issues

VLAN support

Add --vlan switch, enforcing the server to expect vlan-tagged frames. Broadcast, multicast and direct frames must take in account the vlan tag before forwarding a packet.

Thanks to tagged frames, you can "partition" a virtual network.

Documentation on assigning IP address to a network interface

I followed the documentation and created a simple server without auth.
Then, I compiled a client and connected to the server, as the server printed

[Sat Jul  4 21:13:14 2015] registered new peer 5 MAC=1E:8A:66:ED:70:82 REMOTE_ADDR=XXX.XXX.XXX.XXX REMOTE_USER= DN=

and the client printed

[Sun Jul  5 09:13:14 2015] connecting to XXX.XXX.XXX.XXX port 80 (transport: ws)
[Sun Jul  5 09:13:15 2015] connected to XXX.XXX.XXX.XXX port 80 (transport: ws)

Now, I simply visited a web page to show my client IP, but it still displayed my real client IP. I realized that I have not done this step specified in the documentation:

Once your client is connected you can assign it an ip address (or make a dhcp request if one of the connected nodes has a running dhcp server)

I Googled around, and found a simple step on my Ubuntu 14.10:

/sbin/ip link # To show my current network interfaces, and I see vpn-ws0
sudo /sbin/ip addr add 192.168.0.10/24 dev vpn-ws0

No error returned. Then, I re-checked my ip address on a website, it still showed the real client IP.

So, I am wondering what I did wrong.

IPv6 support on client

Client doesn't support IPv6

    [Wed Apr  1 21:57:45 2015] connecting to <ipv6 address> port 8080 (transport: ws)
    [Wed Apr  1 21:57:46 2015] vpn_ws_connect()/gethostbyname(): unable to resolve name
    [Wed Apr  1 21:57:46 2015] disconnected

Connection refused

hi.

yudeMacBook-Air:vpn-ws brite$ sudo ./vpn-ws-client /dev/tap0 wss://mydomain.com/vpn
Password:
[Wed Jul 13 11:36:02 2016] connecting to mydomain.com port 443 (transport: wss)
[Wed Jul 13 11:36:03 2016] vpn_ws_connect()/connect(): Connection refused
[Wed Jul 13 11:36:03 2016] disconnected

how to fix it?
thank u.

SSL-specific build error

Can't build with:

cc   -Wall -Werror -g -o vpn-ws src/main.o src/error.o src/tuntap.o src/memory.o src/bits.o src/base64.o src/exec.o src/websocket.o src/utils.o src/socket.o src/event.o src/io.o src/uwsgi.o src/sha1.o src/macmap.o 
src/ssl.c: In function '_vpn_ws_ssl_wait_read':
src/ssl.c:4:12: error: type of 'fd' defaults to 'int' [-Werror=implicit-int]
 static int _vpn_ws_ssl_wait_read(fd) {
            ^
src/ssl.c: In function '_vpn_ws_ssl_wait_write':
src/ssl.c:15:12: error: type of 'fd' defaults to 'int' [-Werror=implicit-int]
 static int _vpn_ws_ssl_wait_write(fd) {
            ^
cc1: all warnings being treated as errors
make: *** [src/ssl.o] Error 1

Can be fixed by adding types - http://git.altlinux.org/people/enp/packages/?p=vpn-ws.git;a=blobdiff;f=src/ssl.c;h=4f0405cec2c210bd6d4253a66e6ffc3a209d205b;hp=751d101a7daa76943708bebd87f8be12b0ac642c;hb=44bf8b772379f35a1bed4a150081557e3ef5359c;hpb=3d15958bfeb668ae860376f650083ca37146e807

SSL error

./vpn-ws-client vpn-ws0 wss://cctrs.net:943/vpn
[Sun Jul 19 18:51:25 2015] connecting to cctrs.net port 943 (transport: wss)
[Sun Jul 19 18:51:25 2015] vpn_ws_ssl_handshake(): error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
[Sun Jul 19 18:51:25 2015] disconnected
[Sun Jul 19 18:51:26 2015] connecting to cctrs.net port 943 (transport: wss)
[Sun Jul 19 18:51:26 2015] vpn_ws_ssl_handshake(): error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
[Sun Jul 19 18:51:26 2015] disconnected

curl tested https://cctrs.net:943/vpn is OK

Any suggestion? Could vpn-ws-client use tls 1.2 with alpn/npn?

Client proxy support

It doesn't look like the client supports connecting to the WS server from behind a proxy. Any chance this can be added to the roadmap?

the exec trick issue

hi.

yudeMacBook-Air:vpn-ws brite$ sudo ./vpn-ws-client --exec "ifconfig vpn-ws 192.168.10.2 netmask 255.255.255.0" vpn-ws wss://ws.smt.biz.st:449/ --no-verify
Password:
[Mon Sep 16 10:44:29 2019] vpn_ws_tuntap()/if_nametoindex(): Device not configured
yudeMacBook-Air:vpn-ws brite$

how to fix "Device not configured" issue?
tks

can you edit some parts?!

Hi @unbit
I want to port your VPN to android devices but the android VPN service doesn't allow us to directly access the tuntap and instead of that the android gives us a file descriptor to write/read packets, I don't know c much, can you develop something like this, mean I can send the fd of the android tun to the vpn-ws-client instead tuntap

stop immediately without any error

Hello,
I ran this VPN-WS and test it with ping, it was ok. until any large transfer data and ping stop immediately without any error, i should to disconnect and connect to solve the ping, i couldn't find any log to find the issue! may you please help me

the interface ws0 isn't generated at all

hi.
i ran ./vpn-ws --tuntap ws0 /run/vpn.sock > /dev/null & in ~/vpn-ws/,
[root@host vpn-ws]# ps aux|grep vpn-ws
root 7935 0.0 0.0 4384 1364 pts/0 S 15:10 0:00 ./vpn-ws --tuntap ws0 /run/vpn.sock
root 7945 0.0 0.1 112716 2380 pts/0 S+ 15:16 0:00 grep --color=auto vpn-ws
[root@host vpn-ws]#
but the interface ws0 isn't generated at all,as i ran ifconfig,i didn't see the interface ws0 shown:

[root@host vpn-ws]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet my-vps-ip netmask 255.255.240.0 broadcast 174.137.63.255
inet6 fe80::a8aa:ff:fe12:2e1c prefixlen 64 scopeid 0x20
ether aa:aa:00:12:2e:1c txqueuelen 1000 (Ethernet)
RX packets 815736 bytes 364317320 (347.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 720135 bytes 341692080 (325.8 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

wg0: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1420
inet 19.0.0.1 netmask 255.255.255.0 destination 19.0.0.1
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC)
RX packets 177173 bytes 33083912 (31.5 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 192633 bytes 158337140 (151.0 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@host vpn-ws]#

why the interface ws0 isn't generated at all?

Apache2 and uWSGI

Hello,

I try to install vpn-ws server using Apache2 (on Debian Jessie) but it's does not work. But with the same client and the same server using Nginx it's work like a charm. On Apache, I'm using mod_uswgi with a simple configuration :

<Location /vpn>
    SetHandler uwsgi-handler
    uWSGISocket 127.0.0.1:5235
</Location>

With this configuration, my client can connect to the server :

brenard@aconit:~/dev/vpn-ws$ ((detached from v0.2)) sudo ./vpn-ws-client --exec "ifconfig vpn0 172.16.0.2 netmask 255.255.255.0; ip link set mtu 1490 dev vpn0; ifconfig vpn0 up" vpn0 ws://vpn-ws.zionetrix.net:80/vpn --no-verify
[Fri Nov 27 20:24:53 2015] connecting to vpn-ws.zionetrix.net port 80 (transport: ws)
[Fri Nov 27 20:24:53 2015] connected to vpn-ws.zionetrix.net port 80 (transport: ws)

And my server see this new client :

root@gaston:~$ ./vpn-ws --tuntap vpn0 127.0.0.1:5235 --exec 'ifconfig vpn0 172.16.0.1 netmask 255.255.255.0; ip link set mtu 1490 dev vpn0; ifconfig vpn0 up'
[Fri Nov 27 20:24:53 2015] registered new peer 8 MAC=02:76:4A:0B:B4:D3 REMOTE_ADDR=109.190.110.196 REMOTE_USER= DN=

But a ping between to host does work. A tcpdump on vpn0 show that ARP request does not get reply.

Do you have an idea what could be the problem ? Do you have an example of functional configuration with apache ? I also try with mod_proxy_uwsgi but I have the same issue..

Thank you

vpn-ws-client-static

Only server site can static lib , please let me know how to build with the static library with client side

some frameworks are deprecated

when building on macOS 12.4, got this error

src/ssl.c:83:22: error: 'SSLCreateContext' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Werror,-Wdeprecated-declarations]

kSSLClientSide
kSSLStreamType
SSLSetProtocolVersionMin
...
SSLClose

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1689:1: note: 'SSLRead' has been explicitly marked deprecated here
SSLRead (SSLContextRef context,
^

src/ssl.c:188:2: error: 'SSLClose' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Werror,-Wdeprecated-declarations]

and so more

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.