Netgate Discussion Forum
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Search
    • Register
    • Login

    floating rules: why doesn't in+out = any? / how do constraints apply to in/out packets with "any" rules?

    Scheduled Pinned Locked Moved Firewalling
    2 Posts 2 Posters 460 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • M
      msswift
      last edited by msswift

      Question 1: Two adjacent floating rules, one with an "in" direction and one with an "out" direction on the same interface (the NATed WAN) and no other constraints do not behave the same as a single rule with an "any" direction. Why?

      Question 2: In the scenario of visiting a simple website upstream of pfsense with a browser, permitting the browswer request "out" should be sufficient: the reply traffic should match state and pass without an explicit rule. But in my scenario which requires a floating rule on WAN (in order to precede another floating rule), I need an explicit "pass in" rule. Why?

      Question 3: I cannot find any information as to how additional constraints in a floating "any" rule are matched against an "in" packet and an "out" packet. Testing shows that constraints such as "destination <ip-host>, TCP port 80, tagged with `MyTag'" and the like are matched in the "out" direction but not in the "in" direction (the incoming destination would be the pfsense machine itself or an inside host, the destination port would be random, and the packet would not have a mark). This suggests that in this case anyway the "in" direction is not actually being matched with this rule. But as said above, an "out"-only rule fails. The "in" direction is required, but the other constraints are ignored on the "in" direction. Help?

      The destination host is also the gateway for the pfsense. I wonder but can't confirm whether that is a relevant complication. I haven't done the work (yet) of setting up a separate destination host to test.

      pfsense 2.5.2
      excerpts from /tmp/rules.debug are below with comments describing the scenario and results, including state tables and logging of rules.

      # NOTE This is excerpt from /tmp/rules.debug.
      #      It starts here with relevant "boilerplate" reflecting general pfsense configuration before the "user rules" further down.
      #      My comments begin with "NOTE". Other comments were directly in /tmp/rules.debug.
      
      
      #System aliases
        
      CABLE_IF = "{ igb0 }"
      MANAGEMENT_IF = "{ igb1.53 }"
      
      # User Aliases 
      table <Cablemodem_Business> {   10.1.10.1 } 
      Cablemodem_Business = "<Cablemodem_Business>"
      table <Private_and_Loopback_IPs> {   10.0.0.0/8  172.16.0.0/12  192.168.0.0/16  169.254.0.0/16  127.0.0.0/8  fc00::/7 } 
      Private_and_Loopback_IPs = "<Private_and_Loopback_IPs>"
       
      # Gateways
      GWCABLE_IF_DHCP = " route-to ( igb0 10.1.10.1 ) "
      
      # Outbound NAT rules (automatic)
      
      # Subnets to NAT 
      table <tonatsubnets> { 127.0.0.0/8 ::1/128 192.168.103.0/24 192.168.6.0/24 192.168.23.0/24 192.168.22.0/24 192.168.3.0/24 192.168.53.0/24 192.168.200.0/25 192.168.15.0/24 192.168.220.0/24 192.168.223.0/30 192.168.224.0/30 192.168.222.0/30 }
      nat on $CABLE_IF inet from <tonatsubnets> to any port 500 -> 10.1.10.2/32  static-port
      nat on $CABLE_IF inet from <tonatsubnets> to any -> 10.1.10.2/32 port 1024:65535
      
      # block bogon networks (IPv4)
      # http://www.cymru.com/Documents/bogon-bn-nonagg.txt
      block in  quick on $CABLE_IF from <bogons> to any tracker 11001 label "block bogon IPv4 networks from CABLE_IF"
      
      # let out anything from the firewall host itself and decrypted IPsec traffic
      pass out  inet all keep state allow-opts tracker 1000015265 label "let out anything IPv4 from firewall host itself"
      pass out  route-to ( igb0 10.1.10.1 ) from 10.1.10.2 to !10.1.10.0/24 tracker 1000015361 keep state allow-opts label "let out anything from firewall host itself"
      
      
      # User-defined rules follow
      
      
      # NOTE Scenario:
      #	A host on MANAGEMENT_IF (192.168.53.8) attempts to visit the web interface on port 80 of the cable modem at 10.1.10.1 (http://10.1.10.1).
      #       The cable modem is upstream of pfsense but has a private IP address, which are normally forbidden upstream, so I want rules which allow this special case.
      #	Pfsense has 10.1.10.2 on igb0 and talks upstream to the only other host on the subnet, the cable modem at 10.1.10.1.
      #	The cable modem is the default WAN gateway for pfsense; pfsense is configured as the DMZ host for the cable modem.
      #	Everything about pfsense works fine and has worked for years, except for the special case I'm trying to permit.
      #	Browser cache is disabled.
      #	I ensure all pf state for 10.1.10.1 is cleared before testing (monitoring pftop).
      #	IPv6 is turned off in pfsense.
      #	I actually want to allow only tagged requests so that non-management hosts are blocked, but I am omitting tags to debug my other rules. 
      
      # NOTE These are the floating rules.
      #
      #      The first two rules allow all traffic out/in respectively to/from the cable modem.
      #      Visits to http://10.1.10.1 fail with these "out" + "in" rules, however.
      #      But replacing them with a single "any" rule succeeds. I don't understand the difference.
      #      Order of the two rules doesn't matter (out-in and in-out both fail).
      #      Either rule alone fails as well.
      #      It's easy to see why an "in" rule alone fails (the request never goes out, and the block is logged).
      #      But why doesn't the "out" rule alone work? Visiting a website in public internet space requires only an "out" rule, it does not require any explicit rule to allow the reply traffic.
      pass  out log  quick  on {  igb0  } reply-to ( igb0 10.1.10.1 ) inet from any to $Cablemodem_Business tracker 1644710647 keep state  label "USER_RULE: Cablemodem webserver on private address  <-- Cabl..."
      pass   in log  quick  on {  igb0  } reply-to ( igb0 10.1.10.1 ) inet from $Cablemodem_Business to any tracker 1644779982 keep state  label "USER_RULE: Cablemodem webserver on private address  --> Cabl..."
      
      # NOTE After the exception above, this rule blocks all outbound traffic to a private or loopback address.
      block return  out log  quick  on {  igb0  } reply-to ( igb0 10.1.10.1 ) inet from any to $Private_and_Loopback_IPs tracker 1644710485  label "USER_RULE: Private and Loopback Addreses (IPv4) <-- Cable_IF"
      
      # NOTE These additional floating rules, given for completeness, block some other stuff outbound which shouldn't ever get out.
      block return  out  quick  on {  igb0  } reply-to ( igb0 10.1.10.1 ) inet proto { tcp udp }  from any port 136 >< 140 to any tracker 1606163252  label "USER_RULE: block NetBIOS on TCP/UDP outgoing on WAN"
      block return  out  quick  on {  igb0  } reply-to ( igb0 10.1.10.1 ) inet proto tcp  from any to any port 135 tracker 1606163371 flags S/SA  label "USER_RULE: block Microsoft RPC outgoing on WAN"
      block return  out  quick  on {  igb0  } reply-to ( igb0 10.1.10.1 ) inet proto { tcp udp }  from any to any port 445 tracker 1606163497  label "USER_RULE: block SMB/CIFS outgoing on WAN"
      
      # NOTE Rules for CABLE_IF. I expect however that floating rules not these rules are handling all traffic for this scenario.
      pass  in log  quick  on $CABLE_IF reply-to ( igb0 10.1.10.1 ) inet from $Cablemodem_Business to 10.1.10.2 tracker 1644783824 keep state  label "USER_RULE: Cablemodem -> this firewall explicitly as destina..."
      block  in log  quick  on $CABLE_IF reply-to ( igb0 10.1.10.1 ) inet from $Private_and_Loopback_IPs to any tracker 1644783728  label "USER_RULE: Private and Loopback IPs -> any"
      
      
      # NOTE Rules for MANAGEMENT_IF.
      pass  in  quick  on $MANAGEMENT_IF inet from any to (self) tracker 1612646862 keep state  label "USER_RULE: Management VLAN -> Pfsense anti-lockout"
      pass  in log  quick  on $MANAGEMENT_IF inet from any to $Private_and_Loopback_IPs tracker 1613102475 keep state  label "USER_RULE: Management VLAN -> Private and Loopback IPs"
      pass  in  quick  on $MANAGEMENT_IF inet from any to any tracker 1607794050 keep state  label "USER_RULE: Management VLAN -> any"
      
      ###############################
      state table on success:
      
      pfTop: Up State 1-3/3 (251), View: default, Order: dest. addr
      PR        DIR SRC                           DEST                                   STATE                AGE       EXP     PKTS    BYTES
      tcp       In  192.168.53.8:56704            10.1.10.1:80                   FIN_WAIT_2:FIN_WAIT_2   00:00:16  00:01:23       32    20700
      tcp       Out 10.1.10.2:61943               10.1.10.1:80                   FIN_WAIT_2:FIN_WAIT_2   00:00:16  00:01:23       32    20700
      
      
      # Log on success (logged rules above, plus default deny/pass/bogon/private rules) - note tracker for first entry is not above, which is the failing case
      # These are "pass" entries; no relevant blocks.
      [OUT symbol] CABLE_IF  	     	  10.1.10.2:61943	  10.1.10.1:80	TCP:S Cablemodem webserver on private address <--> Cab... (1644787208)
           	     MANAGEMENT_IF	  192.168.53.8:56704	  10.1.10.1:80	TCP:S Management VLAN -> Private and Loopback IPs (1613102475)
      
      state table on fail (separate in + out rules):
      
      # The only differences I see are two pairs instead of one, and the different NATed source ports, and the STATE column contents,
      # which makes sense because the connection is failing.
      # Sometimes there are three pairs, sometimes two.
      
      pfTop: Up State 1-4/4 (346), View: default, Order: dest. addr
      PR        DIR SRC                           DEST                                   STATE                AGE       EXP     PKTS    BYTES
      tcp       In  192.168.53.8:57160            10.1.10.1:80                     SYN_SENT:ESTABLISHED  00:00:13  00:00:28       11      572
      tcp       Out 10.1.10.2:58244               10.1.10.1:80                  ESTABLISHED:SYN_SENT     00:00:13  00:00:28       11      572
      tcp       In  192.168.53.8:57159            10.1.10.1:80                     SYN_SENT:ESTABLISHED  00:00:13  00:00:28       11      572
      tcp       Out 10.1.10.2:32483               10.1.10.1:80                  ESTABLISHED:SYN_SENT     00:00:13  00:00:28       11      572
      
      # Log on fail. Likewise two pairs instead of one. These are "pass" logs; no relevant blocks.
      [OUT symbol] CABLE_IF	  10.1.10.2:41303	  10.1.10.1:80	TCP:S Cablemodem webserver on private address <-- Cabl... (1644710647)
      MANAGEMENT_IF	  	  192.168.53.8:57779	  10.1.10.1:80	TCP:S Management VLAN -> Private and Loopback IPs (1613102475)
      [OUT symbol] CABLE_IF	  10.1.10.2:26164	  10.1.10.1:80	TCP:S Cablemodem webserver on private address <-- Cabl... (1644710647)
      MANAGEMENT_IF	  	  192.168.53.8:57778	  10.1.10.1:80	TCP:S Management VLAN -> Private and Loopback IPs (1613102475)
      
      
      D 1 Reply Last reply Reply Quote 0
      • D
        dma_pf @msswift
        last edited by

        @msswift I don't mess around with floating rules. I define them more explicitly on each interface. But here's some official documentation from Netgate on how they are applied:

        https://docs.netgate.com/pfsense/en/latest/firewall/rule-methodology.html

        Also note that floating rules marked as "Quick" act differently than non-quick floating rules.

        1 Reply Last reply Reply Quote 0
        • First post
          Last post
        Copyright 2025 Rubicon Communications LLC (Netgate). All rights reserved.