Also in FreeBSD Basics:
In last week's article, we used the
sockstat utility to determine which daemons were listening for network connections on a FreeBSD system. Let's continue where we left off by taking a closer look at
inetd is the internet super-server which listens for requests on behalf of other daemons; it reads
/etc/inetd.conf to determine which ports you wish it to listen on. You should always comment out the lines which represent the daemons you don't wish people to make network connections to. A good general rule is that if you don't know what a daemon does, comment it out.
On my system,
inetd is listening for IPV4 connection requests for the following daemons: ftp, telnet, comsat, ntalk, and tftp. I don't intend to maintain a tftp or ftp server, and I'm not totally convinced that I need to provide comsat or ntalk services. However, I do need to telnet into my FreeBSD system on occasion. So, I'll become the superuser, open up
/etc/inetd.conf using the
vi editor, and add comment characters to the four lines that represent the daemons on whose behalf I don't want
inetd to listen. When I'm finished, that portion of my
/etc/inetd.conf file will look like
#ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l
telnet stream tcp nowait root /usr/libexec/telnetd telnetd
#shell stream tcp nowait root /usr/libexec/rshd rshd
#login stream tcp nowait root /usr/libexec/rlogind rlogind
#finger stream tcp nowait/3/10 nobody /usr/libexec/fingerd fingerd -s
#exec stream tcp nowait root /usr/libexec/rexecd rexecd
#uucpd stream tcp nowait root /usr/libexec/uucpd uucpd
#nntp stream tcp nowait usenet /usr/libexec/nntpd nntpd
# run comsat as root to be able to print partial mailbox contents w/ biff,
# or use the safer tty:tty to just print that new mail has been received.
#comsat dgram udp wait tty:tty /usr/libexec/comsat comsat
#ntalk dgram udp wait tty:tty /usr/libexec/ntalkd ntalkd
#tftp dgram udp wait nobody /usr/libexec/tftpd tftpd /tftpboot
#bootps dgram udp wait root /usr/libexec/bootpd bootpd
Now let's see what happens if I try to
ftp into my FreeBSD computer.
ftp 126.96.36.199 Connected to 188.8.131.52. 220 foo.bar.com FTP server (Version 6.00LS) ready. Name (184.108.40.206:genisis): anonymous 530 User anonymous unknown. ftp: Login failed. Remote system type is UNIX. Using binary mode to transfer files. ftp>
What happened here? It looks like my FreeBSD system is still accepting ftp connections. We've just discovered that it is not enough to edit
/etc/inetd.conf; we must also tell
inetd to stop listening on behalf of the daemons we've commented out. We'll have to send a hang up or HUP signal to
inetd to make it aware of our changes. In order to do that, we must find out
ps -acux | grep inetd
root 172 0.0 0.6 1040 784 ?? Is Thu03PM 0:00.52 inetd
Since this process is owned by root, we'll have to become the superuser in order to send it the HUP signal:
su Password: kill -1 172 exit
To see if this worked, let's rerun the
sockstat -4 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS root XF86_SVG 3752 0 tcp4 *:6000 *:* root sshd 185 4 tcp4 *:22 *:* root sendmail 181 4 tcp4 *:25 *:* root sendmail 181 5 tcp4 *:587 *:* root lpd 177 6 tcp4 *:515 *:* root inetd 172 5 tcp4 *:23 *:* daemon portmap 152 3 udp4 *:111 *:* daemon portmap 152 4 tcp4 *:111 *:* root syslogd 149 4 udp4 *:514 *:* root dhclient 125 3 udp4 *:* *:* root dhclient 125 6 udp4 *:68 *:*
Notice that there is now only one entry for
inetd and that it is only monitoring port 23, the telnet port. Let's see what happens now if I try to
ftp into my FreeBSD system:
ftp 220.127.116.11 ftp: connect: Connection refused
To be thorough, we should also see if
inetd is willing to accept connections from IPV6 clients:
sockstat -6 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS root sshd 185 3 tcp46 *:22 *:* root inetd 172 9 tcp6 *:21 *:* root inetd 172 10 tcp6 *:23 *:*
In my example, I have no need to accept ftp (port 21) or telnet (port 23) connections from clients running IPV6, so I'll comment out these two lines in my
/etc/inetd.conf file. The IPV6 section is located towards the bottom third of the file. Again, if you edit this file, don't forget to send the HUP signal and check that your changes took effect by running
/etc/inetd.conf allows you to control which ports
inetd will monitor for incoming connection requests. However, it doesn't have a mechanism to allow control over which clients you are willing to accept connection requests from. As it stands now, any person with a network connection, which unfortunately includes an Internet connection, to my FreeBSD system can telnet to my computer. Fortunately, they'll be prompted to login, which is one reason why it is important to implement a good password policy. But it makes sense to add another layer of security by telling
inetd to refuse the incoming connection request if it does not come from a client that I trust.
This is where
tcp wrappers comes into play. If you are running FreeBSD 3.2 or higher, it is already built into your system and is just waiting for you to configure which clients you are willing to accept connection requests from. The name of its configuration file is
/etc/hosts.allow, but you'll find the syntax of the file under
man 5 hosts_access and additional options under
man 5 hosts_options.
There are two things to keep in mind when editing this file:
The syntax itself is fairly straightforward; each rule has 2 fields and can use an optional third field:
daemon: client :command
If your rule doesn't fit on one line, use the
\ character at the end of the line to indicate that the rule continues to the next line.
There are a few useful wildcards and operators that you should be aware of.
ALLmatches all; can be used in the "daemon" or "client" field
LOCAL-- used in the "client" field and matches hostnames that don't contain a "." in them; for example, it would match "genisis", but not "genisis.istar.ca"
EXCEPT-- used in the "client" field; allows you to allow some clients but not others
ALLOW-- used in the last field to allow the service
DENYused in the last field to deny the service
As you create your rules, you'll want to use the
tcpdmatch utility to ensure that your rules are actually creating the access control you intend. Before I start creating access rules for the telnet daemon on my FreeBSD system, let's see what happens when I try to telnet into my FreeBSD box.
telnet 18.104.22.168 Trying 22.214.171.124... Connected to genisis. Escape character is '^]'. FreeBSD/i386 (genisis) (ttyp1) login: genisis Password: Last login: Thu Jan 18 17:44:29 on ttyv4 Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994 The Regents of the University of California. All rights reserved. FreeBSD 4.2-RELEASE (SOUND) #0: Tue Dec 12 20:01:29 EST 2000 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ ( M | e | r | r | y ) ( C | h | r | i | s | t | m | a | s | ! | ! ) \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ You have mail. genisis@~ logout Connection closed by foreign host.
Notice that my system is accepting telnet connections and responds by giving a login prompt on terminal
ttyp1. Once I've logged in, I receive my MOTD, my customized prompt, and it's just like I'm sitting at my FreeBSD box. It would appear that
inetd did its job properly. Now, let's see what the default
/etc/hosts.allow file looks like.
more /etc/hosts.allow # # hosts.allow access control file for "tcp wrapped" applications. # $FreeBSD: src/etc/hosts.allow,v 126.96.36.199 2000/07/20 15:17:44 ume Exp $ # # NOTE: The hosts.deny file is deprecated. # Place both 'allow' and 'deny' rules in the hosts.allow file. # See hosts_options(5) for the format of this file. # hosts_access(5) no longer fully applies. # _____ _ _ # | ____| __ __ __ _ _ __ ___ _ __ | | ___ | | # | _| \ \/ / / _` | | '_ ` _ \ | '_ \ | | / _ \ | | # | |___ > < | (_| | | | | | | | | |_) | | | | __/ |_| # |_____| /_/\_\ \__,_| |_| |_| |_| | .__/ |_| \___| (_) # |_| # !!! This is an example! You will need to modify it for your specific # !!! requirements! # Start by allowing everything (this prevents the rest of the file # from working, so remove it when you need protection). # The rules here work on a "First match wins" basis. ALL : ALL : allow
Let's stop right here to see if we can determine why that first rule prevents the rest of the file from working. It basically says: for all daemons, from all clients, allow access. Because this rule matches all clients, and it's the first match, it will always be used. Let's see what happens if we comment out that first rule. I'll become the superuser, open
/etc/hosts.allow using the
vi editor, and put a
# in front of that first rule, and save my changes.
Now, I'll try the telnet command again:
telnet 188.8.131.52 Trying 184.108.40.206... Connected to genisis. Escape character is '^]'. You are not welcome to use telnetd from biko. Connection closed by foreign host.
Looks like I've effectively blocked all telnet connections to my system. Let's take a look at the rest of the
/etc/hosts.allow to see where to go from here to allow limited access via telnet.
Continuing with more of
# Wrapping sshd(8) is not normally a good idea, but if you # need to do it, here's how #sshd : .evil.cracker.example.com : deny # Prevent those with no reverse DNS from connecting. ALL : PARANOID : RFC931 20 : deny # Allow anything from localhost. Note that an IP address (not a host # name) *MUST* be specified for portmap(8). ALL : localhost 127.0.0.1 : allow ALL : my.machine.example.com 192.0.2.35 : allow # To use IPv6 addresses you must enclose them in 's ALL : [fe80::%fxp0]/10 : allow ALL : [fe80::]/10 : deny ALL : [3ffe:fffe:2:1:2:3:4:3fe1] : deny ALL : [3ffe:fffe:2:1::]/64 : allow # Sendmail can help protect you against spammers and relay-rapers sendmail : localhost : allow sendmail : .nice.guy.example.com : allow sendmail : .evil.cracker.example.com : deny sendmail : ALL : allow # Exim is an alternative to sendmail, available in the ports tree exim : localhost : allow exim : .nice.guy.example.com : allow exim : .evil.cracker.example.com : deny exim : ALL : allow # Portmapper is used for all RPC services; protect your NFS! # (IP addresses rather than hostnames *MUST* be used here) portmap : 192.0.2.32/255.255.255.224 : allow portmap : 192.0.2.96/255.255.255.224 : allow portmap : ALL : deny # Provide a small amount of protection for ftpd ftpd : localhost : allow ftpd : .nice.guy.example.com : allow ftpd : .evil.cracker.example.com : deny ftpd : ALL : allow # You need to be clever with finger; do _not_ backfinger!! You can easily # start a "finger war". fingerd : ALL \ : spawn (echo Finger. | \ /usr/bin/mail -s "tcpd\: %u@%h[%a] fingered me!" root) & \ : deny # The rest of the daemons are protected. ALL : ALL \ : severity auth.info \ : twist /bin/echo "You are not welcome to use %d from %h."
Notice that there aren't any rules that specifically mention
telnetd. The last rule in the file is intended to cover all the left over daemons that didn't match earlier rules. Notice that the last rule allowed the connection, but closed it after echoing a message, which is what we saw when I attempted the telnet connection. The
%d was replaced by the name of the daemon (
telnetd), and the
%h was replaced by the hostname of the client trying to connect (
We could have predicted this outcome if we had used the
tcpdmatch utility. The syntax to use this utility is very simple:
tcpdmatch daemon_name host_name_of_client
You do not have to be the superuser to run this utility. Let's see what it says will happen if the host "biko" tries to connect to the
telnetd on my FreeBSD system:
tcpdmatch telnetd biko client: hostname biko client: address 220.127.116.11 server: process telnetd matched: /etc/hosts.allow line 77 option: severity auth.info option: twist /bin/echo "You are not welcome to use telnetd from biko." access: delegated
This is very useful output as it tells us which line number in
/etc/hosts.allow contains the matching rule and what the result of that rule will be for that client.
/etc/hosts.allow to allow
telnetd to accept connections form the hosts "genisis" and "biko", but to disallow connections from any other clients. I'll become the superuser and add the following lines; it doesn't matter where in the file I add the lines as long as they appear before that last rule.
telnetd: biko,genisis :ALLOW telnetd: ALL :DENY
I'll then check that my rules work by running
tcpdmatch on biko, genisis, and a third host called creed:
tcpdmatch telnetd biko client: hostname biko client: address 18.104.22.168 server: process telnetd matched: /etc/hosts.allow line 74 option: ALLOW access: granted tcpdmatch telnetd genisis client: hostname genisis client: address 22.214.171.124 server: process telnetd matched: /etc/hosts.allow line 74 option: ALLOW access: granted tcpdmatch telnetd creed client: hostname creed client: address 126.96.36.199 server: process telnetd matched: /etc/hosts.allow line 75 option: DENY access: denied
Let's see what happens when the host creed tries to telnet into my FreeBSD system:
telnet 188.8.131.52 Trying 184.108.40.206... Connected to genisis. Escape character is '^]'. Connection closed by foreign host.
Notice that I didn't receive any message, as the rule on line 75 was the first match, not the rule on line 77.
We've just scratched the surface of the functionality provided by
tcp wrappers, but it should be enough to get you started. Depending on your needs, your rules can range from being very simple to quite elegant. You'll definitely want to check out
man 5 hosts.access and
man 5 hosts.options to see all the configurable options available.
Dru Lavigne is a network and systems administrator, IT instructor, author and international speaker. She has over a decade of experience administering and teaching Netware, Microsoft, Cisco, Checkpoint, SCO, Solaris, Linux, and BSD systems. A prolific author, she pens the popular FreeBSD Basics column for O'Reilly and is author of BSD Hacks and The Best of FreeBSD Basics.
Read more FreeBSD Basics columns.
Discuss this article in the Operating Systems Forum.
Return to the BSD DevCenter.
Copyright © 2009 O'Reilly Media, Inc.