artephius 4 Posted ... Hi all,I was just polishing off my airvpn setup today and decided to share how I use it on linux. I haven't seen any other posts demonstrating this level of control over your vpn traffic so I thought it would make a good addition to the forum! This setup allows me complete control of what traffic uses the vpn, what doesn't, and provides protection using iptables in case of connection failure. This way I can pretty much leave the vpn connected all the time and have netflix running on the vpn while online banking off the vpn and so on and so on... Essentialy its a "policy based routing" setup which uses iptables to mark packets which you want sent through the vpn (or the ones you don't). ip rules then match those packets and send them on their way using custom routing tables. On my setup I mark most traffic for the vpn, but I like to keep my email (imap,smtp,etc), banking and other more sensitive traffic off the vpn (after a bad experience I had with another provider where my email mysteriously got hacked the day after I logged into it through the vpn... coincidence? I doubt it..) It's also nice to keep traffic such as ssh connections to your web hosting provider off the vpn just to avoid issues with logging in from a strange ip on the other side of the globe. I also run a local transparent proxy server (that only listens on localhost) - all traffic sent to that proxy is marked to not go out the vpn which allows me to easily bypass the vpn in my browser of choice (firefox) using a convienient proxy enabler/disabler addon (foxyproxy) whenever I want to access a website without using the vpn. NOTE: This is a ROUGH tutorial that will need some editing and additional information to make it more newbie friendly. I tried to make it as distribution independant as I could but I'm really only familiar with gentoo. It's possible I've forgotten a thing or two in the tutorial or even made some errors while simplifying my setup into a tutorial... WARNING: While I don't consider myself particularly advanced, this is definitely NOT I repeat NOT a newbie tutorial. If you don't know your way around iptables at the very least YOU WILL PROBABLY SCREW SOMETHING UP and have traffic leaks... USE THIS INFO AT YOUR OWN RISK. Needed programs:iptables (firewall)iproute2 (for managing routing tables/ policy based routing)openvpn (obviously you'll be wanting to connect to airvpn!) Optional goodies:tinyproxy (handy for temporarily bypassing the vpn if need be)firefox with foxyproxy addon (for temporarily bypassing the vpn for one or all websites) PART 1 - iptables: Lets start with iptables, after the firewall is your first line of defense!The iptables firewall is where you decide which traffic goes through the vpn and which does not. Here is a sample (and basic) iptables example that allows everything to work the way I want it to.You probably want a more comprehensive firewall script (I do) but if you're reading this tutorial you probably already have one. Pay special attention to the VPN sections of this example I did my best to comment the not so obvious things. #!/bin/bash ### Variables IPTABLES=/sbin/iptables # Ip's and Interfaces AIRVPNIF="tun0" # if to airvpn EXTIF="enp3s0" # network interface to your isp LOOPBACK_INTERFACE="lo" # however your system names it ROUTER_IP="192.168.1.1" # my lan router/gateway ### Default Policy # Set the default policy to drop $IPTABLES --policy INPUT DROP $IPTABLES --policy OUTPUT DROP $IPTABLES --policy FORWARD DROP ### Allow Local Loopback $IPTABLES -A INPUT -i lo -j ACCEPT $IPTABLES -A OUTPUT -o lo -j ACCEPT ### ### Nat ### # MASQUERADE traffic going out $EXTIF $IPTABLES -t nat -A POSTROUTING -o $EXTIF -j MASQUERADE # masquerade vpn traffic $IPTABLES -t nat -A POSTROUTING -o $AIRVPNIF -j MASQUERADE ## MANGLE table # VPN STUFF # create fwmark chains $IPTABLES -t mangle -N mark-never-vpn $IPTABLES -t mangle -F mark-never-vpn $IPTABLES -t mangle -A mark-never-vpn -j MARK --set-mark 3 $IPTABLES -t mangle -N mark-for-vpn $IPTABLES -t mangle -F mark-for-vpn $IPTABLES -t mangle -A mark-for-vpn -m mark ! --mark 3 -j MARK --set-mark 4 # don't mark packets already marked not for vpn! # this chain is where we decide which services go out the vpn, and which ones don't $IPTABLES -t mangle -N vpn-router $IPTABLES -t mangle -F vpn-router ## the following never use vpn # traffic to my router $IPTABLES -t mangle -A vpn-router -d $ROUTER_IP -j mark-never-vpn # rsync $IPTABLES -t mangle -A vpn-router -p tcp --dport 873 -j mark-never-vpn # ntp $IPTABLES -t mangle -A vpn-router -p udp --dport 123 -j mark-never-vpn # imap/smtp $IPTABLES -t mangle -A vpn-router -p tcp --dport 143 -j mark-never-vpn $IPTABLES -t mangle -A vpn-router -p udp --dport 993 -j mark-never-vpn $IPTABLES -t mangle -A vpn-router -p tcp --dport 993 -j mark-never-vpn $IPTABLES -t mangle -A vpn-router -p tcp --dport 110 -j mark-never-vpn $IPTABLES -t mangle -A vpn-router -p tcp --dport 25 -j mark-never-vpn $IPTABLES -t mangle -A vpn-router -p tcp --dport 465 -j mark-never-vpn $IPTABLES -t mangle -A vpn-router -p tcp --dport 587 -j mark-never-vpn ## always use vpn # ping $IPTABLES -t mangle -A vpn-router -p icmp --icmp-type echo-request -m conntrack --ctstate NEW -j mark-for-vpn # dns $IPTABLES -t mangle -A vpn-router -p tcp --dport 53 -j mark-for-vpn $IPTABLES -t mangle -A vpn-router -p udp --dport 53 -j mark-for-vpn # http $IPTABLES -t mangle -A vpn-router -p tcp --dport 80 -j mark-for-vpn $IPTABLES -t mangle -A vpn-router -p tcp --dport 443 -j mark-for-vpn # ftp $IPTABLES -t mangle -A vpn-router -p tcp --dport 21 -j mark-for-vpn # torrent (note: I setup my torrent clients to ONLY use these ports! # This also allows me to selectively allow or deny torrent traffic out.) $IPTABLES -t mangle -A vpn-router -p tcp --sport 54581:54589 -j mark-for-vpn $IPTABLES -t mangle -A vpn-router -p udp --sport 54581:54589 -j mark-for-vpn # Now send all outgoing traffic through vpn-router chain.. # but first mark vpn bypass proxy (tinyproxy) traffic # NOTE: since all dns traffic can use vpn without any security issues the proxied dns # requests have to be sent through vpn even though the actual http traffic is not or it will timeout # because the vpn dns server is in resolv.conf when vpn is active. # NOTE2: the --uid-owner 107 part here is how iptables determines which traffic is coming # from the local proxy server. This was the only way I could figure to do it. 107 will of course # need to be replaced by the uid tinyproxy runs under on your system for this to work! # This is NOT the uid of your normal user account or root, this is a separate user account created # exclusively for tinyproxy to use (done by default on gentoo $IPTABLES -t mangle -A OUTPUT --match owner --uid-owner 107 -p udp --dport 53 -j mark-for-vpn $IPTABLES -t mangle -A OUTPUT --match owner --uid-owner 107 -p udp --dport 53 -j RETURN $IPTABLES -t mangle -A OUTPUT --match owner --uid-owner 107 -j mark-never-vpn $IPTABLES -t mangle -A OUTPUT --match owner --uid-owner 107 -j RETURN $IPTABLES -t mangle -A OUTPUT -j vpn-router $IPTABLES -t mangle -A PREROUTING -j vpn-router # Stateful chain $IPTABLES -N STATEFUL $IPTABLES -F STATEFUL $IPTABLES -A STATEFUL -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT $IPTABLES -A STATEFUL -m conntrack --ctstate INVALID -j LOG --log-prefix '{fw} DROP STATE INVALID: ' $IPTABLES -A STATEFUL -m conntrack --ctstate INVALID -j DROP ### Rest of the chains for allowing specific things... # The main purpose for all these chains is to make my filter tables rules more easily readable # at a glance to help spot errors! Firewall errors are never good! # Allow all (be careful!) $IPTABLES -N allow-ALL $IPTABLES -F allow-ALL $IPTABLES -A allow-ALL -j ACCEPT # Allow ping $IPTABLES -N allow-ping $IPTABLES -F allow-ping $IPTABLES -A allow-ping -p icmp --icmp-type echo-request -m conntrack --ctstate NEW -j ACCEPT # Allow dns $IPTABLES -N allow-dns $IPTABLES -F allow-dns $IPTABLES -A allow-dns -p tcp --dport 53 -j ACCEPT $IPTABLES -A allow-dns -p udp --dport 53 -j ACCEPT # Allow dhcp $IPTABLES -N allow-dhcp $IPTABLES -F allow-dhcp $IPTABLES -A allow-dhcp -p udp --dport 67 -j ACCEPT $IPTABLES -A allow-dhcp -p udp --dport 68 -j ACCEPT # Allow distcc $IPTABLES -N allow-distcc $IPTABLES -F allow-distcc $IPTABLES -A allow-distcc -p tcp --dport 3632 -j ACCEPT # Allow http/https $IPTABLES -N allow-http $IPTABLES -F allow-http $IPTABLES -A allow-http -p tcp --dport 80 -j ACCEPT $IPTABLES -A allow-http -p tcp --dport 443 -j ACCEPT # Allow ftp $IPTABLES -N allow-ftp $IPTABLES -F allow-ftp $IPTABLES -A allow-ftp -p tcp --dport 21 -j ACCEPT $IPTABLES -A allow-ftp -p tcp --dport 20 -j ACCEPT # Allow ssh $IPTABLES -N allow-ssh $IPTABLES -F allow-ssh $IPTABLES -A allow-ssh -p tcp --dport 22 -j ACCEPT # Allow rsync $IPTABLES -N allow-rsync $IPTABLES -F allow-rsync $IPTABLES -A allow-rsync -p tcp --dport 873 -j ACCEPT # Allow ntp $IPTABLES -N allow-ntp $IPTABLES -F allow-ntp $IPTABLES -A allow-ntp -p udp --dport 123 -j ACCEPT # Allow whois $IPTABLES -N allow-whois $IPTABLES -F allow-whois $IPTABLES -A allow-whois -p tcp --dport 43 -j ACCEPT $IPTABLES -A allow-whois -p udp --dport 43 -j ACCEPT # Allow mail services (imap, imaps, pop3, and smtp) $IPTABLES -N allow-mail-services $IPTABLES -F allow-mail-services $IPTABLES -A allow-mail-services -p tcp --dport 143 -j ACCEPT $IPTABLES -A allow-mail-services -p udp --dport 993 -j ACCEPT $IPTABLES -A allow-mail-services -p tcp --dport 993 -j ACCEPT $IPTABLES -A allow-mail-services -p tcp --dport 110 -j ACCEPT # Allow smtp $IPTABLES -N allow-smtp $IPTABLES -F allow-smtp $IPTABLES -A allow-smtp -p tcp --dport 25 -j ACCEPT $IPTABLES -A allow-smtp -p tcp --dport 465 -j ACCEPT # gmail smtp $IPTABLES -A allow-smtp -p tcp --dport 587 -j ACCEPT # allow cups printing $IPTABLES -N allow-printing $IPTABLES -F allow-printing $IPTABLES -A allow-printing -p tcp --dport 631 -j ACCEPT $IPTABLES -A allow-printing -p tcp --dport 515 -j ACCEPT # Allow connection to vpn $IPTABLES -N allow-to-vpn $IPTABLES -F allow-to-vpn # NOTE: I sometimes hop around to different airvpn servers so I just use the protocol/port here to allow that traffic # If you always use the same server you could be more specific and specify it's IP as well $IPTABLES -A allow-to-vpn -p udp --dport 443 -j ACCEPT $IPTABLES -A allow-to-vpn -p udp --dport 2018 -j ACCEPT # allow torrent... my torrent clients are set up to use only these ports. $IPTABLES -N allow-torrent $IPTABLES -F allow-torrent $IPTABLES -A allow-torrent -p tcp --sport 54581:54589 -j ACCEPT $IPTABLES -A allow-torrent -p udp --sport 54581:54589 -j ACCEPT ### ### And finally.... the filter table... ### ## INPUT $IPTABLES -A INPUT -j STATEFUL # input from EXTIF #$IPTABLES -A INPUT -i $EXTIF -s $LAPTOP -j allow-ssh ## OUTPUT # just allow all out through the vpn... # You could lock this down more if you wanted, but I pretty much # leave it open so torrents and other things that connect out on all sorts # of random ports can use the vpn without any trouble. $IPTABLES -A OUTPUT -o $AIRVPNIF -j ACCEPT $IPTABLES -A OUTPUT -j STATEFUL # out to EXTIF # NOTE: This is where you protect yourself from leaks... don't allow ANYTHING out that is untrusted # that way if the vpn drops, iptables will block anything not specified in this section from going # out your insecure WAN interface... $IPTABLES -A OUTPUT -o $EXTIF -j allow-ping $IPTABLES -A OUTPUT -o $EXTIF -j allow-ftp $IPTABLES -A OUTPUT -o $EXTIF -j allow-ssh $IPTABLES -A OUTPUT -o $EXTIF -j allow-smtp $IPTABLES -A OUTPUT -o $EXTIF -j allow-mail-services $IPTABLES -A OUTPUT -o $EXTIF -j allow-http $IPTABLES -A OUTPUT -o $EXTIF -j allow-dns $IPTABLES -A OUTPUT -o $EXTIF -j allow-distcc $IPTABLES -A OUTPUT -o $EXTIF -j allow-rsync $IPTABLES -A OUTPUT -o $EXTIF -j allow-ntp $IPTABLES -A OUTPUT -o $EXTIF -j allow-whois $IPTABLES -A OUTPUT -o $EXTIF -j allow-to-vpn $IPTABLES -A OUTPUT -o $EXTIF -j allow-nfs $IPTABLES -A OUTPUT -o $EXTIF -j allow-printing ### Final Logging and not logging of dropped packets... # Don't log blocked broadcasts from wan... these were filling up my logs like crazy... $IPTABLES -A INPUT -p udp -d 255.255.255.255 -j DROP # log the rest... $IPTABLES -A INPUT -j LOG --log-prefix '{fw} DROP INPUT POLICY:' $IPTABLES -A OUTPUT -j LOG --log-prefix '{fw} DROP OUTPUT POLICY:' $IPTABLES -A FORWARD -j LOG --log-prefix '{fw} DROP FORWARD POLICY:' # not necessary but it makes me feel better $IPTABLES -A INPUT -j DROP $IPTABLES -A OUTPUT -j DROP $IPTABLES -A FORWARD -j DROP I'll assume you know enough about iptables to not need an explanation on how to implement such a setup... if you don't you probably shouldn't be attempting to follow this tutorial. PART 2 - openvpn + iproute2:Next we set up openvpn to connect to airvpn and the scripts necessary to make the policy routing work. You'll need to define a couple custom routing tables for iproute2 to use to separate your vpn and non vpn traffic...edit the /etc/iproute2/rt_tables file as follows: # # reserved values # 255 local 254 main 253 default 0 unspec # # local # #1 inr.ruhep ## Add these two lines 100 real_default 101 vpn ## END of stuff to add Grab a config file from airvpn (I prefer to just keep the certs/keys and all in the config file so I don't have a bunch of other files to keep track of). Next you'll need to modify the config file... I have mine saved as /etc/openvpn/openvpn.conf which allows me to start the vpn by saying /etc/init.d/openvpn start on gentoo...I'll leave it up to you to determine how you'll run openvpn on your system as I'm not really familiar with other distributions. # -------------------------------------------------------- # Air VPN | https://airvpn.org | Friday 28th of November 2014 11:59:36 PM # OpenVPN Client Configuration # AirVPN_CA-Cephei_UDP-443 # -------------------------------------------------------- client dev tun proto udp remote 184.75.214.162 443 resolv-retry infinite nobind persist-key persist-tun remote-cert-tls server cipher AES-256-CBC comp-lzo no verb 3 explicit-exit-notify 5 ## ADD THE NEXT 8 LINES: nobind # I don't remember why I use this option but I'm pretty sure there was a good reason! route-noexec # this will stop openvpn from taking over your whole internet connection up /etc/openvpn/up.sh # script to set up routing tables upon connection to server down /etc/openvpn/down.sh # script to restore normal routing table upon exit down-pre # run down script before tun/tap interface is closed log-append /var/log/openvpn.log # I like to keep a log.... script-security 2 system # This is necessary to allow your up/down scripts to run (covered next) pull # I don't know if this is needed or not, but tell it to still pull the routing info even # though openvpn won't be altering the routing tables directly.. ## END of stuff to add <ca> # all the certificate stuff would follow here... Now that you've configured openvpn to not hog your whole internet connection, you'll need to create the up/down scripts to set up the routing tables and rules... /etc/openvpn/up.sh: #!/bin/bash echo "---- up.sh running... ----" ## First, set up the routing tables using info pushed from vpn server # route traffic to vpn server through normal gateway (or else it couldn't talk to the vpn server at all) echo "ip route add $trusted_ip/32 via $route_net_gateway" ip route add $trusted_ip/32 via $route_net_gateway # determine current default route (isp) if ! ip route show table main default | grep -qs 'default'; then echo "No default route in table main! Don't know what to do..." exit 1 else real_default_route=`ip route show table main | grep default` fi # remove default route from main routing table echo "ip route del table main $real_default_route" ip route del table main $real_default_route # put isp default route in real_default table for use by anything not going through vpn if ip route show table real_default default | grep -qs 'default'; then ip route del table real_default default fi echo "ip route add table real_default $real_default_route" ip route add table real_default $real_default_route # put vpn default route in vpn table for use by traffic that will go through vpn if ip route show table vpn default | grep -qs 'default'; then ip route del table vpn default fi echo "ip route add table vpn default via $route_vpn_gateway" ip route add table vpn default via $route_vpn_gateway # also put vpn default route in table main so routing works and defaults to vpn if not matched by iprule # in other words, ALL traffic not explicitely set to bypass vpn, will use vpn... you could default to your # isp default route here... but what if you're torrent program tried to use an unexpected port? Yikes! echo "ip route add table main default via $route_vpn_gateway" ip route add table main default via $route_vpn_gateway ## Now add the ip rules to match traffic thats supposed to go through the vpn ## based on the fwmarks, but still allow all other traffic to use isp connection. # first flush existing rules... echo "ip rule flush" ip rule flush # now add policy routing rules echo "ip rule add prio 97 from all fwmark 3 table real_default" ip rule add prio 97 from all fwmark 3 table real_default echo "ip rule add prio 98 from all fwmark 4 table vpn" ip rule add prio 98 from all fwmark 4 table vpn echo "ip rule add prio 198 from all lookup main" ip rule add prio 198 from all lookup main echo "ip rule add prio 199 from all lookup default" ip rule add prio 199 from all lookup default # not sure if this does anything or is needed but it doesn't hurt... echo "ip route flush cache" ip route flush cache # disable rp_filtering... this was fucking up everything # once i disabled these by setting to 0 it magically worked again! echo "Disabling rp_filtering in kernel..." for i in /proc/sys/net/ipv4/conf/*; do /bin/echo "0" > $i/rp_filter done # Update resolv.conf with nameserver pushed by vpn server. # I found this little snippet of code in a forum somewhere... works perfectly. echo "Setting up resolv.conf for vpn..." dns=dns for opt in ${!foreign_option_*} do eval "dns=\${$opt#dhcp-option DNS }" if [ "$dns" != "dns" ] then echo ";; created by openvpn --up ${0} " >/tmp/resolv.conf grep search /etc/resolv.conf >>/tmp/resolv.conf echo "nameserver $dns" >>/tmp/resolv.conf if [[ ! -e /etc/resolv.conf.novpn ]] then mv /etc/resolv.conf /etc/resolv.conf.novpn fi mv -f /tmp/resolv.conf /etc fi done echo "---- up.sh finished ----" Now create your /etc/openvpn/down.sh script which essentially just reverses the actions taken by the up.sh script: #!/bin/bash echo "---- down.sh starting... ----" # get the isp default route/gateway because on my system anyway sometimes # the predefined variables like $route_net_gateway don't make it here for some reason... REAL_DEFAULT_ROUTE=$(ip route show table real_default | grep default) GATEWAY=$(ip route show table real_default | awk '{print $3;}') ## First restore the routing tables # remove special route to vpn server through isp default gateway # that was added to routeing table by up.sh echo "ip route del $trusted_ip/32 via $GATEWAY" ip route del $trusted_ip/32 via $GATEWAY ## restore the default ip rules # first flush existing rules... echo "ip rule flush" ip rule flush # now add policy routing rules # local lookup rule doesn't seemed to be purged when running ip rule flush... # commented out to prevent adding duplicate... #ip rule add prio 0 from all lookup local echo "ip rule add prio 32766 from all lookup main" ip rule add prio 32766 from all lookup main echo "ip rule add prio 32767 from all lookup default" ip rule add prio 32767 from all lookup default # restore isp default route echo "ip route del default" ip route del default echo "ip route add $REAL_DEFAULT_ROUTE" ip route add $REAL_DEFAULT_ROUTE # clear routes from table real_default echo "ip route flush table real_default" ip route flush table real_default # clear routes from table vpn echo "ip route flush table vpn" ip route flush table vpn # not sure if this does anything or is needed... echo "ip route flush cache" ip route flush cache echo "Restoring /etc/resolv.conf..." mv -f /etc/resolv.conf.novpn /etc/resolv.conf echo "---- down.sh finished ----" That takes care of setting up openvpn! At this point assuming everything is set up correctly you should be able to start openvpn and connect to the server!But We're not finished yet... PART 3 - tiny proxy:I often found myself wanting to go to a particular website without using the vpn but while still using it for other sites and other non http traffic. For example I could be using the vpn for news sites or youtube or netflix or whatever, and still wanted to log in to something like my bank's website without shutting everything down. The solution was tinyproxy! So after you've installed tinyproxy open up it's configuration file at /etc/tinyproxy.conf: NOTE: I removed everything that isn't critical ## ## tinyproxy.conf -- tinyproxy daemon configuration file ## ## This example tinyproxy.conf file contains example settings ## with explanations in comments. For decriptions of all ## parameters, see the tinproxy.conf(5) manual page. ## # # User/Group: This allows you to set the user and group that will be # used for tinyproxy after the initial binding to the port has been done # as the root user. Either the user or group name or the UID or GID # number may be used. # ###### NOTE This user/uid need to match the uid you put in your iptables rules in PART 1 of this tutorial! User tinyproxy Group tinyproxy # # Port: Specify the port which tinyproxy will listen on. Please note # that should you choose to run on a port lower than 1024 you will need # to start tinyproxy using root. # Port 8888 # # Listen: If you have multiple interfaces this allows you to bind to # only one. If this is commented out, tinyproxy will bind to all # interfaces present. # Listen 127.0.0.1 # # Allow: Customization of authorization controls. If there are any # access control keywords then the default action is to DENY. Otherwise, # the default action is ALLOW. # # The order of the controls are important. All incoming connections are # tested against the controls based on order. # Allow 127.0.0.1 Don't forget you'll need to be running tinyproxy under it's own user... this is the default behavior in gentoo.. This is required so iptables can distinguish between http traffic from tinyproxy and regular http traffic (which all looks the same to iptables). The --match owner --uid-owner iptables rules can catch all packets coming from tinyproxy based on its uid and handle them differently from other http traffic. Once running, whenever you want to bypass the vpn to connect to a website simply point your browser at localhost:8888 where tinyproxy is listening! To make this easy I use the foxyproxy addon for firefox.I'm not going to go into much detail about this for now, but this addon lets you easily enable/disable a proxy and even predefine rules (for instance always use proxy for urls containing "mail.google.com") and use vpn for everything else. That's about it!Hopefully someone finds this useful! This setup works wonderfully for me! If anyone has any suggestions for improvements I'd love to hear them! Edit 1: Edited to include better method for setting nameserver in resolv.conf automatically. 3 lisbeth316, floody and sarum4n reacted to this Quote Share this post Link to post