ssh tunnels with tap and -w

My earlier article on ssh trickiness didn’t include mention of the newish “-w” option, which turns ssh into a full-on VPN solution rather than just a port-at-a-time port forwarder.

The useful piece of information which I haven’t seen elsewhere is this: you don’t need to allow root ssh logins to use it. Instead, you can use ‘tunctl’ to preconfigure tun or tap devices on each end with the -u option to set their permissions to a non-root user. The easiest place to do this, on Debian/Ubuntu systems, is in /etc/network/interfaces, for example:

in host1:/etc/network/interfaces

auto tap9
iface tap9 inet static
    pre-up tunctl -u nick -t $IFACE
    post-down tunctl -d $IFACE

in host2:/etc/network/interfaces

auto tap9
iface tap9 inet static 
    pre-up tunctl -u nick -t $IFACE
    post-down tunctl -d $IFACE

Now you can ‘ifup’ those interfaces, and then start the VPN by running:

user@host2$  ssh -o Tunnel=Ethernet -w9:9 host1

And the tunnel will be up and running, without needing to create the tunnel as root. You could easily take this one further for an automatic tunnel, setting up an guest user ‘vpn’ in a chroot or similar who exists only to manage the tunnels.

Be the first to like this post.

11 Responses to ssh tunnels with tap and -w

  1. Paul says:

    But openvpn is probably easier :P

  2. nickzoic says:

    Yeah, well, probably. But its still a handy little technique, esp. given things like ssh over dns tunnelling and similar perversions.

  3. Jai says:

    God, it’s all so *boring*. Don’t you ride motorbikes anymore?

  4. Pierpaolo says:

    Hi nickzoic ,

    thanks for share your information.
    I’m doing the same tests , but I have a doubt.
    I’m creating a vpn with openssh and tun/tap device , so the two linux boxs have a virtual ip to use for connecting.
    I want to create a “star” VPN were a central server permit to the other client to communicate between their by the server , so very packets between the clients are sended by the server.

    Sorry for my bad explanation , I hope that is sufficient clear for uderstand.

    Best regards


    • nickzoic says:

      Can’t see why it wouldn’t work, but you’d have to enable IP routing on the central host, like so (on Linux);

      # echo 1 > /proc/sys/net/ipv4/ip_forward

      Under Ubuntu, you can automate this in /etc/sysctl.conf. As Paul points out above, this is probably more like a job for OpenVPN though.

  5. Juac says:

    Here is a script that automates the whole process, eliminating the need to touch config files. I wrote it today impulsed by the amazingness felt when i knew you could do L2 tunnels with ssh %P. I wish more people were using this technique, so strange it isn’t more widespread.



    # prereqs:
    # remote host’s sshd_config must have “PermitRootLogin=no”, “AllowUsers user”, and “PermitTunnel=yes”
    # “tunctl”, in debians it is found in uml-utils, redhats another (dont remember but “yum provides tunctl” must tell)
    # remote user must be able to sudo-as-root
    # can opt by routing as in this case or soft bridge with brctl and you get full remote ethernet segment membership :D
    # that last i think i’ll implement later as an option
    # other stuff to do is error checking, etcetc, this is just as came from the oven

    sshflags=’-Ap 2020 -i /path/to/some/authkey’

    if [ "$1" == "start" ]; then
    echo setting up local tap …
    ltap=$(tunctl -b)
    ifconfig $ltap ${vpn%%?/*}2/${vpn##*/} up

    echo setting remote configuration and enabling root login …
    rtap=”ssh $sshflags $userhost sudo ‘bash -c \”rtap=\\\$(tunctl -b); echo \\\$rtap; ifconfig \\\$rtap ${vpn%%?/*}1/${vpn##*/} up; iptables -A FORWARD -i \\\$rtap -j ACCEPT; iptables -A FORWARD -o \\\$rtap -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -s ${vpn%%?/*}2 -j SNAT –to \\\$(ip r | grep $rnet | sed \\\”s/^.*src \\\(.*\\\$\\\)/\1/g\\\”); sed -i -e \\\”s/\\\(PermitRootLogin\\\).*\\\$/\1 without-password/g\\\” -e \\\”s/\\\(AllowUsers.*\\\)\\\$/\1 root/g\\\” /etc/ssh/sshd_config; /usr/sbin/sshd -t\”‘”
    rtap=$(sh -c “$rtap”)

    echo setting up local routes …
    # since my ISP sucks with transparent filters (i can’t opt for another where i live), i’ll just use my work net as gateway
    ip r a $(ip r | grep default | sed “s/default/${userhost##*@}/”)
    ip r c default via ${vpn%%?/*}1 dev $ltap

    echo bringing up the tunnel and disabling root login …
    ssh $sshflags -f -w ${ltap##tap}:${rtap##tap} -o Tunnel=ethernet -o ControlMaster=yes -o ControlPath=/root/.ssh/vpn-$userhost-l$ltap-r$rtap root@${userhost##*@} bash -c “\”sed -i -e ‘s/\(PermitRootLogin\).*\$/\1 no/g’ -e ‘s/\(AllowUsers.*\) root\$/\1/g’ /etc/ssh/sshd_config; /usr/sbin/sshd -t\”"

    echo connected.

    # STOP VPN
    elif [ "$1" == "stop" ]; then
    echo searching control socket and determining configuration …
    controlpath=$(echo /root/.ssh/vpn-$userhost*)
    ltap=${controlpath%%-rtap*} && ltap=tap${ltap##*-ltap}
    rtap=${controlpath##*rtap} && rtap=tap${rtap%%-*}

    echo bringing the tunnel down …
    ssh $sshflags -o ControlPath=$controlpath -O exit $userhost

    echo restoring local routes …
    ip r c default $(ip r | grep ${userhost##*@} | sed “s/${userhost##*@}\(.*$\)/\1/g”)
    ip r d ${userhost##*@}

    echo restoring remote configuration …
    sh -c “ssh $sshflags $userhost sudo ‘bash -c \”tunctl -d $rtap; iptables -D FORWARD -i $rtap -j ACCEPT; iptables -D FORWARD -o $rtap -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -s ${vpn%%?/*}2 -j SNAT –to \$(ip r | grep $rnet | sed \”s/^.*src \(.*\$\)/\1/g\”)\”‘”

    echo deleting local tap …
    tunctl -d $ltap

    echo disconnected.

    • nickzoic says:

      Thanks Juac. That’s an interesting approach, and you’ve considered iptables setup which I hadn’t …


  6. halil baysal says:


    Just a quick reaction to “Juac”,
    This is a L3 tunneling system.
    SSH with -w option that is.

    And btw you can even take it a little bit further by,

    My Setup
    Work Network = 172.16.x.x / Work Computer ( mac-mini ) = 172.16.x.x
    Home Network = 192.168.0.x / Home Server =
    ( VPN Network = 10.0.0.x ) Server = / Client =

    So i ssh to my server, who has ip-forwarding on, i give my work computer the ip and my server Now i can communicate with private L3 scheme with my server at home. I Add to my router the following static route
    “ip route 1 ”

    If the ssh server isn’ the machine that is doing the routing for you on the remote network, you must then add a static route on the router so the packets destined to the vpn-client from your home network know the route back. This means you can communicate with every other computer on the remote network. And no, ip-forwarding does not do this.

    Because the host on the remote network gets a request from an ip he doens’t know the path back to on L2, ( your ip from the remote location, in my case, there is no natting ;) since arp is only for the local configured subnet ) and sends the reply to the default route he has, to be routed on L3 , because is a different subnet. And your router sends it back to your server, and your server exactly knows where the packets should go and you got a fully working vpn.
    I Can do natted routing on my work computer with iptables, so that i can reach not only my workstation at work but every node on the network from home over the tunnel. But for me this isn’t necessary.

    you can now add static routes on your client ( which is my work computer ), which will be tunneled ( encrypted ) to the internet and back from your home. Meaning if i add specific ip routes to specific sites the sys admins at work can’t know what i’m doing, they only see encrypted ssh packets.
    I also even use the google dns server and also have a static route for that through the tunnel ( ). DNS requests are open and unencrypted, everyone with a sniffer can see what you are requesting, so by statically routing your dns server’s traffic through the tunnel they also won’t know which sites you are visiting EXCEPT the ones you WANT them to see which are routed NOT through the tunnel.

    Halil Baysal
    Network Engineer

    • Juac says:

      Good suggestions, it’s good to have information on routing for other scenarios.

      “This means you can communicate with every other computer on the remote network. And no, ip-forwarding does not do this.”

      I understand this as “setting net.ipv4.ip_forward=1 does not do this in itself”, so you have to setup the routing or nat as necessary.

      In my case i ssh’ed into a machine that also routes, so i had (after setting up my local tunnel as gateway for the remote net and snat on the remote server) full access too. These are all different alternatives.

      However, this is what most amazes me: if you use “-w -o Tunnel=ethernet” ssh does send ethernet packets over a tap device, and if you bridge with brtcl the tap devices at both endpoint you’ll have full ethernet bridging over IP (EoIP) more exactly over the ssh tunnel, which is remarcable to have in ssh and few people knows that ssh+tap+brctl are capable of that.

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>