> Is there a complete list of the ports that amanda will use during the
> data transfer? I though I had them all (the client could talk to the
> server) as amcheck didn't report any errors. However, I remove all
> packet filtering and things start working smoothly...

see JRJ attached
>From - Sat Jan 26 00:52:51 2002
Return-Path: <cyrus@ente>
X-Sieve: cmu-sieve 2.0
Return-path: <[EMAIL PROTECTED]>
Envelope-to: [EMAIL PROTECTED]
Delivery-date: Sat, 23 Jun 2001 04:57:31 +0200
Received: from ente.berdmann.de ([192.168.1.6] helo=localhost)
        by ente.berdmann.de with esmtp (Exim 3.16 #2)
        id 15Ddc2-0006ur-00
        for [EMAIL PROTECTED]; Sat, 23 Jun 2001 04:57:30 +0200
X-Flags: 0000
Delivered-To: GMX delivery to [EMAIL PROTECTED]
Received: from pop.gmx.net [194.221.183.20]
        by localhost with POP3 (fetchmail-5.5.0)
        for [EMAIL PROTECTED] (single-drop); Sat, 23 Jun 2001 04:57:30 +0200 (CEST)
Received: (qmail 15453 invoked by uid 0); 23 Jun 2001 02:53:52 -0000
Received: from surly.omniscient.com (64.134.101.69)
  by mx0.gmx.net (mx13) with SMTP; 23 Jun 2001 02:53:52 -0000
Received: (from root@localhost)
        by surly.omniscient.com (8.11.1/8.11.1) id f5N0bkX199799
        for amanda.org.amanda-hackers-list; Sat, 23 Jun 2001 00:37:46 GMT
Received: from gandalf.cc.purdue.edu ([EMAIL PROTECTED] [128.210.135.25])
        by surly.omniscient.com (8.11.1/8.11.1) with ESMTP id f5N0bgX207608
        for <[EMAIL PROTECTED]>; Fri, 22 Jun 2001 20:37:42 -0400 (EDT)
Received: from gandalf.cc.purdue.edu (jrj@localhost [127.0.0.1])
        by gandalf.cc.purdue.edu (8.9.1/8.9.1) with ESMTP id TAA26259
        for <[EMAIL PROTECTED]>; Fri, 22 Jun 2001 19:37:40 -0500 (EST)
Message-Id: <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]
Subject: Use of UDP/TCP ports in Amanda document, and amrecover bug
Reply-to: [EMAIL PROTECTED]
Date: Fri, 22 Jun 2001 19:37:40 -0500
From: "John R. Jackson" <[EMAIL PROTECTED]>
Sender: [EMAIL PROTECTED]
Precedence: bulk

This is yet another of my really long letters.  Sorry.

I applied a change to amrecover.c a while back to use stream_client()
instead of the "by hand" socket setup that was there.  This stopped
binding the socket to a particular interface which was causing trouble
on some systems.

Unfortunately, I didn't fully comprehend the interaction between a user
port range (e.g. --with-portrange), stream_client() and amrecover.  Now,
if amrecover is built with --with-portrange, which must be non-privileged
(>= 1024) for other parts of Amanda to work, it will fail because
amrecover requires a privileged port (to prove to the other side it is
who it says it is).

Before going into potential solutions, a little more background.
If this bores you silly, skip to "Solutions" below.  I'm only spelling
it out in detail because I'll probably put this in the docs directory
(once you nice folks edit it :-) since the issue comes up fairly often.

The key questions for the amrecover problem are:

  * When dumper and taper set up their localhost TCP connection for direct
    to tape, does the port matter?  If NAT, for instance, is also on the
    machine, will it scramble things?

  * When not being used for security (non-privileged), does the source
    port of an incoming TCP connection matter?  Amanda doesn't care.
    Would a firewall or NAT in between cause problems?

John R. Jackson, Technical Software Specialist, [EMAIL PROTECTED]

=================================
How Amanda uses UDP and TCP ports
=================================

Amanda uses both UDP and TCP ports during its operation.  The amandad
service is listening (via inetd/xinetd) at a well known (fixed) port on
each client for UDP connections.  The amindexd and amidxtaped services
are listening (also via inetd/xinetd) at well known ports on the tape
server for TCP connections.

When a process on the tape server wants to talk to a client amandad,
it creates a UDP socket and binds it to a port on its side, then
sends the packet to the well known amandad service port on the client.
Because security information is being passed, the port bound on the
connecting side must be privileged (< 1024).  This "proves" to amandad
whoever is connecting is running as root, and therefor is trustworthy
(there are all sorts of issues with this "trust" that are beyond the
scope of this document).

A similar sequence of events happens when amrecover on a client wants
to contact amindexd or amidxtaped on the tape server.  The port that
amrecover binds to its TCP socket must be privileged, which is one of
the reasons it must be run as root.

