PF configuration for FTP on FreeBSD 9.1

I have the following in my pf.conf, all works nice so far, except FTP, what do I need to add to this to get FTP working?

I tried many things already, but am getting nowhere because apparently I am doing stuff wrong in the order of things.

Code:
# ----------------------- simple server pf.conf ----------------------
# For FreeBSD 9.1
# j65nko 2011, 2012, 2013
#
# If you adapt this ruleset for a resolving caching name server please
# make sure you don't allow the whole world to use your name server
# Creating an open resolving name server can allow the bad guys to use your nameserver
# in an DNS amplification attack

ext_if="vtnet0"
icmp_types="echoreq"

# Custom port for ssh
SSH_CUSTOM = xxxx

scrub in on $ext_if all fragment reassemble

set skip on lo0
#set skip on lo1

antispoof for $ext_if

# --- EXTERNAL INTERFACE
# --- INCOMING -------------------------------------------------------------------

# --- TCP
pass in  quick on $ext_if inet proto tcp from any to $ext_if  port http
pass in  quick on $ext_if inet proto tcp from any to $ext_if  port https
pass in  quick on $ext_if inet proto tcp from xx.xx.xx.xx to $ext_if  port $SSH_CUSTOM
pass in  quick on $ext_if inet proto tcp from any to $ext_if  port smtp
pass in  quick on $ext_if inet proto tcp from xx.xx.xx.xx to $ext_if  port pop3
pass in  quick on $ext_if inet proto tcp from xx.xx.xx.xx to $ext_if  port 2222

# --- for authoritative DNS server
#pass in  quick on $ext_if inet proto udp from any to $ext_if  port domain

# --- UDP
# --- for authoritative DNS server
#pass in  quick on $ext_if inet proto udp from any to $ext_if  port domain

# --- ICMP
pass in  quick on $ext_if inet proto icmp from any to $ext_if icmp-type $icmp_types

# --- EXTERNAL INTERFACE
# --- OUTGOING --------------------------------------------------------------------

anchor TMP

# --- TCP
pass  out quick log on $ext_if inet proto tcp from $ext_if to any port smtp
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port domain
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port http
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port https
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port whois
pass  out quick     on $ext_if inet proto tcp from $ext_if to xx.xx.xx.xx port $SSH_CUSTOM
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port smtp
pass  out quick     on $ext_if inet proto tcp from $ext_if to xx.xx.xx.xx port pop3
pass  out quick     on $ext_if inet proto tcp from $ext_if to xx.xx.xx.xx port 2222

# --- UDP
pass  out quick on $ext_if inet proto udp from $ext_if to any port domain
pass  out quick on $ext_if inet proto udp from $ext_if to any port ntp

# --- ICMP
pass  out quick on $ext_if inet proto icmp  from $ext_if to any

# ------------------------------------------------------
# --- DEFAULT POLICY
# ------------------------------------------------------
block log all
# ----- end of pf.conf
 
Hi @asteriskRoss, thanks for answering.

I first did this:
To enable the FTP proxy, add this line to /etc/rc.conf:
Code:
ftpproxy_enable="YES"
Then started the proxy by running service ftp-proxy start.

Then I added the following to pf.conf:
Code:
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"
rdr pass on $ext_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021
pass out proto tcp from $proxy to any port ftp

And then things just stopped working, I do not know where to put the above lines in the first mentioned file.

I hope this makes it more clear.
 
Last edited by a moderator:
Per pf.conf(), "By default pfctl(8) enforces an ordering of the statement types in the ruleset to: options, normalization, queueing, translation, filtering." So the first three lines are your "translation" and could go after antispoof and before your first pass rule. The fourth line is a "filtering" rule and could go anywhere below that.

EDIT:
You should put your block line right at the top of your filtering, as in before all the pass rules. Remember that PF is last match wins. I see you are trying to get around this by using quick on all your rules but really you should put your least specific rules like that generic block line right at the top and whittle it down as you go.
 
Thanks @junovitch, I now tried the following, but still can not get in, anybody see what I do wrong?

Code:
# ----------------------- simple server pf.conf ----------------------
# For FreeBSD 9.1
# j65nko 2011, 2012, 2013
#
# If you adapt this ruleset for a resolving caching name server please
# make sure you don't allow the whole world to use your name server
# Creating an open resolving name server can allow the bad guys to use your nameserver
# in an DNS amplification attack

