PF Packet Filter no use with ftp?

Hi folks!

I spent two weeks now trying to get an FTP connection to ftp.freebsd.org working without pfctl -d.

Code:
> ftp -a ftp.freebsd.org
Trying 193.162.146.4:21 ...
Connected to ftp.freebsd.org.
220 beastie.tdk.net FTP server (Version 6.00LS) ready.
331 Guest login ok, send your email address as password.
230 Guest login ok, access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd pub
250 CWD command successful.
ftp> ls
229 Entering Extended Passive Mode (|||51788|)
ftp: Can't connect to `193.162.146.4:51788': Operation not permitted
200 PORT command successful.

421 Service not available, remote server timed out. Connection closed.
ftp>

Resulting in logs from tcpdump -ne -tttt -r /var/log/pflog like this:

Code:
 2013-08-26 17:06:55.433719 rule 1..16777216/0(match): block out on ext_if: xxx.xxx.xxx.xxx.32030 > 204.152.184.73.26581
: Flags [S], seq 3347934968, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS[|tcp]>

I did what Peter N.M. Hansteen writes in The Book of PF, 2nd Ed., on page 34
Enabling FTP transfers through your gateway is amazingly simple, thanks to
the FTP proxy program included in the OpenBSD [and FreeBSD] base system. The program
is called—you guessed it—ftp-proxy.
and ended up like a complete idiot, having read the manuals on FTP, ftp-proxy, pf.conf, etc. in addition meantime.

Of course I also started working on the almost 700 results from http://www.google.de/search?lr=&hl=de&as_qdr=all&q=pf+ftp-proxy+solved++site:forums.freebsd.org but found no working solution for FTP sessions from the same system where PF (and ftp-proxy) are installed on.

So the neat little question is: how do real FreeBSD professionals do FTP connections from their PF systems?


PS: Don't ask for any configuration unless you can offer a working one!
 
scottro said:
http://forums.freebsd.org/showthread.php?t=12298 from 2010 offers a configuration that worked for someone.

An even older one from 2008, but cyberciti tends to have easy to understand articles.

http://www.cyberciti.biz/faq/freebsd-opebsd-pf-firewall-ftp-configuration/
Thanks, but nothing new to me. Did you read the last two posts of your first hint? The how-to you mention (second hint) did not solve my problem. It produces just what I described in my entry post.
 
Sorry, I should have added something to the effect that, "You've probably seen these but..." and more importantly, should have said untested by me. I just vaguely remembered something similar many years ago. Apologies.
 
The Handbook PF Tutorial has a section on ftp-proxy(8): http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/firewalls-pf.html#pftut-ftp

If at all possible, switch to sftp(1), really just an alternate form of scp(1). It's secure and much easier to handle. There are Windows clients like WinSCP.

PS: Don't ask for any configuration unless you can offer a working one!

That makes no sense. Anyone might be able to spot a misconfiguration, even if they can't write a config file from scratch.
 
Looks like I got it work, but not really... still testing.

But what about SFTP: are ftp.freebsd.org and/or other public FTP servers configured for anonymous access without authentication?
 
Dear @wblock@, I appreciate your contributions, and I thanked you for the reference to http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/firewalls-pf.html#pftut-ftp which was a new one. Having read this, and compared with what I've done so far I found, that
30.4.6.4. That Sad Old FTP Thing

The short list of real life TCP ports above contained, among other things, FTP. FTP is a sad old thing and a problem child, emphatically so for anyone trying to combine FTP and firewalls. FTP is an old and weird protocol, with a lot to not like. The most common points against it are

• Passwords are transferred in the clear

• The protocol demands the use of at least two TCP connections (control and data) on separate ports

• When a session is established, data is communicated via ports selected at random

All of these points make for challenges security-wise, even before considering any potential weaknesses in client or server software which may lead to security issues. These things have tended to happen.

Under any circumstances, other more modern and more secure options for file transfer exist, such as sftp(1) or scp(1), which feature both authentication and data transfer via encrypted connections. Competent IT professionals should have a preference for some other form of file transfer than FTP.

