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:
# My /etc/pf.conf
# FILTER RULES:
scrub-anchor "com.apple/*"
# TRANSLATION RULES:
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"
# Note that this rule has to be added after rdr-anchor "com.apple/*"
rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8000
# 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
# 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 -vf /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
- https://unix.stackexchange.com/questions/505712/how-to-setup-simple-port-forwarding-on-macos-with-pf-rules-must-be-in-order-o
- https://srobb.net/pf.html
- https://man.freebsd.org/cgi/man.cgi?query=pf.conf&sektion=5&manpath=FreeBSD+13.0-RELEASE+and+Ports
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:
- The IP of 1.1.1.1 is xx.rwlb.singapore.rds.aliyuncs.com
- My local development env is not able to directly access 1.1.1.1 due to security concerns
- 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
- However, all middleware addresses are hard coded in my code repo, i.e., 1.1.1.1
- 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/)
- https://gist.github.com/kujohn/7209628
- https://apple.stackexchange.com/questions/363099/how-to-forward-traffic-from-one-machine-to-another-with-pfctl
- https://serverfault.com/questions/662366/forwarding-ip-address-using-pf
- https://forums.freebsd.org/threads/redirect-all-traffic-from-ip-to-another.59364/
Reference
- https://en.wikipedia.org/wiki/PF_(firewall)
- https://man.freebsd.org/cgi/man.cgi?query=pfctl(8)
- https://docs.oracle.com/cd/E88353_01/html/E72487/pfctl-8.html
- https://www.dragonflybsd.org/~aggelos/pf/rdr.html