PF sincerely ask for help about "lo0" settings

Hi there, I'm new to PF but want to dive deep into it, as it seems very handy and powerful:)

When I was reading the man pages of pf.conf(), I saw the following information:

Code:
BLOCKING SPOOFED TRAFFIC
     "Spoofing" is the faking of IP addresses, typically for malicious
     purposes.  The antispoof directive expands to a set of filter rules which
     will block all traffic with a source IP from the network(s) directly
     connected to the specified interface(s) from entering the system through
     any other interface.

     For example, the line

           antispoof for lo0

     expands to

           block drop in on ! lo0 inet from 127.0.0.1/8 to any
           block drop in on ! lo0 inet6 from ::1 to any

     For non-loopback interfaces, there are additional rules to block incoming
     packets with a source IP address identical to the interface's IP(s).  For
     example, assuming the interface wi0 had an IP address of 10.0.0.1 and a
     netmask of 255.255.255.0, the line

           antispoof for wi0 inet

     expands to

           block drop in on ! wi0 inet from 10.0.0.0/24 to any
           block drop in inet from 10.0.0.1 to any

     Caveat: Rules created by the antispoof directive interfere with packets
     sent over loopback interfaces to local addresses.  One should pass these
     explicitly.

But actually, how to understand it in the right direction? At least I still have the following questions:

1. If I've already set set skip lo0, does it means antispoof for lo0 does nothing? Or should not do that at all?

I tried have the following settings in /etc/pf.conf:

Code:
set skip lo0
antispoof for lo0

So the pfctl -s rules shows me:

Code:
block drop in on ! lo0 inet6 from ::1 to any
block drop in on ! lo0 inet from 127.0.0.0/8 to any

But when I run pfctl -vs rules | rg lo0 -A3 I still see all those packets have been evaluated:

Code:
No ALTQ support in kernel
ALTQ related functions disabled
block drop in on ! lo0 inet6 from ::1 to any
  [ Evaluations: 371       Packets: 0         Bytes: 0           States: 0     ]
  [ Inserted: uid 0 pid 88512 State Creations: 0     ]
block drop in on ! lo0 inet from 127.0.0.0/8 to any
  [ Evaluations: 90        Packets: 0         Bytes: 0           States: 0     ]
  [ Inserted: uid 0 pid 88512 State Creations: 0     ]

Does it still make sense? (as I think set skip lo0 already told PF no need to evaluate on that particular interface, or I went into the wrong direction? plz please correct me)

2. So actually, should I use antispoof for lo0 or not when I have set skip lo0?:)

3. Based on the Caveat part, is that means I should have the rule like pass quick on lo0 proto tcp from any to any? As I will run some local dev servers (webpack dev server, Firebase Emulator, etc.) for my daily development.

4. By the way, here is my first version /etc/pf.conf, not a server, just a personal development environment that working inside a HOME office or company office, does anything I need to improve? Or something I'm missing? What I'm looking for just a balance between protecting my FreeBSD and non-blocking working ports/packets to go out:)

Code:
# Default `block` behaviour: drop silently
set block-policy drop

# Pass loopback
set skip on lo0

# ==============================================================================
#
# "Spoofing" is the faking of IP addresses, typically for malicious purposes.
# The `antispoof` setting is equal the following rules:
#
# block drop in on ! lo0 inet from 127.0.0.1/8 to any
# block drop in on ! lo0 inet6 from ::1 to any
#
# ==============================================================================
antispoof for lo0


# ==============================================================================
#
# Allow loopback
#
# pass in lo0 to any port domain keep state
# ==============================================================================
# pass on lo0 proto tcp all
pass quick on lo0 proto tcp from any to any
#pass quick on lo0 proto udp from any to any



# ==============================================================================
#
# Macros
#
# - `$nic:network` means the NIC assigned IP network, e.g. 192.168.1.0/24
# - `($nic)` means the NIC assigned IP
#
# ==============================================================================
nic = "em0"
local_network = $nic:network
trust_node = "192.168.1.188"


# ==============================================================================
#
# Allow {DNS,NTP,Multicast DNS, SSDP} query & response (statefully by default)
#
# DNS  - Domain Name Server (53 UDP)
# NTP  - Network Time Protocol (123 UDP)
# mdsn - Multicast DNS (5353 UDP)
# SSDP - Selective Service Discovery Protocol (1900 UDP)
#
# ==============================================================================
pass quick on $nic proto udp from any to any port {domain,ntp,mdns,ssdp}


