OpenSSH on R7000

From DD-WRT Wiki

(Difference between revisions)
Jump to: navigation, search
Revision as of 22:50, 28 February 2014 (edit)
Magick777 (Talk | contribs)
(Use tunnel as default route)
← Previous diff
Revision as of 22:50, 28 February 2014 (edit) (undo)
Magick777 (Talk | contribs)
(Use tunnel as the default route)
Next diff →
Line 214: Line 214:
This will connect your client to the router using a point-to-point tunnel, set up a route between the client router and the server, then route traffic for 192.168.1.x via the tunnel. If you merely want hosts on LAN A to be able to talk to hosts on LAN B and vice versa, but keeping their own separate routes to the rest of the world, then the job is done. This will connect your client to the router using a point-to-point tunnel, set up a route between the client router and the server, then route traffic for 192.168.1.x via the tunnel. If you merely want hosts on LAN A to be able to talk to hosts on LAN B and vice versa, but keeping their own separate routes to the rest of the world, then the job is done.
-===== Use tunnel as the default route =====+===== Use the tunnel as the default route =====
If you want all traffic from the client router to go via the VPN, i.e. to use the Internet connection of the target router, then you'll need a couple of extra steps, as per the Layer 2 section. We'll need to change the client's default route, and before we do that, we need to preserve a route to the SSH server. We'll also want to have the client use DD-WRT's DNS server, which is only configured to respond on br0 by default. And, finally, we'll want to have the router perform NAT for any traffic from our VPN clients being routed out of the WAN interface. If you want all traffic from the client router to go via the VPN, i.e. to use the Internet connection of the target router, then you'll need a couple of extra steps, as per the Layer 2 section. We'll need to change the client's default route, and before we do that, we need to preserve a route to the SSH server. We'll also want to have the client use DD-WRT's DNS server, which is only configured to respond on br0 by default. And, finally, we'll want to have the router perform NAT for any traffic from our VPN clients being routed out of the WAN interface.

Revision as of 22:50, 28 February 2014

Contents

Use cases for OpenSSH

For regular SSH/SFTP access to the router, DD-WRT's built-in dropbear SSH client and server are perfectly adequate. However, OpenSSH offers a few useful features over and above dropbear, specifically

  • SOCKS5 proxy over SSH
  • PPP connections over SSH
  • VPN connections over SSH

Installing OpenSSH

Prerequisites

You will need to install the packages from a compatible Optware repository. OpenSSH packages are available in the imx6 repository; the latest version of OpenSSH is in magick's devel repo. Make sure that you have Optware working properly and can install packages successfully.

Client

If all you want is the client, then it is easily installed with

opkg install openssh-client

This will be sufficient to enable you to create an on-the-fly SOCKS5 proxy on the router, connected to the SSH server of your choice. You may wish to look at public key authentication, server keep alive, and the reconnect option, if you want this connection to be permanent.

Server

Installing the OpenSSH server is slightly more complicated. First, let's relocate dropbear to another port so we don't lose SSH access.

Change the dropbear port

You can change the port that dropbear listens on with:

nvram set sshd_wanport=2222
nvram set sshd_port=2222
stopservice sshd
startservice sshd

As this will disconnect your SSH connection, you can also paste these commands into the Web interface and click Run Commands, then SSH to the router on port 2222 to check that it worked. If it did, we now have port 22 free for OpenSSH.

Install and configure the OpenSSH server

opkg update
opkg install openssh-server
sshd_config

