Force clients to use proxy

16,410

Solution 1

My 2 cents:

Regarding HTTP: Having a firewall transparently forward port 80 traffic to a proxy/filter is the simplest way of doing things. No need for client configuration, + you can exempt any host/subnet from using the proxy without the need for client reconfiguration. This is the only way you can be assured that everything that is supposed to pass through proxy, is.

Any method other than blocking all outgoing HTTPS 443 traffic and allowing only a subset of sites based on ip allowed to outgoing port 443 won't work as expected. HTTPS' secure protocol is designed (a few flaws aside) to prevent Man-In-The-Middle attacks (proxies ARE "legal" MITM). This way, HTTPS is allowed to do what it was designed to do. If however, you want to IGNORE HTTPS, you should setup your squid to use DIRECT and not use CONNECT (tapping), but even if this is the case, you still may run into problematic websites with mixed HTTP/HTTPS parts. This way, your Squid proxy will manage HTTPS as well. This should also be reflected in transparent forwarding part of your firewall.

Solution 2

Whenever you see that something that should work isn't working, you need to ask if there is some other factor involved that you are not seeing. The rule

sudo iptables -A INPUT -p tcp ! --dport 8080 -j REJECT

Looks like it should work, but you have appended it to the INPUT chain, so it is probably circumvented by a previous rule in the chain. This rule should be adequate by itself, as the INPUT chain is the first chain that incoming packets hit. If they are REJECTed at the INPUT chain, they never get to the FORWARD or OUTPUT chains. Of course, this rule will block everything TCP that is not destined for port 8080, which is probably not what you really want in the end unless the proxy at 8080 is the only service on the machine and you only log in from the console.

So the first thing to do is to list the rules and look for what might be causing the packets to pass:

sudo iptables -L

If your firewall is reverse NATting then you should also list the NAT table.

sudo iptables -L -t nat

Afterwards, try the putting the same rule at the beginning of the chain:

sudo iptables -I INPUT -p tcp ! --dport 8080 -j REJECT

and see if that doesn't solve the problem, or at least give you more confidence that you understand how iptables works so that you can complete your rule set. If you are working on this host from remote, my suggestion will cut you off, so you should do this only from the console. To work safely from remote see my colleague Eli Rosencruft's post about tweaking firewalls from remote.

Solution 3

Standard

A separate (user-defined) chain might help.

# create a chain just for user 100 (192.168.1.100)
iptables -N custom_user_100

# redirect all traffic FROM user 100 to custom chain
iptables -A INPUT -p tcp -s 192.168.1.100 -j custom_user_100

# return from user chain for valid traffic, drop all other
iptables -A custom_user_100 -p tcp --dport 8080 -j RETURN
iptables -A custom_user_100 -j DROP

So - what this does is redirect any traffic FROM 192.168.1.100 into a custom chain. This custom (user-defined) chain merely returns if a valid match is found (traffic destined for port 8080). All other non-matching traffic that doesn't result in a return from the chain is dropped.

Later you can view the tables statistics to verify this is what happened:

iptables -L -v -n

Forwarding

Now, on the off-chance you are processing forwarding traffic, there will be a different set of rules - but the idea of using a custom (user-defined) chain is the same. I like to refer to the diagram at this link: http://www.csie.ntu.edu.tw/~b93070/CNL/v4.0/CNLv4.0.files/Page697.htm when trying to understand the flow of packets.

In this case you might want to do something like the following:

# create a chain just for user 100 (192.168.1.100)
iptables -N custom_user_100

# redirect all traffic FROM user 100 to custom chain
iptables -A FORWARD -p tcp -s 192.168.1.100 -j custom_user_100

# return from user chain for valid traffic, drop all other
iptables -A custom_user_100 -p tcp --dport 8080 -j RETURN
iptables -A custom_user_100 -j DROP

This is identical to the first except that the rule that was applied to the INPUT chain is, instead, applied to the FORWARD chain.

