PF I do not understand why certain packets are blocked when the src and dst IP are specifically unconditionally allowed

Code:
[root@gway05 ~]# pfctl -vv -s rules | grep '@5 '
No ALTQ support in kernel
ALTQ related functions disabled
@5 block drop in log all
[root@gway05 ~]# pfctl -t BLOCKTEMP -T show
[root@gway05 ~]#

I had already removed much of the detail from Calomel's example in my own configuration. The only things left are shown below:

Code:
# /etc/pf.conf
. . .
### Define states
TcpState ="modulate state"
 . . .
### Define stateful tracking options (STO)
OpenSTO ="(max 90000, source-track rule, max-src-conn 1000, max-src-nodes 256)"
SmtpSTO ="(max   200, source-track rule, max-src-conn   10, max-src-nodes 256, max-src-conn-rate 200/30)"
SshSTO  ="(max     5, source-track rule, max-src-conn    5, max-src-nodes   5, max-src-conn-rate   5/30,  overload <BLOCKTEMP> flush global)"
WebSTO  ="(max  4096, source-track rule, max-src-conn   64, max-src-nodes 512, max-src-conn-rate 500/100, overload <BLOCKTEMP> flush global)"
. . .

None of which I thought applied to the rule I imagined would handle inside traffic.

Code:
pass                    quick on $int_if \
                  from  $int_if:network \
                  to    $int_if:network
 
Code:
### icmp permitted types { 0, 3, 4, 8, 11, 12 }
#icmp_types = "{ echorep, unreach, squench, echoreq, timex, paramprob }"
icmp_types = "{ echoreq }"


### Define states - keep state is the default
TcpState ="modulate state"
UdpState ="keep state"


### Define stateful tracking options (STO)
OpenSTO ="(max 90000, source-track rule, max-src-conn 1000, max-src-nodes 256)"
SmtpSTO ="(max   200, source-track rule, max-src-conn   10, max-src-nodes 256, max-src-conn-rate 200/30)"
SshSTO  ="(max     5, source-track rule, max-src-conn    5, max-src-nodes   5, max-src-conn-rate   5/30,  overload <BLOCKTEMP> flush global)"
WebSTO  ="(max  4096, source-track rule, max-src-conn   64, max-src-nodes 512, max-src-conn-rate 500/100, overload <BLOCKTEMP> flush global)"


### Tables
### set persistent table for permanaent banned accessing IPs
table <BLOCKPERM> counters persist file "/var/db/pf/pf_block_perm"

### set persistent table for suspect brute force attampts
table <BLOCKTEMP> counters
#
#
### Options
### by default drop blocked packets and do not return a return packet
set block-policy drop
### Set none for no debug messages.  Alternatively set to urgent.
set debug urgent
### reorder and combine rules as logic permits (none - basic - profile)
set ruleset-optimization none
### do not filter on the loopback interface(s)
set skip on lo0
### do not filter on internal interface - duplicates iptables behaviour
set skip on em0
### bind state matching to i/f (if-bound) or any (floating [default])
#set state-policy if-bound


### Normalisation
## clean up incoming packets and reassemble fragments
#scrub in all fragment reassemble no-df max-mss 1440
scrub in all fragment reassemble
### Or not if rfc1323 timestamp integrity is required


### Queueing
# none - Not available without a custom kernel built with ALTQ


### Network Address Translation
nat               log   on $ext_if \
                  from  192.168.8.0/24 \
                  to    any -> ($ext_if:0)


### Filtering
### set anti-lockout rule
pass              log   quick inet proto tcp \
                  from  { 216.185.71.0/26 192.168.216.0/26 } \
                  to    (self) port { 22 10000 } \
                  keep state

### allow LAN ssh to ssh
pass              log   quick on $int_if proto { tcp } \
                  from  { $int_if:network } \
                  to    { $int_if:network } port  $port_ssh \
                  keep state


### set default action to block everything ELSE
block return  out log all
block drop    in  log all


### Allow LAN traffic
pass                    quick on $int_if \
                  from  $int_if:network \
                  to    $int_if:network

pass                    quick on $int_if \
                  from  { self 192.168.0.0/16 216.185.71.0/25 } \
                  to    { 192.168.0.0/16 216.185.71.0/25 }