Now, edit /opt/etc/ssh/sshd_config to your requirements. Most of the defaults should be OK, but some things I changed included:

  • have it use the same host keys as dropbear (in /tmp/root/.ssh)
    • dropbearconvert dropbear openssh /tmp/root/.ssh/ssh_host_rsa_key /tmp/root/.ssh/openssh_host_rsa_key
    • dropbearconvert dropbear openssh /tmp/root/.ssh/ssh_host_dss_key /tmp/root/.ssh/openssh_host_dsa_key
    • put the latter filenames in sshd_config
    • the keys are in /tmp, written from nvram, so we'll need to perform the key conversions on every startup
  • turned on ClientAliveInterval 60 and ClientAliveCountMax 3
  • turned on PermitTunnel
  • commented out the SFTP subsystem as we haven't installed it yet
  • set UsePrivilegeSeparation no, we don't have an sshd user and will run as root
start sshd manually

To start sshd, the command line we need is

LD_LIBRARY_PATH=/opt/usr/lib /opt/usr/sbin/sshd -f /opt/etc/ssh/sshd_config

which points it to the correct OpenSSL version and the desired config file. If all is well, this should return without error, your sshd process should be listening on port 22 and you should be able to gain SSH access to the router on port 22 using your normal credentials.

enable SFTP subsystem

So far, so good, but whilst this enables some new functionality, it is currently a regression as compared with dropbear, because SFTP doesn't work. Fix that with:

opkg install openssh-sftp-server

then edit sshd_config to enable the SFTP subsystem and point it to /opt/usr/lib/sftp-server - you should now find that SFTP works as well. Issue a SIGHUP to sshd to reload the config file, of course.

start sshd automatically on startup

To have the OpenSSH server run automatically on DD-WRT startup, we need to convert the dropbear keys supplied by DD-WRT, and then run the required command line. Adding

dropbearconvert dropbear openssh /tmp/root/.ssh/ssh_host_rsa_key /tmp/root/.ssh/openssh_host_rsa_key
dropbearconvert dropbear openssh /tmp/root/.ssh/ssh_host_dss_key /tmp/root/.ssh/openssh_host_dsa_key
LD_LIBRARY_PATH=/opt/usr/lib /opt/usr/sbin/sshd -f /opt/etc/ssh/sshd_config

to your DD-WRT startup commands should do the trick, and you should now have a fully functional OpenSSH server on port 22.

Using OpenSSH for VPN connections

Server & account configuration

We assume here that DD-WRT is the OpenSSH server, providing access to remote clients. You'll want PermitTunnel enabled in your sshd_config, and you will need to be root at both ends of the connection if you intend to create tunnels and change routes on the fly.

Client configuration

You will almost certainly want to have set up passwordless, public-key based SSH access between client and server before you try to use it for VPN purposes. Put your public key into the relevant section of the DD-WRT web interface to have it stored in nvram and copied to the right place on startup. You will also want to be sure that on your client, you have OpenSSH client v4.3 or newer, root access, ability to create tun/tap interfaces, and use of routing tools.

Background reading

