====== "Split" VPN routing with OpenWRT/Tomato ====== This is an explanation of how I route traffic for specific LAN IP addresses (my NAS) through an OpenVPN connection on a router running OpenWRT/Tomato firmware. I'm also running "tinyproxy" on the NAS so other clients can use the VPN connection on the router as necessary. I've never used awk before and didn't devote a lot of time to these scripts, so save your judgement please. ===== Pre-requisite: Optware ===== Get optware installed and mounting automatically on boot. Set your USB support with the option ''Automatically mount all partitions to sub-directories in /mnt.'' Set the USB ''Run after mounting'' script: if [ -d /mnt/optware ]; then mount -o bind /mnt/optware /opt fi Contents of Administration->Scripts->Init: //(I don't use this anymore but figured I'd leave it documented)// #Mount optware echo "LABEL=optware /opt ext3 defaults 1 1" >> /etc/fstab /bin/mount /opt /opt These are the packages I have installed: ipkg-opt - 0.99.163-10 - The Itsy Package Manager libcurl - 7.24.0-1 - Curl is a command line tool for transferring files with URL syntax, supporting FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FI libidn - 1.25-1 - GNU Libidn is an implementation of the Stringprep, Punycode and IDNA specifications defined by the IETF Internationalized Domai netcat - 1.10pl32-5 - TCP/IP swiss army knife. openssl - 0.9.7m-6 - Openssl provides the ssl implementation in libraries libcrypto and libssl, and is needed by many other applications and librari uclibc-opt - 0.9.28-13 - micro C library for embedded Linux systems wget-ssl - 1.12-2 - A network utility to retrieve files from the Web zlib - 1.2.5-1 - zlib is a library implementing the 'deflate' compression system. ===== OpenVPN settings ===== * Ensure the option "Create NAT on tunnel" is checked * Advanced settings: persist-key persist-tun tls-client auth-user-pass /opt/etc/pia/pia.txt comp-lzo verb 1 reneg-sec 0 route-noexec route-up /opt/etc/scripts/vpnrouteup.sh down /opt/etc/scripts/vpnroutedown.sh Two files are necessary: Contents of pia.txt is VPN username on first line, password on second line. Contents of pia_client_id is a random string: ''head -n 100 /dev/urandom | md5 > pia_client_id'' ===== VPN Scripts ===== The vpnrouteup.sh script sets a second routing table with ip route to direct traffic for ip's listed in "vpndhosts" (space delimited) through the VPN connection. It then calls a script to get a forwarding port from my VPN provider, which then calls a script to update the transmission client config that's running on the NAS. I don't remember why I needed to use my ISP's DNS servers over the VPN's DNS servers, but there must be a good reason. Maybe it was because I couldn't figure out how to dynamically update the /etc/resolv.conf file on my NAS. If you don't need to use your ISPs DNS, comment out the third for loop. Contents of vpnrouteup.sh: #!/bin/sh privateinternetaccess=$(nslookup www.privateinternetaccess.com |grep ^A |tail -1 |awk -F ' ' '{ print $3 }') vpndhosts="192.168.xxx.xxx" dnsservers="89.xxx.xxx.xxx 89.xxx.xxx.xxx" tun_iface=$(ifconfig|grep tun|awk '{ print $1 }') tun_inet=$(ifconfig $tun_iface|grep P-t-P|awk -F ':' '{ print $2 }'|awk -F ' ' '{ print $1 }') tun_ptp=$(ifconfig $tun_iface|grep P-t-P|awk -F ':' '{ print $3 }'|awk -F ' ' '{ print $1 }') tun_gw=$(echo $tun_inet |awk -F '.' '{print $1"."$2"."$3".1"}') tun_net=$(echo $tun_inet |awk -F '.' '{print $1"."$2"."$3".0/24"}') ip route add 0.0.0.0/1 via $tun_ptp dev $tun_iface table 10 ip route add 128.0.0.0/1 via $tun_ptp dev $tun_iface table 10 ip route add $tun_gw via $tun_ptp dev $tun_iface metric 1 table 10 ip rule add from $tun_net table 10 ip rule add to $tun_net table 10 for host in $privateinternetaccess;do ip rule add from $host table 10;ip rule add to $host table 10;done for host in $vpndhosts;do ip rule add from $host table 10;done for host in $vpndhosts;do for server in $dnsservers;do ip rule add from $host to $server lookup main;ip rule add from $server to $host lookup main;done;done /opt/etc/scripts/port_forward_update.sh & Contents of vpnroutedown.sh: #!/bin/sh for rule in $(ip rule list |grep -v "all lookup"|awk -F ":" '{ print $1 }');do ip rule delete pref $rule;done ===== Port-forward Script ===== This is specific to Private Internet Access's method for getting a port forwarding port via an HTTPS request. It uses iptables PREROUTING with the NAT table to forward the port to the internal LAN address. Once the port is retrieved, if there's no forwarding enabled currently, it gets enabled. If it's different from what is currently forwarded, it replaces what's currently in place. Once the ports are setup it calls the script to update the transmission config. Contents of port_forward_update.sh: #!/bin/sh #to-do: get current nat preroute rule and compare port, if it's the same do nothing, if it's different, delete the old and add the new insidehost=192.168.xxx.xxx pia_user=$(head -1 /opt/etc/pia/pia.txt) pia_pw=$(tail -1 /opt/etc/pia/pia.txt) pia_client_id=$(head -1 /opt/etc/pia/pia_client_id) tun_iface=$(ifconfig | grep tun | awk '{ print $1 }') echo $tun_iface tun_inet=$(ifconfig $tun_iface|grep P-t-P|awk -F ':' '{ print $2 }'|awk -F ' ' '{ print $1 }') echo $tun_inet #get our forwarding port from PIA forwarded_port=$(/opt/bin/wget --post-data="user=$pia_user&pass=$pia_pw&client_id=$pia_client_id&local_ip=$tun_inet" \ --no-check-certificate \ -q -O - https://www.privateinternetaccess.com/vpninfo/port_forward_assignment \ | awk -F ':' '{ print $2 }'| awk -F '}' '{ print $1 }') echo $forwarded_port current_port=$(iptables -t nat -L PREROUTING -vn |grep tun11 |awk -F ':' '{ print $2 }'|awk -F ' ' '{ print $1 }'|tail -1) current_rule=$(iptables -t nat -L PREROUTING -vn --line-numbers|grep tun11 |cut -b1-2) if [ -z "$current_port" ] && [ -z "$current_rule" ] #we have no existing port forward and no existing current rule in place then echo inside 1 iptables -t nat -I PREROUTING -p tcp -i tun11 --dport $forwarded_port -j DNAT --to $insidehost:$forwarded_port else if [ "$current_port" -ne "$forwarded_port" ] #current port forward and port returned by website do not match then echo inside 2 #delet the current rule and set forwarding to our NAS host iptables -t nat -D PREROUTING $current_rule iptables -t nat -I PREROUTING -p tcp -i tun11 --dport $forwarded_port -j DNAT --to $insidehost:$forwarded_port /opt/etc/scripts/transmission_port_update.sh $current_port $forwarded_port else echo current and requested ports match fi fi ===== Transmission Update Script ===== Transmission supports updating client configuration settings on the fly via a ''kill -HUP''. Open file handles remain open, so if you're like me and you have a download directory that is used only temporarily while something other than transmission moves your files around, no problem. In order to ssh to your NAS from OpenWRT, you need a set of ssh keys. On OpenWRT that command is ''dropbearkey -t rsa -f ~/.ssh/id_rsa''. Copy the .pub key file to your nas and append that key to your authorized_keys file. As always, make sure your .ssh dir is set to 700 and authorized_keys set to 600. Contents of transmission_port_update.sh: #!/bin/sh echo $1 to $2 transmissionhost=192.168.33.200 settingspath=/usr/local/transmission/var settingsfile=$settingspath/settings.json ssh -i /opt/etc/pia/id_rsa root@$transmissionhost cp $settingsfile $settingsfile.bak #ssh -i /opt/etc/pia/id_rsa root@$transmissionhost ls -l $settingsfile $settingsfile.bak ssh -i /opt/etc/pia/id_rsa root@$transmissionhost 'cat '"'$settingsfile.bak'"' | sed -e s#\ \ \ \"peer-port\"\:\ [0-9][0-9][0-9][0-9][0-9],#\ \ \ \"peer-port\"\:\ '"'$2'"',#g > '"'$settingsfile'"'' ssh -i /opt/etc/pia/id_rsa root@$transmissionhost grep peer-port $settingsfile $settingsfile.bak ssh -i /opt/etc/pia/id_rsa root@$transmissionhost killall -HUP transmission-daemon ===== Scheduled port forward update ===== My VPN provider requires that you routinely refresh the request for a forwarded port. That is scheduled in OpenWRT via Administration->Scheduler. Execute ''/opt/etc/scripts/port_forward_update.sh'' every 30 minutes to keep the port enabled. ===== Appendix ===== [[unix:networking:openwrt_routing:notes|Original on-the-fly Notes]]