### Allow DNS to and from authorised hosts
pass              log   quick proto { tcp udp } \
                  from  any \
                  to    $host_dns   port  domain  keep state

pass              log   quick proto { tcp udp } \
                  from  $host_dns \
                  to    any  keep state

### Allow HTTP from authorised hosts
pass                    quick proto { tcp } \
                  from  { self 192.168.0.0/16 216.185.71.0/25 } \
                  to    any         port  $port_http

pass                    quick proto { tcp } \
                  from  any \
                  to    $host_http  port  $port_http

### Allow NTP from authorised hosts
pass                    quick proto { udp }\
                  from  { 192.168.8.0/24 216.185.71.0/25 } \
                  to    $host_ntp port  ntp keep state

pass                    quick proto { udp } \
                  from  $host_ntp \
                  to    any

### Allow SMTP from authorised hosts
pass              log   quick proto { tcp } \
                  from  any \
                  to    $host_smtp  port  $port_smtp \
                  $TcpState $SmtpSTO

pass              log   quick proto { tcp } \
                  from  $host_smtp \
                  to    any         port  $port_smtp \
                  $TcpState $SmtpSTO

### Allow SSH from authorised hosts
pass              log   quick proto { tcp } \
                  from  { $int_if:network } \
                  to    { $int_if:network } port  $port_ssh \
                  $TcpState $SshSTO

pass              log   quick proto { tcp } \
                  from  { self $host_trust } \
                  to    any         port  $port_ssh \
                  $TcpState $SshSTO

pass              log   quick proto { tcp } \
                  from  any \
                  to    $host_ssh   port  $port_ssh \
                  $TcpState $SshSTO


### Allow icmp ping and traceroute
# Allow select ICMP types in and PING to leave the server
pass          inet proto icmp all icmp-type $icmp_types keep state

# Traceroute
pass          out       quick on $ext_if inet proto udp \
                  from  any \
                  to    any         port  33433 >< 33626 keep state
 
Is $inf_if assigned to em0? Keep in mind that you have a set skip on em0 in your rules. So all the pass in on em0 ... rules are ignored.

Traffic is NAT'ted first, so the outgoing traffic matches from ($ext_if:0) to any not from $int_if:network to any
 
Yes, int_if is em0 and ext_if is em1. The set skip em0 is set because otherwise I cannot get ssh to work on the LAN. Traffic going out to the internet works as does incoming, but between two internal sites if em0 is filtered then PF blocks sshd's response to the client's request.

The internal ip addresses in the present case are 192.168.216.x./24 which fall outside the nat specification of 192.168.8.0/24 unless PF behaves differently in this case to what I expect.
 
Yes, int_if is em0 and ext_if is em1.
Ok, that means all the pass in on $int_if rules are ignored. As PF has a default allow rule (your block all is also ignored for traffic on $int_if) the traffic is probably not allowed out on the outgoing interface ($ext_if) so it runs into your default block all.

Which networks are attached to ext_if and which ones are attached to int_if? You appear to have a really confusing setup with different network segments attached to the same interface. That's usually a bad idea but you may have a good reason to do this.
 
Which networks are attached to ext_if and which ones are attached to int_if? You appear to have a really confusing setup with different network segments attached to the same interface. That's usually a bad idea but you may have a good reason to do this.


Code:
ifconfig_em0="inet 216.185.71.5/25"
ifconfig_em0_alias192="inet 192.168.0.1/16"

ifconfig_em1="inet 72.142.105.234/29"

The reason is historical usage. We have internal workstations and servers that need to talk to external resources. I never considered this to be an unusual setup. We previously had multiple aliases for 192.168.X.1 where different groups of hosts were assigned to specific /24 subnets of 192.168.0.1/16. Again, I never saw this as being particularly unusual. I expected that however the traffic arrived at the router internal interface that the software would be able to detect what was meant to stay inside and what was to be forwarded on.

P.S. I appreciate the help in understanding this.
 
It is generally a bad idea to mix public and private traffic on the same segment because its insecure. It's better to use separate interfaces (VLANs) with strict rules to keep traffic isolated from the private network.
 
