If you have a valuable or fragile network to protect, you may want to protect it with a very strong, well-proven firewall. In this article, Benjamin Ewy explains very thoroughly how to build your own 'bastion host' firewall with Linux.
As more and more companies try to develop a presence on the Internet, establishing a secure network perimeter is becoming a very important topic. There are many varieties of what are loosely referred to as firewalls. The general principle behind a firewall is that it serves as a choke point between an internal network and the outside world. The choke point only allows traffic through that is deemed safe.
IP-based filters are one common form of firewall that rely on the source and destination addresses to decide which kind of traffic to pass through. They have the advantage of flexibility in that they can easily be adapted to different types of traffic as needed. The primary disadvantage of IP-based filters is that they rely on IP addresses as the principle form of authentication, and they also lack the ability to look higher into the protocol layer to determine exactly what kind of traffic is being sent.
Application-level gateways are another form of firewall that often consist of a computer called a bastion host. The bastion host runs a set of firewall software which implements the policy “that which is not expressly permitted is prohibited”. This policy is implemented at the application level, which allows the bastion host to more completely control the traffic that passes through it.
Implementing the interface between the internal and external networks at the application level allows much more control over the authentication for particular services and, in particular, allows for many forms of strong authentication. The main disadvantage of application-level Firewalls is that they require interfaces for every specific application that is to pass through the gateway. If a new application interface is desired, either custom software must be written or the service cannot be provided. The Trusted Information Systems Firewall Toolkit (fwtk) is a very useful kit for creating bastion hosts.
The fwtk supports the functions of a bastion host by providing several small programs that can be pieced together as the site operator desires while simplifying management with a common configuration file. For each service the security policy allows to pass through the firewall, a specific application level proxy is required. The fwtk comes with proxies for telnet, rlogin, SMTP mail, ftp, http, X window, and a generic TCP plug-board server that works as a transparent pass-through proxy for many other services.
Additionally, the fwtk comes with a tool called netacl, which implements network level access control, and authsrv, which implements a network authentication service. This article focuses on preparing a generic Linux host to be a bastion host, obtaining and compiling the fwtk, and configuring its services to support a secure network environment.
The first step is to prepare the Linux host to be a functional and secure bastion host. There are several principles that firewall builders should adhere to. The ideal bastion host should only provide proxy services and should not be a general purpose machine. Only administrative accounts should be allowed, and if possible, logins to the bastion host should be restricted to the console, although allowing strongly authenticated remote access for remote maintenance will be discussed. The bastion host should not rely on any network services such as NIS or any form of remote file access, such as NFS. Allowing either of these opens up numerous holes that can compromise your bastion host.
Next, it is necessary to verify that the required functionality is available with the Linux host. Every bastion host has at least two network interfaces, one connected to the internal network and the other connected to the external network access point. These interfaces should be configured and tested prior to any further modifications, and you should verify the accessibility of your bastion host from both the internal and external networks. Refer to the Linux NET2 HOWTO and the Linux Multiple Ethernet mini-HOWTO as necessary.
The kernel should be rebuilt, ensuring that IP forwarding (CONFIG_IP_FORWARD) is disabled when you do makei config. If IP forwarding were enabled, the kernel would automatically forward packets from one interface to the other interface if a route has been established. Controlling this forwarding is what building a bastion host is all about. Finally, if you want to provide a secure mechanism for SMTP mail service, it is necessary to first configure and test sendmail. Refer to the Linux Kernel HOWTO and the Linux Electronic Mail HOWTO, as appropriate.
The next task is to secure the bastion host so that only the proxy services are available. Begin by removing all unneeded services from the inetd configuration file, /etc/inetd.conf. Simply put a # in front of each unneeded service line, and when done editing the file, issue a kill -HUP to the process id of inetd (perhaps with killall -HUP inetd). Remove ftp, telnet, SMTP, nntp, shell, login, talk, stalk, pop, uucp, ftp, bootp, finger, systat, netstat and every other service you are not expressly sure you want to provide. We will be defining our proxy services in this file later.
Finally, prevent the startup of any stand-alone daemons by cleaning out the boot files in /etc/rc.d, removing unneeded programs. In particular, check the rc.inet2 file and comment out rpc.portmap, rwhod, rpc.mountd, rpc.nfsd, rpc.ugidd, and ypbind. After you are done removing services, reboot your bastion host and carefully examine the output from a ps aux and check that you didn't miss any unnecessary programs. It is also a good idea to run rpcinfo -p and the port scanner that comes with the fwtk in the tools directory to verify that all unnecessary services are dead.
Obtain the toolkit, Linux patches, and, if desired, the S/Key package, as detailed in Obtaining Firewall Resources. Bellcore's S/Key provides one-time password support for network authentication if built into the toolkit. A number of commercial one-time password systems are also supported by the fwtk, but their use is not detailed in this article.
Put the fwtk archive in /usr/src/, and run tar xfz fwtk-v1.3.tar.Z to unpack it. If you prefer to build in a different place, modify the Makefile.config as appropriate. The Linux patches that we will be applying assume /usr/src/fwtk is the source code directory.
These patches are based on the work of Marco Pauck (pauck@wmd.de) and the firewall-users mailing list. In addition, there are some modifications and additions done by the author to allow the x-gw proxy to work and to support the S/Key authentication mechanism. Most of the patches work around Linux's select() function. Put the fwtkpatches.tgz file into /usr/src/fwtk. Then run tar xfz fwtkpatches.tgz which will create a patches directory. Go into the patches directory and run the INSTALL script or apply the patches by hand. If you do not want S/Key support, run the INSTALL.noskey script instead.
Assuming you want S/Key, put the S/Key archive in /usr/src and run tar xfz skey-2.2.tar.gz to un-archive it. It is necessary to compile S/Key first so that its libraries can be linked into the authentication service when we compile the fwtk. This S/Key is already ported to Linux, so all that is necessary to build it is to run make inside the /usr/src/skey-2.2 directory.
When the patches have been installed and S/Key has been compiled, you can modify the Makefile.config if you want to change any of the defaults, but the rest of this article will assume you have left the Makefile.config as patched. Go to /usr/src/fwtk/ and run make and make install. The firewall components will be installed in /usr/local/etc/ by default.
The firewall toolkit is made up of three main components: netacl, authsrv, and the various service proxies. Netacl is similar to the tcp wrapper, tcpd, that is common on many Linux systems. Netacl is used to check rules on a per-service basis and take the defined action. You edit your /etc/inetd.conf file to call netacl, passing the normal service to netacl as its first parameter. An example entry for the finger service might look like Listing 1. inetd will invoke netacl when a connection is made on the finger port.
Netacl looks in the common configuration file to see what action to take. The common configuration file is called the netperm-table and is found in /usr/local/etc/netperm-table. A default netperm-table that has many examples, in addition to the ones presented in this article, is installed automatically. Netacl looks in the netperm-table and reads entries that start with netacl. Netacl understands the permit-hosts and deny-hosts options for defining access lists and requires -exec to be defined for each line as well, as shown in Listing 2.
A given service can have multiple lines of both the permit and deny varieties. Address can be a list of hosts addresses, and wildcards such as *.my.domain or 129.17.* are supported. The keyword unknown matches hosts that cannot be resolved using DNS. The first rule that matches is the one performed. Lines may not be broken.
In the examples given in this article, the following conventions will be used: .internal.net will represent your internal network. ftp.server.internal.net will represent a ftp server internal to your network. www.server.internal.net will represent a www server internal to your network. compute.server.internal.net is a compute server internal to your network. trusted.external.net is an external network that you trust. bastion.host.internal.network will represent your bastion host's IP address.
An important point is that these rules are not associated with a network interface. If you write rules for a service to allow your internal private network to have special access rights, you must ensure that those IP addresses could only have been received from your internal network. IP spooling can be used to pretend to be a host on your internal network and take advantage of your rules. This can be prevented by using screening routers to block packets claiming to be from the internal network when they arrive on the external network connection. Often your Internet provider can implement this type of rule in the router that feeds your site.
The first line of Listing 3 will exec in.fingerd if the requesting host is a member of trusted.external.network. The second line will match all others and cat a message of your choice. This might be a “fake” finger output to mislead attackers or a simple explanation that the service is unavailable. Netacl is often used for permissions on services that you are not proxying but can also be used to switch among the proxy or the real service based on the origin of the connection. This feature will be discussed in more detail later.
The netperm-table also contains configuration information for all of the proxies, as shown in Listing 4. The format is similar to the netacl format, and each rule can contain multiple lines. These options will be discussed in more detail for each proxy later.
Authsrv is the authentication server for the firewall toolkit. The authentication server is optional but allows multiple types of authentication to be managed in a consistent manner. Support for the authsrv daemon's authentication by is built into all of the proxies in the firewall toolkit, and can be selectively enabled in the netperm-table on a per-proxy or even per-permiti-hosts basis.
Authsrv has support for many different types of authentication, including internal plain-text passwords, and several forms of strong authentication using one time passwords compatible with Bellcore's S/Key, Security Dynamic's SecurID, Enigma Logics' Silver Card, and Digital Pathways' SNK004 Secure Net Key. In our example, we have compiled S/Key and its support into authsrv, but the other mechanisms are similar and their details can be found by looking in the /usr/src/fwtk/auth directory. S/Key is a challenge-response one-time password system that will present you with a sequence number and a key at login time. You must give the sequence number, key, and your own private pass-phrase to an S/Key calculator, and it will return a 6 word password. That password will be valid only for that particular sequence number, and it has the property that it was created using a non-reversible algorithm, so it is not possible to easily calculate the next password even if the current one is known. This type of strong authentication is one of the best features of using the fwtk.
To configure the authsrv it must be added to the inetd.conf so that inetd will start it, as shown in Listing 5.
Since authsrv is not a well known service, an unused port must be selected and added to the /etc/services file. The fwtk configuration manual suggests port 7777. The corresponding /etc/services entry would then be:
# Example services entry authsrv 7777/tcp
As before, when you change the /etc/inetd.conf file, you must send a -HUP signal to inetd to cause it to read in the changes.
Configure authsrv itself by setting up its options in the netperm-table. Authsrv recognizes the database, permit-hosts, nobugus, userid, and badsleep options. The database option tells authsrv where to find its database, and the permit-hosts can be used to restrict which hosts can query the authsrv. It is recommended that authsrv be run on the bastion host so that the database is protected from misuse. An example config might include the entries shown in Listing 6 in the netperm-table.
In our example, the bastion host is running the authsrv daemon, and the bastion host is the only host with proxies requiring authentication by our server. We restrict the authsrv requests to come only from the bastion host to prevent unauthorized probing of the database.
Next, we set up an S/Key-based admin account on our bastion host. First, the auth database needs to be initialized. The best way to do this is to run authsrv as root. Then add an admin user and enable logins for that user by entering the following at the authsrv prompt:
authsrv# adduser admin authsrv# enable admin
Set the protocol to skey, and give the the user wizard privileges:
authsrv# proto admin skey authsrv# superwiz admin
Then you need to set the admin password. S/Key allows your password to be several words long. Enter your phrase between quotes:
authsrv# password admin "my neat password phrase" ID admin s/key is 664 wa56038 authsrv# exit
The output returned by authsrv after your pass-phrase is the next sequence number it will use for a challenge and its key. You can use them to generate one-time passwords, as needed, using the key program (read its man page, found in /usr/src/skey-2.2/key). For example, if you are challenged with:
S/Key Challenge: s/key 663 wa56038
run key 663 wa56038 and enter in your pass-phrase. It will respond with a six-word phrase that you can enter to authenticate yourself. If you are going to be traveling, there are Macintosh and DOS versions of the S/Key calculator, or you can have key print out a list of your next passwords by running key -n number 663 wa56038, and it will print out your next number passwords and their appropriate sequence numbers.
There is another program called authmgr which can be used to remotely administer the authentication database. There are many features in addition to those shown here, such as groups and group permissions for users, and the ability to specify authentication on a per-user, per-time basis. These additional features can be found in the authd man pages. Finally, there are two utilities called authdump and authload which allow you to take snapshots of the current database, for archival or administrative purposes, and then reload the database.
This section focuses on the configuration and use of the service proxies for telnet, ftp, http, and SMTP mail. Common methods for configuring these services are discussed, but each of them has many options and are very flexible. The man pages for each of the services should be reviewed to determine if other configurations might suit your installation better. The proxies for rlogin and X-Windows are configured similarly to the telnet proxy. The Generic TCP plug-through proxy can be used for NNTP news transfers, talk sessions, or any other TCP service a site wishes to pass through the firewall.
The telnet proxy is called tn-gw. The tn-gw has many options, including the permit-hosts and deny-hosts lines as seen on other services, and a number of message options. The fwtk configuration manual discusses how to use netacl to allow both telnet service to the bastion host and the telnet proxy to coexist on the bastion host, although this is just one of many possible configurations.
First, use netacl to switch the service based on the origin, as shown in Listing 7, which shows an /etc/inetd.conf entry, and Listing 8, which shows the additions to the netperm-table.
When a telnet connection is started, inetd calls netacl. Netacl looks at the source IP address, and if it is not from the bastion host, it calls the tn-gw proxy. The tn-gw proxy prints a denial message and closes, if the source address is unknown, and will allow a non-authenticated connection to compute.server.internal.net only from .trusted.external.net. Connections from the internal net have no restrictions (the default) on their destinations, and users are allowed to change their bastion host passwords if they are coming from the internal network. Additionally, connections from the internal net to the bastion host itself are allowed. Finally, all other hosts are allowed to go to any destination other than the bastion host itself, after they authenticate with the specified authserver. You might not want to use this setup (allowing unauthenticated access from any external site is not a good idea) but it presents many of the options the toolkit offers.
If a user is on the internal network and wants to have telnet access to the external network, they telnet bastion.host and then type c external.host to connect to the external host.
If a user is on the external network and wants to connect to an internal host they will have to telnet bastion.host, enter their userid and authentication as required by specific authentication type, and then c internal.host.
Finally, if an administrator wants to connect to the bastion host from the internal network, they telnet bastion host, then c bastion.host, and netacl will start the real telnet service on the bastion host.
Next, we will configure the ftp proxy system. We will assume your site does not want to provide anonymous ftp service from the bastion host to the external network. The TIS configuration guide discusses in more detail how to configure a site that supports anonymous ftp and the ftp proxy on the same host. Our example will only have the ftp proxy on the bastion host.
The inetd.conf file will need to be modified to call the ftp-gw proxy. Netacl is not used since we are not switching the service being provided, as we were in the telnet example. The inetd.conf line is shown in Listing 9. Then establish the permissions in the netperm-table, as shown in Listing 10.
These lines will print the ftp-deny.txt file and close the connection if the reverse name lookup fails; internal nodes will be allowed to ftp through the gateway without authentication, but RETR and STOR transactions are logged. Additionally, it will allow external nodes to connect to the internal ftp server after authentication with the authserver, logging RETR and STOR transactions.
Users on the internal network will ftp to the bastion host and enter their destination at the username prompt. To be forwarded to the site big.archive as user bob, they need to enter bob@big.archive at the bastion host's username prompt, and it will forward the connection. Users on the external network will have to authenticate themselves first and then enter their destination. This is discussed in more detail in the ftp-gw man page.
Now, configure the proxy for http. This is similar to the other proxies and can have a relatively simple configuration. First, add the line in Listing 11 to the /etc/inetd.conf file, and then set up the netperm-table configuration entries shown in Listing 12.
This configuration will allow internal hosts to go out to wherever they want without authentication and allow external hosts to connect to the www.server.internal.net host without authentication. Check the http-gw man page for options involving specific authentication of particular http actions.
For users on the internal network, we can configure their proxy-aware web browser to transparently pass through the firewall http proxy. We will use the Netscape browser as our example. Find the Network Preferences menu under the Options menu. Under that, there is a section on proxy configuration. Select manual proxy configuration and enter the IP address of your bastion host for each of the proxied services and their respective ports. Now you can again use normal http addresses, and the browser will do all necessary requests automatically through the bastion host.
Our final service to be proxied is SMTP mail. The fwtk comes with two programs—smap and smapd—which serve to reduce problems with sendmail and insulate it from some attacks. They do use sendmail, however, so this section will assume that the sendmail configuration for the bastion host has already been setup and debugged. Sendmail configuration is well outside the scope of this article. The Linux Electronic Mail HOWTO can be consulted as necessary.
Smap is a minimal SMTP client that is invoked by inetd, accepts SMTP mail messages, and writes them to a special spool directory. Smapd is a daemon that replaces sendmail in rc.M (or whichever boot script starts sendmail in your distribution). Smapd will look at the spool directory and deliver messages using sendmail periodically. This time, three changes need to be made. Listing 13 shows an /etc/inetd.conf entry to smap, Listing 14 shows how to start smapd from a boot script, and Listing 15 shows the netperm-table entries.
Next, create the /var/spool/inspool directory and make it owned by uucp. Run mkdir /var/spool/inspool; chown uccp /var/spool/inspool. Finally, run sendmail from a cron job so that it can process any entries that could not be delivered. A line like:
0,30 * * * * /usr/lib/sendmail -q >/dev/null 2>&1
should be added to root's crontab.
The TIS Firewall Toolkit is a very flexible and useful collection of programs for creating bastion hosts. A collection of examples of how to configure a Linux-based bastion host have been presented. Many of these programs have additional features, and the documentation that comes with the toolkit should be read to get the most out of these programs. Several additional tools, such as a portscanner and several log summary generators, come with the fwtk.
One final step before completing your bastion host is the removing of any unnecessary programs that may have been installed. In general, new holes are found every day, so the fewer programs installed, the better. This includes gcc! Without a compiler, many hackers are limited in what they can do if they should break in. It is a good idea to run Tripwire on your system after it is configured, to provide a safeguard against unauthorized modifications to the system. Tripwire verifies the checksums of files and alerts you to modifications. Finally, make a complete backup of your bastion host so that you have a “Day 1” copy to revert to in case of emergency.
There are many useful references for information on firewalls. The fwtk comes with an overview, an installation and configuration guide, a user manual that shows users how to access services through the firewall, and man pages for all of the programs associated with the fwtk.
Useful Linux resources include the Linux NET-2 HOWTO, the Linux Firewall HOWTO, the Linux Multiple Ethernet mini-HOWTO, and the Linux Kernel HOWTO. All of these are available on sunsite.unc.edu, tsx-11.mit.edu, and their mirrors.
These and other useful online information about firewalls can be found at TIS Resources.
Several excellent books on firewalls are:
Firewalls and Internet Security. Cheswick & Bellovin, Addison Wesley.
Building Internet Firewalls. Chapman & Zwicky, O'Reilly & Associates.
Internet Firewalls and Network Security. Siyan & Hare, New Riders Publishing.