【FreeBSD】pfctl

Posted by 西维蜀黍 on 2024-05-06, Last Modified on 2024-05-07

PF (Packet Filter)

PF (Packet Filter, also written pf) is a BSD licensed stateful packet filter, a central piece of software for firewalling. It is comparable to netfilter (iptables), ipfw, and ipfilter.

PF was developed for OpenBSD, but has been ported to many other operating systems.

Usages

Show

# shows how it has interpreted the filtering rules in your config file, including substitutions and defaults
$ pfctl -s

# Shows the NAT rules
sudo pfctl -s nat (or pfctl -sn): 

# Shows all the things
sudo pfctl -s all (or pfctl -sa): 

On/Off

View status

sudo pfctl -s info | grep Status

Enable pf if not already enabled: If pf is not already running, you can enable it with:

sudo pfctl -e

disable the PF firewall:

sudo pfctl -d

Edit Rules

  • The pf configuration files are located at /etc/pf.conf. You can edit this file to add or modify rules. After editing, you need to reload the configuration with: sudo pfctl -f /etc/pf.conf

Foward Ports

Forward A Local Port to A Local Port

for the packet to be forwarded, you need to enable it with sysctl or set it permanently in sysctl.conf (see man pfctl):

$ sudo sysctl net.inet.ip.forwarding=1

To make this change permanent, you can add the line net.inet.ip.forwarding=1 to /etc/sysctl.conf.


Create a pf rule: You need to write a rule in the pf configuration file that specifies which port to forward to another port. Below is an example of how you can forward traffic from port 80 to port 8080 on the local machine.

You can add the following line to your /etc/pf.conf file:

# Note that this rule has to be added after rdr-anchor "com.apple/*"
rdr pass on lo0 inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080
# not working below (my IP is 192.168.18.66)
# rdr pass on en0 inet proto tcp from any to 192.168.18.66 port 80 -> 192.168.18.66 port 8000

# My /etc/pf.conf
# FILTER RULES:
scrub-anchor "com.apple/*"
# TRANSLATION RULES:
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"
rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8000
# DUMMYNET RULES: 
dummynet-anchor "com.apple/*"
# FILTER RULES:
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"

This rule tells pf to redirect TCP traffic that is destined for port 80 on any interface to port 8080 on the localhost (127.0.0.1).

Load the pf configuration: After modifying the pf configuration file, you need to load the new rules. You can do this by running the following command:

sudo pfctl -f /etc/pf.conf

This command will load the configuration from /etc/pf.conf.

Enable pf if not already enabled: If pf is not already running, you can enable it with:

sudo pfctl -e

Ref

Forward A Local Port to An External Port

As a Jump Server For Another Server

# on 192.168.18.66
rdr pass on en0 inet proto tcp from any to 192.168.18.66 port 3307 -> 2.2.2.2 port 32001
nat on en0 inet proto tcp from any to 2.2.2.2 port 32001 -> 192.168.18.66

# on 192.168.18.32
# i.e., access 2.2.2.2 via 192.168.18.66 on 192.168.18.32, i.e., 192.168.18.66 is a jump server 
$ telnet 192.168.18.66 3307

# But, if on 192.168.18.66, telnet 192.168.18.66 3307 doesn't work, which is what I didn't figure out.

Not As a Jump Server

My scenarios is:

  1. The IP of 1.1.1.1 is xx.rwlb.singapore.rds.aliyuncs.com
  2. My local development env is not able to directly access 1.1.1.1 due to security concerns
  3. My DBA sets a jump server (2.2.2.2:3306) as a proxy to allow my local development env to indirectly access 1.1.1.1
  4. However, all middleware addresses are hard coded in my code repo, i.e., 1.1.1.1
  5. I wanna not modify the code and am still able to connect to all middleware

Idea: map 1.1.1.1:32111 to 2.2.2.2:3306 on kernel level, so that connecting 1.1.1.1:32111 actually is forwarded to 2.2.2.2:3306. As a result, my code doesn’t need any

# on 192.168.18.66, I wanna forward the traffic reaching 1.1.1.1:32111 to 2.2.2.2:3306
rdr pass on en0 inet proto tcp from 127.0.0.1 to 1.1.1.1 port 32111 -> 2.2.2.2 port 3306
#nat on en0 inet proto tcp from 127.0.0.1 to 2.2.2.2 port 3306 -> 1.1.1.1

# But it doesn't work, which may because

     Translation rules apply only to packets that pass through the specified
     interface, and if no interface is specified, translation is applied to
     packets on all interfaces.  For instance, redirecting port 80 on an
     external interface to an internal web server will only work for
     connections originating from the outside.  Connections to the address of
     the external interface from local hosts will not be redirected, since
     such packets do not actually pass through the external interface.
     [b]Redirections cannot reflect packets back through the interface they
     arrive on, they can only be redirected to hosts connected to different
     interfaces or to the firewall itself.[/b]
     
     (from https://forums.freebsd.org/threads/redirect-all-traffic-from-ip-to-another.59364/)

Reference