Monday, July 31, 2017

Re: Question about pf tables and limitation of addresses 0.0.0.0/0 or 0/0

On Thu, Jul 27, 2017 at 12:52 PM, Donald Clark Jackson
<don.jackson@gmail.com> wrote:
> table <public> const { !10/8 !172.16/12 !192.168/16 0/0 }
> guest_hq_if = "em3"
> guest_hq_net = $guest_hq_if:network
> pass log (matches) from $guest_hq_net to <public> keep state
>
> match out log (matches) on $external_if inet from $guest_hq_net nat-to ($external_if)

I'm not sure this ruleset is doing what you think it's doing. Try
running the following command to verify that your <public> table
contains what you think it should:

# pfctl -t public -T show

Putting aside for the moment whether 0/0 works or not, I think you
have another problem with the ! marks. To explain the problem, let's
pretend the entire Internet consists of just 3 class A networks: 1/8,
2/8 and 3/8. So what does { !1/8 !2/8 } mean? It's a table
containing all the addresses not in 1/8 (i.e. 2/8 3/8), plus all the
addresses not in 2/8 (i.e. 1/8 3/8):

{ 2/8 3/8 1/8 3/8 } simplifies to { 1/8 2/8 3/8 }, which matches
everything and is probably not what you intended.

I don't think you can even use ! in a table, even though "pfctl -nf"
doesn't appear to complain about it at all. When I try it in
5.9/amd64 the tables end up being not defined.

If you want to provide an Internet-only guest network, I recommend you
try something like this:

table <rfc1918> { 10/8, 172.16/12, 192.168/16 }
guest_if = "em3"
ext_if = "em0" # or whatever your egress interface is
pass in log on $guest_if from $guest_if:network to any
block in log quick on $guest_if to $guest_if # note 1
block out log quick on $ext_if from $guest_if:network to <rfc1918> # note 2
pass out log on $ext_if from $guest_if:network nat-to ($ext_if)

This lets guest traffic into the router, then relies on your routing
table to decide which outbound interface the traffic should then be
forwarded to. Traffic to the Internet (going out through $ext_if) is
passed with an explicit "pass out" rule (and NAT is applied at the
same time). Traffic attempting to reach your other internal networks
is not forwarded because there is no "pass out" rule on any other
interface allowing it.

note 1: Connections *to* your router rather than *through* your router
are dropped, to prevent guests from trying to ssh into your router.
If you want to allow guests to connect to your router for certain
services (e.g. dns) then you will want to be more selective.

note 2: Internet traffic to "unrouteable" Internet addresses is
dropped. Note that this rule explicitly applies only to traffic "from
$guest_if:network" because other outbound traffic might legitimately
need to send to such an address. For example, some ISP's will provide
a 10/8 address as your default route, and blocking 10/8
unconditionally will prevent you from doing things like pinging that
router.

As you have discovered, once you have more than two networks (internal
and external) things can start to get complicated, which means the
opportunity to make mistakes goes up dramatically. What I do to make
things clearer (and to reduce the impact of mistakes) is to use tags
to categorize traffic:

### BEGIN ###
private = "em0"
guest = "em1"
inet = "em2"

table <rfc1918> const { 10/8, 172.16/12, 192.168/16 }

# Inbound traffic is blocked by default and tagged TBD until it can be
categorized
block in log all tag TBD

# Inbound / ACCEPT - traffic that should be accepted by this router
(not forwarded)
pass in log quick on $private inet proto tcp from $private:network to
$private port ssh tagged TBD tag ACCEPT
pass in log quick on $private inet proto icmp from $private:network to
(self) icmp-type echoreq tagged TBD tag ACCEPT
pass in log quick on $guest inet proto icmp from $guest:network to
(self) icmp-type echoreq tagged TBD tag ACCEPT
pass in log quick on $private inet proto {udp tcp} from
$private:network to em0 port {domain ntp} tagged TBD tag ACCEPT
pass in log quick on $guest inet proto {udp tcp} from $guest:network
to $guest port {domain ntp} tagged TBD tag ACCEPT
pass in log quick on $private inet proto tcp from $private:network to
port ftp divert-to 127.0.0.1 port 8021 tagged TBD tag ACCEPT
pass in log quick on $guest inet proto tcp from $guest:network to port
ftp divert-to 127.0.0.1 port 8021 tagged TBD tag ACCEPT
anchor "ftp-proxy/*"

# Inbound / FORWARD - traffic that should be forwarded by this router
pass in log quick on $private from $private:network tagged TBD tag FORWARD
block in log quick on $guest from $guest:network to $private:network
tagged TBD tag BLOCK
pass in log quick on $guest from $guest:network tagged TBD tag FORWARD

# Outbound / FORWARD
block out log all # by default block all outbound traffic not explicitly allowed
pass out log quick on $private tagged FORWARD
pass out log quick on $guest tagged FORWARD
block out log quick on $inet to <rfc1918> tag BLOCK
pass out log quick on $inet nat-to ($inet) tagged FORWARD

# Outbound - traffic that may originate from this router (even this is
blocked by default)
pass out log quick on $inet inet proto udp from ($inet) port bootpc to
any port bootps # ISP DHCP service
pass out log quick on $inet proto icmp from ($inet) icmp-type echoreq # ping
pass out log quick on $inet proto {udp tcp} from ($inet) to any port
domain # DNS
pass out log quick on $inet proto {udp tcp} from ($inet) to any port ntp # NTP
pass out log quick on $inet proto tcp from ($inet) to any port 443 #
HTTPS for ntpd constraints
pass out log quick on $inet proto tcp from ($inet) to any port ftp # ftp-proxy

### END ###

Note that the "FORWARD" tag is the only one that actually gets used
for something (it gets set by an "in" rule, then is used in a later
"out" rule to allow specifically that traffic to pass). The other
tags don't do anything once set except act as documentation of intent.
They are also a bit of a safety net -- I could accidentally omit any
"quick" and the ruleset wouldn't behave (very) differently. As a side
benefit, the tags show up if you use "pfctl -s rules" which helps to
make the rules easier to understand even with all the comments
removed.

-ken

No comments:

Post a Comment