# ==============================================================================
#
# - Allow out ping request and reply (statefully by default)
# - Allow trust node ping me
#
# - In the `from ⟨source⟩ port ⟨source⟩ os ⟨source⟩ to ⟨dest⟩ port ⟨dest⟩` part:
#
#     `all` - This is equivalent to `from any to any`
#
# - For knowing the `icmp-type` name and number: `man icmp`
#
# ==============================================================================
# pass in log(all) quick on $nic proto icmp from $trust_node to ($nic) icmp-type echoreq
pass in quick on $nic proto icmp from $trust_node to ($nic) icmp-type echoreq
pass out quick inet proto icmp all icmp-type echoreq


# ==============================================================================
#
# - Allow trust node ssh me
#
# ==============================================================================
pass in quick on $nic proto tcp from $trust_node to ($nic) port ssh


# ==============================================================================
#
# Allow dialy app to use: all outgoing TCP and related reply
#
# ==============================================================================
pass out quick on $nic proto tcp from ($nic) to any

# Youtube needed
# https       443/udp
# ssdp        1900/udp   #Selective Service Discovery Protocol (UPnP) 
pass out quick on $nic proto udp from ($nic) to any port {https,ssdp}


# ==============================================================================
#
# Last step before `block all`:
#
# Block all incoming and outgoing packets via default out NIC and either:
#
# - Want to go out from my ip
# - Want to come in to my ip
#
# The purpose for those rules is that: 
#
# I can review the pflog file to see whether I'missing any allows rules or not:)
#
# ==============================================================================
block in quick log(all) on $nic from any to ($nic)
block out quick log(all) on $nic from ($nic) to any

# ==============================================================================
# Block everything else
# ==============================================================================
block all
 
Try "set skip on lo"
It doesn't change anything:)

Btw, all the above set skip lo0 actually mean set skip on lo0 (missing the on), but in the posted /etc/pf.conf content, it does say set skip on lo0, just in case you're focusing the on :)
 
pf is "last match wins", my understanding of the information in your pfctl -vs rules command a rule can still be evaluated and will bump that counter and your antispoof for lo0 is after set skip lo0 so both are evaluated but only one will "match". The keyword "quick" is used to short circuit rule processing; if quick is on a rule that matches any other potential matches following are never evaluated.
I would have thought that a set skip rule should not even evaluate following rules, but perhaps it runs the evaluation anyway.
 
pf is "last match wins", my understanding of the information in your pfctl -vs rules command a rule can still be evaluated and will bump that counter and your antispoof for lo0 is after set skip lo0 so both are evaluated but only one will "match". The keyword "quick" is used to short circuit rule processing; if quick is on a rule that matches any other potential matches following are never evaluated.
I would have thought that a set skip rule should not even evaluate following rules, but perhaps it runs the evaluation anyway.
That sounds very make sense, I will double-check when I have time, thanks for the useful tips :)
 
pf is "last match wins", my understanding of the information in your pfctl -vs rules command a rule can still be evaluated and will bump that counter and your antispoof for lo0 is after set skip lo0 so both are evaluated but only one will "match". The keyword "quick" is used to short circuit rule processing; if quick is on a rule that matches any other potential matches following are never evaluated.
I would have thought that a set skip rule should not even evaluate following rules, but perhaps it runs the evaluation anyway.

I would have thought that a set skip rule should not even evaluate following rules, but perhaps it runs the evaluation anyway.

Tested, the order of set skip on lo0 doesn't matter, lo0 has to be evaluated, as antispoof for lo0 expands rules on lo0 NIC:)
 
  • Like
Reactions: mer
Oh my god, how stupid I'm.... Actually, everything is fine, it's totally not about evaluating the `lo0` at all:)

Take a look at the rules generated by antispoof for lo0 again (look carefully):

Code:
pfctl -vs rules | rg lo0 -A3

# No ALTQ support in kernel
# ALTQ related functions disabled
#
# block drop in on ! lo0 inet6 from ::1 to any
#   [ Evaluations: 38        Packets: 0         Bytes: 0           States: 0     ]
#   [ Inserted: uid 0 pid 40108 State Creations: 0     ]
# block drop in on ! lo0 inet from 127.0.0.0/8 to any
#   [ Evaluations: 14        Packets: 0         Bytes: 0           States: 0     ]
#   [ Inserted: uid 0 pid 40108 State Creations: 0     ]

It's saying ! lo0, it means basically every packet that try to go through or try to come into my BSD will be evaluated if the packet doesn't through the lo0 NIC....that's why those 2 rules always have
Evaluations value (I think)
 
Back
Top