I am working at a web-host. I have pf setup (using rdr-to) to redirect internet IP's to the local LAN. The problem is that the local boxes see my Internet addy as the source and not the clients internet addy. Traffic is still getting routed correctly, but my SQL logs all show access from _myIP_ and not the real IP.

When a client hits our web-server or sql box the logs on these boxes show "connection from 1.1.1.3" (which is the mapping from pf.conf for that 'service')
I want it to show: "connection from 123.54.22.244" or the clients actual IP

I have been mucking around with the pf.conf rules changing rdr-to into nat-to (and others...) but nothing 'fixes' it.

History: We have an old OpenBSD4.8 box that is currently running as our firewall/gateway. It does this behaviour as desired. I only see this issue on a new OBSDv5.3 that we are trying to migrate to.

Without seeing your PF configuration, based on your problem description I'm going to assume you have NAT configured to translate on the inward facing network. If so, that is the cause of the trouble.

A typical network configuration is to translate only traffic on the outward facing network. See this line from the example configuration in the PF User's Guide:

match out on egress inet from !(egress:network) to any nat-to (egress:0)

The "match" command is not a pass or a block. It is used when you want set specific filtering options to apply to later rules.

The "egress" group contains the NICs assigned to the default route -- also known as the gateway route -- that should be the NICs connecting to your ISPs, or, NICs that route traffic to routers that connect to your ISPs.

Note that the traffic to be NATted comes from all non-egress networks that transit this router. They can go to any address, and will be translated to the first address assigned to the applicable egress NIC.

Note that there is no translation on any of the inner, non-egress network(s).

EDIT: If anybody could give me some hints about paring this down, and/or optimizing the layout and code I would be greatful. I am trying to make this thing very efficient too but I know I have a lot of things that are redundant.

Though this is but a pair of excerpts from your configuration -- thank you.

One thing to note, if you use match rules to apply options like nat-to, the match rules need to appear before any applicable block or pass rules. And then, with any subsequent matching rule, the prior match settings will also apply, so that a nat-to option need only be used once. From your excerpt, it appears the match rules were thrown in without that understanding.

Additionally, you are using standard NAT translation on your inward network, and that is causing the issue with external addresses being translated. Specifically, this line, the last in the configuration, is the root cause of the problem:

Code:

pass in log on $ext_if from $screen_pub_ip:0 nat-to $localscreen

Other than that, I note that you are combining bidirectional NAT and standard NAT for the same platforms. There is no need. And, the rules are out of order, as your binat-to rules appear before your general nat-to.

Remember: the last matching rule applies.

I've never used bidirectional NAT myself, but I have done some light reading on the subject. When bidirectional NAT is combined with standard NAT, it is possible to misconfigure. Michael Lucas, in his second edition of Absolute OpenBSD, recommends these sort of rules shown below for combining your main NAT with bidirectional NAT. I've adapted his recommended rules to the macros and redacted addresses you used in your configuration excerpt.

Note that Michael uses pass, rather than match, because he does not have multiple followon rules that require the nat-to option. (That's the value of match. Use it once to set an option that you would otherwise have to apply to multiple rules.)

But with a match for nat-to and then a subsequent pass, or with a pass that includes nat-to, the order of rules is critical. The last matching rule applies.

The first rule applies the general translation, the subsequent rule applies the specific translation. Michael writes that these rules produce unambigous address translations. Only the one specific server uses the dedicated external address, and all other hosts use the main address.

Last edited by jggimi; 11th October 2013 at 10:24 PM.
Reason: typos, clarity

For each packet processed by the packet filter, the filter rules are
evaluated in sequential order, from first to last. For block and pass,
the last matching rule decides what action is taken; if no rule matches
the packet, the default action is to pass the packet. For match, rules
are evaluated every time they match; the pass/block state of a packet
remains unchanged.

To circumvent this strategy you have to use quick:

Code:

quick If a packet matches a rule which has the quick option set, this
rule is considered the last matching rule, and evaluation of
subsequent rules is skipped.

Code:

match
The packet is matched. This mechanism is used to provide fine
grained filtering without altering the block/pass state of a
packet. match rules differ from block and pass rules in that
parameters are set every time a packet matches the rule, not only
on the last matching rule. For the following parameters, this
means that the parameter effectively becomes ``sticky'' until
explicitly overridden:nat-to, binat-to, rdr-to, queue, rtable, and
scrub.

With this in mind, and because I don't quite understand what you are trying to accomplish, the following effort to re-organize and clean up the rule set may thus not work at all :

Code:

DEBUG = log
# --- NAT
match out $DEBUG inet from ! $ext_if to any nat-to $ext_if
match out $DEBUG on $ext_if from $screen_pub_ip:0 to $localscreen nat-to $ext_if
pass in $DEBUG on $ext_if:0 proto { tcp, udp } to $screen_pub_ip port { 81 82 3306 3312 } nat-to $localscreen
pass out $DEBUG on $ext_if from $localscreen nat-to $screen_pub_ip
match out $DEBUG on $ext_if from $localscreen to any nat-to $screen_pub_ip
pass in $DEBUG on $ext_if from $screen_pub_ip:0 nat-to $localscreen
# --- BINAT
pass $DEBUG on $ext_if from $netfs to any binat-to $sync_pub_ip
pass $DEBUG on $ext_if from $localscreen to any binat-to $screen_pub_ip
# --- RDR
pass in $DEBUG on $ext_if proto { tcp, udp } from any to $sync_pub_ip port { 873 ftp ftp-data 22 21 } rdr-to $netfs
match in $DEBUG on $ext_if proto { tcp, udp } from any to $screen_pub_ip:0 port { 81 82 3306 3312 52530 } rdr-to $localscreen
# --- Block RFC 1918 non publicly routable addresses
block in $DEBUG quick on $ext_if from <rfc1918> to any
block out $DEBUG quick on $ext_if from <rfc1918> to any
# ==================================================================================
######## Allow ICMP ping ####Ping test to make sure we can get to host
pass in quick inet proto icmp from any to any icmp-type $icmp_types
### Note: it does work for $sync_pub_ip but not $screen_pub_ip ??? # <- THIS IS A CLUE!?!
# ==================================================================================
match in $DEBUG on $ext_if to $screen_pub_ip
-------------------------------------------------------------------------------
pass out $DEBUG on $ext_if from $int_if:network to any
# --- Default policy
block log all
# ---------------------------

You can see I defined a variable DEBUG that allows you to easily flip logging on or off.

__________________
You don't need to be a genius to debug a pf.conf firewall ruleset, you just need the guts to run tcpdump