It is generally a bad idea to mix public and private traffic on the same segment because its insecure. It's better to use separate interfaces (VLANs) with strict rules to keep traffic isolated from the private network.

Given that the existing network infrastructure is built into the building and that it has existed in that form since 1995 it is doubtful that a physical separation is possible. And given the way our hubs and switches are arranged is seems doubtful to me that vlans would provide much in the way of an improvement. The possibility of a compromised public service being used to stage an attack against private ips, while real, has a very,very low probability of occurrence. In any case here it would be mostly pointless unless one wishes to attack printers and fax machines, or user workstations that are normally switched off when not in use and contain no user or business data.

I am not downplaying the importance of security. It is just that some things are less immediately useful than others. In this case I am transitioning our firm from RHEL to FreeBSD and, as we built our own routers out of commodity hosts, I need to get what we already have working on RHEL to work on FreeBSD, Then we can look at what will be required to breakout our internal subnets from one another.

The main purpose that the private IPs serve, outside the Active-Directory Domain, is to allow backchannel ssh access to public hosts. This allows us to simply disable ssh from listening on public addresses. I always considered this practice to significantly improve security as it works whether a firewall is active or not.
 
We previously had multiple aliases for 192.168.X.1 where different groups of hosts were assigned to specific /24 subnets of 192.168.0.1/16.
It looks like you're trying to supernet here. Not something I would set up as it's quite tricky to get it right. There is no routing between the different 192.168.x.0/24 networks. I suspect you're seeing a lot of ICMP redirects on the $int_if.

The internal ip addresses in the present case are 192.168.216.x./24 which fall outside the nat specification of 192.168.8.0/24 unless PF behaves differently in this case to what I expect.
What's the traffic's destination supposed to be? I can imagine traffic from 192.168.216.0/24 arriving on $ext_if but I don't know where it's supposed to go.
 
It looks like you're trying to supernet here. Not something I would set up as it's quite tricky to get it right. There is no routing between the different 192.168.x.0/24 networks. I suspect you're seeing a lot of ICMP redirects on the $int_if.

There is not supposed to be any routing between the different 192.168.x.0/24 nets. They serve distinct purposes and there is no reason for traffic to pass between them. There is supposed to be routing between 216.185.71.0/25 and 192.168.x.0/24. Which is how it works on RHEL with IPTables.

What's the traffic's destination supposed to be? I can imagine traffic from 192.168.216.0/24 arriving on $ext_if but I don't know where it's supposed to go.

The destination of all 192.168.216.0/24 is any other address on 192.168.216.0/24. It is not supposed to go anywhere else.

I realise that I am causing most, if not all, of my own difficulties. I am trying to map 24 years of prior experience on one platform over to another and dealing with stuff I set up two decades ago and have not really had any need to touch since. So, I am far beyond just rusty.

On IPtables we have int_if set up with gateway aliases to each of the pseudo networks, for want of a more accurate term. Say: 192.168.7.1/24, 192.168.8.1/24, 192.168.216.1/24, etc., while the gateway host itself is 216.185.71.5/25 and the gateway alias is 216.185.71.1/25. This is done to allow rapid relocation of the main gateway address to our hot standby (216.185.71.2/25) without having to physically disconnect or down hosts.

Only some of the 192.168.0.0/24 addresses are nat'ed. The rest are internal use only. This all worked without any evident problems. Obviously, things are different on FreeBSD and PF. I am working from the premise that what is possible on one platform may be replicated on another, if only in effect. I just have to discover how, I suspect that playing around with 192.168.0.0/16 was a terrible error and I am planning to remove that as soon as I can, without further impact on the users.

I suspect that a great deal of my problem stems from the fact that PF does NAT before Filtering. Which I suppose is required because PF does not really implement the concept of a forwarded packet so that NAT addresses cannot be examined before transformation. SO, I am having some difficulty mapping how what was done in IPTables is accomplished in PF.
 
I have always understood that if two or more ip addresses from different subnets A,B and C, are all defined on a single i/f acting as a gateway that traffic from any one of them to another one, A-->C will be routed out the same i/f. Am I mistaken?
 
You have firewall which block that routing. You totally skip the pf processing on internal interface and you are missing the creation of the dynamic states.
 
