1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

ipfilter (now pf) for dummies

Discussion in 'Firewalls' started by tomh009, Jan 18, 2009.

  1. tomh009

    tomh009 New Member

    Messages:
    38
    Thanks Received:
    2
    Our mail setup uses Postini to handle spam and viruses. It costs money but does a decent job, with no admin required (anyway, changing that isn't an option at the moment). The trouble is with the spammers who ignore MX and scan for open SMTP ports, and thus bypass Postini.

    Having just upgraded our mail server to 7.0-RELEASE (yeah, started too early for 7.1), I want to address this issue, too. I spent most of the day fighting with the mailfromd milter, but I think it's not the right tool in the end.

    So I'm looking for a dead-simple ipfilter configuration:
    • allow all traffic not on port 25
    • allow port 25 from 64.18.0.0/20
    • allow port 25 from 207.126.144/20
    • disallow other port 25

    So trying to figure out what I need ...
    Code:
    pass out on bge0 from any to any
    pass in on bge0 from any to any port < 25
    pass in on bge0 from any to any port > 25
    pass in on bge0 from 64.18.0.0/20 to any port = 25
    pass in on bge0 from 207.126.144/20 to any port = 25
    pass in on bge0 from 127.0.0.1 to any port = 25
    block in on bge0 from any to any port = 25
    


    Does this make sense? I'm always hesitant about firewall rules lest I block myself out! :\
     
  2. anomie

    anomie New Member

    Messages:
    783
    Thanks Received:
    116
    I can't evaluate your ipfilter ruleset (I've used only pf and ipfw), but your concern is definitely legit.

    I recommend using an at(1) job for insurance against this. I wrote up a brief example here: http://daemonforums.org/showthread.php?t=887

    I do the same thing on both FreeBSD (pf) and RHEL (iptables) boxes to be sure I don't get locked out.
     
  3. tomh009

    tomh009 New Member

    Messages:
    38
    Thanks Received:
    2
    Should I use ipfw instead of ipf? It wasn't clear to me which would be the most appropriate tool for this.

    Thanks for the suggestion on at(1).
     
  4. anomie

    anomie New Member

    Messages:
    783
    Thanks Received:
    116
    Well, what I can tell you is that pf and ipfw (in that order) certainly seem to be more popular. i.e. You'll likely get better peer/community support using one of those two.
     
  5. tomh009

    tomh009 New Member

    Messages:
    38
    Thanks Received:
    2
    All right ... that's the trouble with so many tools, never know which one is the best! So trying to adapt to the pf.conf syntax (not so much different), does this look reasonable?
    Code:
    pass out from any to any
    pass in proto tcp from any to any port < 25
    pass in proto tcp from any to any port > 25
    pass in proto tcp from 64.18.0.0/20 to any port = 25 label "postini-1"
    pass in proto tcp from 207.126.144/20 to any port = 25 label "postini-2"
    pass in proto tcp from 127.0.0.1 to any port = 25 label "local"
    block in proto tcp from any to any port = 25 label "spam"
    pass in from any to any
    
     
  6. SirDice

    SirDice Moderator Staff Member Moderator

    Messages:
    17,631
    Thanks Received:
    2,386
    Your last rule annihilates everything else and lets everything in.
    PF and IPFILTER don't stop matching rules when one hits ;)

    I'd change it slightly to this:
    Code:
    ext_if="bge0"
    block in all
    # Allow traffic to/from localhost
    pass in quick on lo0 all
    pass out quick on lo0 all
    
    # We trust our own host :}
    pass out on $ext_if from any to any keep state
    
    # SMTP in
    pass in quick on $ext_if proto tcp from 64.18.0.0/20 to any port = 25 label "postini-1" keep state
    pass in quick on $ext_if proto tcp from 207.126.144/20 to any port = 25 label "postini-2" keep state
    


    You can "short-circuit" rules with the quick keyword. The "keep state" keeps track of tcp/ip sessions, so you don't have to worry about the returning answer.
    Not sure what you want to do with the labels though. They're only used in the logs. I have these:
    Code:
    block in log on $ext_if inet proto udp all label "BlockIn_ExtIF_UDP"
    block in log on $ext_if inet proto icmp all label "BlockIn_ExtIF_ICMP"
    block return-rst in log on $ext_if inet proto tcp all label "BlockIn_ExtIF_TCP"
    


    Edit: Oh.. remote editing a firewall can be slightly dangerous indeed :e
    Code:
    # check syntax:
    # pfctl -nf /etc/pf.conf
    # test: run rules for 60 sec. then disable firewall
    # pfctl -f /etc/pf.conf && sleep 60 && pfctl -d
    # green light :)
    # pfctl -f /etc/pf.conf
    
     
    tomh009 thanks for this.
  7. anomie

    anomie New Member

    Messages:
    783
    Thanks Received:
    116
    Just a couple references, if you haven't already located them on your own, to get you up to speed quickly on pf:

    The first is a thorough pf tutorial and reference. The second contains some important FreeBSD-specific pf notes.

    To elaborate on SirDice's point, there is one key gotcha when using pf for packet filtering: the last matching rule wins (with some exceptions, but no need to split hairs yet). This is very different than e.g. ipfw, iptables (Linux), Cisco FWSM, etc.
     
    tomh009 thanks for this.
  8. tomh009

    tomh009 New Member

    Messages:
    38
    Thanks Received:
    2
    Ah -- last-match rather than first-match. Good to know! :)

    So using that principle, and opening things up some more, I'm thinking that this might possibly work -- what do you think?

    Code:
    ext_if="bge0"
    block in all
    # Allow traffic to/from localhost
    pass in quick on lo0 all
    pass out quick on lo0 all
    
    # Default to allow all, unless matched later
    pass in on $ext_if from any to any keep state
    pass out on $ext_if from any to any keep state
    
    # Block SMTP by default
    block in log on $ext_if inet proto tcp from any to any port = 25 label "Block SMTP"
    
    # Allow SMTP from Postini
    pass in quick on $ext_if proto tcp from 64.18.0.0/20 to any port = 25 label "postini-1" keep state
    pass in quick on $ext_if proto tcp from 207.126.144/20 to any port = 25 label "postini-2" keep state
    


    And thanks for the tip on testing!
     
  9. SirDice

    SirDice Moderator Staff Member Moderator

    Messages:
    17,631
    Thanks Received:
    2,386
    Yes. The quick keyword short-circuits this, in case of lo0 traffic (our ruleset :e ) it'll stop matching any of the rules following it.

    If you're going to allow all traffic anyway you could loose that block in all at the beginning. And the quick keywords in the last 2 rules are a bit pointless. But it should work fine as is though :)
     
  10. tomh009

    tomh009 New Member

    Messages:
    38
    Thanks Received:
    2
    After figuring out that the pf and pflog modules are not loaded by default (used kldload for now, and added to loader.conf for the future), I got it running. The script worked great, no trouble there. And within a few minutes ...
    Code:
    montecarlo 95 # tcpdump -n -r /var/log/pflog      
    reading from file /var/log/pflog, link-type PFLOG (OpenBSD pflog file)
    19:10:47.559784 IP 58.60.97.164.2056 > *.*.98.4.25: S 3141617944:3141617944(0) win 65535 <mss 1440,nop,nop,sackOK>
    19:10:50.477568 IP 58.60.97.164.2056 > *.*.98.4.25: S 3141617944:3141617944(0) win 65535 <mss 1440,nop,nop,sackOK>
    19:10:56.487070 IP 58.60.97.164.2056 > *.*.98.4.25: S 3141617944:3141617944(0) win 65535 <mss 1440,nop,nop,sackOK>
    


    Ah-ha! So who is that?

    Code:
    montecarlo 97 # whois -A 58.60.97.164
    % [whois.apnic.net node-1]
    % Whois data copyright terms    http://www.apnic.net/db/dbcopyright.html
    
    inetnum:      58.60.0.0 - 58.63.255.255
    netname:      CHINANET-GD
    descr:        CHINANET Guangdong province network
    descr:        China Telecom
    (...)
    


    The usual suspects at work -- but foiled by pf! :e

    Thanks very much for the help, guys!
     
  11. J65nko

    J65nko Member

    Messages:
    445
    Thanks Received:
    120
    To prevent problems with TCP window scaling please add flags S/SA to those 'keep states' rules.
    Code:
    pass in quick on $ext_if proto tcp from 64.18.0.0/20 to any port = 25 label "postini-1"  flags S/SA keep state
    pass in quick on $ext_if proto tcp from 207.126.144/20 to any port = 25 label "postini-2"  flags S/SA keep state

    From http://undeadly.org/cgi?action=article&sid=20060928081238
     
  12. SirDice

    SirDice Moderator Staff Member Moderator

    Messages:
    17,631
    Thanks Received:
    2,386
    Doesn't scrub in all take care of that?
     
  13. DutchDaemon

    DutchDaemon Administrator Staff Member Administrator Moderator

    Messages:
    10,769
    Thanks Received:
    1,872
    Scrub handles various things like reassembling/normalisation of fragments, protecting/hardening of sequence numbers and/or timestamps, sequence number wrapping (paws), but does not handle window scaling. One could call it 'window cleaning' at best ;)