Amanda also uses TCP connections for transmitting the backup image,
messages and (optionally) the index list from a client back to the dumper
process on the tape server.  A process called sendbackup is started by
amandad on the client.  It creates two (or three, if indexing is enabled)
TCP sockets and sends their port numbers back to dumper in a UDP message.
Then dumper creates and binds TCP sockets on its side and connects to
the waiting sendbackup.

Because sendbackup does not run as root on the client, it cannot allocate
privileged TCP ports to listen on.  The dumper process is setuid root
and could bind privileged ports on its side (it currently does not),
but because sendbackup does not care what port connects back to it (it
assumes the only process that could have knowledge of the port numbers
to use is dumper), it does not check the peer (connecting) port number.

===================
TCP port allocation
===================

When Amanda creates a TCP socket, either to listen for incoming
connections (sendbackup) or on the connecting side (amrecover or dumper),
it goes through the following bind steps:

  * try for the user TCP port range (--with-portrange), if defined.
    If that fails ...

  * try for a privileged port (512 .. 1023).  If that fails ...

  * get any available port.

This sequence is implemented in both stream_server() and stream_client().

The stream_server() routine is used in two ways:

  * taper to set up direct to tape communication with dumper on localhost.

    If a user TCP port range is defined, it needs to be unprivileged
    because taper is not running as root.

  * sendbackup to set up a transfer with its dumper.

    If a user TCP port range is defined, it needs to be unprivileged
    because sendbackup is not running as root.

    A user TCP port range needs to be large enough for three ports
    (data, message and index) times the number of simultaneous backups
    the client may be asked to perform ("maxdumps" in amanda.conf).

The stream_client() routine is used in three ways:

  * dumper to connect to taper for a direct to tape operation.  Except
    for making sure what is connecting is not (ftp) port 20, taper does
    not pay any attention to the source (dumper) port number.

  * dumper to connect to sendbackup on a client.  Again, except for
    port 20, sendbackup does not care what port the request comes from.

  * amrecover to connect to amindexd and amidxtaped.

    Because security information is being passed, amindexd/amidxtaped
    (via security_ok() in security.c) insist the other end (amrecover) be
    bound to a privileged port.  So if a user TCP port range is defined,
    it must be privileged.  Note that this is the opposite of what was
    stated above for stream_server.  It's a bug.

===================
UDP port allocation
===================

When Amanda creates a UDP socket, the same order of assignment as above
is used by dgram_bind():

  * try for the user UDP port range (--with-udpportrange), if defined.
    If that fails ...

  * try for a privileged port (512 .. 1023).  If that fails ...

  * get any available port.

The dgram_bind() routine is called from three places, amcheck, planner
and dumper.  In each case, a connection to amandad on a client is being
set up.  Amandad, in turn, calls security_ok(), which insists the other
end of the connection be a privileged port, so --with-udpportrange must
specify privileged port numbers.

A user UDP port range must allow for one port for each client that
might be contacted at a time.  Planner and amcheck use a single socket
to contact all their clients, but there may be multiple dumpers (based
on "inparallel" in amanda.conf) and each needs its own port.

=================
Firewalls and NAT
=================

I'm not familiar with firewalls or NAT -- one of the benefits of working
in a University environment :-).  So the following is likely to be
completely wrong, but I have tried to get the advice of folks who do
really understand this stuff.

Firewalls and Amanda should be pretty easy to set up.  Just pick user
UDP and TCP port ranges, build Amanda with them (--with-udpportrange and
--with-portrange) and let them through the firewall.  You also need to
let the well known Amanda ports through, just as you would ftp or telnet.

NAT has other issues.  If the Amanda client is "outside" NAT, there
should not be a problem for backups.  Sendbackup will set up the ports
and tell dumper what they are.  Then dumper will connect to them from
"inside" and NAT should leave that alone, although it doesn't really
matter since sendbackup does not care who connects to it (other than it
not be port 20).

If the Amanda tape server is outside, NAT will have to be told how to
translate the incoming connections from dumper to the client.  To do
that, the UDP and TCP port ranges will have to be known and only one
client can be inside.

The reverse is true for amrecover.  If amrecover is run from inside NAT,
there should not be a problem -- it's just like running ftp or telnet.
But from the outside, NAT will have to know where the amindexd/amidxtaped
services are and allow them through (much like ftp or telnet daemons).
Since they are on known port numbers, the user TCP port range is not
an issue.

A user TCP port range is probably not important in the case of dumper
and taper talking to each other since only the one machine (localhost)
is involved and so it does not go through a firewall.  But I could be
wrong, especially if NAT is involved.

The details of how you configure a specific firewall or NAT are beyond the
scope of this document (although examples would be welcome).  You need
to read up on the documentation that comes with them.

