Over the past year, this column has focused on configuring and administering several standard Unix tools. This month, I'd like to switch to a bit of networking. Specifically, I introduce how to configure TCP/IP network services for Linux.
To start, imagine the TCP/IP connection you have (anything from an Ethernet connection to a SLIP/PPP connection from your friendly neighborhood network provider) as really 65,536 lines (each of which can handle many different conversations at once) coming into your machine. Some of these lines are dedicated to serving a single purpose, but a majority of them are open for use.
Each of these separate lines coming into your machine is known as a port, and each Linux machine, connected to another via TCP/IP, has 65,536 ports available on it. Each of these ports allows a connection between the two machines, assuming there is a program on one end that is listening to that port in question and a program on the other end attempting to connect to it. If a connection with the remote side is requested and a program on the remote side is listening to that port, a connection is established. This can be just about any kind of connection, as telnet, FTP, HTTP (World Wide Web), and SMTP (mail) all use sockets to get and receive data. Sockets are the entire combination of local-machine, local port, remote machine, and remote port that defines one of the communications channels available. The number of these channels is really only limited by available memory.
Now, there are two ways to make sure the remote side will be able to pick up on the connection. The first is to start a program running in the background all the time, waiting for a connection. This can be the easiest way to do it, but requires some programming skills and requires the program to be in memory all the time. If you choose to run a program as a daemon, and it is not used often, it will wind up being just wasted memory and get swapped to disk.
The second option is to have one program listen to ALL the ports and then, if a connection is requested, start the associated program. This is good because programs are only run when they are needed, but for programs that require loading a lot of files on startup or may be called very often, it might be too slow and waste CPU power as well.
In Linux, as in most versions of Unix, both of these options exist. Some programs (like the http daemon or sendmail/smail or the NFS daemon) run in the background and make and remove copies of themselves in memory as necessary. These programs usually can have large data files that require up to 30 seconds to load. By loading the programs once, then spawning themselves as needed, these programs can cut their startup time to only a few seconds. Other programs, such as in.telnetd, which handles incoming login connections, do not need as much time to load into memory and can be left out of memory until needed.
In the case of in.telnetd and other small programs, inetd (for Internet Daemon; also called the “superserver”) service comes in. inetd watches all the ports it can, and if it sees a connection request, it checks its list to see if there is something that wants to watch that port. If there is an entry in its list, it starts the program up with input and output, both directed through the socket. Otherwise, it refuses a connection, and you see the familiar “Connection refused” on your terminal.
The way to set up the inetd program is to edit a file called /etc/inetd.conf. In this file, you may find some lines that look like figure 1.
Now let's decrypt some of this. Any line starting with a # is treated as a comment by inetd. Any other line is broken up into 6 pieces:
1) Service-This defines the port that is being watched by inetd. The name for this is in the /etc/services file. If you look there, you'll see lines like:
ftp 21/tcp telnet 23/tcp smtp 25/tcp mail
Here are three of our favorite services defined. ftp, telnet and smtp. ftp uses port 21, telnet uses port 23, and smtp uses port 25. These connections are in /etc/services so that you don't have to remember that ftp is port 21. You just tell inetd ftp and it figures out the rest.
2) Socket type-This can be stream or dgram (for datagram). A stream is usually for a connection that opens for a long time, and (for every case you are likely to see) uses the tcp protocol. Telnet or FTP are great examples of this. A datagram is a small packet of data where there is no real connection and (again, for every case you are likely to see) will use the udp protocol. Also available are raw, rdm, and seqpacket.
3) Protocol-tcp for streams, udp for dgram socket types. These types are defined in /etc/protocols.
4) Flags-Wait and Nowait. This is applicable only to dgram socket types. Anything else should be defined nowait. If a datagram socket connects to another socket and frees the socket for inetd to open another port, it is defined nowait. Otherwise, inetd should wait for the connection to close.
5) User[.group]-This defines which user (and group optionally) to run the following program under. It's usually root, but some programs you may want security on and run as a lower user.
6) Command line-Command (including any command line parameters) to run when inetd finds activity on that port. Almost all programs that are intended to be run from inetd have names starting with “in” to make this obvious.
You may note that you see a /usr/bin/tcpd in front of the programs. The tcpd program performs a few functions that inetd doesn't. For example, tcpd can log the connection through the syslog(3) facility (see Linux Journal issue 11, for a discussion of syslog), verify a hostname, find the name of the remote user that is connecting, and deny or allow services to hosts that you can specify Some of these options require re-compiling the tcpd program, but can greatly increase the security of your system. One thing you can do, without re-compiling, is limit the services available to sites known for causing you trouble. To deny telnet (and other) access to a site, create a file called /etc/hosts.deny. In it, you can list first the access you want to deny, a colon, then the hosts to deny that access to.
First, list the name of the program that you want to deny. This can be in.fingerd, in.telnetd, in.ftpd, etc. You can also use the keyword ALL to signify all services.
Next, list the hosts you want to deny access to by the following methods:
1) Network names starting with a “.” will deny access to all hosts that have it as its last network name. .clarkson.edu will deny access from any host from Clarkson, such as craft.camp.clarkson.edu. A .edu will deny anyone in the .edu domain.
2) Network names that end in a “.” will deny access to all hosts that have the matching string as the front portion of the network name. For example, 128.153. will deny all of the Clarkson domain, while 128.153.16. will deny a portion of the Clarkson domain.
3) ALL which denies access to everyone, and LOCAL which matches hosts whose resolved name does not contain a period (.). Many domain name servers will resolve a name on the local subnet to just the hostname instead of host.subnet.net. For example, craft.capm.clarkson.edu could appear to another host on the same subnet as just craft. The man pages for host_access (5) will explain more wildcards.
4) The keyword EXCEPT will exempt specific hosts who would be denied under other rules from being denied.
So a sample /etc/hosts.deny could look like this:
ALL: .clarkson.edu EXCEPT: craft.camp.clarkson.edu
Which would deny all access to anyone in the Clarkson domain except for users on the machine craft.camp.clarkson.edu.
You can also set up an /etc/hosts.allow, following the same methods as the /etc/hosts.deny, except that the hosts.allow specifies who to specifically allow access to. In the case of a conflict between a host being denied and allowed, the entry in /etc/hosts.allow takes precedence, and access is allowed. To make a site more secure, you could put ALL: ALL in your /etc/hosts.deny (to deny access to everyone), then list in the /etc/hosts.allow all the hosts you want to allow in. This way, only the hosts you specify have access to the services that tcpd runs. Also, if you have only a hosts.allow file, and no hosts.deny file, only hosts listed in the hosts.allow will be allowed any access at all.
See the man pages for tcpd(8) and hosts_allow(5) for more information about how to use tcpd at your site.
Now, how does this all work? Let's add something to our /etc/inetd.conf. Something simple and easy, say a “fortune” port. Many Linux installations contain the /usr/games/fortune command, and a qotd (quote of the day) port exists at port 17. So we'll set inetd up so that if you telnet to port 17, you get the output of the fortune command. So, log into your machine as root and make sure that inetd is running. If it is not, you will want to set up TCP/IP for your machine. Even if you're not connected to anything, you can still set up the loopback device and connect to yourself.
First, make sure that qotd is defined in your /etc/services:
Next, we'll add the line in the /etc/inetd.conf to make inetd start fortune. This can be added anywhere in the /etc/inetd.conf:
qotd stream tcp nowait root /usr/sbin/tcpd \ /usr/games/fortune
You'll have to restart inetd to make it re-read the inetd.conf file. An easy way that only works under Linux, but should always work under Linux, is:
linux:/# killall -HUP inetd
On some systems, the PID of the inetd process may be kept in a file, such as /var/run/inetd.pid, and on non-Linux systems without the inetd.pid file, you will have to use the ps command to find the PID of the inetd process.
Now if you telnet to localhost port 17, you'll find something like this:
linux:/# telnet localhost 17 Trying 127.0.0.1 Connected to localhost Escape character is '^]'. Money is the root of all wealth Connection closed by foreign host. linux:/#
There are only a few programs that you can use for this. Things that use curses, like joe, or anything that uses the SVGAlib, won't work, as it won't be able to open your tty (remember: to Linux, you're telnetting in from somewhere else).
Any programs you do put in your inetd.conf file should have good security. This means:
1) Verify (and modify if necessary) the user that the process is running under. Many need root privileges, but some don't.
2) Verify the security of the program that is being connected to a TCP/IP socket. Something like /usr/games/fortune is not interactive, but a program like the old sendmail allowed the Internet worm to wind its way through machines a few years ago. (Note that the sendmail bug was fixed.)
3) Add extra security to inetd by adding something like tcpd, which will allow you to deny or allow various hosts from connecting to your machine. Check the tcpd man pages for more information about tcpd.
Now that you have your services set up, you can hook in your own services and use them for whatever you want. If you have questions or comments about his article, or have some topic you would like to see in a future issue of the Linux Journal, please send me an e-mail note at email@example.com.