Update 2013-05-24

I've re-read your question. So will start over.

Let's assume your "proxy" is actually a router. That is - it is passing all packets from one interface to another - perhaps using NAT. This means that all packets of interest are flowing through the FORWARD chain.

Next: you say you will configure all clients using port 8080 to hit the proxy itself. Fine. This means that all those packets will enter the "proxy" via the INPUT chain.

So: you just want to prevent anybody from going out port 8080 on the FORWARD chain.

iptables -A FORWARD -p tcp --dport 8080 -j REJECT

This rule will ensure anything that is to be forwarded to a destination port of 8080 will be rejected (an ICMP packet will be sent to the client that attempted to pass the packet through the proxy).

After you implement this rule it is VITAL that you test this by attempting to make such an forbidden connection - then you list the rules by typing:

iptables -L -v -n |grep 8080

and ensure the counter has increased. If not then something is wrong with the router configuration.

Solution 4

First read a bit of iptables documentation - specifically you at least need to know what chains in what tables a packet will enter (take a look here: http://www.iptables.info/en/structure-of-iptables.html).

You'll have to balance between two things: your goal to force everyone to use proxy and everybody's convenience. On one hand you can disallow forwarding of any packets at all (you can even disable ip_forwarding flag in the kernel). This way local computers will be able to access the internet only by your proxy server. But even that won't stop everyone from using a tunneled connection through your http proxy (and it's even easier with https ones). So you won't be able to fully monitor or limit internet usage.

On the other hand you can take an even softer approach: just limit outgoing traffic to port 80. In that case the software which doesn't use http (e.g. skype) won't have to tunnel traffic in http and the good-behaving clients will have all the benefits of a local http cache (faster access to sites).

Your current configuration looks like the first option above. But you have the unnecessary ACCEPTing rules in FORWARD chain. You don't want the router to forward any packets. Local computers connect to your proxy (packets go through INPUT chain) and not to the remote hosts. With your current setup someone could find a proxy listening on port 8080 anywhere on the internet and use that instead of your proxy.

Solution 5

If you don't want your clients just accessing http{s} sites without the proxy, than you may just DROP or REJECT forwarded packets to ports 80 and 443:

IPTABLES -I FORWARD -i eth1 -p tcp -m multiport --dports 80,443 -j REJECT

Where eth1 is your internal interface. Doing this way you won't have to mess around with other ports/access.

Share:
16,410
Big McLargeHuge
Author by

Big McLargeHuge

Updated on September 18, 2022

