Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

Here's my network configuration:

My network configuration

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.

share|improve this question
    
Where is this firewall, and your proxy server, in relation to the rest of the network? Draw a diagram if necessary. –  Michael Hampton May 20 '13 at 5:56
    
Where are you actually putting this rule? –  Cian May 20 '13 at 9:03
    
If this server is also the default gw for the clients, then maybe just set ip_forward to 0 ( net.ipv4.ip_forward ). –  Sandor Marton May 20 '13 at 14:01
    
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. –  Koveras May 22 '13 at 20:10
    
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? –  Nathan C May 23 '13 at 12:35

6 Answers 6

up vote 1 down vote accepted
+100

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.

share|improve this answer
    
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. –  Koveras May 25 '13 at 3:35
    
Please see my edit above and let me know if I'm close. –  Koveras May 25 '13 at 5:01
    
"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. –  Alexandru-Florin Vintiloiu May 26 '13 at 2:01
    
By filtering I mean taking the packets apart as they cross the proxy server, inspecting the contents, and then doing something with them. This is DansGuardian's normal mode of operation when it comes to HTTP traffic. For HTTPS traffic, this constitutes a MITM attack and requires setting up a certificate that's trusted by the clients. I don't want to do all that. I just want to know where the packet is headed and reject if if it's on my blacklist (or not on my whitelist, in this case.) –  Koveras May 27 '13 at 22:43
    
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. –  Koveras May 27 '13 at 22:47

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.

share|improve this answer
    
Thanks for the upvote, despite the error in my original post. I had quoted the wrong rule from the OP, with --sport. Now fixed. –  Jonathan Ben-Avraham May 20 '13 at 12:49
    
So the correct way is to check the dport in the INPUT chain then? –  Koveras May 20 '13 at 15:31
    
@davidkennedy85: Added some clarifications to my answer to address your question. –  Jonathan Ben-Avraham May 20 '13 at 15:57
    
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 –  Koveras May 21 '13 at 20:26
1  
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 . –  skarap May 24 '13 at 4:32

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.

share|improve this answer
    
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. –  Koveras May 21 '13 at 20:36
    
@davidkennedy85 added an update that hopefully follows your question more closely –  PP. May 24 '13 at 9:49
    
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. –  Koveras May 25 '13 at 3:28
    
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. –  PP. May 28 '13 at 8:38

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.

share|improve this answer
    
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... –  Koveras May 25 '13 at 3:48
    
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. –  Koveras May 25 '13 at 3:52
    
As for your other solution, if I limit outgoing traffic to port 80 don't I lose all ability to use HTTPS on client machines? I don't necessarily want that. i.e. clients should still be able to use HTTPS for internet banking, email and the like. –  Koveras May 25 '13 at 3:56
    
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. –  skarap May 25 '13 at 6:59
    
I see. The problem is I need to be able to block access to certain websites on any port, including 443. –  Koveras May 25 '13 at 15:02

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|improve this answer
    
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. –  Koveras May 25 '13 at 15:01

Personally, I like to set the default policies for the INPUT and FORWARD chain to DROP

iptables -P INOUT DROP
iptables -P FORWARD DROP

Then allow only the traffic I want - this way, I find less surprises and am very certain only what I allow is going where I direct it.

share|improve this answer
    
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. –  Koveras May 20 '13 at 22:06
    
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. –  PP. May 21 '13 at 12:59
    
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. –  Koveras May 22 '13 at 17:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.