From owner-freebsd-security Fri Aug 10 6: 2:50 2001 Delivered-To: freebsd-security@freebsd.org Received: from pa169.kurdwanowa.sdi.tpnet.pl (pa169.kurdwanowa.sdi.tpnet.pl [213.77.148.169]) by hub.freebsd.org (Postfix) with ESMTP id 2A51C37B405 for ; Fri, 10 Aug 2001 06:02:31 -0700 (PDT) (envelope-from kzaraska@student.uci.agh.edu.pl) Received: by pa169.kurdwanowa.sdi.tpnet.pl (Postfix, from userid 1001) id AA69C1C87; Fri, 10 Aug 2001 14:23:57 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by pa169.kurdwanowa.sdi.tpnet.pl (Postfix) with ESMTP id 4B05A5493; Fri, 10 Aug 2001 14:23:57 +0200 (CEST) Date: Fri, 10 Aug 2001 14:23:56 +0200 (CEST) From: Krzysztof Zaraska X-Sender: kzaraska@lhotse.zaraska.dhs.org To: Jon Loeliger Cc: security@FreeBSD.ORG Subject: Re: IPFW Dynamic Rules In-Reply-To: Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Sender: owner-freebsd-security@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org On Thu, 9 Aug 2001, Jon Loeliger wrote: > So if the dynamic rule has the same behaviour as the origination > rule on the same port with the same protocol, why can't packets > simply continue to be matched against that original base rule? > Why does the dynamic rule even need to come into existence? It does not have _the same_ behavior. Simply speaking, if you add keep-state to the rule it means "pass the packet which is a reply to this one". Assuming x.x.x.x is your ip, z.z.z.z is outside (client) ip. If client sends UDP query to DNS on your machine, you get the packet: z.z.z.z:z -> x.x.x.x:53 INCOMING Yet in yor machine's reply x.x.x.x:53 -> z.z.z.z:z OUTGOING Firewall code matches data from IP/TCP/UDP header to the ruleset. If ALL fields in packet match those in the rule the specified action (allow, deny, deny & log, etc.) is triggered. So if you have a rule "allow INCOMING packet from ANY host ANY port to z.z.z.z port 53" it will pass the first packet. But the reply packet is an OUTGOING packet from x.x.x.x:53 to z.z.z.z:z so it _doesn't_ match the rule. And now what keep-state does: In our example, if you have the same rule as above but with keep-state, after the rule is matched and packet is let through, a new rule is added saying: "allow OUTGOING packet from x.x.x.x port 53 to z.z.z.z port z" and this will match exactly the reply packet. Dynamic rules are deleted after a given period of time. More typical use of this is: add pass udp from yourip to any keep-state so you may initiate UDP queries and the replies will be allowed back, without keep-state they would be dropped. If you're running a public DNS for example, you may just allow all traffic to (queries to your server) port 53 and from port 53 (replies from your server). > How many dynamic rules do you need to allow for, roughly, based on > some simple system paramters? Pure heuristic and guess work here? > Markov chain arrival rate rule decay rate blah blah tune it blah blah? > I filled the default 256 readily, and bumped it to 1024 on a whim. One active connection = one dynamic rule. Also take into account the rule is deleted after connection is inactive for some time. I generally use keep-state for UDP traffic, not for TCP. ipfw can inspect the state of TCP connection, so there is a better way (see below). > So I think I may be doing something vaguely Not Quite Right with > some "keep-state" rules too. I think I got to this NQR state due > to some early wrong rule tinkering. To be concrete: > > I first made the mistake of being too uni-directional and had a > rule like this, intending to mean "anything that is established > between the Big Bad Outside and my net, let it through." > > 00800 allow tcp from any to MY_REAL_NET/MASK established > > and this one intended to allow access to a web server: > > 01200 allow tcp from any to 209.39.144.0/27 80 setup First, a brief explanation of TCP: BEFORE ANYTHING can be sent over a TCP connection, a connection MUST be stablished. The procedure is as follows (client connects to server): 1. client sents the packet with the SYN flag sent 2. if server wishes to open this connection, it replies with packet with both flags SYN and ACK set; if it doesn't wish to open the connection (e.g. no httpd running), it replies with packet with set both RST and ACK flags. 3. if client received SYN+ACK packet (=server wants to talk) it replies with a packet with ACK flag set. CONNECTION ESTABLISHED. 4. all packets exchanged between client and server have ACK flag set. The idea behind example in /etc/rc.firewall is: we allow _all_ ESTABLISHED connections and control only the SETUP of new connections. Since nothing may be sent without prior establishing the session (steps 1-3 above) this provides adequate security. In other words: allow all packets with ACKs, control only SYNs. So the rules read (/etc/rc.firewall, CLIENT section): # Allow TCP through if setup succeeded ${fwcmd} add pass tcp from any to any established Then # Allow setup of incoming email ${fwcmd} add pass tcp from any to ${ip} 25 setup At this point we allow only incoming SYNs to port 25, that is, the only connection that may be established is this to port 25. Any other SYNs sent by the client will be dropped and the client never receives SYN+ACK. A SYN to port 25 will generate the reply of SYN+ACK which will be passed by the "established" rule above. Later we must allow ourselves to initiate the connections, so we have: # Allow setup of outgoing TCP connections only ${fwcmd} add pass tcp from ${ip} to any setup Assuming everything else is blocked, we end up with: connections to port 25 allowed from outside, we may connect to everybody else, any other connection attempts to us are forbidden. Guess this is what you want. UDP unfortunately, does not define connections, so we must use the keep-state technique as above. With TCP there's no need for that, since you may allow all enstablished connections and filter only connection requests. > What I need to do is change the 800 rule to be: > > 00800 allow tcp from any to any established > > and take the keep-state off the 1200 rule again: > > 01200 allow tcp from any to MY_REAL_NET/MASK 80 setup Exactly! (see above) Generally I construct firewall rules like this: 1. deny everything 2. allow all connections from inside to outside world. TEST. 3. allow the outside world to connect to selected services. TEST. 4. TEST. Specifically check if no unwanted connections may be initiated from outside. To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-security" in the body of the message