Regardless of our professionalism and preferences, we are all too aware that at times we will need to handle things we would prefer not to. In the case of FTP through firewalls, the main part of our handling consists of redirecting the traffic to a small program which is written specifically for this purpose.
is completely copied and pasted from Hansteen, P., The Book of PF, 2nd Ed., page 34 without any reference.

You asked for my configuration and here it is:
Code:
 # pf_ftp_only.conf
# The FreeBSD packet filter (pf) code has version OpenBSD 4.5
# pf configuration file for testing ftp with ftp-proxy only

# Macros
ext_if = "em0"

tcp_services = "{ ftp, ssh, www, domain, 3128, 8021, 8118 }"
udp_services = "{ domain }"

# Tables
#table <blockIP> { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4, 240.0.0.0/5, 127.0.0.0/8, 0.0.0.0 }
table <blockIP> { 10.0.0.0/8, 224.0.0.0/4, 240.0.0.0/5, 127.0.0.0/8, 0.0.0.0 }

# Options
#set block-policy drop          # drop= silent; return=TCP RST
set skip on lo0                 # no filtering on loopback
set debug urgent

# Scrub
scrub in on $ext_if all fragment reassemble

# NAT & RDR
nat-anchor "ftp-proxy/*"                                                # Version 4.5 compliant
rdr-anchor "ftp-proxy/*"                                                # Version 4.5 compliant

# Redirect ftp traffic to proxy
rdr pass proto tcp from any to any port ftp -> 127.0.0.1 port 8021      # Version 4.5 compliant

# anchor for ftp-proxy
#anchor "ftp-proxy/*"

# pass packets from ftp-proxy to rest of world
pass out proto tcp from any to any port ftp

# Filters
block log quick inet6 all
block log all

#pass in quick inet proto tcp to port ftp divert-to 127.0.0.1 port 8021

pass out proto tcp to port $tcp_services
pass in  proto tcp to port $tcp_services
pass out proto udp to port $udp_services
pass in  proto udp to port $udp_services
pass inet proto icmp all icmp-type echoreq keep state

I had ftp-proxy running as daemon, now trying to debug like this:

Code:
# ftp-proxy -d -D5 -v -v
listening on 127.0.0.1 port 8021
nothing else can be read here.

Next round opened.
 
Last edited by a moderator:
Ftp ports are:
Code:
ftp-data	 20/tcp	   #File Transfer [Default Data]
ftp-data	 20/udp	   #File Transfer [Default Data]
ftp		 21/tcp	   #File Transfer [Control]
ftp		 21/udp	   #File Transfer [Control]

And those FTP anchors... Its black magic in OpenBSD, so I'm not sure if it works as supposed in FreeBSD. No hints in pflog's?
 
Erratus said:
Dear wblock@, I appreciate your contributions, and I thanked you for the referece to http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/firewalls-pf.html#pftut-ftp which was a new one. Having red this, and compared with what I've done so far I found, that is completely copied and pasted from Hansteen, P., The Book of PF, 2nd Ed., page 34 without any reference.

Yes, he wrote it, and contributed it to the Handbook. There's a byline at the start of that tutorial, http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/firewalls-pf.html#pf-tutorial.

You asked for my configuration and here it is:
Code:
 # pf_ftp_only.conf
# NAT & RDR
nat-anchor "ftp-proxy/*"                                                # Version 4.5 compliant
rdr-anchor "ftp-proxy/*"                                                # Version 4.5 compliant
...

The main NAT statement is missing:
Code:
nat on $ext_if from $localnet to any -> ($ext_if)
 
This box is no gateway. Therefore no NAT should be used? The box provides PF, DNS, proxy to a LAN. The gateway is a separate router.
 
Instead of adding the main NAT statement I removed the NAT anchor. Looks like this is the solution.

For completness here is the working PF test configurtation which shows the difference between a gateway and a non gateway use:

Code:
# pf_ftp_only.conf
# The FreeBSD packet filter (pf) code has version OpenBSD 4.5
# pf configuration file for testing ftp with ftp-proxy only

# Macros
ext_if = "em0"
#localnet = "$ext_if:xxx.xxx.xxx.0/24"
tcp_services = "{ ssh, www, domain }"
udp_services = "{ domain }"

# Tables
table <blockIP> { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4, 240.0.0.0/5, 127.0.0.0/8, 0.0.0.0 }