You have firewall which block that routing. You totally skip the pf processing on internal interface and you are missing the creation of the dynamic states.
I know that. The thing has to work and it does not if em0 is not skipped. So for the time being it is again put to one side. I just need to know whether or not my understanding is correct respecting how network interfaces work with multiple aliases
 
Yes your understandings are right. When you have gateway enabled all L3 connected interfaces are routed if there's a route in your routing tables. By default each connected network has a route in it. The mistake that you are doing is that you are trying to translate an IPtables firewall over PF firewall which work in totally different way. In PF all rules are proceed and last matching rule is applied unless there's a rule which terminate the search with the "quick" option. I would suggest you to rewrite your entire pf configuration and start a new one based on the following example bellow and after evrything is connected and working to start extend your configuration one changes at time until you are happy with the result.

Note: when you have more than one alias on WAN facing interface you can use :0 to match only the primary address for the NAT or if you can specify the exact ip address for the nat. Because you have a public ip address in your int_if then you need to exclude it from the nat using !network

# this will match all ip on ext_if
nat on $ext_if from $int_if:network to any -> ($ext_if)
# this will match only the primary ip address without aliases on ext_if
nat on $ext_if from $int_if:network to any -> ($ext_if:0)
# this will redirect to the specific ip (downside is when you regular change your public ip address you need to rewrite your firewall)
nat on $ext_if from $int_if:network to any -> 72.142.105.234

In the new pf there's no need to specify keep states as they are applied by default

Code:
# macros
int_if = "fxp0"
ext_if = "ep0"

tcp_services = "{ 22, 113 }"
icmp_types = "echoreq"

priv_nets = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }"
 
# options
set block-policy return
set loginterface $ext_if

# scrub
scrub in all

# nat/rdr
nat on $ext_if from $int_if:network to any -> ($ext_if)
rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 \
   port 8021

# filter rules
block all

pass quick on lo0 all

block drop in  quick on $ext_if from $priv_nets to any
block drop out quick on $ext_if from any to $priv_nets

pass in on $ext_if inet proto tcp from any to ($ext_if) \
   port $tcp_services flags S/SA keep state

pass in inet proto icmp all icmp-type $icmp_types keep state

pass in  on $int_if from $int_if:network to any keep state
pass out on $int_if from any to $int_if:network keep state

pass out on $ext_if proto tcp all modulate state flags S/SA
pass out on $ext_if proto { udp, icmp } all keep state
 
I know that. The thing has to work and it does not if em0 is not skipped. So for the time being it is again put to one side. I just need to know whether or not my understanding is correct respecting how network interfaces work with multiple aliases

Apparently it does, as you say it works when em0 is skipped. (So FreeBSD is forwarding the traffic back out the same physical port it came in on with no translation.)

Which leads me to a different question...

For example: ssh from 192.168.216.44 to 192.168.216.31 gets this treatment:
Code:
00:00:00.020825 rule 5/0(match):
block in on em0: 192.168.216.31.22 > 192.168.216.44.64123: Flags [S.],
seq 283093415, ack 1513496196, win 65535,
options [mss 1440,nop,wscale 6,sackOK,TS val 956747543 ecr 3683277312],
length 0

Why is this traffic coming into this box at all? Are the subnet masks on 192.168.216.44 and 192.168.216.31 different? (Such that the ethernet layer routes .44 -> .31 vs. .31 -> .1 [reflect back out em0] -> .44?)

This would explain what you're seeing. (Works when skip em0; broken with a SYN/ACK blocked (due to no state-creating SYN) when not skipped.)
 
most likely because of the wrong network mask /32 and 192.168.216.31 is using his default gateway .1 to reach back .44
 
Agreed, traffic between hosts in the 192.168.216.0/24 network shouldn't even touch this gateway host. It's all directly connected. Unless there's something seriously wrong with the network configuration downstream of the gateway host.
 
The problem is as described in the post by VladiBG. Unfortunately, at the moment there is nothing I can do to deal with the network topography so the workaround will remain to disable PW filtering on em0. Actually, at the moment we have returned to using IPTables on an CentOS-6 gateway. The switch-over to PF on FreeBSD is temporarily in abeyance.
 
Back
Top