<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<article id="FTP">
  <!--$Id$-->

  <articleinfo>
    <title>Shorewall and FTP</title>

    <authorgroup>
      <author>
        <firstname>Tom</firstname>

        <surname>Eastep</surname>
      </author>
    </authorgroup>

    <pubdate>2003-12-01</pubdate>

    <copyright>
      <year>2003</year>

      <holder>Thomas M. Eastep</holder>
    </copyright>

    <legalnotice>
      <para>Permission is granted to copy, distribute and/or modify this
      document under the terms of the GNU Free Documentation License, Version
      1.2 or any later version published by the Free Software Foundation; with
      no Invariant Sections, with no Front-Cover, and with no Back-Cover
      Texts. A copy of the license is included in the section entitled
      <quote><ulink url="GnuCopyright.htm">GNU Free Documentation License</ulink></quote>.</para>
    </legalnotice>
  </articleinfo>

  <important>
    <para>If you are running Mandrake 9.1 or 9.2 and are having problems with
    FTP, you have three choices:</para>

    <orderedlist>
      <listitem>
        <para>Edit /usr/share/shorewall/firewall and replace this line:</para>

        <programlisting>for suffix in o gz ko ; do</programlisting>

        <para>with</para>

        <programlisting>for suffix in o gz ko o.gz ; do</programlisting>

        <para>and at a root shell prompt:</para>

        <programlisting><command>shorewall restart</command></programlisting>
      </listitem>

      <listitem>
        <para>Install the Mandrake <quote>cooker</quote> version of Shorewall.</para>
      </listitem>

      <listitem>
        <para>Upgrade to Shorewall 1.4.7 or later.</para>
      </listitem>
    </orderedlist>
  </important>

  <section>
    <title>FTP Protocol</title>

    <para>FTP transfers involve two TCP connections. The first <emphasis
    role="bold">control</emphasis> connection goes from the FTP client to port
    21 on the FTP server. This connection is used for logon and to send
    commands and responses between the endpoints. Data transfers (including
    the output of <quote>ls</quote> and <quote>dir</quote> commands) requires
    a second data connection. The <emphasis role="bold">data</emphasis>
    connection is dependent on the <emphasis role="bold">mode</emphasis> that
    the client is operating in:</para>

    <variablelist>
      <varlistentry>
        <term>Passive Mode</term>

        <listitem>
          <para>(often the default for web browsers) -- The client issues a
          PASV command. Upon receipt of this command, the server listens on a
          dynamically-allocated port then sends a PASV reply to the client.
          The PASV reply gives the IP address and port number that the server
          is listening on. The client then opens a second connection to that
          IP address and port number.</para>
        </listitem>
      </varlistentry>

      <varlistentry>
        <term>Active Mode</term>

        <listitem>
          <para>(often the default for line-mode clients) -- The client
          listens on a dynamically-allocated port then sends a PORT command to
          the server. The PORT command gives the IP address and port number
          that the client is listening on. The server then opens a connection
          to that IP address and port number; the <emphasis role="bold">source
          port</emphasis> for this connection is 20 (ftp-data in
          /etc/services).</para>
        </listitem>
      </varlistentry>
    </variablelist>

    <para>You can see these commands in action using your linux ftp
    command-line client in debugging mode. Note that my ftp client defaults to
    passive mode and that I can toggle between passive and active mode by
    issuing a <quote>passive</quote> command:</para>

    <programlisting>[teastep@wookie Shorewall]$ <emphasis role="bold">ftp ftp1.shorewall.net</emphasis>