Comments

  • Big McLargeHuge
    Big McLargeHuge almost 2 years

    Here's my network configuration:

    My network configuration http://daveden.files.wordpress.com/2013/05/network-configuration.png

    The proxy server is running Ubuntu with Squid on port 3128 and DansGuardian on port 8080.

    I'd like to force all clients to use the proxy server - specifically, port 8080 - for any HTTP/HTTPS access.

    However, I don't want to transparently redirect because that doesn't work for HTTPS. I don't mind configuring each client, and I don't mind that each client knows it's using a proxy server. I just don't want the clients to be able to surf the web without the proxy settings.

    How do I do this? Can I just drop packets if the client isn't configured to use the proxy server on port 8080?

    I tried using iptables to drop packets that had a dport other than 8080, but that rejected too much I think and I could no longer access anything.

    EDIT

    I re-wrote this question so that it's not iptables specific, but I am not against using iptables at all. I just want to attract a wider range of possible solutions.

    EDIT 2

    I think I may have given some the wrong impression. Just to be clear, I'm not at all interested in filtering HTTPS traffic (i.e., looking taking packets apart at the proxy and inspecting the contents). I'm more interested in blocking sites with DansGuardian, whether it's over HTTP or HTTPS (by looking at the destination of the packets).

    EDIT 3

    Based off of Alexandru-Florin Vintil's suggestion below, here's what I'm currently doing:

    # Redirect HTTP traffic to port 8080 (DansGuardian)
    iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 8080
    
    # Check TCP and UDP traffic against whitelist
    iptables -A FORWARD -i eth1 -p tcp --dport 443 -j whitelist
    iptables -A FORWARD -i eth1 -p udp --dport 443 -j whitelist
    
    # Drop all other HTTPS traffic
    iptables -A FORWARD -i eth1 -p tcp --dport 443 -j DROP
    iptables -A FORWARD -i eth1 -p udp --dport 443 -j DROP
    
    # Drop all traffic aimed straight at the proxy
    iptables -A FORWARD -i eth1 -p tcp --dport 3128 -j DROP
    iptables -A FORWARD -i eth1 -p udp --dport 3128 -j DROP
    iptables -A FORWARD -i eth1 -p tcp --dport 8080 -j DROP
    iptables -A FORWARD -i eth1 -p udp --dport 8080 -j DROP
    

    In a nutshell, redirect HTTP traffic to port 8080, drop all HTTPS traffic that isn't whitelisted (in a separate chain), and drop all traffic that explicitly uses the proxy. Without the last rule, a client can access any website using HTTPS as long as they configure their browser to use the proxy, because then the destination port is 8080 and not 443. So even dropping all traffic bound to 443 doesn't block HTTPS altogether.

    • Michael Hampton
      Michael Hampton about 11 years
      Where is this firewall, and your proxy server, in relation to the rest of the network? Draw a diagram if necessary.
    • Cian
      Cian about 11 years
      Where are you actually putting this rule?
    • Sandor Marton
      Sandor Marton about 11 years
      If this server is also the default gw for the clients, then maybe just set ip_forward to 0 ( net.ipv4.ip_forward ).
    • Big McLargeHuge
      Big McLargeHuge about 11 years
      By the way, I asked this question on security.stackexchange.com and it was closed as "off topic." So please don't tell me to go there.
    • Nathan C
      Nathan C about 11 years
      What kind of router is this? From your diagram, I'm thinking the router is bypassing your proxy for all your clients. Is the router's upstream connection (WAN) connected directly to the proxy?
    • Big McLargeHuge
      Big McLargeHuge about 11 years
      The router is a TP-Link TL-WR1043ND v1.8 running OpenWrt Attitude Adjustment 12.09-rc1. There's a bug that affects the WAN port so it is disabled: forum.openwrt.org/viewtopic.php?id=44276
  • Jonathan Ben-Avraham
    Jonathan Ben-Avraham about 11 years
    Thanks for the upvote, despite the error in my original post. I had quoted the wrong rule from the OP, with --sport. Now fixed.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    So the correct way is to check the dport in the INPUT chain then?
  • Jonathan Ben-Avraham
    Jonathan Ben-Avraham about 11 years
    @davidkennedy85: Added some clarifications to my answer to address your question.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    Maybe I'm confused, but I thought REJECT acted just like DROP except it happened quicker. i.e., when it's rejected you're not twiddling your thumbs waiting for it to drop.
  • PP.
    PP. about 11 years
    REJECT actually sends an ICMP packet back to the originating host. DROP silently drops the packet. You want REJECT if you trust the originator and like to inform them when things have gone wrong. But on the Internet you usually want DROP because you don't want remote attackers getting a response - and it makes you less likely to suffer from a denial of service attack.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    I tried the rule you suggested and ended up with a ton of rejected packets that when logged look this this: iptables_log_reject: IN=lo OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=8080 DPT=40312 WINDOW=43690 RES=0x00 ACK SYN URGP=0. Others look like this: iptables_log_reject: IN=lo OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=8080 DPT=40312 WINDOW=43690 RES=0x00 ACK SYN URGP=0
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    Neither the INPUT or FORWARD rules seem to work. I don't have any proxy configured on my client machine and yet it can still access the web.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    Alright, sounds like a good idea. You didn't exactly tell me how to drop the packets unless they're routed through the proxy though.
  • skarap
    skarap about 11 years
    No packet ever goes though INPUT then FORWARD. Every packet may pass only one of them: the ones directed to the router - INPUT and the ones which are directed at other systems - FORWARD. Take a look at iptables.info/en/structure-of-iptables.html .
  • PP.
    PP. about 11 years
    @davidkennedy85 added an update that hopefully follows your question more closely
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    I'm not sure I understand why I should reject anything on the forward chain using port 8080. What I really want is to ensure that all clients use port 8080. This sounds like it would do the opposite of what I want.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    You brought up a really good point. Blocking all traffic on port 443 and then accepting traffic from a few sites sounds too easy. I'm not sure what you mean about the whole ignore thing. Just to be clear, I'm not at all interested in filtering HTTPS traffic. I'm more interested in blocking sites wholesale in DansGuardian, whether it's HTTP or HTTPS.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    I don't think I can turn off ip_forwarding altogether, since I had to enable ip_forwarding to get any internet at all this side of the router. (Client side, er, downstream? Whatever.) But maybe the reason for that is I was trying to get internet working via wireless on the router without the proxy... Investigating...
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    Ignore that last comment. Some clients have to be able to use the forward chain because they can't be configured to use a proxy. e.g., Steam for Windows. Also, when the PlayStation 3 connects to PlayStation Network it does not do it over 8080 even if you tell it to. (It does for internet browsing.) Currently, to get those to work I have an enormous whitelist of IP addresses in iptables that those clients use. Point is: I can't just turn off ip_forwarding, unless I'm missing something.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    Please see my edit above and let me know if I'm close.
  • skarap
    skarap about 11 years
    I think I wasn't clear enough. What I meant is to leave alone all traffic except which goes to port 80. That means everyone will be able to use Skype, PSN, Steam. The only thing that will be accessible through proxy only is http (port 80). Again: it all depends on what you're trying to achieve.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    This seems to meet the requirements - doesn't rely on port forwarding, forces clients to use the proxy, and it's simple. Unfortunately, some clients have to be able to use the forward chain because they can't be configured to use a proxy. e.g., Steam for Windows, PlayStation 3.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    I see. The problem is I need to be able to block access to certain websites on any port, including 443.
  • Alexandru-Florin Vintiloiu
    Alexandru-Florin Vintiloiu about 11 years
    "I'm more interested in blocking sites wholesale in DansGuardian, whether it's HTTP or HTTPS." What do you mean by wholesale? [examples/counterexamples work best if you can provide some. Also, your statement kinda contradicts your previous one "Just to be clear, I'm not at all interested in filtering HTTPS traffic." an edit maybe but ... What do you want to do about HTTPS? Just allow it to pass through disregarding content? ... PS: just saw the edit in the question section. Glad I could help.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    For example: I'd like to block somewebsite.com. I think I should be able to do that whether a client uses HTTP or HTTPS. But blocking somewebsite.com/somepage.html is another story, particularly if a client uses HTTPS. The proxy has no way of seeing the whole URL - it can only see the domain. In order to see the part after the forward slash (somepage.html), it would need to inspect the contents of the packet - i.e. content filtering. I'd rather just block somewebsite.com altogether.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    By the way, the firewall rules I posted above allow a client to access just about any website over HTTPS if they configure their browser to use the proxy, because then the destination port is 8080 and not 443 :(
  • PP.
    PP. about 11 years
    What you want is for clients to connect to your server on port 8080. All packets destined for your server go through the INPUT chain. Any packets to be routed via your server go through the FORWARD chain. You don't want packets for port 8080 to be forwarded.
  • Big McLargeHuge
    Big McLargeHuge about 11 years
    This is the closest solution to what I actually ended up doing. See the last edit in the question above for what I ended up with.