====================
Port Range Solutions
====================

The immediate problem is with the TCP port range, which, as stated above,
must be unprivileged for sendbackup and privileged for amrecover.  Here
are some possible solutions.  I'm looking for comments on which would be
the best way to go.

The ideas are not presented in any particular order.

========================
stream_client_privileged
========================

Change the name of stream_client to stream_client_internal, make it
static and add a parameter for whether a privileged port is required.
Write two new functions called stream_client and stream_client_privileged
that call stream_client_internal with the appropriate flag.

If a privileged port is required, only try to bind that range.  Otherwise
try the user TCP port range, if defined, then fall back to anything.
Do not try to get a privileged port unless the flag is set.

  if (need_privileged) {
    bind_portrange(server_socket, &server, 512, IPPORT_RESERVED - 1);
    ...
  } else {
#if defined(PORTRANGE)
    bind_portrange(server_socket, &server, PORTRANGE);
#endif
    bind(... INADDR_ANY ...);
  }

Existing calls to stream_client in dumper are unchanged.  Calls in
amrecover would be changed to stream_client_privileged.

A variant of this would be to not try the user port range at all
in the non-privileged case.  As mentioned above, setting a specific
(non-privileged) port range on the connecting side does not appear to be
used.  The user TCP port range would only be referenced in stream_server
(i.e. taper and sendbackup).

But I'm not sure if a specific port range is needed for the connecting
side to deal with any firewall or NAT issues.

===============================
stream_client always privileged
===============================

Change stream_client to always get a privileged port.  It would no longer
try the user port range or fall back to any port.

This will fix amrecover outright.  The only other caller is dumper,
and as a setuid root program, appropriate seteuid() calls can be added
(which sounds easy, but has issues on some OS's, e.g. AIX).

Nobody looks for specific source port addresses, other than that they be
privileged in some cases (amrecover -> amindexd/amidxtaped), so limiting
them to a specific range of ports is not useful.  The only requirement
is that NAT leave privileged ports privileged, but that's a NAT issue.

An additional check could be added to stream_server to make sure
the connecting client is coming from a privileged port.  This would
add a little bit of security, but introduces a potential backward
incompatibility.  If a site has a user TCP port range defined, and they
update the client but not the server, the incoming ports from dumper
will not be privileged and sendbackup will refuse them.  The solution
would be to update the server.

One other disadvantage is the number of privileged ports in use during a
backup run.  Each dumper will use up to three, so if inparallel is set to
10 (the default), and all dumpers are active, 30 privileged ports will
be in use.

A variant of this would be to try for privileged and then fall back
to anything.  When run from amrecover (root), it will get a privileged
port and work.  When run from dumper (non-root), it will fail to get a
privileged port, but fall back to a normal port and that will also work.
This is the way things worked pre-portrange.

This has the advantage of not adding seteuid calls to dumper.  And it
would not introduce any incompatibilities.  It's also probably the
easiest to implement, only requiring the deletion of a few lines of code.

But I'm not sure if a specific port range is needed for the connecting
side to deal with any firewall or NAT issues.

===================
--with-tcpportrange
===================

If it turns out it's necessary for the side that does the connect() to
be bound to a specific range of TCP ports, we could replace the existing
--with-portrange ./configure option with --with-tcppportrange (which
makes its name match up better with --with-udpportrange) and require it
to have four values.  The first pair would be the privileged ports that
amrecover would try for.  The second pair would be the non-privileged
range sendbackup would allocated from and dumper would bind to its side.

The older --with-portrange argument could be kept and internally
translated to --with-tcpportrange=512,1023,<--with-portrange value>.

The stream_client_privileged solution above would also be put in place to
pick either the privileged or non-privileged pair.  In stream_server,
only the non-privileged pair would be used (although I suppose a
corresponding stream_server_privileged setup could be implemented for
potential future use).

===========
amanda.conf
===========

A much larger variant would get rid of the --with-udpportrange argument
and move the port range specifications (UDP and non-privileged TCP) to new
dumptype amanda.conf parameters.  The TCP port range would be passed to
sendbackup via the OPTIONS portion of the protocol (I've already looked
and new things can be added without introducing compatibility problems).

At the same time, another new dumptype parameter would be added to set the
Amanda service to connect to.  This gets rid of one more built in value,
and also allows a single client to run more than one amandad (we use it
here to do archival runs at the same time as the normal daily activity).

The above would be done in conjunction with one of the stream_client
changes if a user TCP port range is not needed.

If the range is needed, the --with-portrange argument would be used
to build and run amrecover so it could limit itself to a specific
privileged range (since it does not have access to amanda.conf to get
run time values).  The argument would be renamed to --with-tcpportrange
(but only requiring a single privileged pair) to avoid confusion with
the current --with-portrange.


Reply via email to