Pages

Monday, 6 April 2020

Setting up a transparent pass-through proxy with iptables

Transparent Proxy This repository contains files relevant for setting up a transparent proxy for HTTP and HTTPS over a DD-WRT by forwarding requests to a secondary HTTP proxy. This setup can work on any Linux distributions. 

 frm https://github.com/phinfinity/router_tproxy https://github.com/phinfinity/router_tproxy/tree/master/transparent_https
---------------

 Setting up a transparent pass-through proxy with iptables

 So for a very long now I’ve had a nagging issue with proxies. My primary source of internet is through my college HTTP Proxy and this adds a couple of issues whenever I am dealing with applications that don’t have proxy support coded in them. I have this issue often both on my laptop as well as on my android tablet (Youtube streaming!). Its a very distressing situation and I’ve always wanted to set-up a transparent proxy solution which could re-direct the traffic out of such applications to a sort of secondary proxy server which can interpret the requests and forward them to my college proxy server. Recently I managed to get this working! The main tool used for this was iptables. For those of you who haven’t heard of iptables at a glance it is a flexible firewall which is now part of the Linux kernel by default. But iptables is actually much more powerful and flexible than just a simple firewall to block ports. iptables is capable of doing complicated re-routing of packets on several criteria. So much so that a lot of routers running linux (Yes quite a lot of them run Linux) use iptables to manage packets. In my current setup I have a router which is connected to the IIIT LAN which has access to the proxy server. And laptop is connected to the router. I managed to install DD-WRT which is a linux based firmware for routers. So I can now SSH to my router and mess with the iptables on it. The idea was to redirect all traffic on port 80 which goes through the router back to a server running on my laptop. iptables has rules across 5 tables : filter,nat,mangle,raw,security. The default table is filter which as its name suggests is used to filter traffic and block certain traffic etc. The nat table which is what we are interested in has to do with actual routing of a tcp stream when a new connection is created. In each of these tables iptables has a standard set of chains like “PREROUTING”,”POSTROUTING” in nat , which corresponds to sub-categories of packets. In this case before routing,etc. Here are the rules I used based off DD-WRT Wiki – Transparent Web Proxy PROXY_IP=192.168.128.2 # My Laptop's IP Adress PROXY_PORT=1234 # Port number to redirect traffic to LAN_IP=`nvram get lan_ipaddr` # This gets the IP Address of the router LAN_NET=$LAN_IP/`nvram get lan_netmask` # 192.168.128.1/255.255.255.0 iptables -t nat -A PREROUTING -i br0 -s $LAN_NET -d $LAN_NET -p tcp --dport 80 -j ACCEPT iptables -t nat -A PREROUTING -i br0 -p tcp --dport 80 -j DNAT --to $PROXY_IP:$PROXY_PORT iptables -t nat -I POSTROUTING -o br0 -s $LAN_NET -d $PROXY_IP -p tcp -j SNAT --to $LAN_IP iptables -I FORWARD -i br0 -o br0 -s $LAN_NET -d $PROXY_IP -p tcp --dport $PROXY_PORT -j ACCEPT iptables -t nat -I PREROUTING -i br0 -d 192.168.36.0/24 -j ACCEPT iptables -t nat -I PREROUTING -i br0 -d 10.0.0.0/13 -j ACCEPTSo lets see what these mean. The first rule is a rule which makes sure that all internal traffic on port 80 (from the router’s subnet to the router’s subnet) are not affected and continue to get processed normally. The next rule is what actually re-routes traffic on port 80 to the my laptop on port 1234. the jump target “-j DNAT” tells iptables to modify the destination address and port of the packet matched by the criteria specified The 3rd rule modifies the source address of the packet so that it appears as if it is coming from the router. This is necessary as otherwise traffic from my laptop when redirected back to my laptop will not be accepted by my laptop as it may consider it as illegal routing. (I am not completely sure about this one, but it didn’t work for me without it) The 4th rule ensures that actual traffic intended directly for the PROXY_PORT on my laptop continues to reach my laptop. The last 2 rules ensure that traffic directed to the IIIT Network are not re-directed to my laptop as I don’t need a proxy for them. Note that iptables processes rules in chained fashion. so it stops with the first rule which matches. each command has a ‘-A’ or a ‘-I’ flag, which stands for Append at the end of the chain and Insert at the front. This setup now ensures that all the traffic gets re-routed to a secondary proxy server I will run on my laptop. Initially I was planning on writing my own proxy handler to modify the HTTP headers as I get them to be compatible with the proxy. But eventually I decided its simpler and safer to go with an existing proxy server. I went with tinyproxy over well established ones like squid simply due to the tiny footprint of tinyproxy. I simply had to add proxy.iiit.ac.in:8080 as an upstream proxy in tinyproxy’s configuration and voila , Its done! A similar setup can more easily be done on a single system without the need for an external router. As we will simply have to re-direct outgoing traffic from the system on port 80 back to localhost. Although we now have transparent proxying this does not mean that every application will work. There’s still the major restriction that only HTTP requests on port 80 are handled. In my case the IIIT proxy server blocks other ports and proxying might not work in those cases. Keep in mind that all information about the target host is lost in this method and only the fact that the HTTP request actually contains the host name allows us to use this. Hope you find this useful! I certainly did now that I can finally watch youtube videos on my android tablet and more importantly download updates for Minecraft! Edit1 : I came across a version of tinyproxy , pre-configured and built for the atheros architecture (the same as my router). You can find it at : https://downloads.openwrt.org/snapshots/trunk/ar71xx/packages/packages/tinyproxy_1.8.3-2_ar71xx.ipk Make sure this is the version compatible for your router, before trying it. Here’s the installation commands I used (on the router) mkdir /tmp/tinyproxy cd /tmp/tinyproxy wget http://someserver/tinyproxy.ipk #get the file here somehow, not proxy server won't work to get from original source ipkg -d /tmp/tinyproxy tinyproxy.ipk /tmp/tinyproxy/usr/sbin/tinyproxy -c tinyproxy.conf #specify ur config file somewhere After a bit of tweaking and adding this on the startup script , we have it setup to work automatically on reboot from the router alone. Edit 2 : A lot of people where too lazy to come up with an iptables filter to work on a single laptop/computer. So here’s the iptable rules for the same: #!/bin/bash DEV=eth1 PROXY_IP=127.0.0.1 PROXY_PORT=1234 iptables -t nat -A OUTPUT -d 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,127.0.0.1 -j ACCEPT iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to $PROXY_IP:$PROXY_PORT If you just want to get it working , without understanding any of it , I’ve made a all-in-one script just for you :D You can get it Here. It has tiny proxy included in it, and it automatically sets everything up including the iptables. Its currently configured for the IIIT proxy server , so you might want to change it depending on your proxy’s ip.

 frm https://www.phinfinity.com/2012/09/setting-up-a-transparent-pass-through-proxy-with-iptables.html
