ftp6proxy is an ftp proxy for IPv6 gateways and firewalls. It is meant for FreeBSD, but may run on any other (BSD)
operating system that support divert sockets, kqueue, IPv6, and pf and/or
ipfw. It may be of use for passive FTP with IPv4, too. In contrast to
ftp-proxy and the
libalias family (natd, ppp),
ftp6proxy does not support NAT as we do not need NAT with IPv6, do we?
Instead, ftp6proxy intercepts the ftp command channel via a divert socket, and
then, if the client wants to initiate a transfer, opens the firewall for two
seconds. If the data channel has been established within these 2 seconds, it
will have created a dynamic firewall rule in order to let through subsequent
packets.
Both ftp-proxy and libalias/natd rewrite each ftp connection in order to make
it look like as if it originates on the gateway machine (read: they do NAT).
This is great if you're dealing with private (IPv4) addresses. With public
addresses or IPv6, NAT becomes less desirable. Furthermore, libalias does not
support IPv6 yet.
# make FW=pf # make installIf you prefer ipfw:
# make FW=pf # make installFrom version 0.4 on ftp6proxy supports netgraph:
# make -DUSE_NG FW=ipfw # make installPlease note that ftp6proxy makes full use of Capsicum only if it has been compiled with support for netgraph.
ftp6proxy_enable="YES"
# service ftp6proxy start
anchor "ftp6proxy/*" pass out quick inet6 proto tcp from port >= 1024 to port ftpCreate /etc/ipfw.rules and replace $LAN_INTERFACE by the name of your actual LAN facing interface, e.g. em0 or bridge0:
add divert 10004 ip6 from any to not me6 proto tcp src-port 1024-65535 dst-port 21 in recv $LAN_INTERFACE add divert 10004 ip6 from not me6 to any proto tcp src-port 21 dst-port 1024-65535 out xmit $LAN_INTERFACE add allow ip from any to anyThen activate ipfw in /etc/rc.conf:
kld_list="ipdivert" firewall_enable="YES" firewall_type="/etc/ipfw.rules"
# kldload ipdivert # service ipfw restart # service pf reload
pf has issues diverting IPv6 traffic - at least on FreeBSD 9.2 and 10.0, see my message on the freebsd-pf mailing list.
add divert 10004 tcp from any 1024-65535 to not me 21 in recv $LAN_INTERFACE add divert 10004 tcp from not me 21 to any 1024-65535 out xmit $LAN_INTERFACE ... # rules 64000 - 64099: temporary stateless rules which are dynamically created by ftp6proxy and which call rule # 65000 # at the end of your ruleset: add 64500 deny ip from any to any # target for temporary stateless rules add 65000 allow tag 1 tcp from any 1024-65535 to any 1024-65535 in keep-state add return via any
# sysrc ftp6proxy_flags+=" -j 65000 -u root" # service ftp6proxy restartRoot privileges are required to create and delete ipfw rules.
From version 0.4 on ftp6proxy supports netgraph and uses ng_ipfw to get a copy of each FTP command packet. If you ftp6proxy along with netgraph, it does not reinject any packets. Instead you have to use the ngtee action of ipfw.
add ngtee 10004 tcp from any ftp to any 1024-65535 out xmit $LAN_INTERFACEIf you want active FTP as well:
add ngtee 10004 tcp from any 1024-65535 to any ftp out xmit $WAN_INTERFACE
# rules 64000 - 64099: temporary stateless rules which are dynamically created by ftp6proxy and which call rule # 65000 # at the end of your ruleset: add 64500 deny ip from any to any # target for temporary stateless rules add 65000 allow tag 1 tcp from any 1024-65535 to any 1024-65535 in keep-state add return via any
ftp6proxy V0.3 - IPv6/IPv4 FTP firewall helper compiled-in firewall module: ipfw usage: ftp6proxy [-b <base>] [-c <count>] [-d [-d]] [-f <facility>] [-i <pidfile>] [-j <target>] [-p <port>] [-q <queue>] [-r <dir>] [-t <timeout>] [-u <user>] ftp6proxy -h -b <base> punch holes into firewall starting at this rule number; default: 64000 -c <count> use up to <count> firewall rules; default: 100 -d run in foreground, don't write pidfile -d -d run in foreground, don't write pidfile, don't chroot, debug log to stderr, don't drop privileges -f <facility> send syslog to <facility>; default: daemon -i <pidfile> write pid to <pidfile> instead of /var/run/ftp6proxy.pid -j <target> call this ipfw rule number instead of allowing the data connection -p <port> divert(4) port number to listen on; defaults to 10004 -q <queue> place data connections into this pf QoS class -r <dir> chroot into <dir> instead of /var/empty -t <timeout> wait up to <timeout> seconds for data connections; defaults to 2 seconds -u <user> run as user <user> instead of proxy -h show this help ;-)
ftp6proxy V0.4 - IPv6/IPv4 FTP firewall helper compiled-in firewall module: ipfw/netgraph usage: ftp6proxy [-b <base>] [-c <count>] [-d [-d]] [-f <facility>] [-i <pidfile>] [-j <target>] [-p <port>] [-q <queue>] [-r <dir>] [-t <timeout>] [-u <user>] ftp6proxy -h -b <base> punch holes into firewall starting at this rule number; default: 64000 -c <count> use up to <count> firewall rules; default: 100 -d run in foreground, don't write pidfile -d -d run in foreground, don't write pidfile, don't chroot, debug log to stderr, don't drop privileges -f <facility> send syslog to <facility>; default: daemon -i <pidfile> write pid to <pidfile> instead of /var/run/ftp6proxy.pid -j <target> call this ipfw rule number instead of allowing the data connection -p <hook> ng_ipfw(4) hook to connect to; defaults to 10004 -q <queue> place data connections into this pf QoS class -r <dir> chroot into <dir> instead of /var/empty -t <timeout> wait up to <timeout> seconds for data connections; defaults to 2 seconds -u <user> run as user <user> instead of proxy -h show this help ;-)
You can override default options by setting ftp6proxy_flags in /etc/rc.conf.
Before I started ftp6proxy, I wrote a prototype in Perl. I uploaded it for educational reference. It's called alg.pl.