# Options
set skip on lo0                 # no filtering on loopback
set debug urgent

# Scrub
scrub in on $ext_if all fragment reassemble

# NAT for gateway configurations only
#nat-anchor "ftp-proxy/*"                                               # Version 4.5 compliant
#nat on $ext_if from $localnet to any -> ($ext_if)

# Redirect ftp traffic to proxy
rdr-anchor "ftp-proxy/*"                                                # Version 4.5 compliant
rdr pass proto tcp from any to any port ftp -> 127.0.0.1 port 8021      # Version 4.5 compliant

# Filters
block log all

# pass packets from ftp-proxy to rest of world
pass out proto tcp from any to any port ftp

pass out proto tcp to port $tcp_services
pass in  proto tcp to port $tcp_services
pass out proto udp to port $udp_services
pass in  proto udp to port $udp_services

Thanks @wblock@!
 
Last edited by a moderator:
As an aside - are there any plans to migrate pkg_add from using FTP by default (does it work any other way?) to some other transport (HTTP perhaps, or even better HTTPS)?

FTP is horribly broken and needs to die in a fire.
 
PKGNG uses the fetch(3) library for fetching the packages and the repository metadata. HTTP will work by just using a http:// URL in PACKAGESITE. HTTPS should be doable as well. I doubt that anything will be done for the old pkg_* tools.
 
Folks, we are back to zero. My posted test configuration for FTP definitively does NOT work. What happened in the meantime? A simple shutdown after many times of pfctl -f /etc/pf_ftp_only.conf and ftp -4a ftp.freebsd.org - and a fresh morning-boot.

This kind of PF testing seems not to be the state of the art. But why?

The criterion for a working solution was that the connection to the FTP server does not break after issuing an ls command like this:
Code:
 ftp> ls
229 Entering Extended Passive Mode (|||51788|)
ftp: Can't connect to `193.162.146.4:51788': Operation not permitted
200 PORT command successful.

421 Service not available, remote server timed out. Connection closed.
ftp>

After removing
Code:
nat-anchor "ftp-proxy/*"
from the PF configuration file the connection did not break. I was able to download from the FTP server. This I tested approx ten times with different public FTP servers and made other minor cleaning to the PF test configuration. It did work all the time.

This morning the system booted and used the default /etc/pf.conf. Of course this file was edited according to my proudly findings last night too. Back to of pfctl -f /etc/pf_ftp_only.conf confirmed, that this was never a solution. But why did it work the day before? What happened to PF? Was it wide open or did some states survive? I do not know.

Is the flush option -F of pfctl mandatory when testing PF? I did not read any recommendation to use this option and I do not even know if it would have helped.

We are back to the heading of the post: PF Packet Filter no use with FTP in a non gateway configuration.
 
Erratus said:
Hi folks!

I spent two weeks now trying to get an FTP connection to ftp.freebsd.org working without pfctl -d.

[snip]

Of course I also started working on the almost 700 results from http://www.google.de/search?lr=&hl=de&as_qdr=all&q=pf+ftp-proxy+solved++site:forums.freebsd.org but found no working solution for FTP sessions from the same system where PF (and ftp-proxy) are installed on.

So the neat little question is: how do real FreeBSD professionals do FTP connections from their PF systems?

For the clients in my home LAN I use an OpenBSD pf firewall box with two NICs with ftp-proxy(8) listening on port 8021 on 127.0.0.1