See pvpn for a wrapper script that supports two approaches to VPN over SSH; it can use the OpenSSH Layer 3 VPN built into versions 4.3 and newer and/or it can use PPP over SSH (you'll need pppd at both ends, of course). See also start_vpn.sh, but don't expect either of these to work out of the box. Also read OpenSSH-based VPNs, which provides a good overview of what we're trying to do.

Bridged Layer 2 VPN

The most typical use case with DD-WRT as a home router is for a bridged VPN, which means that the client, when connected, is bridged to the local LAN as though it were physically plugged into an Ethernet port. The client should receive an IP address from the DHCP server on the local LAN in the usual way if it requests one via DHCP, and should be able to reach everything on the LAN as if it were plugged in directly.

To make that happen, we'll need a layer-2 tunnel (i.e. tap interfaces at both ends) per client; at the server end on DD-WRT, this interface will be added into the existing LAN bridge br0, which is already a bridge of the wired Ethernet ports and the wireless Ethernet adaptors on the router. Think of it as plugging an additional virtual network adaptor into that bridge, and using a layer 2 VPN (over SSH, over TCP/IP) as the virtual "cable". On the client end of that "cable", we have to configure our new virtual network adaptor exactly like we'd configure a physical one: either provide it with static IP address and routing settings, or run a DHCP client on it to achieve the same via dynamic host configuration.

Manual configuration

Basing ourselves on that HOWTO, we first need to create the tap devices. On DD-WRT, I had no luck with "ip tuntap add tap140 mode tap" (our ip command doesn't seem to support it), no luck with tunctl (not present), no luck installing either from Optware - but, we have an OpenVPN binary, and it can be used to create tun/tap interfaces for us, so let's resort to that.

That HOWTO is out of date; OpenSSH can create the tap interfaces for us on the fly. So, let's start by creating the two tunnelled interfaces with (on the client):

ssh -fN -o Tunnel=ethernet -w 140:140 root@router

which will create a device "tap140" at each end, and bridge the two tap devices together using OpenSSH. What it doesn't do, however, is tell DD-WRT (or the client) how to make use of the VPN bridge. So, we need to tell DD-WRT to put the tap interface into promiscuous mode, and add it to DDWRT's existing LAN bridge br0.

ssh root@router "ifconfig tap140 up promisc && brctl addif br0 tap140"

should achieve what we need at the router end. Then we need to have the client make use of the connection, which we can do either by configuring a static address and routing, or simply by using DHCP. However, before we change the default route to the tap device, we need to ensure that we have a route to the SSH server itself, using our underlying Internet connection. Otherwise, we change the default route and break the connection.

The following snippet looks at the current default route (on my phone this could be GPRS or WiFi), resolves the servername to an IP address, deletes any previous route for the same IP and adds a route to the server's IP address using whatever is the default route at the time we build the tunnel.

# Before we change our default route, make sure that we have a route to the SSH server
CURRENT=`ip route show | grep default | sed -e s/default\ //g`
HOST=`echo $REMOTE | sed -e s/root@//`
IP=`host $HOST | awk '{print $4}'`
ip route del $IP >/dev/null 2>&1
ip route add $IP $CURRENT

Then we can run something like

udhcpc -i tap140

and your client should make a DHCP request across the tunnel, receive a DHCP lease from DD-WRT, configure the tap adapter with the relevant settings and add the necessary default routes and DNS settings. Your client will now use the VPN as default connection.

To take it down again, use something like

ifconfig tap140 down
kill -TERM `pgrep -f "ethernet -w 140:140"`

which will get rid of the interface and the SSH process. You don't need to worry about the router end of things; the tap device will be destroyed when the SSH session is killed and will drop out of the bridge naturally when that happens. You may also have to fix your DNS server settings, depending upon the settings that were made by udhcpc. The script below does this by backing up /var/run/resolv.conf before we make any changes, and restoring it as part of the takedown, but you may have to vary this to suit the DNS arrangements on your client.

Scripted configuration

The above can easily be abstracted into a script to be installed on your client as e.g. /usr/local/bin/vpnssh

#!/bin/sh

REMOTE="root@router.wherever.net"
DEVNUM="140"

[ "$1" == "down" ] && ifconfig tap$DEVNUM down && kill -TERM `pgrep -f $DEVNUM:$DEVNUM` >/dev/null 2>&1 && cp /var/run/resolv.conf.old /var/run/resolv.conf && exit

kill -TERM `pgrep -f $DEVNUM:$DEVNUM` >/dev/null 2>&1
cp /var/run/resolv.conf /var/run/resolv.conf.old
ssh -fN -o Tunnel=ethernet -w $DEVNUM:$DEVNUM $REMOTE
ssh $REMOTE "ifconfig tap$DEVNUM up promisc && brctl addif br0 tap$DEVNUM"

# Before we change our default route, make sure that we have a route to the SSH server
CURRENT=`ip route show | grep default | sed -e s/default\ //g`
HOST=`echo $REMOTE | sed -e s/root@//`
IP=`host $HOST | awk '{print $4}'`
ip route del $IP >/dev/null 2>&1
ip route add $IP $CURRENT
udhcpc -i tap$DEVNUM
echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all

Then you can just run "vpnssh" to connect, and "vpnssh down" to disconnect. Works well for connecting a Linux phone (Nokia N900) to the local network when needed.

Routed Layer 3 VPN

Bridging is great for convenience and for access to the entire LAN, but copying Ethernet frames over SSH may not always be what you want to do, and it's not bandwidth efficient. Let's say that my home network is in 192.168.1.x, and I have a mobile hotspot with connected clients in 192.168.2.x; we'll use a third subnet for the point-to-point connection between them.

In this case, we want a routed layer 3 solution (IP tunnelling as opposed to Ethernet bridging), such that our mobile hotspot still routes connections to the Internet through its own WAN connection, but routes any connections to 192.168.1.x over a point-to-point tunnel to DD-WRT. DD-WRT should of course reciprocate, routing anything for 192.168.2.x over the tunnel to the mobile hotspot.

Create the point-to-point tunnel

The following snippet creates the point-to-point tunnel, with the client at 192.168.22.2 and the router at 192.168.22.1, plus it adds a route on the client to the router, and also on the router to the client. You should be able to ping server from client, and vice versa, using the PPP tunnel addresses in 192.168.22.x.

#!/bin/sh

REMOTE="root@router.wherever.net"
DEVNUM="140"

[ "$1" == "down" ] && ifconfig tun$DEVNUM down && kill -TERM `pgrep -f $DEVNUM:$DEVNUM` >/dev/null 2>&1 && exit

kill -TERM `pgrep -f $DEVNUM:$DEVNUM` >/dev/null 2>&1
ssh -fN -o Tunnel=point-to-point -w $DEVNUM:$DEVNUM $REMOTE
ssh $REMOTE "ifconfig tun$DEVNUM up 192.168.22.1 netmask 255.255.255.255 && route add 192.168.22.2 dev tun$DEVNUM" 
ifconfig tun$DEVNUM up 192.168.22.2 netmask 255.255.255.255
route add 192.168.22.1 dev tun$DEVNUM
Route traffic between two networks

The above only takes care of the point-to-point tunnelling; it does not (yet) route traffic over that connection. If we want to route traffic between 192.168.1.x (server) and 192.168.2.x (client router), as per the example above, then we need to add something to the effect of:

route add 192.168.1.0/24 via 192.168.22.1 dev tun140
ssh root@router route add 192.168.2.0/24 via 192.168.22.2 dev tun140

which makes the final script look something like

#!/bin/sh

REMOTE="root@router.wherever.net"
DEVNUM="140"

[ "$1" == "down" ] && ifconfig tun$DEVNUM down && kill -TERM `pgrep -f $DEVNUM:$DEVNUM` >/dev/null 2>&1 && exit

kill -TERM `pgrep -f $DEVNUM:$DEVNUM` >/dev/null 2>&1
ssh -fN -o Tunnel=point-to-point -w $DEVNUM:$DEVNUM $REMOTE
ssh $REMOTE "ifconfig tun$DEVNUM up 192.168.22.1 netmask 255.255.255.255 && ip route add 192.168.22.2 dev tun$DEVNUM"
ifconfig tun$DEVNUM up 192.168.22.2 netmask 255.255.255.255
echo 1 > /proc/sys/net/ipv4/ip_forward
ip route add 192.168.22.1 dev tun$DEVNUM
ip route add 192.168.1.0/24 via 192.168.22.1 dev tun$DEVNUM
ssh $REMOTE "ip route add 192.168.2.0/24 via 192.168.22.2 dev tun$DEVNUM"

This will connect your client to the router using a point-to-point tunnel, set up a route between the client router and the server, then route traffic for 192.168.1.x via the tunnel. If you merely want hosts on LAN A to be able to talk to hosts on LAN B and vice versa, but keeping their own separate routes to the rest of the world, then the job is done.

Use the tunnel as the default route

If you want all traffic from the client router to go via the VPN, i.e. to use the Internet connection of the target router, then you'll need a couple of extra steps, as per the Layer 2 section. We'll need to change the client's default route, and before we do that, we need to preserve a route to the SSH server. We'll also want to have the client use DD-WRT's DNS server, which is only configured to respond on br0 by default. And, finally, we'll want to have the router perform NAT for any traffic from our VPN clients being routed out of the WAN interface.

The following script achieves this on a Nokia N900 (acting in this scenario as both VPN client and MiFi router); traffic from the N900 is routed via the SSH tunnel, and so is all traffic from any machines wirelessly connected to the phone via QtMobileHotspot. Both the phone and any machines connected to it can reach hosts in 192.168.1.x, and can reach the Internet via the router's Internet connection.

#!/bin/sh

REMOTE="root@router.wherever.net"
DEVNUM="140"

if [ "$1" == "down" ]; then
  ifconfig tun$DEVNUM down >/dev/null 2>&1 
  kill -TERM `pgrep -f $DEVNUM:$DEVNUM` >/dev/null 2>&1 
  rm -f /var/run/resolv.conf
  mv /var/run/resolv.conf.gprs /var/run/resolv.conf >/dev/null 2>&1
  mv /var/run/resolv.conf.wlan0 /var/run/resolv.conf >/dev/null 2>&1
  if [ ! -f "/var/run/resolv.conf" ]; then
    echo "nameserver 8.8.8.8" > /var/run/resolv.conf
  fi
  if [ ! -z "`ifconfig gprs0 2>/dev/null`" ]; then
    route add default dev gprs0;
  fi
exit
fi

kill -TERM `pgrep -f $DEVNUM:$DEVNUM` >/dev/null 2>&1
echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all
ssh -fN -o Tunnel=point-to-point -w $DEVNUM:$DEVNUM $REMOTE
ssh $REMOTE "ifconfig tun$DEVNUM up 192.168.22.1 netmask 255.255.255.255 && ip route add 192.168.22.2 dev tun$DEVNUM"
ifconfig tun$DEVNUM up 192.168.22.2 netmask 255.255.255.255
echo 1 > /proc/sys/net/ipv4/ip_forward
ip route add 192.168.22.1 dev tun$DEVNUM
ip route add 192.168.1.0/24 via 192.168.22.1 dev tun$DEVNUM
ssh $REMOTE "ip route add 192.168.2.0/24 via 192.168.22.2 dev tun$DEVNUM"

# Before we change our default route, make sure that we have a route to the SSH server
CURRENT=`ip route show | grep default | sed -e s/default\ //g`
HOST=`echo $REMOTE | sed -e s/root@//`
IP=`host $HOST | awk '{print $4}'`
ip route del $IP >/dev/null 2>&1
ip route add $IP $CURRENT

# Now we can change the default route
ip route del default
ip route add default via 192.168.22.1 dev tun$DEVNUM

# And the default DNS server
mv /var/run/resolv.conf /var/run/resolv.conf.bak >/dev/null 2>&1
echo "nameserver 192.168.1.1" > /var/run/resolv.conf
ssh $REMOTE "sed -i s/interface=br0/except-interface=vlan2/ /tmp/dnsmasq.conf && killall -TERM dnsmasq && dnsmasq --conf-file=/tmp/dnsmasq.conf"
ssh $REMOTE "iptables -t nat -D POSTROUTING -s 192.168.22.0/24 -o vlan2 -j MASQUERADE"
ssh $REMOTE "iptables -t nat -I POSTROUTING -s 192.168.22.0/24 -o vlan2 -j MASQUERADE"
ssh $REMOTE "iptables -t nat -D POSTROUTING -s 192.168.2.0/24 -o vlan2 -j MASQUERADE"
ssh $REMOTE "iptables -t nat -I POSTROUTING -s 192.168.2.0/24 -o vlan2 -j MASQUERADE"