--------------------

 Transparent Pass-through proxy with iptables – Part 2 (for HTTPS)

 this is part 2 of my earlier post on how to set configure to use a http proxy transparently. This post deals with extending the same for transparent HTTPS proxying. Click Here for my earlier post which deals with HTTP proxying. For a quick-fix solution and list of files mentioned in this post skip to the bottom of the post. After setting up a transparent http proxy on my dd-wrt router to transparently proxy my HTTP requests I haven’t had any issues for more than a year and was happily able to use it. But up until recently my local network used to allow direct HTTPS connections to external IP addresses. Now my network has disabled that, which means I need to forcefully use the HTTP proxy in order to be able to make HTTPS connections. Surprisingly this caused many more problems than I had anticipated. Lots of applications on android which seemed to work fine after setting proxy settings started failing badly! Notably gmail, hangouts, facebook messenger all only worked very sporadically. Forcefully requesting a sync didn’t work and kept failing inspite of the fact that proxy settings was configured. There were a lot of other issues to for some websites which I use like music streaming services which used websockets over 443 which stopped working as they didn’t use the proxy settings. As a result I set out to figure out a method to get HTTPS proxy working over my DD-WRT router to solve the issue once and for all. There were a couple of differences from my HTTP transparent proxying method which doesn’t allow me to use the same here: HTTPS is an encrypted protocol and I can’t read the protocol stream to figure out which host I’m attempting to connect to. In HTTP I could blindly intercept the connection and simply look at the Host: header in the HTTP protocol to figure out the destination. After that tinyproxy would re-write the headers and path so that the request will work fine with the HTTP proxy instead of the intended HTTP server. Now however I didn’t have that luxury. Lets have a look at how HTTPS connections works over a HTTP proxy. As HTTPS is an encrypted connection working over SSL , a HTTP proxy cannot to do most of the caching or other functionality it normally does on a HTTP connection. HTTP Proxies come with a CONNECT method which allows the user to make an arbitrary TCP connection over the proxy to a specified host and port. Most HTTP proxies will allow the CONNECT method over port 443 (which is the port for HTTPS) , and often blocking other ports which the proxy does not want to allow access to. The CONNECT method over a HTTP proxy’s protocol looks like this: Client: CONNECT google.com:443 Server: HTTP/1.0 200 Connection established Basically you send a single CONNECT request with the hostname and port number ended with “\r\n” to which the proxy replies with a status code 200 to show successful connection again ended with “\r\n”. Thereafter the rest of the stream is as good as a raw TCP connection. Usually after this the standard SSL handshake begins normally over this stream as if it was a normal direct TCP connection. Alternatively services can use the port 443 for other purposes such as in my case for music streaming and the like. This still works because the proxy just makes a raw TCP connection and doesn’t attempt (usually) to monitor the protocol followed by the rest of the connection since it is usually an encrypted SSL and so mostly meaningless for the proxy. So in our case a transparent proxying for HTTPS would have to first intercept a TCP connection intended for an external internet IP for port 443 and re-route that to some intermediate proxy program (which we shall design) intended to do the proxy handshake using the CONNECT method and once the raw TCP connection has been established, blindly forward packets so that the client is unaware that it is actually using a proxy and thinks that it is directly communicating with the HTTPS server. Making the CONNECT request is trivial, but the only problem we face now is in finding out who the original intended host was? This problem luckily has a simple solution! The linux netfilter/iptables luckily add’s the original destination’s IP address to the tcp socket as an extra socket parameter SO_ORIGINAL_DST. Whenever we redirect packets in iptables using a DNAT to a new destination IP address the original destination IP is accessible using getsockopt with SO_ORIGINAL_DST on the socket to get the original destination. Using this information I had previously written a simple python proxy forwarder + iptables combo designed to run on my system to do exactly this. You can get this here. To use the script simply run the iptables commands given in comments as root, and then run the python script (normal user suffices). The python script acts as the intermediary proxy to tunnel HTTPS connections through the HTTP proxy by making a CONNECT request and then forwarding packets in the rest of the connection. So great, it works! But we’re still not done. All right well and done that we got it working , but we really need this for our wifi router! Luckily it being a linux box gives us some hope of being able to do the same thing, except on the router. A router with little under a few MB or ram and storage would not be the ideal device to be running a badly written script in python. Clearly I need a more lean and mean solution, written in C. The main pain point here would be how on earth to cross-compile this for my router’s CPU. It’s an atheros chipset and hence I would need to cross-compile it for the mips architecture. I struggled a lot (a really lot, trust me) to set up my own mips cross-compiling tool chain from gcc. After a lot of effort I compiled gcc for mips , but I got stuck on some issue of not being able to import anything at all. I suspect I overlooked setting up libc now in retrospect. But anyways I gave up on this entirely in exasperation. This was a while back when https was working in my local network and I didn’t have that much of an inspiration to take the effort. Now that my local network forced me to get this working I finally scourged the net and finally realized that openwrt provided a MIPS toolchain for my chipset which is in fact the same toolchain used at dd-wrt and other places for compiling binaries for my router! (gcc toolchain for MIPS_34KC ar71XX at OpenWrt – Newer version might exist at their download site) . I still had no idea if the SO_ORIGINAL_DST would still work on my dd-wrt router, so first thing I did was code a very short program to test that and lo an behold it works, so now it was simply a matter of translating the python code I had already written into C using Linux sockets. So although a bit mundane to just translate, I went ahead and did just that and copied over the iptables I had used to work on my router for HTTP intercepting and modified the same for port 443. I set it all up on my router and ran it, and Sweet Success :D. Initially it seemed a bit laggy and slow to setup the HTTPS connections, but it seemed quite usable for most purposes after that! So with it configured to autorun on router-reboot I have a flawlessly working solution which makes all my android applications sync again, my music streaming works again and I’m Happy! I’ve added links to the code, toolchain, as well as binaries for my atheros chipset below at the end of this post. Enjoy ! Note: The approach I have finally used assumes that a working DNS server is accessible directly which can resolve external domain names to their valid ip addresses correctly. If such a DNS server is not accessible it is still possible to hack around this (though I have not attempted it) by using your own custom DNS server which forwards local name requests to the local DNS but intelligently provides unique fake IP addresses for external domain names. Then using the method above we can intercept connections and find the intended destination IP address. The intermediate proxy can communicate with the fake DNS and have a unique mapping of the fake IP-address to the correct original domain name. Then it’s a simple matter of making a CONNECT request to the Hostname instead of the ip addres! Links/Files from post: HTTPS transparent proxying for a linux desktop (works only for the single system) Instructions: run iptables given in comments as root, run script as normal user. OpenWrt toolchain for ar71xx Instructions: Just extract and use for gcc. use to copmpile C programs to be run on the router HTTPS transparent proxy code: tproxyhttps.c Instructions: compile for mips as “mips-openwrt-linux-gcc proxy.c -ldl -lpthread -o tproxyhttps” This is just my attempt at getting this working , it is not the most efficiently written code and could do better certainly. May degrade performance, but seems reasonably usable in my experience. Use at your own discretion. Avoid excessive logging when running on the router for a long-term. HTTPS transparent proxy binary/source/router-script : tproxyhttps.tgz This package includes a pre-compiled binary for mips along with the source code and a startup script onrouter.sh intended to be run on the router giving an idea of what iptables rules need to be set and how to run the binary. For an explanation of the iptables refer to my earlier post part-1.

 frm https://www.phinfinity.com/2014/04/transparent-pass-through-proxy-with-iptables-part-2-for-https.html