The classic inetd daemon has been around for a long time. There are several ways to replace its functionality, but the most flexible and easiest way seems to be xinetd. Xinetd does all the things inetd can do, and a lot more. TCP wrapping, modular configuration, connection redirection, and load limits on incoming connections are just a few of the features that make xinetd a nice choice for system administrators.
This article is meant for the beginner to intermediate system administrator and the explanations and examples will try not to assume that you are already familiar with inetd. In this article we will look at some simple uses of xinetd, from installation to implementation of security policies.
For the purposes of this article, ideally your system should be a recent (2000 or later) mainstream UNIX (Linux, Solaris, BSD) installation. The examples may work with earlier versions of Perl and UNIX, and other operating systems, but their failure to function should be considered an exercise for the reader to solve. The specific examples given are for Red Hat Linux, but they should work (with the exception of chkconfig) on other systems as well.
To a UNIX system administrator, inetd is as basic as the cp/rm/mv commands. It's always there, ready to handle incoming connections. But what is it, really? What does it do?
The answer begins with TCP/IP (it also includes UDP, but let's not worry about that for now). When you make a connection to a host, you are in fact creating a TCP/IP connection (a socket, usually) -- that's like a telephone call between you and the host. A TCP/IP connection is uniquely defined by the originating host and the receiving host, but there's another identifier. If we all connected to a server, how would it know to distinguish between webserver, telnet, SSH, FTP, and other connections? Sockets are also defined by the port used to make the connection. For instance, port 21 is incoming FTP, 22 is SSH, and 23 is TELNET (you can look in /etc/services on a UNIX system for most of the rest).
Once a connection is made, someone has picked up the phone on the other end. It can either be an operator or a direct line. The direct line means you are directly connected to the server, while the operator is the approach that involves inetd. The operator is really handling a bunch of incoming direct lines (ports on a host), and handing them off in person to the program (server) responsible.
UDP is a different connection method. Like TCP, it is basically a conversation with someone, but it's not guaranteed to be reliable. UDP, to continue the telephone analogy, is like throwing messages on a conveyor belt, and having the recipient stand on the other end of it. You can get a lot more messages through on the conveyor belt, but the recipient may miss a few if there are too many (network traffic is high) or he takes too long reading the messages (the server is busy).
With inetd, you are redirected to a specific server after some checks are performed. There's just one configuration file, inetd.conf, managing all the incoming connections. That makes life easier when adding, deleting, changing, or reviewing services on a system. For instance, ftp is defined as follows on a Solaris system with TCP wrappers:
Listing 1. inetd.conf definition of the FTP service
ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd |
These are all the necessary parameters for creating an FTP connection. In brief, we're using TCP/IP (tcp) in stream-oriented (stream) mode, allowing more than one FTP connection at a time (nowait), running as root, and invoking FTP (TCP wrappers, which will in turn invoke the FTP daemon).
Is this hard to parse at 1 AM? Absolutely. Is the complexity necessary? No. Xinetd took the inetd design and modularized it, meaning that each service can be in its own configuration file. Xinetd also added features like the TCP wrappers, making configuration easier.
Xinetd preserved the central configuration (operator) approach, storing all the configuration files in a single place, usually /etc/xinetd.conf and /etc/xinetd.d/* so that system administration gets easier. The modular configuration means that you can distribute a service to several machines by copying it to the xinetd.d directory, and you can remove it similarly. You can even specify extra include directories.
Finally, the xinetd FAQ (see Resources later in this article) states that RPC programs don't work too well with xinetd. No problem, use inetd for RPC and xinetd for everything else. It's like hiring two operators because one speaks Spanish and the other speaks everything else.
So what is xinetd? It's just a program, that's all. There's nothing magical about handling incoming network connections. You could do it in Perl, Python, or Java. Xinetd was written in C, and it's just as fast as its predecessor, inetd, if not faster (for instance, the TCP wrappers don't have to be executed for each incoming connection; they are loaded into memory at startup).
Xinetd is a work in progress. (You may have an outdated version, so make sure you check the home page for the latest; see Resources.) Because it's a work in progress, security holes in xinetd are patched quickly, unlike inetd vulnerabilities, which usually take a lot longer to get patched. Of course, xinetd comes with the source code, so you can review it and see for yourself where vulnerabilities might exist.
How do you define services with xinetd? You write a service file that specifies the specific configuration in addition to the generic parameters specified in /etc/xinetd.conf. So, if /etc/xinetd.conf says:
Listing 2. Example xinetd.conf (standard Red Hat 7.1)
defaults
{
instances = 60
log_type = SYSLOG authpriv
log_on_success = HOST PID
log_on_failure = HOST
cps = 25 30
}
service telnet
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.telnetd
log_on_failure += USERID
disable = yes
}
includedir /etc/xinetd.d
|
Every service file you place in /etc/xinetd.d will inherit those defaults, and specify its own parameters. The telnet service is defined at the top level here, instead of in the subdirectory. That's perfectly fine, and this modularity allows complex configurations.
To get xinetd to re-read the configuration file, you don't have to restart it. Simply send it the USR2 signal.
What do those parameters mean? Let's run through the whole list. You
can also see the list with man xinetd.conf from the command line, if
that man page was installed properly, but this summary will try to
explain the parameters in simpler terms, without assuming you already
know all about sockets and services. Some parameters (rpc_version,
rpc_number) were skipped.
- id
- The unique name for this service. A service name is already
specified before the braces, but the ID makes it possible to have
several protocols for a service that's logically the same. This is
of limited use to the casual user. The NFS services, for
instance, can run over the UDP or TCP transmission protocols. The
time service, which is internal to xinetd, is provided in a TCP and
a UDP version with Red Hat Linux 7.1 in /etc/xinetd.d/time and
/etc/xinetd.d/time-udp.
- type
- This should actually be called "specialtype", because it only
applies to special services. It can be a combination of "RPC" for
RPC services (remote procedure calls, introduced by Sun, causes of
many security issues, and best avoided); "INTERNAL" for services
built into xinetd, such as the time service; and "UNLISTED" for
nonstandard services you won't find in the system lists
(/etc/services or /etc/rpc for RPC services).
- flags
- All the extra flags go here. The list is long and quite
technical; the interesting flags are REUSE (for socket reuse,
such as telnet), NAMEINARGS/NOLIBWRAP (if you want to invoke the TCP
wrappers manually or you want to avoid the wrappers altogether),
NODELAY/KEEPALIVE (for tuning TCP sockets), DISABLE (overrides the
top-level "disable" parameter), and SENSOR (for detecting and
thwarting some types of denial-of-service network attacks).
- disable
- You always want it set to "no" unless you want the service
to be disabled. The Red Hat Linux chkconfig program will switch the
"disable" parameter on or off for you; it is probably easier to
enable and disable specific services with chkconfig under Red Hat
than to do it manually. Note that chkconfig expects to find the
service file in /etc/xinetd.d/SERVICE. So for the example in
Listing 2 above, chkconfig will not turn telnet on/off when requested.
This could be considered a bug or a feature, depending on your
viewpoint.
- socket_type
- Usually you want this set to "stream" unless you are
using UDP services, in which case set this to "dgram". There's also
"raw" and "seqpacket", but those are pretty unusual.
- protocol
- This is the protocol to be used for the connection,
usually "tcp" or "udp", though in theory you can use anything from
/etc/protocols.
- wait
- If set to "no", xinetd will start a new handler for that
service on every connection. If "yes", xinetd expects the handler
to deal with all subsequent connections until it dies. In most
cases, this is "no".
- server, server_args
- The program name for the handler, and the
parameters it should get. The handler name should not be in the
arguments, as is the case with inetd.
- port
- The port of the service. Usually unnecessary, since the port is mapped to the service by the /etc/services file.
- redirect
- Allows xinetd to send all traffic to the service to
another host. Thus, firewalled hosts can accept secure traffic
through a central xinetd forwarder, yet have no connections to the
outside network. This feature can also be adopted, with some work,
to do service failover between two hosts.
- banner, banner_success, banner_fail
- A custom block of text from a
file to be printed upon any/a successful/an unsuccessful
connection.
- enabled
- Supplements the "disabled" parameter and the DISABLE flag
on a global level.
- include, includedir
- Tells xinetd to include a file or a directory.
- user, group, umask, groups
- What UNIX attributes xinetd should
impersonate when starting the service handler. This is mostly for
insecure services.
- nice
- A UNIX priority level that determines how important this
service is to the system. You can tune it for your system; take a
look at the "nice" man page.
- env
- Environment variables for the service handler.
- passenv
- What environment variables from xinetd should be passed down to the service handler.
Resource management parameters
- instances
- Number of handlers that can be started at once. This can be
tuned to prevent denial-of-service attacks. Set it to "UNLIMITED"
if you want the default (no limits) behavior.
- max_load
- I: )
If the system is too loaded, stop accepting connections.
The load numbers are system dependent, and should be tuned only
if you really know what you are doing.
- rlimit_as, rlmist_cpu, rlimit_data, rlimit_rss, rlimit_stack
- The rlimit parameters specify resource limits (memory, CPU, and specific memory areas) for service handlers.
- only_from, no_access
- Supplementary to the TCP wrappers, this is a
way to block hosts making connections to us. Note that the default
is to allow access to everyone, unless the TCP wrappers (whose rules
are usually in /etc/hosts.allow) say otherwise.
- access_times
- The times of day that a service is available. For
instance, "6:00-23:00" means the service will be available from 6 AM
to 11:01 PM.
- log_type, log_on_success, log_on_failure
- Various logging options.
The USERID flag in particular can be troublesome, because it slows
things down to interrogate the connecting machine about the user
connecting to us. Avoid USERID if possible.
- bind
- Allows a service to be specific to an interface, usually in
the interest of security. For instance, the FTP service on the
inside network would be just FTP, while outside FTP connections
would generate intruder alerts. The "id" parameter would be useful
here.
- per_source
- Specifies the maximum instances of a service from a
source IP. Useful for handling single-source denial-of-service
attacks or buggy programs making too many connections.
- cps
- Maximum connections per second allowed, and the number of
seconds before the service is enabled again. "30 45" would mean "30
incoming connections per second, and wait 45 seconds if that limit
is exceeded". For counteracting denial-of-service attacks, mostly.
- deny_time
- How long service will be denied to someone who sets off a
SENSOR flag.
The classic TCP wrappers package is a very useful tool. Through a centralized file, usually /etc/hosts.allow and /etc/hosts.deny, access can be allowed or denied to any hosts necessary, per service. Unfortunately, the TCP wrapper library does not know much about system loads, resource limits, multiple attacks, and so on. Xinetd incorporates the TCP wrapper functionality (through the libwrap library), so you can move to xinetd smoothly and keep using the same configuration files from before.
That's pretty much all there is to the migration. Keep your old hosts.deny and hosts.allow files, and xinetd will happily follow them. Keep in mind, however, that xinetd has many options for control of connections that improve on the TCP wrappers. For instance, limiting connections per second, or connections when the load is too high, can be an invaluable aid to server management.
Make sure you compile xinetd with the libwrap option, or it will not know about the TCP wrappers. If xinetd comes from an RPM on Red Hat Linux, make sure you test that the TCP wrapper files work properly BEFORE you put the machine out in the open.
Although there are many ways in which xinetd could be used, the redirect parameter gives us the most interesting avenues. Failover, we all know, is pretty hard to do, and hardware failover is expensive. The approach described here is cheap and effective, through simple software. It does have a single point of failure -- the redirecting station, so you should consider whether that's acceptable. If it's not, well, hardware failover is expensive for a reason.
First, decide on a way to pick an "active" machine out of two or more. Let's assume you do it through a script, set_active.pl (we are going to do this for the telnet service, but it will work for any other service that can be switched to an alternate server with no ill effects). The script will take a machine name, which we will use to set the new failover, and a service name, which will give us the right /etc/xinetd.d/SERVICE file to edit. Feel free to customize the script to edit a different file, or take different parameters. The job could be done with a one-liner "perl -p -i -e" script, but this way you can expand a lot more in the future, and error-check your parameters.
This is simple enough. Now you just have to decide on a procedure to invoke this script - either manually, through a cron job, or triggered by another program. It becomes an architecture decision at this point. Don't forget to send the USR2 signal to xinetd at this point, or just restart it if you wish. Automating signals can be done with "pkill -USR2 xinetd" on Red Hat Linux, and restarting xinetd is just "/etc/rc.d/init.d/xinetd restart" on Linux or something similar on most UNIX systems.
This sort of failover will NOT work for database connectivity without a lot of extra work on the database side. You are best advised to use it for protocols such as rsync, ssh, ftp, and telnet, where the failover machines don't depend on each other.
Clearly, the plethora of features xinetd offers is a good reason to use it. Don't forget, however, the other benefits of xinetd: bugs are fixed as soon as they are reported, the source code is freely available, and migrating the existing inetd configuration (when you use the itox helper program that comes with xinetd) is a snap.
Why not use xinetd? Backward compatibility would be your best reason, followed closely by incompatibility with your specific platform. The xinetd software is most popular on Solaris and Linux servers, so your particular platform may have issues that are unresolved.
- Read Ted's other Perl articles in the "Cultured Perl" series on developerWorks.
- Grab the Perl script to pick an "active" machine from two or more: set_active.pl.
- Read an article on xinetd by Frederic Raynal.
- Browse more Linux resources on developerWorks.
- Browse more Open source resources on developerWorks.
Teodor Zlatanov graduated with an M.S. in computer engineering from Boston University in 1999. He has worked as a programmer since 1992, using Perl, Java, C, and C++. His interests are in open source work on text parsing, 3-tier client-server database architectures, UNIX system administration, CORBA, and project management.



