OpenSSH on R7000

From DD-WRT Wiki

Revision as of 14:58, 1 March 2014 by Magick777 (Talk | contribs)
Jump to: navigation, search

Contents

Use cases for OpenSSH vs. dropbear

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
  • Point-to-point (layer 3) connections over SSH
  • VPN (layer 2) 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 available for the R7000 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-libwrap

NB: the regular openssh-server package is also fine, but this one is compiled with support for TCP wrappers, meaning you can use it with BlockHosts, below.

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.

Brute force attack prevention using Blockhosts [optional]

If you've ever looked at the log files of an SSH server, you'll notice a bunch of connection/exploit attempts from machines sending hundreds of login attempts in hopes of brute-forcing the password. Obviously, this can be forcibly prevented by disabling password-based logins and forcing all users to supply public keys, but that is often less than convenient. So, you want password access, but not brute-forcing attempts.

Enter BlockHosts, a Python script that monitors the log files and blocks the IP address of anything with more than half a dozen failed logins. You'll need OpenSSH with libwrap support, libwrap compiled to use /opt/etc/hosts.allow, Python, and a handful of config files. It's not packaged for DD-WRT yet, so, the steps to install it are:

  • Download the .tar.gz on the router, probably in /tmp/root, unpack it
  • Forget the setup.py, we're using non-standard file locations so do it manually
  • Copy blockhosts.py to /opt/usr/bin
  • Copy blockhosts.cfg to /opt/etc
    • edit the config file to point to /opt/etc/hosts.allow
    • edit the config file to look in /var/log/messages for login errors
    • be sure you have syslog enabled in the DD-WRT GUI
  • Create /opt/etc/hosts.allow if not present, and have it contain the following lines:
#---- BlockHosts Additions

#bh: logfile: /var/log/auth.log
#bh: offset: 0
#bh: first line:

#---- BlockHosts Additions
sshd : ALL : spawn (/opt/usr/bin/blockhosts.py --configfile=/opt/etc/blockhosts.cfg)
sshd : ALL : allow
  • Run "blockhosts.py --configfile=/opt/etc/blockhosts.cfg" a couple of times, should return without error
  • You can add any permanent exempt allow lines above the #---- BlockHosts Additions line
  • Guilty IPs will be added between these lines with a deny rule, deleted after the age specified in the config file
  • Each successful connection to SSHd spawns blockhosts.py to check the logs for brute-forcing attempts
  • Too many failed logins and the host is blocked for a while
  • You can also use this for applications that are not libwrap-aware, see the config file to make it work with iptables.

Using OpenSSH for VPN connections

General configuration

We assume here in this section 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.

DD-WRT as VPN-over-SSH server

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.

Use cases

This is more use in Unix/Linux/BSD environments than it is to Windows users, who should probably look instead at Softether_on_R7000, or the built-in PPTP and OpenVPN support, for a much more Windows-friendly VPN solution. Possible use cases for DD-WRT as a VPN-over-SSH server include:

  • Ad-hoc VPN access for Linux phones, tablets, laptops, hotspots, anything that can run OpenSSH
    • useful combined with a Linux-based mobile hotspot, connect entire remote networks
    • connections over non-standard TCP/IP or Ethernet channels, for instance Ethernet-over-USB
  • Another VPN-over-TCP protocol if you're stuck behind an unfriendly firewall
    • works for clients on NAT-behind-NAT connections, hotspots and similarly restrictive networks
  • Routing of Linux devices/remote networks through anything available to DD-WRT, e.g. Privoxy, Tor, etc.
    • useful in circumventing mobile web filtering and web blocking in general

If what you want is a link from your router to a remote server (such as a VPS), or to your workplace, or to another SSH server where you have the necessary access, see the section on DD-WRT as client, below.

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 a point-to-point tunnel

The following snippet just 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
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


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"

DD-WRT as VPN-over-SSH client [WIP]

Use cases

An OpenSSH client on DD-WRT can be useful for making network-to-network and network-to-server connections, such as connecting to work from home, connecting a private interface to a remote virtual server, etc.

Prerequisites

  • You will need the OpenSSH client (and not the dropbear SSH client) if you wish to make VPN connections to remote SSH servers. We assume that you have installed this via Optware, as above.
  • You will need to be root at both ends of the planned connection, if you are to create VPN tunnels and adjust the routing table on demand.

Routed Layer 3 VPN

Here we assume that we want DD-WRT to connect a VPN interface to a remote server using a point-to-point link, providing us the ability to route traffic selectively over that link. For example, we have a virtual private server in a datacentre somewhere, and we want the router to connect to it, and optionally use it as a route to certain addresses.

The principle is the same as in the above section on using DD-WRT as server; we need to set up tunnel interfaces at both ends and connect them via SSH, then issue any necessary routing and firewalling incantations to achieve what we want from the link.