Code:
[cmd=#]netstat -an -f inet[/cmd]

Active Internet connections (including servers)
Proto   Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp          0      0  127.0.0.1.8021         *.*                    LISTEN

I have configured ftp-proxy(8) to tag or label the packets of the passive FTP data connection:
Code:
[cmd=#]# ps -aux | grep ftp-proxy[/cmd]
proxy    30018  0.0  0.4   508   912 ??  Is    28Jul13    0:00.35 /usr/sbin/ftp-proxy -T FTP_DATA
I then use that tag or label to pass the passive ftp data connections.

Here is my /etc/pf.conf.

CAVEATS Some OpenBSD specific stuff that is incompatible with FreeBSD!

  • The NAT rule uses the new OpenBSD match construct, which has not yet been ported to FreeBSD.
    *]The rule that redirects incoming FTP command channel connections to ftp-proxy uses divert-to which does not yet exist in FreeBSD as far as I know.
    *]egress is an OpenBSD interface group name automatically given out to the external NIC.
    Code:
     xl0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
            lladdr 00:10:4b:d1:ab:5d
            priority: 0
            groups: [color=blue]egress[/color]
            media: Ethernet autoselect (100baseTX full-duplex)
            status: active
            inet6 fe80::xxxxxxxxxx prefixlen 64 scopeid 0x1
            inet 188.xxx.x.xx netmask 0xffffff00 broadcast 188.xxx.x.xxx
    This way you do not have to define a macro like ext_if = xl0. On FreeBSD you also can assign a network interface to a group (ifconfig(8), but I do not know if you can use this group name in pf.conf.
  • internal is the interface group of the internal NIC:
    Code:
    xl1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
            lladdr 00:10:5a:14:52:a5
            priority: 0
            groups: [color=blue]internal[/color]
            media: Ethernet 100baseTX full-duplex

# cat /etc/pf.conf

Code:
services = "{ imaps, pop3, pop3s, domain, submission, www, cddb, 8080, https, \
              whois, ssh, telnet, rsync, ftp, 3306, 4444, 5999, 6667, 1022, 2022, 5050 }"

set skip on lo0

# ---- external/egress interface
[color=red]match out inet from ! egress to any  nat-to (egress)[/color] # OpenBSD NAT

[color=blue]# --- anchor for misc purposes, like temporarily allowing outgoing ftp from firewall itself
anchor 'TMP'[/color]

# --- allow outgoing TCP 
pass out quick     on egress inet proto tcp from any to any port $services label "$nr:$proto:$dstport"
pass out quick log on egress inet proto tcp from any to any port smtp      label "$nr:$proto:SMTP"

anchor "ftp-proxy/*"
# --- ftp-proxy tags the ftp data connection packets.
# 
[color=blue]pass out quick     on egress inet            tagged FTP_DATA                          label "$nr:$proto:FTP_DATA"[/color]

# --- allow outgoing UDP 
pass out     quick on egress inet proto udp from any    to any port domain  label "$nr:$proto:DOMAIN"
pass out     quick on egress inet proto udp from any    to any port ntp     label "$nr:$proto:NTP"
pass out log quick on egress inet proto udp from egress to any port 67      label "$nr:$proto:BOOTP" 

pass out     quick on egress inet proto udp             to port 33433 >< 33626 label "$nr:$proto:TRACEROUTE"

# --- allow outgoing ICMP
#  ping and 'traceroute -P icmp' 
pass out quick on egress inet proto icmp from any to any icmp-type echoreq keep state label "$nr:$proto:ICMP"

# ---- internal network interface
[color=blue]pass in quick on internal inet proto tcp to port ftp [color=red]divert-to[/color] 127.0.0.1 port 8021[/color]
pass    quick on internal inet

# ---- default block
block log all label BLOCKED


To be able to use ftp on the firewall itself I use an anchor:
Code:
anchor 'TMP'

I have a optical fiber internet connection of 100 Mbit (up/down) and when I don't want to lose that speed advantage because of the lousy wireless connection to the firewall I ftp something on the firewall itself and save it on an USB stick.

For temporarily adding the rules to the anchor I use a small script:
# cat enable_ftp
Code:
#!/bin/sh

# --- temporarily enable ftp on firewall
bitnl='ftp.bit.nl'
stockholm=ftp.eu.openbsd.org
main=ftp.openbsd.org
nluug="ftp.nluug.nl"

SITE="${stockholm}"
SITE="${bitnl}"
SITE="${nluug}"

cat <<END
Enabling ftp on $(uname -a) by creating
a pf rule attached to the TMP anchor
END

cat <<END | pfctl -a TMP -f -
pass out quick on egress inet proto tcp to ${SITE} 
END

# --- alternative
# echo "pass out quick on egress inet proto tcp to ${SITE}" | pfctl -nv -a TMP -f - 

cat <<END
The rule(s) added :
-----------------------------------------------------------
$(pfctl -a TMP -sr)
-----------------------------------------------------------
END

When running ftp on the firewall itself the connections are initiated from the external interface. So these can never be grabbed by a ftp-proxy only receiving redirected FTP connections entering the internal interface.

Running the script:
Code:
[cmd=#] ./enable_ftp[/cmd]
Enabling ftp on OpenBSD plato.utp.xnet 5.3 GENERIC#xxx i386 by creating
a pf rule attached to the TMP anchor
The rule(s) added :
-----------------------------------------------------------
pass out quick on egress inet proto tcp from any to 192.87.102.43 flags S/SA
pass out quick on egress inet proto tcp from any to 192.87.102.42 flags S/SA
-----------------------------------------------------------
Because that site has two addresses, two rules are being added to the anchor.

Now I can download with a simple script:
Code:
[cmd=#] sh -vx getFreebsd92[/cmd]
    
#/bin/sh

SITE='ftp://ftp.nluug.nl'
+ SITE=ftp://ftp.nluug.nl
DIR='pub/FreeBSD/releases/amd64/amd64/ISO-IMAGES/9.2'
+ DIR=pub/FreeBSD/releases/amd64/amd64/ISO-IMAGES/9.2
FILE='FreeBSD-9.2-RC3-amd64-memstick.img'
+ FILE=FreeBSD-9.2-RC3-amd64-memstick.img

ftp -a "${SITE}/${DIR}/${FILE}" 
+ ftp -a [url]ftp://ftp.nluug.nl/pub/FreeBSD/releases/amd64/amd64/ISO-IMAGES/9.2/FreeBSD-9.2-RC3-amd64-memstick.img[/url]
Trying 192.87.102.43...
Connected to ftp.nluug.nl.
220-Welcome to the FTP archive of 
220-The Netherlands Unix Users Group (NLUUG).
220-
220-This server is located in The Netherlands, Europe.
220-If you are abroad, please find an ftp site near you.
220-Most information on this site is mirrored.
220-
220-Information about your login and any transfers you do are logged.
220-If you don't like this, disconnect now.
220-
220-For statistics, see [url]http://ftp.nluug.nl/.statistics/[/url]
220-Problems?  Mail ftp-admin @ nluug.nl
220-
220-You may login as "ftp" or "anonymous".
220-
220 
331 Please specify the password.
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
200 Switching to Binary mode.
250 Directory successfully changed.
Retrieving pub/FreeBSD/releases/amd64/amd64/ISO-IMAGES/9.2/FreeBSD-9.2-RC3-amd64-memstick.img
local: FreeBSD-9.2-RC3-amd64-memstick.img remote: FreeBSD-9.2-RC3-amd64-memstick.img
150 Opening BINARY mode data connection for FreeBSD-9.2-RC3-amd64-memstick.img (783990784 bytes).
100% |************************************************************************************************|   747 MB    03:23    
226 Transfer complete.
200 NOOP ok.
783990784 bytes received in 203.90 seconds (3.67 MB/s)
221 Goodbye.
Then I disable the rules in the anchor with another small script:
Code:
[cmd=#] cat disable_ftp[/cmd]
#!/bin/sh

echo Current rules in anchor TMP:
pfctl -a TMP -vvvsr

echo Flushing all rules in anchor TMP .....
pfctl -a TMP -F all

echo Current rules in anchor TMP:
pfctl -a TMP -vvvsr

[cmd=#]./disable_ftp[/cmd]                                                                                                              
Current rules in anchor TMP:
@0 pass out quick on egress inet proto tcp from any to 192.87.102.43 flags S/SA
  [ Evaluations: 194       Packets: 923829    Bytes: 832566656   States: 0     ]
  [ Inserted: uid 0 pid 3413 [color=blue]State Creations: 2[/color]     ]
@1 pass out quick on egress inet proto tcp from any to 192.87.102.42 flags S/SA
  [ Evaluations: 56        Packets: 0         Bytes: 0           States: 0     ]
  [ Inserted: uid 0 pid 3413 State Creations: 0     ]
Flushing all rules in anchor TMP .....
rules cleared
0 tables deleted.
Current rules in anchor TMP:

That is how I use it ;)

BTW the rules for the anchor could have been tighter, but that is left an exercise for the reader.
 
Back
Top