ext_if="vtnet0"
icmp_types="echoreq"

# Custom port for ssh
SSH_CUSTOM = xxxx

scrub in on $ext_if all fragment reassemble

set skip on lo0
#set skip on lo1

proxy="127.0.0.1" # ftp proxy IP
proxyport="8021" # ftp proxy port

#### Normalization
scrub in all

#### NAT and RDR start
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"

# Redirect ftp traffic to proxy
rdr pass proto tcp from any to any port ftp -> $proxy port $proxyport

#### Start filtering
# Drop incoming everything
block in all

# Default connection refused message to client
block return

# keep stats of outging connections
pass out keep state

# We need to have an anchor for ftp-proxy
anchor "ftp-proxy/*"

antispoof for $ext_if

# --- EXTERNAL INTERFACE
# --- INCOMING -------------------------------------------------------------------

# --- TCP
pass in  quick on $ext_if inet proto tcp from any to $ext_if  port http
pass in  quick on $ext_if inet proto tcp from any to $ext_if  port https
pass in  quick on $ext_if inet proto tcp from any to $ext_if  port $SSH_CUSTOM
pass in  quick on $ext_if inet proto tcp from any to $ext_if  port smtp
pass in  quick on $ext_if inet proto tcp from 85.150.217.242 to $ext_if  port pop3
pass in  quick on $ext_if inet proto tcp from 85.150.217.242 to $ext_if  port 2222

# --- for authoritative DNS server
#pass in  quick on $ext_if inet proto udp from any to $ext_if  port domain

# --- UDP
# --- for authoritative DNS server
#pass in  quick on $ext_if inet proto udp from any to $ext_if  port domain

# --- ICMP
pass in  quick on $ext_if inet proto icmp from any to $ext_if icmp-type $icmp_types

# --- EXTERNAL INTERFACE
# --- OUTGOING --------------------------------------------------------------------

anchor TMP

# --- TCP
pass  out quick log on $ext_if inet proto tcp from $ext_if to any port smtp
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port domain
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port http
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port https
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port whois
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port $SSH_CUSTOM
pass  out quick     on $ext_if inet proto tcp from $ext_if to any port smtp
pass  out quick     on $ext_if inet proto tcp from $ext_if to 85.150.217.242 port pop3
pass  out quick     on $ext_if inet proto tcp from $ext_if to 85.150.217.242 port 2222

# --- UDP
pass  out quick on $ext_if inet proto udp from $ext_if to any port domain
pass  out quick on $ext_if inet proto udp from $ext_if to any port ntp

# --- ICMP
pass  out quick on $ext_if inet proto icmp  from $ext_if to any

# ------------------------------------------------------
# --- DEFAULT POLICY
# ------------------------------------------------------
block all

# ----- end of pf.conf
 
Last edited by a moderator:
Peter Hansteen's "Firewalling with PF" mentions a rule that doesn't appear in your pf.conf:
Finally, add a pass rule to let the packets pass from the proxy to the rest of the world:
Code:
pass out proto tcp from $proxy to any port ftp
where $proxy expands to the address the proxy daemon is bound to.

However, as I understand it, this is intended for gateways, where the firewall protects other hosts on the network. Your firewall rules appear to show that you're running PF on the server it is protecting, and not using it as a gateway. As I understand it, ftp-proxy cannot be used to handle FTP traffic originating from the host; it is intended where you are running a gateway for other network clients. From pf.conf(8):
[...]
the rdr rules are evaluated on an
inbound packet or the nat rules on an outbound packet.
[...]
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 connec-
tions 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. Redirec-
tions 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.
[...]

What I take from that is therefore that packets originating from the host running PF will never be redirected since they can never be considered to be incoming. The approach I use to restrict outbound FTP connections where PF is protecting the machine it is running on is to use a table of allowed FTP servers with the following rule, noting that I use the normal PF rule ordering and don't have "quick" rules like your configuration:
Code:
pass out inet proto tcp from <myhostandjails> to <ftpservers> port {ftp, >1023}

This allows the initial outgoing FTP connection and then permits traffic relating to passive mode to connect to the random port the server allocates. You could be less restrictive and allow such connections to any host, rather than a list you need to maintain, though that will also allow TCP connections to any host on all ports from 1024 upwards, which may be more permissive than you need.
 
These threads should always include the suggestion that it's time to stop using FTP if at all possible. sftp(1), for example, is more secure and less painful.
 
Back
Top