Connected to lists.shorewall.net.
220-=(&#60;*&#62;)=-.:. (( Welcome to PureFTPd 1.0.12 )) .:.-=(&#60;*&#62;)=-
220-You are user number 1 of 50 allowed.
220-Local time is now 10:21 and the load is 0.14. Server port: 21.
220 You will be disconnected after 15 minutes of inactivity.
500 Security extensions not implemented
500 Security extensions not implemented
KERBEROS_V4 rejected as an authentication type
Name (ftp1.shorewall.net:teastep): ftp
331-Welcome to ftp.shorewall.net
331-
331 Any password will work
Password:
230 Any password will work
Remote system type is UNIX.
Using binary mode to transfer files.
ftp&#62; <emphasis role="bold">debug</emphasis>
Debugging on (debug=1).
ftp&#62; <emphasis role="bold">ls</emphasis>
---&#62; <emphasis>PASV</emphasis>
<emphasis>227 Entering Passive Mode (192,168,1,193,195,210)</emphasis>
---&#62; LIST
150 Accepted data connection
drwxr-xr-x    5 0        0            4096 Nov  9  2002 archives
drwxr-xr-x    2 0        0            4096 Feb 12  2002 etc
drwxr-sr-x    6 0        50           4096 Feb 19 15:24 pub
226-Options: -l
226 3 matches total
ftp&#62; <emphasis role="bold">passive</emphasis>
Passive mode off.
ftp&#62; <emphasis role="bold">ls</emphasis>
<emphasis>---&#62; PORT 192,168,1,3,142,58</emphasis>
200 PORT command successful
---&#62; LIST
150 Connecting to port 36410
drwxr-xr-x    5 0        0            4096 Nov  9  2002 archives
drwxr-xr-x    2 0        0            4096 Feb 12  2002 etc
drwxr-sr-x    6 0        50           4096 Feb 19 15:24 pub
226-Options: -l
226 3 matches total
ftp&#62;</programlisting>

    <para>Things to notice:</para>

    <orderedlist>
      <listitem>
        <para>The commands that I issued are <emphasis role="bold">strongly
        emphasized</emphasis>.</para>
      </listitem>

      <listitem>
        <para>Commands sent by the client to the server are preceded by
        ---&#62;</para>
      </listitem>

      <listitem>
        <para>Command responses from the server over the control connection
        are numbered.</para>
      </listitem>

      <listitem>
        <para>FTP uses a comma as a separator between the bytes of the IP
        address; and</para>
      </listitem>

      <listitem>
        <para>When sending a port number, FTP sends the MSB then the LSB and
        separates the two bytes by a comma. As shown in the PORT command, port
        142,58 translates to 142*256+58 = 36410.</para>
      </listitem>
    </orderedlist>
  </section>

  <section>
    <title>Linux FTP connection-tracking</title>

    <para>Given the normal loc-&#62;net policy of ACCEPT, passive mode access
    from local clients to remote servers will always work but active mode
    requires the firewall to dynamically open a <quote>hole</quote> for the
    server&#39;s connection back to the client. Similarly, if you are running
    an FTP server in your local zone then active mode should always work but
    passive mode requires the firewall to dynamically open a <quote>hole</quote>
    for the client&#39;s second connection to the server. This is the role of
    FTP connection-tracking support in the Linux kernel.</para>

    <para>Where any form of NAT (SNAT, DNAT, Masquerading) on your firewall is
    involved, the PORT commands and PASV responses may also need to be
    modified by the firewall. This is the job of the FTP nat support kernel
    function.</para>

    <para>Including FTP connection-tracking and NAT support normally means
    that the modules <quote>ip_conntrack_ftp</quote> and <quote>ip_nat_ftp</quote>
    need to be loaded. Shorewall automatically loads these <quote>helper</quote>
    modules from /lib/modules/&#60;<emphasis>kernel-version</emphasis>&#62;/kernel/net/ipv4/netfilter/
    and you can determine if they are loaded using the <quote>lsmod</quote>
    command. The &#60;<emphasis>kernel-version</emphasis>&#62; may be obtained
    by typing</para>

    <programlisting><command>uname -r</command></programlisting>

    <example>
      <title></title>

      <programlisting>[root@lists etc]# lsmod
Module                  Size  Used by    Not tainted
autofs                 12148   0  (autoclean) (unused)
ipt_TOS                 1560  12  (autoclean)
ipt_LOG                 4120   5  (autoclean)
ipt_REDIRECT            1304   1  (autoclean)
ipt_REJECT              3736   4  (autoclean)
ipt_state               1048  13  (autoclean)
ip_nat_irc              3152   0  (unused)
<emphasis role="bold">ip_nat_ftp              3888   0  (unused)</emphasis>
ip_conntrack_irc        3984   1
<emphasis role="bold">ip_conntrack_ftp        5008   1</emphasis>
ipt_multiport           1144   2  (autoclean)
ipt_conntrack           1592   0  (autoclean)
iptable_filter          2316   1  (autoclean)
iptable_mangle          2680   1  (autoclean)
iptable_nat            20568   3  (autoclean) [ipt_REDIRECT ip_nat_irc ip_nat_ftp]
ip_conntrack           26088   5  (autoclean) [ipt_REDIRECT ipt_state ip_nat_irc
                                              ip_nat_ftp ip_conntrack_irc ip_conntrack_ftp
                                              ipt_conntrack iptable_nat]
ip_tables              14488  12  [ipt_TOS ipt_LOG ipt_REDIRECT ipt_REJECT ipt_state
                                  ipt_multiport ipt_conntrack iptable_filter
                                  iptable_mangle iptable_nat]
tulip                  42464   0  (unused)
e100                   50596   1
keybdev                 2752   0  (unused)
mousedev                5236   0  (unused)
hid                    20868   0  (unused)
input                   5632   0  [keybdev mousedev hid]
usb-uhci               24684   0  (unused)
usbcore                73280   1  [hid usb-uhci]
ext3                   64704   2
jbd                    47860   2  [ext3]
[root@lists etc]#</programlisting>
    </example>

    <para>If you want Shorewall to load these modules from an alternate
    directory, you need to set the MODULESDIR variable in
    /etc/shorewall/shorewall.conf to point to that directory.</para>

    <para>If your FTP helper modules are compressed and have the names
    <emphasis>ip_nat_ftp.o.gz and ip_conntrack_ftp.o.gz</emphasis> then you
    will need Shorewall 1.4.7 or later if you want Shorewall to load them for
    you.</para>

    <para>Server configuration is covered in the <ulink type=""
    url="Documentation.htm#Rules">/etc/shorewall/rules documentation</ulink>,</para>

    <para>For a client, you must open outbound TCP port 21.</para>

    <para>The above discussion about commands and responses makes it clear
    that the FTP connection-tracking and NAT helpers must scan the traffic on
    the control connection looking for PASV and PORT commands as well as PASV
    responses. If you run an FTP server on a nonstandard port or you need to
    access such a server, you must therefore let the helpers know by
    specifying the port in /etc/shorewall/modules entries for the helpers. For
    example, if you run an FTP server that listens on port 49 or you need to
    access a server on the internet that listens on that port then you would
    have:</para>

    <example>
      <title>if you run an FTP server that listens on port 49 or you need to
      access a server on the internet that listens on that port then you would
      have:</title>

      <programlisting>loadmodule ip_conntrack_ftp ports=21,49
loadmodule ip_nat_ftp ports=21,49</programlisting>

      <para><note><para>you MUST include port 21 in the ports list or you may
      have problems accessing regular FTP servers.</para></note></para>

      <para>If there is a possibility that these modules might be loaded
      before Shorewall starts, then you should include the port list in
      /etc/modules.conf:</para>

      <programlisting>options ip_conntrack_ftp ports=21,49
options ip_nat_ftp ports=21,49</programlisting>

      <para><important><para>Once you have made these changes to
      /etc/shorewall/modules and/or /etc/modules.conf, you must either:</para><orderedlist><listitem><para>Unload
      the modules and restart shorewall:</para><programlisting><command>rmmod ip_nat_ftp; rmmod ip_conntrack_ftp; shorewall restart</command></programlisting></listitem><listitem><para>Reboot</para></listitem></orderedlist></important></para>
    </example>

    <para>One problem that I see occasionally involves active mode and the FTP
    server in my DMZ. I see the active data connection to <emphasis
    role="bold">certain client IP addresses</emphasis> being continuously
    rejected by my firewall. It is my conjecture that there is some broken
    client out there that is sending a PORT command that is being either
    missed or mis-interpreted by the FTP connection tracking helper yet it is
    being accepted by my FTP server. My solution is to add the following rule:</para>

    <informaltable>
      <tgroup cols="7">
        <thead>
          <row>
            <entry align="center">ACTION</entry>

            <entry align="center">SOURCE</entry>

            <entry align="center">DESTINATION</entry>

            <entry align="center">PROTOCOL</entry>

            <entry align="center">PORT(S)</entry>

            <entry align="center">SOURCE PORT(S)</entry>

            <entry align="center">ORIGINAL DESTINATION</entry>
          </row>
        </thead>

        <tbody>
          <row>
            <entry>ACCEPT:info</entry>

            <entry>dmz</entry>

            <entry>net</entry>

            <entry>tcp</entry>

            <entry>-</entry>

            <entry>20</entry>

            <entry></entry>
          </row>
        </tbody>
      </tgroup>
    </informaltable>

    <para>The above rule accepts and logs all active mode connections from my
    DMZ to the net.</para>
  </section>
</article>