Original in fr Frédéric Raynal
fr to en Georges Tarbouriech
xinetd provides access control capabilities similar to the ones provided by tcp_wrapper. However, its capabilities extend much further :
The first part of this article explains how xinetd works. We'll spend some time on a service configuration, on some specific options (binding to an interface, redirection) and demonstrate this with the help of examples. The second part shows xinetd working, the logs it generates and finishes with a useful tip.
Some signals can be used to modify xinetd behavior:
Two utilities (itox and xconv.pl)
are provided with xinetd and allow to convert the /etc/inetd.conf
file into a configuration file for xinetd. Obviously, that's not enough
since the rules specified in the wrapper configuration are ignored.
The itox program is no longer developed if it's
still maintained. xconv.pl
is a better solution, even if the result has to be modified, since
xinetd offers more features than inetd:
>>/usr/local/sbin/xconv.pl < /etc/inetd.conf > /etc/xinetd.confThe configuration file begins with a default section. Attributes in this section will be used by every service xinetd manages. After that, you find as many sections as services, everyone of them being able to re-define specific options in relation to the default ones.
The default values section looks like:
defaultsEach attribute defined in this section keeps the provided value(s) for all the next described services. Thus, the only_from attribute, allows to give a list of authorized addresses that should be able to connect to servers:
{
attribute operator value(s)
...
}
only_from = 192.168.1.0/24 192.168.5.0/24 192.168.10.17Next, every declared service allows access to machines having an address contained in the list. However, these default values can be modified for each service (check the operators, explained a bit further down). Nevertheless, this process is a bit risky. As a matter of fact, to keep things simple and secure, it's much better not to define default values and change them later on within a service. For instance, talking about access rights, the simplest policy consists in denying access to everyone and next allowing access to each service to those who really need it (with tcp_wrapper, this is done from an hosts.deny file containing ALL:ALL@ALL, and an hosts.allow file only providing authorized services and addresses).
Each section describing a service in the config file looks like:
serviceservice_nameThree operators are available: '=', '+=' and '-='. Most of the attributes only support the '=' operator. It gives a fix value to an attribute. The '+=' operator adds an item to a list of values, while the '-=' operator removes this item.
{
attribute operator value(s)
...
}
The table 1 briefly describes some of these
attributes. We'll see how to use them with a few examples.
Reading the xinetd.conf man page provides more information.
Attribute | Values and description |
flags | Only the most current values are mentioned here, check the documentation to
find new ones:
|
log_type | xinetd uses syslogd and the
daemon.info selector by default.
|
log_on_success | Different information can be logged when a server starts:
|
log_on_failure | Here again, xinetd can log a lot of information when a server can't start,
either by lack of resources or because of access rules:
|
nice | Changes the server priority like the nice command does. |
no_access | List of clients not having access to this service. |
only_from | List of authorized clients. If this attribute has no value, the access to the service is denied. |
port | The port associated to the service. If it's also defined in the /etc/services file, the 2 port numbers must match. |
protocol | The specified protocol must exist in the /etc/protocols file. If no protocol is given, the service's default one is used instead. |
server | The path to the server. |
server_args | Arguments to be given to the server. |
socket_type | stream (TCP), dgram (UDP), raw (IP direct access) or seqpacket (). |
type | xinetd can manage 3 types of services :
|
wait | Defines the service behavior towards threads.
Two values are acceptable:
|
cps | Limits the number of incoming connexions. The first argument is this number itself. When the threshold is exceeded, the service is deactivated for a given time, expressed in seconds, provided with the second argument. |
instances | Defines the maximum number of servers of a same type able to work at the same time. |
max_load | This gives really the maximum load for a server (for example, 2 or 2.5). Beyond this limit, requests on this server are rejected. |
per_source | Either an integer, or UNLIMITED, to restrict the number of connexion from a same origin to a server |
The four last attributes shown in table1 allow to control the resources depending on a server. This is efficient to protect from Deny of Service (DoS) attacks (freezing a machine by using all of its resources)
This section presented a few xinetd features. The next sections show how to use it and give some rules to make it work properly.
By default denying access to a machine, is the first step of a reliable security policy. Next, it'll be enough to allow access according to the services. We've seen two attributes allowing to control access to a machine, based on IP addresses: only_from and no_access. Selecting the second one we write:
no_access = 0.0.0.0/0fully blocks services access. However, if you wish to allow everyone to access echo (ping) for instance, you then should write in the echo service:
only_from = 0.0.0.0/0Here is the logging you get with this configuration:
Sep 17 15:11:12 charly xinetd[26686]: Service=echo-stream: only_from list and no_access list match equally the address 192.168.1.1As a matter of fact, the access control is done comparing the lists of addresses contained in both attributes. When the client address matches the 2 lists, the least general one is preferred. In case of equality, like in our example, xinetd is unable to choose and refuses the connexion. To get rid of this ambiguity, you should have written:
only_from = 192.0.0.0/8An easier solution is to only control the access with the attribute:
only_from =Not giving a value makes every connexion fail :) Then, every service allows access by means of this same attribute.
Important, not to say essential: in case of no access rules at all (i.e. neither only_from, nor no_access) for a given service (allocated either directly or with the default) section, the access to the service is allowed!
Here is an example of defaults :
defaultsamong internal services, servers, services, and xadmin allow to manage xinetd. More on this later.
{
instances = 15
log_type = FILE /var/log/servicelog
log_on_success = HOST PID USERID DURATION EXIT
log_on_failure = HOST USERID RECORD
only_from =
per_source = 5disabled = shell login exec comsat
disabled = telnet ftp
disabled = name uucp tftp
disabled = finger systat netstat#INTERNAL
disabled = time daytime chargen servers services xadmin#RPC
disabled = rstatd rquotad rusersd sprayd walld
}
Some attributes must be present according to the type of service
(INTERNAL, UNLISTED ou RPC) :
Attribute | Comment |
socket-type | Every service. |
user | Only for non INTERNAL services |
server | Only for non INTERNAL services |
wait | Every service. |
protocol | Every RPC service and the ones not contained in /etc/services. |
rpc_version | Every RPC service. |
rpc_number | Every RPC service, not contained in /etc/rpc. |
port | Every non RPC service, not contained in /etc/services. |
This example shows how to define services:
service ntalkLet's note these services are only allowed on the local network (192.168.1.0/24). Concerning FTP, some more restrictions are expected: only 4 instances are allowed and it'll be available only during segments of time.
{
socket_type = dgram
wait = yes
user = nobody
server = /usr/sbin/in.ntalkd
only_from = 192.168.1.0/24
}service ftp
{
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.ftpd
server_args = -l
instances = 4
access_times = 7:00-12:30 13:30-21:00
nice = 10
only_from = 192.168.1.0/24
}
For instance, a company wishes to install an FTP server for its employees (to
access and read internal documentation). This company wants to provide its
clients with an FTP access towards its products: bind
has been made for this company :) Let's define two FTP services.
However, xinetd must be able to differentiate them: the solution is the id attribute. It defines a service in a unique
way (when not defined within a service, its value defaults to the name of the
service).
service ftpThe use of bind will allow to call the corresponding daemon, according to the destination of the packets. Thus, with this configuration, a client on the local network must give the local address (or the associated name) to access internal data. In the log file, you can read:
{
id = ftp-public
wait = no
user = root
server = /usr/sbin/in.ftpd
server_args = -l
instances = 4
nice = 10
only_from = 0.0.0.0/0 #allows every client
bind = 212.198.253.142 #public IP address for this server
}service ftp
{
id = ftp-private
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.ftpd
server_args = -l
only_from = 192.168.1.0/24 #only for internal use
bind = 192.168.1.1 #local IP address for this server (charly)
}
00/9/17@16:47:46: START: ftp-public pid=26861 from=212.198.253.142The first part comes from the command ftp 212.198.253.142, while the second part is about the command from charly to itself: ftp 192.168.1.1.
00/9/17@16:47:46: EXIT: ftp-public status=0 pid=26861 duration=30(sec)
00/9/17@16:48:19: START: ftp-internal pid=26864 from=192.168.1.1
00/9/17@16:48:19: EXIT: ftp-internal status=0 pid=26864 duration=15(sec)
Obviously, there's a problem: what happens if a machine doesn't have two static IP addresses? This can happen with ppp connections or when using the dhcp protocol. It seems it would be much better to bind services to interfaces than to addresses. However, this is still not expected in xinetd and is a real problem (for instance, writing a C module to access an interface or address depends on the OS, and since xinetd is supported on many OSes...). Using a script allows to solve the problem:
#!/bin/shThis script takes the /etc/xinetd.base file, containing the desired configuration with PUBLIC_ADDRESS as a replacement for the dynamic address, and changes it in /etc/xinetd.conf, modifying the PUBLIC_ADDRESS string with the address associated to the interface passed as an argument to the script. Next, the call to this script depends on the type of connection: the simplest is to add the call into the right ifup-* file and to restart xinetd.PUBLIC_ADDRESS=`/sbin/ifconfig $1 | grep "inet addr" | awk '{print $2}'| awk -F: '{print $2}'`
sed s/PUBLIC_ADDRESS/"$PUBLIC_ADDRESS"/g /etc/xinetd.base > /etc/xinetd.conf
telnet serviceLet's watch what's going on now:
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.telnetd
only_from = 192.168.1.0/24
redirect = 192.168.1.15 23
}
>>telnet charlyAs a matter of fact, the connection seems to be established on charly, but the following shows that sabrina (an alpha machine, hence "Digital UNIX") took over. This mecanism can be both useful and dangerous. When setting it up, logging must be done on both ends of the connection. Furthermore, for this type of service, the use of DMZ and firewall is strongly recommended;-)
Trying 192.168.1.1...
Connected to charly.
Escape character is '^]'.
Digital UNIX (sabrina) (ttyp1)
login:
defaults {Before activating them, you should take some precautions:
...
disabled = servers services xadmin
...
}
service xadminThe xadmin service has 5 commands :
{
type = INTERNAL UNLISTED
port = 9100
protocol = tcp
socket_type = stream
wait = no
instances = 1
only_from = 192.168.1.1 #charly
}
We only need the finger service :
finger servicexinetd wasn't compiled with the --with-libwrap option (check the attribute server). The defaults section is of the same kind of the one previously provided: every access to charly is denied wherever the connexion comes from. The finger service is not deactivated, nevertheless:
{
flags = REUSE NAMEINARGS
server = /usr/sbin/tcpd
server_args = in.fingerd
socket_type = stream
wait = no
user = nobody
only_from = 192.168.1.1 #charly
}
pappy@charly >> finger pappy@charly
[charly]
pappy@charly >>pappy@bosley >> finger pappy@charly
[charly]pappy@bosley >>
It seems the request didn't work properly, neither from
charly
(192.168.1.1), an authorized machine, nor from bosley
(192.168.1.10). Let's have a look at the log files:
/var/log/servicelog :The request from charly (the two first lines) works properly according to xinetd: the access is allowed and the request takes 5 seconds. On the other hand, the request from bosley is rejected (FAIL).
00/9/18@17:15:42: START: finger pid=28857 from=192.168.1.1
00/9/18@17:15:47: EXIT: finger status=0 pid=28857 duration=5(sec)
00/9/18@17:15:55: FAIL: finger address from=192.168.1.10
/var/log/services :We see that there's only one line matching our two queries! The one from bosley (the second one) was intercepted by xinetd, so it's quite normal not to find it in that log. The selected line really corresponds to the request xinetd allowed, sent from charly to charly (the first one): time and PID are identical.
Sep 18 17:15:42 charly in.fingerd[28857]: refused connect from 192.168.1.1
Let's summarize what we have:
According to the way the server and server_args service lines have been defined, the wrapper features are still accessible (banner - there's a banner attribute in xinetd-, spawn, twist, ...). Remember that the --with-libwrap compilation option only adds access rights control (with the help of hosts.{allow,deny} files), before xinetd process starts. In this example we saw that this configuration allows us to continue using the tcp wrapper features.
This overlapping of features, if it can work, may as well lead to stange behaviors. To use xinetd together with inetd and portmap, it's much better to manage a service with only one of these "super-daemons".
chroot [options] new_rootThis is often used to protect services such as bind/DNS or ftp. To duplicate this behavior while benefiting from xinetd features, you have to declare chroot as a server. Then, you just have to pass other arguments via the server_args attribute :)
service ftpThus, when a request is sent to this service, the first instruction used is chroot. Next, the first argument passed to it is the first one on the server_args line, that is the new root. Last, the server is started.
{
id = ftp
socket_type = stream
wait = no
user = root
server = /usr/sbin/chroot
server_args = /var/servers/ftp /usr/sbin/in.ftpd -l
}
You could now ask which daemon should I choose xinetd or inetd.
As a matter of fact, xinetd requires a bit more administration, especially as
long as it won't be included into distributions (it is in Red Hat 7.0).
The most secure solution is to use xinetd on machines with public
access (like Internet) since it offers a better defense. For machines within a
local network inetd should be enough.