How to Build a FreeBSD-STABLE Firewall with IPFILTER

Applicable to: FreeBSD 4.2-STABLE
Updated: March 8, 2001
Author: Marty Schlacter

This procedure was written before the release of FreeBSD-4.3.  While there probably aren't too many differences between FreeBSD-4.2 and 4.3 (at least from the perspective of installing & configuring), it has not been verified to work correctly with versions of FreeBSD later than 4.2.  If you encounter problems, please e-mail me with suggested corrections.

This howto walks you through the process of building one of the most stable and secure firewalls available - a FreeBSD-STABLE firewall with IPFILTER. As a part of the installation process, all services will be disabled except OpenSSH, which will have its access controlled via TCP-Wrappers. The firewall will be configured to log through the syslog facility, but will have its own firewall log files (rather than filling up /var/log/messages). We'll add VESA support into the kernel so that we can use 132x43 screen resolutions, as well as compile support into the kernel for a second ISA Ethernet card if you have one. After we add a warning banner to the system, we'll make BASH the default shell for root, perform a rudimentary setup for root's BASH environment, and redirect root's email to your "normal" account so that the root account on the firewall itself doesn't fill up. Lasty, we'll download, compile, install, and configure Tripwire, as well as install cvsup so that your ports collection stays up to date.

This is an all-encompassing how-to, and should take 1/2 of a Saturday to complete, but when you're finished, you'll not only have a great firewall, but will be better able to compare and contrast FreeBSD/IPFILTER to Linux2.4/IPTABLES so that you can consider the pros/cons of each on their merits...and that learning process is what all of this about anyway. So, grab a cup of coffee, sit down with that old Pentium, and get ready to broaden your horizons.

Before we start, I'd like to thank Dan O'Connor for the work he put in on his great site, FreeBSD Cheat Sheets, since it was his great site that gave me the motivation to start this howto. You will undoubtedly see some of his tips and tricks sprinkled throughout this document. For those of you that are new to FreeBSD, I highly recommend his site.

And, as always, before performing this procedure, I highly recommend that you review the Installing FreeBSD chapter of the FreeBSD Handbook.

Installing FreeBSD-STABLE

To build the most stable and security-patched system you can, you'll want to make sure you're running the latest version of FreeBSD-STABLE. When I built my last system, the March 2, 2001 version of FreeBSD-4.2 was the latest snapshot in the 4.2-STABLE branch. For those of you new to FreeBSD, the STABLE branch is the version of the operating system that has all of the latest patches, bugfixes, and enhancements after the previous release was made. If you've installed FreeBSD-4.2 from CD-ROM, you probably installed 4.2-RELEASE, which is (simplistically) nothing more than a version of the 4.2-STABLE branch that was exhaustively tested, burned to CD-ROM and made available for sale.  After the release date of 4.2-RELEASE (in late November, 2000), the 4.2 tree continued to evolve & be patched after that point.  Since there's no way the folks at can burn & sell CD-ROMs for each day's version of the 4.2 tree, 4.2-RELEASE is the only one made available for sale on CD, and subsequent snapshots of the 4.2-STABLE tree are only available on-line. 

So, what are the benefits of loading 4.2-STABLE rather than 4.2-RELEASE?  Well, the biggest answer (if you're building a firewall, like we are here) is that all of the security patches have been applied to the O/S and the associated applications.  For example, FreeBSD-4.2-RELEASE (which was released in November 2000) uses OpenSSH-2.2.0, which is a great product but also has a remote buffer overflow that wasn't discovered until early February, 2001.  If a hacker exploits this vulnerability on your 4.2-RELEASE box, they can gain remote root access and ruin your day.  The relevant info can be found on When you load FreeBSD-4.2-STABLE, by comparison, you're getting FreeBSD-4.2-RELEASE with all of the patches applied after the November 2000 release, so you're system will have OpenSSH-2.3.0 (not OpenSSH-2.2.0) which is not vulnerable to the remote buffer overflow.  So loading the latest snapshot from the STABLE branch saves you a lot of time of loading patches after your OS load is finished. 

  1. Inventory your computer hardware and ensure that it is compatible with FreeBSD. The current compatibility list can be found in the FreeBSD 4.2 Release Notes.
  2. Download the boot floppy images:
    1. FTP to
    2. Change directory into /pub/FreeBSD/snapshots/i386/4.2-20010302-STABLE/floppies ...where 4.2-20010302-STABLE is the directory that houses the latest snapshot - in this case, representing the 4.2 baseline, year 2001, month 03, and day 02. Undoubtedly, when you get to the "/pub/FreeBSD/snapshots/i386" directory there will definitely be a snapshot available that's newer than March 2, 2001.  Use the newer one since it'll have even more bug fixes, enhancements, etc. than the March 2, 20001 snapshot that I used when I built my system.
    3. Download the kern.flp and mfsroot.flp images & store them in your /tmp directory (on Linux or FreeBSD) or c:\windows\temp directory (for Windows),  depending on what system you're downloading from.
  3. Download the floppy creation tools if you're a DOS/Windows users.
    1. FTP to
    2. Change directory into /pub/FreeBSD/tools
    3. Download the program, rawrite.exe, and store it in the same directory that you used, above.
  4. Create Boot Floppies
    1. If you're using Linux or FreeBSD, use the dd command as follows, and create one floppy from the kern.flp image, and another disk from the mfsroot.flp image.
      [root@yoursys /tmp]# dd if=/tmp/kern.flp of=/dev/fd0
      2880+1 records in
      2880+0 records out
      1474560 bytes transferred in 49.931306 secs (30135 bytes/sec)
    2. If you're using DOS/Windows, use the rawrite program that you downloaded.  Just like with Linux, make one floppy from the kern.flp image, and another one from the mfsroot.flp image.
      RaWrite 1.3 - Write disk file to raw floppy diskette
      Enter source file name: mfsroot.flp
      Enter destination drive: a:
      Please insert a formatted diskette into drive A: and press -ENTER- :
      Number of sectors per track for this disk is 18
      Writing image to drive A:. Press ^C to abort.
      Track: 79 Head: 1 Sector: 16
  5. On the FreeBSD machine, insert the kernel floppy (kern.flp) in your floppy drive and boot from it.  When prompted, insert the 'MFS root' floppy (mfsroot.flp).
  6. Run the kernel configuration utility in full-screen visual mode to clear any conflicts and ensure the kernel matches your hardware.  For example, remove SCSI controllers if you don't have any, etc.  On my system (where I don't have any SCSI controllers or a PS/2 mouse), here's the only active drivers I left enabled (I deleted the rest):
       ATA/ATAPI compatible disk controller ata0 14 0x1f0
       ATA/ATAPI compatible disk controller ata1 15 0x170
       Floppy disk controller fdc0 6 0x3f0
       NE1000,NE2000,3C503,WD/SMC80xx Ethernet adapters ed0 10 0x280
       Parallel Port chipset ppc0 7
       8250/16450/16550 Serial port sio0 4 0x3f8
       8250/16450/16550 Serial port sio1 3 0x2f8
       Keyboard atkbd0 1
       Syscons console driver sc0
       Math coprocessor npx0 13 0xf0

    Note: If you have PCI-based Ethernet cards, you can delete all of the network cards in the list - yours will be found and configured automatically.  If you're on the other end of the scale (like me) and you have two old NE2000-compliant ISA network cards, you'll only be able to configure one of them at this time (ed0).  After your installation is complete, you'll have to build a custom kernel & add in a "placeholder" for the 2nd generic ISA card, and then run through the kernel configuration utility again after you reboot. We'll do this at the end of this document.

    Hit 'Q' then 'Y' to save your changes and exit. 

  7. From the main menu, choose a 'Standard' installation.
  8. In the FDISK Partition Editor, choose 'A' to use the entire disk. This will let FreeBSD take the entire disk and eliminate the need for a bootloader. At the warning, choose 'NO' when asked if you want to accept a dangerously dedicated disk.  Press 'Q' to continue.
  9. In the Disklabel Editor, create the following partitions, then choose 'Q' to continue.  Note that we want a separate /var partition so that firewall logs don't fill up other partitions and cause a denial of service:
    256MB swap partition
    256MB (or more, if you can) file system mounted as /var
    128MB file system mounted as /
    Remainder of your hard drive mounted as /usr
  10. Choose "Kern-Developer" as the Distribution you want to install by highlighting it and pressing the 'space' bar.  Remember, this is going to become a gateway/firewall system, and you'll need the kernel source code to recompile IPFILTER into the kernel.  Also, you don't need (or want) X Windows running on it.
  11. Select "Yes" to install the FreeBSD ports collection.
  12. Arrow back up to "<<< X Exit" and hit the 'space' bar to exit the Distribution Menu
  13. Select either an FTP or FTP Passive install (depending on what your current network's firewall will support).
  14. Select "4.0 SNAP Server (" as your Distribution Site
  15. Select your Ethernet card as the network interface to install from (e.g. "ed0" if you're using a generic NE2000-compatible ISA card).
  16. Select "no" for IPv6 config
  17. Select "yes" for DHCP configuration if your network card is directly connected to your cable modem, etc.  Select "no" if you're on a pre-existing network, then enter your interface configuration information manually - host name, domain name, IPv4 gateway IP address, name server IP address, IPv4 address, and netmask.
  18. At the "Last Chance" warning, select "yes".

    (System Installs...With a cable modem, the download & install took 22 minutes.)

  19. Miscellaneous configuration:
    1. Do you want this machine to function as a gateway? Yes
    2. Do you want to have anonymous FTP access to this machine? No
    3. Do you want to configure this machine as an NFS Server: No
    4. Do you want to configure this machine as an NFS Client: No
    5. Select "Yes" when asked "Do you want to select a default security profile for this host"  Select "Extreme - Very restrictive security settings".  Note that this turns off inetd - for firewalls, that's a "good thing".  
    6. Select "Yes" when asked to modify the system console configuration.  Under "Saver - Configure a screen saver", select "Fire - Flames effect screen saver"...or whatever screen saver you want.  Then arrow up and select to "Exit - Exit this menu (returning to previous)"
    7. Select "Yes" when asked "Would you like to set this machine's time zone now?"  Then, select "No" when asked if your machine's CMOS clock is set to UTC.  Then select the appropriate time zone - by region, country, and then the applicable time zone.
    8. Select "No" when asked if you'd like to install Linux Binary support.
    9. Select "No" when asked if you want to enable USB mouse support (unless, of course, you have one...)
    10. Make the following configuration changes for the mouse configuration, then enable it & test it, then select "Exit" to return to the previous menu. Note that I have a 2-button serial mouse - that's why I'm using COM1 and 3-button emulation: 
      Type: Auto
      Port: COM1
      Flags: -3
    11. When asked to browse the FreeBSD packages collection, select "Yes", then select the "4.0 SNAP Server" as the distribution site, elect to skip over a network reconfiguration (you've already done it correctly need to do it again), and then install the following packages:
      WWW - lynx-
      Editors - vim-lite-5.7.24
      FTP - ncftp3-3.0.2
      Mail - pine-4.33
           - mutt-1.2.5
           - elm+ME-2.4.88_1,1
      Net - cvsup-bin-16.1
      Shells - bash-2.0.4

      Then tab over and select "Install", select "OK" to confirm your choices

      (Packages are installed...takes about 60 seconds)

    12. Select "No" when asked if you want to add any additional user accounts.  This is a firewall, not a common user machine, so we won't need any
    13. Set the 'root' password: ******
    14. When asked if you'd like to visit the General Configuration menu to set any last options, select "Yes" and configure the following options:
        - Enable "ntpdate - Select a clock-synchronization server"
        - Enable "sshd - This machine wants to run the ssh daemon"

      Then select Exit and return to the previous menu, and then tab over and select "Exit Install"

    15. Select OK when asked if you're sure you want to exit the install & reboot the system. Remove your floppy disk (probably the mfsroot disk) and your system will reboot.

      (System reboots...)

Compiling IPFILTER into the Kernel, & Configuring the System

Now that you have FreeBSD-STABLE installed on the system, we need to spend about 2-3 hours adding in IPFILTER support as well as finishing the rest of the configuration.  Here's what we're going to do in this section (in no particular order):

In order to save time, I'm going to do some steps in what will appear to be an "out of order" sequence.  This is being done on purpose so that we will minimize the number of re-boots you'll have to do.  In fact, the goal is to configure the system, then recompile the kernel, and when the system reboots, you're done.  That's it.  

  1. Make "bash" the default shell for 'root' and perform an initial set up of root's bash environment.
    1. Use FreeBSD's password file manipulation utility, vipw, to modify root's default shell.  At a root prompt, type vipw.  A copy of the /etc/passwd file will be displayed.  Use standard vi editing commands to change root's default shell from /bin/csh (all of the way at the end of the first line) to /usr/local/bin/bash. While you're already editing the file, go ahead and change root's unofficial name 'Charlie &' to 'Super-User' or any other name that envisions Superman, etc. When you get mail from root (e.g. from the cron jobs that run every night), it'll now be maked as coming from 'Super-User' and not 'Charlie &'...just a little bit nicer. Save & exit.
    2. Create a .bashrc file in root's home directory (/root) and enter the following items (as a starting point).  After the file has been created, chmod 600 on it so that it's only readable & writable by root.
      umask 077
      PS1="[\u@\h \W]\\$ "
      alias ls='ls -alFG'
    3. Create a .bash_profile file in root's home directory and enter the following items (as a starting point).  After the file has been created, chmod 600 on it so that it's only readable & writable by root.
      PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin:$HOME/bin; export PATH
      umask 077
      PS1="[\u@\h \W]\\$ "
      alias ls='ls -alFG'
    4. Test your settings by logging out & logging back in again.  Verify that you're using the bash shell, your cursor line looks different (i.e. it has your userid & current working directory), and that you get colorized directory listings.
  2. Redirect root's email to your "normal" email account so that it doesn't get backed up the firewall
    1. Use vim to open the /etc/aliases file for editing.
    2. Modify line that says "# root: me@my.domain" by removing the "#" comment at the beginning of the line, and then modifying the "me@my.domain" email address so that it points to your "normal" email address instead.
    3. After saving & exiting, then run the command "newaliases" from the command prompt to update the email alias database. 
  3. Add an entry for our host in its own hosts table (so you don't get those annoying sendmail error messages).
    1. Use vim to open the /etc/hosts file for editing.
    2. After line 11 (the one with the entry for, add a new line similar to this one (except change the IP address and hostname to the "real" ones you're using):      firewall
    3. Save & exit.
  4. Create & install a warning banner.  Use vim to replace your /etc/motd file with the following text (or some other equivalent legal disclaimer):
    * * * * * * * * * * * * W A R N I N G * * * * * * * * * * * * *
    * * * * * * * * * * * * W A R N I N G * * * * * * * * * * * * *
  5. Configure cvsup and update your ports collection. Note: after you configure cvsup and update your ports collection, you might want to re-run 'cvsup /etc/ports-supfile' every once in a while to ensure your ports collection is up-to-date (in case you want to install any new software).
    [root@numa /root]# cp /usr/share/examples/cvsup/ports-supfile /etc
    [root@numa /root]# vi /etc/ports-supfile
    - Change line 51 of the file so that it reads '*default'
    [root@numa /root]# cvsup /etc/ports-supfile
  6. Modify /etc/inetd.conf so that every line is commented out...just in case it gets accidentally turned on later.  More specifically, the following 6 lines are enabled in a default install of inetd.  Comment them out by placing a "#" at the beginning of each line.  There's no need to kill & restart inetd after your done...because you're not running inetd.  We're only doing this just in case it accidentally gets turned on later.
    ftp     stream  tcp  nowait root    /usr/libexec/ftpd    ftpd=20 -l
    telnet  stream  tcp  nowait root    /usr/libexec/telnetd telnetd
    comsat  dgram   udp  wait   tty:tty /usr/libexec/comsat  comsat
    ntalk   dgram   udp  wait   tty:tty /usr/libexec/ntalkd  ntalkd
    ftp     stream  tcp6 nowait root    /usr/libexec/ftpd    ftpd -l
    telnet  stream  tcp6 nowait root    /usr/libexec/telnetd telnetd
  7. Modify the SSH daemon configuration file, /etc/ssh/sshd_config, so that it reads as follows. The comments in the file explain what we're locking down and why.
    # This is ssh server systemwide configuration file.
    # Listen on port 22, the standard
    Port 22
    # Support SSH Protocol 1 only (SSH 1.X baseline), which means RSA
    # keys are used
    Protocol 1
    # Listen on your internal network's address only so that hackers
    # from the internet can't access the SSH daemon on your box and
    # try to log on. Note that you'll have to change to
    # whatever IP address your internal NIC has.
    # Standard settings for a bunch of stuff...HostKey, ServerKeyBits,
    # LoginGraceTime, etc.
    HostKey /etc/ssh/ssh_host_key
    ServerKeyBits 768
    LoginGraceTime 120
    KeyRegenerationInterval 3600
    StrictModes yes
    PrintMotd yes
    KeepAlive yes
    CheckMail no
    # Permit 'root' login...that's the only account we have on this
    # box anyway
    PermitRootLogin yes
    # After 10 unauthenticated connections, refuse 30% of the new
    # ones, and refuse any more than 60 total.
    MaxStartups 10:30:60
    # Don't read ~/.rhosts and ~/.shosts files
    IgnoreRhosts yes
    # Don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
    IgnoreUserKnownHosts yes
    # Disable X11 forwarding...we're not even running X on our firewall
    X11Forwarding no
    # Implement severe logging...potentially invasive, but we're the
    # only authorized users & we do have a legal warning banner,
    # so everyone's been warned....
    SyslogFacility AUTH
    LogLevel DEBUG
    # Set up SSHD so that you must have a RSA key in root's
    # authorized_keys file to successfully log in. No Rhosts, no
    # PasswordAuthentication, etc.
    RhostsAuthentication no
    RhostsRSAAuthentication no
    RSAAuthentication yes
    PasswordAuthentication no
    PermitEmptyPasswords no
    UseLogin no
  8. Open up your /etc/hosts.allow file, delete all of the lines, and ensure that it reads as follows.  Note that is the address space of your internal network in this example.  If you're using a different internal address space (e.g., then make the appropriate modifications. 
    # hosts.allow access control file for "tcp wrapped" applications.
    ALL : localhost : allow
    sshd : : allow
    ALL : ALL : deny
  9. Install and configure Tripwire
    1. First, install gmake from the FreeBSD ports collection:
      [root@numa /root]# cd /usr/ports/devel/gmake
      [root@numa gmake]# make && make install
    2. Download Tripwire-2.3.1-2 from If a new version exists, then use it instead. The configuration changes itemized, below, should remain consistent between versions of Tripwire. If not, please email me with suggested corrections.
      [root@numa gmake]# cd /root
      [root@numa /root]# lynx
      - Use the down-arrow to move through the hyperlinks until the file, tripwire-2.3.1-2.tar.gz, is highlighted, then press [Enter]
      - When asked if you want to D)ownload the file, or C)ancel, hit 'd'
      - ...file downloads...
      - After the file downloads, you'll be presented with lynx's Download Options screen. The 'Save to disk' hyperlink is automatically highlighted in red, so just hit [Enter].
      - Either accept the original filename by pressing [Enter], or modify the filename then hit [Enter] to save it.
      - After the file is saved, press 'q' to quit lynx.
      [root@numa /root]# gunzip tripwire-2.3.1-2.tar.gz
      [root@numa /root]# tar xvf tripwire-2.3.1-2.tar
    3. Modify the Makefile so that it will compile for FreeBSD
      [root@numa /root]# cd tripwire-2.3.1-2/src
      [root@numa src]# vim Makefile
      - Add a comment at the beginning of line 82 (SYSPRE =3D i686-pc-linux)
      - Remove the '#' comment delimeter at the beginning of line 84 (SYSPRE = i386-unknown-freebsd)
      - Save and exit.
    4. Compile Tripwire. On a P-200MMX, this takes almost an hour.
      [root@numa src]# gmake release
    5. Configure and install Tripwire
      1. Open Tripwire's installation configuration file using vim, and edit it as follows
        [root@numa src]# cd ../install/
        [root@numa install]# vim install.cfg
        - Change line 27 so that it reads 'TWBIN="/usr/local/sbin"'
        - Change line 30 so that it reads 'TWPOLICY="/usr/local/etc/tripwire"'
        - Change line 33 so that it reads 'TWMAN="/usr/share/man"'
        - Change line 36 so that it reads 'TWDB="/usr/local/lib/tripwire"'
        - Change line 39 so that it reads 'TWDOCS="/usr/share/doc/tripwire"'
        - Change line 51 so that it reads 'TWEDITOR="/usr/local/bin/vim"'
        - Change line 88 so that it reads 'TWMAILPROGRAM="/usr/sbin/sendmail -oi -t"'
        - Save and exit.
      2. Open Tripwire's installation script using vim, and edit it as follows
        [root@numa install]# vim
        - Change line 319 so that it reads 'EULA_PATH="../$TWLICENSEFILE"'
        - Change line 491 so that it reads 'BIN_DIR="../bin/i386-unknown-freebsd_r"'
        - Change lines 621-638 so that they read as follows:
            f1=' ff=$README ; d="/.." ; dd=$TWDOCS ; rr=0444 '
            f2=' ff=$REL_NOTES ; d="/.." ; dd=$TWDOCS ; rr=0444 '
            f3=' ff=$TWLICENSEFILE ; d="/.." ; dd=$TWDOCS ; rr=0444 '
            f4=' ff=tripwire ; d="/../bin/i386-unknown-freebsd_r" ; dd=$TWBIN ; rr=0550 '
            f5=' ff=twadmin ; d="/../bin/i386-unknown-freebsd_r" ; dd=$TWBIN ; rr=0550 '
            f6=' ff=twprint ; d="/../bin/i386-unknown-freebsd_r" ; dd=$TWBIN ; rr=0550 '
            f7=' ff=siggen ; d="/../bin/i386-unknown-freebsd_r" ; dd=$TWBIN ; rr=0550 '
            f8=' ff=TRADEMARK ; d="/.." ; dd=$TWDOCS ; rr=0444 '
            f9=' ff=policyguide.txt ; d="/../policy" ; dd=$TWDOCS ; rr=0444 '
            f10=' ff=twpol.txt ; d="/../policy" ; dd=$TWPOLICY ; rr=0640 '
            f11=' ff=twpolicy.4 ; d="/../man/man4" ; dd=$TWMAN/man4 ; rr=0444 '
            f12=' ff=twconfig.4 ; d="/../man/man4" ; dd=$TWMAN/man4 ; rr=0444 '
            f13=' ff=twfiles.5 ; d="/../man/man5" ; dd=$TWMAN/man5 ; rr=0444 '
            f14=' ff=siggen.8 ; d="/../man/man8" ; dd=$TWMAN/man8 ; rr=0444 '
            f15=' ff=tripwire.8 ; d="/../man/man8" ; dd=$TWMAN/man8 ; rr=0444 '
            f16=' ff=twadmin.8 ; d="/../man/man8" ; dd=$TWMAN/man8 ; rr=0444 '
            f17=' ff=twintro.8 ; d="/../man/man8" ; dd=$TWMAN/man8 ; rr=0444 '
            f18=' ff=twprint.8 ; d="/../man/man8" ; dd=$TWMAN/man8 ; rr=0444 '
        - Save and exit.
      3. Install Tripwire
        [root@numa install]# ./
        - Answer 'y' to continue with the installation
        - Press [Enter] to view the license agreement...when complete, type 'accept' and [Enter]
        - The install script will verify that sendmail and vim are installed, then verify that the tripwire binaries are available, and then echo back all of the configuration parameters for the installation script (e.g. TWBIN, TWMAN, etc.). If everything looks good, answer 'y' to continue with the installation.
        - The install script copies all of the files, the asks you to enter a new site keyfile passphrase. Enter it, and then enter it again when asked to verify it.
        - The install script then asks you to enter a new local keyfile passphrase. Enter it, and then enter it again when asked to verify it.
        - The install script will then create a signed configuration file, but will need you to enter the site passphrase you just set, above. Enter it.
        - The install script will then create a signed policy file, but will need you to enter the site passphrase you just set, above. Enter it.
        - ...installation is complete.
    6. Install a new Tripwire text policy file. Replace the contents of the file /usr/local/etc/tripwire/twpol.txt with the following. It is a functional Tripwire policy configuration file for FreeBSD-4.2, but feel free to edit it based upon your own special installation & configuration.:
      @@section GLOBAL

      @@section FS
      SEC_CRIT = $(IgnoreNone)-SHa; # Critical files - we can't afford to miss any changes.
      SEC_SUID = $(IgnoreNone)-SHa; # Binaries with the SUID or SGID flags set.
      SEC_TCB = $(ReadOnly); # Members of the Trusted Computing Base.
      SEC_BIN = $(ReadOnly); # Binaries that shouldn't change
      SEC_CONFIG = $(Dynamic); # Config files that are changed infrequently but accessed often.
      SEC_LOG = $(Growing); # Files that grow, but that should never change ownership.
      SEC_INVARIANT = +pug; # Directories that should never change permission or ownership.
      SIG_LOW = 33; # Non-critical files that are of minimal security impact
      SIG_MED = 66; # Non-critical files that are of significant security impact
      SIG_HI = 100; # Critical files that are significant points of vulnerability

      # Tripwire Binaries
      (rulename = "Tripwire Binaries", severity = $(SIG_HI))
        $(TWBIN)/siggen -> $(SEC_TCB);
        $(TWBIN)/tripwire -> $(SEC_TCB);
        $(TWBIN)/twadmin -> $(SEC_TCB);
        $(TWBIN)/twprint -> $(SEC_TCB);

      # Tripwire Data Files - Configuration Files, Policy Files, Keys, Reports, Databases
      (rulename = "Tripwire Data Files", severity = $(SIG_HI))
        # NOTE: Removing the inode attribute because when Tripwire creates a backup
        # it does so by renaming the old file and creating a new one (which will
        # have a new inode number). Leaving inode turned on for keys, which shouldn't
        # ever change.

        # NOTE: this rule will trigger on the first integrity check after database
        # initialization, and each integrity check afterward until a database update
        # is run, since the database file will not exist before that point.
        $(TWDB) -> $(SEC_CONFIG) -i;
        $(TWPOL)/tw.pol -> $(SEC_BIN) -i;
        $(TWPOL)/tw.cfg -> $(SEC_BIN) -i;
        $(TWLKEY)/$(HOSTNAME)-local.key -> $(SEC_BIN);
        $(TWSKEY)/site.key -> $(SEC_BIN);

        #don't scan the individual reports
        $(TWREPORT) -> $(SEC_CONFIG) (recurse=0);

      # These files are critical to a correct system boot.
      (rulename = "Critical system boot files", severity = 100)
        /boot -> $(SEC_CRIT);
        /kernel -> $(SEC_CRIT);

      # These files change the behavior of the root account
      (rulename = "Root config files", severity = 100)
        /root -> $(SEC_CRIT);
        /root/.bash_history -> $(SEC_LOG);
        /root/.bash_profile -> $(SEC_CRIT);
        /root/.bashrc -> $(SEC_CRIT);
        /root/.ssh/authorized_keys -> $(SEC_CRIT);

      # Commonly accessed directories that should remain static with regards to owner and group
      (rulename = "Invariant Directories", severity = $(SIG_MED))
        / -> $(SEC_INVARIANT) (recurse = 0);
        /etc -> $(SEC_INVARIANT) (recurse = 0);
        /usr/local/etc -> $(SEC_INVARIANT) (recurse = 0);

      (rulename = "Shell Binaries", severity = $(SIG_HI))
        /usr/local/bin/bash -> $(SEC_BIN);
        /bin/csh -> $(SEC_BIN);
        /bin/sh -> $(SEC_BIN);
        /bin/tcsh -> $(SEC_BIN);

      # Rest of critical system binaries
      (rulename = "OS executables and libraries", severity = $(SIG_HI))
        /bin -> $(SEC_BIN) (recurse = 1);
        /usr/bin -> $(SEC_BIN) (recurse = 1);
        /usr/lib -> $(SEC_BIN) (recurse = 1);
        /sbin -> $(SEC_BIN) (recurse = 1);
        /usr/sbin -> $(SEC_BIN) (recurse = 1);

      # Local files
      (rulename = "User executables and libraries", severity = $(SIG_MED))
        /usr/local/bin -> $(SEC_BIN) (recurse = 1);
        /usr/local/sbin -> $(SEC_BIN) (recurse = 1);

      # Temporary directories
      (rulename = "Temporary directories", recurse = false, severity = $(SIG_LOW))
        /usr/tmp -> $(SEC_INVARIANT);
        /var/tmp -> $(SEC_INVARIANT);
        /tmp -> $(SEC_INVARIANT);

      # Include
      (rulename = "OS Development Files", severity = $(SIG_MED))
        /usr/include -> $(SEC_BIN);
        /usr/local/include -> $(SEC_BIN);

      # Shared
      (rulename = "OS Shared Files", severity = $(SIG_MED))
        /usr/share -> $(SEC_BIN);
        /usr/local/share -> $(SEC_BIN);

      # setuid/setgid root programs
      (rulename = "setuid/setgid", severity = $(SIG_HI))
        /bin/df -> $(SEC_SUID);
        /bin/rcp -> $(SEC_SUID);
        /sbin/ccdconfig -> $(SEC_SUID);
        /sbin/dmesg -> $(SEC_SUID);
        /sbin/dump -> $(SEC_SUID);
        /sbin/ping -> $(SEC_SUID);
        /sbin/ping6 -> $(SEC_SUID);
        /sbin/rdump -> $(SEC_SUID);
        /sbin/restore -> $(SEC_SUID);
        /sbin/route -> $(SEC_SUID);
        /sbin/rrestore -> $(SEC_SUID);
        /sbin/shutdown -> $(SEC_SUID);
        /usr/bin/at -> $(SEC_SUID);
        /usr/bin/atq -> $(SEC_SUID);
        /usr/bin/atrm -> $(SEC_SUID);
        /usr/bin/batch -> $(SEC_SUID);
        /usr/bin/chfn -> $(SEC_SUID);
        /usr/bin/chpass -> $(SEC_SUID);
        /usr/bin/chsh -> $(SEC_SUID);
        /usr/bin/crontab -> $(SEC_SUID);
        /usr/bin/cu -> $(SEC_SUID);
        /usr/bin/fstat -> $(SEC_SUID);
        /usr/bin/ipcs -> $(SEC_SUID);
        /usr/bin/keyinfo -> $(SEC_SUID);
        /usr/bin/keyinit -> $(SEC_SUID);
        /usr/bin/lock -> $(SEC_SUID);
        /usr/bin/login -> $(SEC_SUID);
        /usr/bin/lpq -> $(SEC_SUID);
        /usr/bin/lpr -> $(SEC_SUID);
        /usr/bin/lprm -> $(SEC_SUID);
        /usr/bin/man -> $(SEC_SUID);
        /usr/bin/netstat -> $(SEC_SUID);
        /usr/bin/nfsstat -> $(SEC_SUID);
        /usr/bin/passwd -> $(SEC_SUID);
        /usr/bin/quota -> $(SEC_SUID);
        /usr/bin/rlogin -> $(SEC_SUID);
        /usr/bin/rsh -> $(SEC_SUID);
        /usr/bin/su -> $(SEC_SUID);
        /usr/bin/systat -> $(SEC_SUID);
        /usr/bin/top -> $(SEC_SUID);
        /usr/bin/uucp -> $(SEC_SUID);
        /usr/bin/uuname -> $(SEC_SUID);
        /usr/bin/uustat -> $(SEC_SUID);
        /usr/bin/uux -> $(SEC_SUID);
        /usr/bin/vmstat -> $(SEC_SUID);
        /usr/bin/wall -> $(SEC_SUID);
        /usr/bin/write -> $(SEC_SUID);
        /usr/bin/ypchfn -> $(SEC_SUID);
        /usr/bin/ypchpass -> $(SEC_SUID);
        /usr/bin/ypchsh -> $(SEC_SUID);
        /usr/bin/yppasswd -> $(SEC_SUID);
        /usr/libexec/sendmail/sendmail -> $(SEC_SUID);
        /usr/libexec/uucp/uucico -> $(SEC_SUID);
        /usr/libexec/uucp/uuxqt -> $(SEC_SUID);
        /usr/local/bin/elm -> $(SEC_SUID);
        /usr/local/bin/mutt_dotlock -> $(SEC_SUID);
        /usr/sbin/ifmcstat -> $(SEC_SUID);
        /usr/sbin/iostat -> $(SEC_SUID);
        /usr/sbin/lpc -> $(SEC_SUID);
        /usr/sbin/mrinfo -> $(SEC_SUID);
        /usr/sbin/mtrace -> $(SEC_SUID);
        /usr/sbin/ppp -> $(SEC_SUID);
        /usr/sbin/pppd -> $(SEC_SUID);
        /usr/sbin/pstat -> $(SEC_SUID);
        /usr/sbin/sliplogin -> $(SEC_SUID);
        /usr/sbin/swapinfo -> $(SEC_SUID);
        /usr/sbin/timedc -> $(SEC_SUID);
        /usr/sbin/traceroute -> $(SEC_SUID);
        /usr/sbin/traceroute6 -> $(SEC_SUID);
        /usr/sbin/trpt -> $(SEC_SUID);

      (rulename = "Configuration Files", severity = $(SIG_MED))
        /etc/hosts -> $(SEC_CONFIG);
        /etc/inetd.conf -> $(SEC_CONFIG);
        /etc/resolv.conf -> $(SEC_CONFIG);
        /etc/syslog.conf -> $(SEC_CONFIG);
        /etc/newsyslog.conf -> $(SEC_CONFIG);

      (rulename = "Security Control", severity = $(SIG_HI))
        /etc/group -> $(SEC_CRIT);
        /etc/security/ -> $(SEC_CRIT);

      (rulename = "Login Scripts", severity =3D $(SIG_HI))
        /etc/csh.login -> $(SEC_CONFIG);
        /etc/csh.logout -> $(SEC_CONFIG);
        /etc/csh.cshrc -> $(SEC_CONFIG);
        /etc/profile -> $(SEC_CONFIG);

      # These files change every time the system boots
      (rulename = "System boot changes", severity = $(SIG_HI))
        /dev/log -> $(Dynamic);
        /dev/cuaa0 -> $(Dynamic);
        /dev/console -> $(Dynamic);
        /dev/ttyv0 -> $(Dynamic);
        /dev/ttyv1 -> $(Dynamic);
        /dev/ttyv2 -> $(Dynamic);
        /dev/ttyv3 -> $(Dynamic);
        /dev/ttyv4 -> $(Dynamic);
        /dev/ttyv5 -> $(Dynamic);
        /dev/ttyv6 -> $(Dynamic);
        /dev/ttyp0 -> $(Dynamic);
        /dev/ttyp1 -> $(Dynamic);
        /dev/ttyp2 -> $(Dynamic);
        /dev/ttyp3 -> $(Dynamic);
        /dev/ttyp4 -> $(Dynamic);
        /dev/ttyp5 -> $(Dynamic);
        /dev/ttyp6 -> $(Dynamic);
        /dev/urandom -> $(Dynamic);
        /var/run -> $(Dynamic);
        /var/log -> $(Dynamic);

      # Critical configuration files
      (rulename = "Critical configuration files", severity = $(SIG_HI))
        /etc/crontab -> $(ReadOnly);
        /etc/periodic/daily -> $(ReadOnly);
        /etc/periodic/weekly -> $(ReadOnly);
        /etc/periodic/monthly -> $(ReadOnly);
        /etc/defaults -> $(ReadOnly);
        /etc/fstab -> $(ReadOnly);
        /etc/hosts.allow -> $(ReadOnly);
        /etc/ttys -> $(ReadOnly);
        /etc/gettytab -> $(ReadOnly);
        /etc/protocols -> $(ReadOnly);
        /etc/services -> $(ReadOnly);
        /etc/rc -> $(ReadOnly);
        /etc/rc.conf -> $(ReadOnly);
        /etc/rc.atm -> $(ReadOnly);
        /etc/rc.devfs -> $(ReadOnly);
        /etc/rc.diskless1 -> $(ReadOnly);
        /etc/rc.diskless2 -> $(ReadOnly);
        /etc/rc.firewall -> $(ReadOnly);
        /etc/rc.firewall6 -> $(ReadOnly);
        /etc/rc.i386 -> $(ReadOnly);
        /etc/rc.isdn -> $(ReadOnly);
        /etc/ -> $(ReadOnly);
        /etc/rc.network6 -> $(ReadOnly);
        /etc/rc.pccard -> $(ReadOnly);
        /etc/rc.resume -> $(ReadOnly);
        /etc/rc.serial -> $(ReadOnly);
        /etc/rc.shutdown -> $(ReadOnly);
        /etc/rc.suspend -> $(ReadOnly);
        /etc/rc.syscons -> $(ReadOnly);
        /etc/rc.sysctl -> $(ReadOnly);
        /etc/motd -> $(ReadOnly);
        /etc/passwd -> $(ReadOnly);
        /etc/master.passwd -> $(ReadOnly);
        /etc/pwd.db -> $(ReadOnly);
        /etc/spwd.db -> $(ReadOnly);
        /etc/rpc -> $(ReadOnly);
        /etc/shells -> $(ReadOnly);
        /etc/ipf.rules -> $(ReadOnly);
        /etc/ipnat.rules -> $(ReadOnly);
        /etc/ssh/sshd_config -> $(ReadOnly);

      # Critical devices
      (rulename = "Critical devices", severity = $(SIG_HI), recurse = false)
        /dev/kmem -> $(Device);
        /dev/mem -> $(Device);
        /dev/null -> $(Device);
        /dev/zero -> $(Device);
    7. Re-generate the Tripwire policy file and database - Now that you have a good Tripwire text policy file, we need to actually create the policy file from it and the Tripwire database itself. To do that, just type the following commands:
      [root@numa /root]# twadmin --create-polfile /usr/local/etc/tripwire/twpol.txt
      [root@numa /root]# tripwire --init
    8. Create a cron job to check the integrity of your system every day at 4AM:
      [root@numa /root]# cd /etc
      [root@numa /etc]# vi crontab
      - Add the following line to the file:
         0   4   *   *   *    root   /usr/local/bin/tripwire --check
    9. In the future, when you want to interactively check your system for changes, and then incorporate those changes into the Tripwire database, run the following command. After you run it, you'll be presented with a report of the policy violations. Edit it with vim, leaving an 'x' next to each policy violation that you want the database updated with. Then edit & save.
      [root@numa /root]# tripwire --check --interactive
  10. Edit your /etc/rc.conf file and make the following changes.
    1. Miscellaneous cleanup.  Towards the middle of the file, there's a line that reads 'sshd_enable="NO"', yet at the bottom of the file, there's a line that reads 'sshd_enable="YES"'.  Delete the first one (the one that says "NO") and keep the one that says "YES"
    2. Add the following lines at the bottom of the file to support 132x43 screen resolution (after we compile VESA support into the kernel, below).
    3. Add the following line at the bottom of the file so that syslog won't log to remote machines, nor will it accept logs from remote machines. This still allows syslog to function, but stops it from being a 'listening' service.
    4. Add the following line at the bottom of the file so that SSHD only listens for IPv4 addresses
    5. Add the following lines at the bottom of the file so that IPFILTER, IPNAT, and IPMON will work correctly after we compile support for it into the kernel and create the appropriate files, below. Note, the options for ipmon perform the following - D causes it to run as a daemon, s tells it to log to syslog rather than a file, v tells it to log the tcp window, ack and sequence fields, and n tells it to map the IP addresses and port numbers back to hostnames and service names.
    6. Modify the following line so that your 2nd ISA network card is a valid network interface.  Note, we're adding "ed1" to the 'network_interfaces' line that was already there.
      network_interfaces="ed0 ed1 lo0"
    7. Add the following line so that your new 2nd ISA network card is configured correctly.  Again, we're assuming that you're using as the internal network:
      ifconfig_ed1="inet netmask"
  11. Set up a separate logfile so that our firewall logs are separated from the rest of the standard syslog events.
    1. Create a new file for the firewall logs with the following commands:
      [root@numa /root]# touch /var/log/firewall_logs
      [root@numa /root]# chmod 600 /var/log/firewall_logs
    2. Modify your syslog configuration file (/etc/syslog.conf) so that the IPFILTER logged events (logged with IPMON) are sent to your new separate firewall log file.  Insert the following line at the top of the file:
      local0.*      /var/log/firewall_logs
    3. Modify your newsyslog configuration file (/etc/newsyslog.conf) so that your new firewall log files get rotated just like the primary syslog file (/var/log/messages). Add the following new line to the bottom of the file:
      /var/log/firewall_logs    600   5   100   *   Z
  12. Create your IPFILTER and IPNAT rulesets
    1. Using vim, create a new IPFILTER firewall ruleset, /etc/ipf.rules, & add the following lines to it. Note: The assumption is that ed0 is the "outside" interface (i.e. connected to your ISP), and ed1 is the "inside" interface (i.e. connected to your internal network).  Also note that we're not performing egress filtering here.  We're blocking all inbound packets from the internet and allowing all internal network packets out (and keeping state on them so that they're allowed back in).  After your box is configured to your liking, I heavily recommend implementing egress filtering. And as a final note, since we're using IPFILTER's stateful packet inspection abilities, we don't need to reject traffic spoofing non-routable or reserved addresses...they'll be blocked automatically since they don't match a corresponding packet in the state table. Use this IPFILTER ruleset as a starting point. After you have everything running, add in whatever you want (egress filtering, protection from non-routable addresses, IP spoofing protection, etc.) to complete the job. This is only a starting point.
      # Outside Interface

      # Allow out all TCP, UDP, and ICMP traffic & keep state on it
      # so that it's allowed back in.
      pass out quick on ed0 proto tcp from any to any keep state
      pass out quick on ed0 proto udp from any to any keep state
      pass out quick on ed0 proto icmp from any to any keep state
      block out quick on ed0 all

      # Allow bootp traffic in from your ISP's DHCP server only.
      # Replace X.X.X.X/32 with your ISP's DHCP server address.
      pass in quick on ed0 proto udp from X.X.X.X/32 to any port = 68 keep state

      # Block and log all remaining traffic coming into the firewall
      # - Block TCP with a RST (to make it appear as if the service
      # isn't listening)
      # - Block UDP with an ICMP Port Unreachable (to make it appear
      # as if the service isn't listening)
      # - Block all remaining traffic the good 'ol fashioned way
      block return-rst in log quick on ed0 proto tcp from any to any
      block return-icmp-as-dest(port-unr) in log quick on ed0 proto udp from any to any
      block in log quick on ed0 all

      # Inside Interface

      # Allow out all TCP, UDP, and ICMP traffic & keep state
      pass out quick on ed1 proto tcp from any to any keep state
      pass out quick on ed1 proto udp from any to any keep state
      pass out quick on ed1 proto icmp from any to any keep state

      # Allow in all TCP, UDP, and ICMP traffic & keep state
      pass in quick on ed1 proto tcp from any to any keep state
      pass in quick on ed1 proto udp from any to any keep state
      pass in quick on ed1 proto icmp from any to any keep state
    2. Using vim, create a new IPNAT translation ruleset, /etc/ipnat.rules, & add the following lines to it.  This single line will take all packets going out on your external NIC (ed0) that have a source address coming from your internal network (, and translates it to whatever IP address your external NIC happens to have at that time (translating it to "0/32" is the way IPNAT does it).
      map ed0 -> 0/32
  13. Compile your kernel for IPFILTER support, VESA support, and your 2nd ISA Ethernet card
    1. Change directory into /usr/src/sys/i386/conf
      cd /usr/src/sys/i386/conf
    2. Copy the file GENERIC to a new file - typically named after your hostname (I'll assume that your hostname is "FIREWALL")
    3. Using vim, edit your new file, FIREWALL, and make the following changes:
      1. In line 2 of the file (part of the main comment block) change the word, GENERIC, to your hostname, FIREWALL.
      2. On line 18 of the file (still part of the main comment block), change the word, GENERIC, to your hostname, FIREWALL
      3. On lines 21-24, comment out the "cpu" lines so that only the one for your specific chip is left. For a Pentium MMX, I commented out all of them except line 23 - cpu "I586_CPU"
      4. On line 25, change the value of the ident parameter so that it's your hostname, FIREWALL
      5. Starting at about line 32 (right after the line with "options INET"), add the following 3 new lines to add IPFILTER support into your firewall and have it automatically block all packets by default:
        options IPFILTER
        options IPFILTER_LOG
      6. At about line 130 (immediately following the line "device vga0 at isa?"), add the following line to add support for VESA video modes (for 132x43 resolution):
        options VESA
      7. At about line 194, (in the networking portion...right after the title "ISA Ethernet NICs" and after the line with "device ed0 at isa? port 0x280 irq 10 iomem 0xd8000"), add the following new line to add support for a 2nd generic NE2000-compatible ISA Ethernet card.  Pre-configure the IRQ and port to whatever your 2nd ISA Ethernet card is configured to (to save you a little time after you reboot):
        device ed1 at isa? port 0x280 irq 10 iomem 0xd8000
    4. After saving your kernel configuration file, FIREWALL, type the following commands in this order to compile your kernel (assuming the name is "FIREWALL"), install it, and then reboot your system.  Note that you're starting this sequence while you're still in the /usr/src/sys/i386/conf directory.  With my old 200MHz Pentium MMX (overclocked to 225MHz) with 96MB or RAM, the "make depend" command took 5 minutes; the "make" command took 25 minutes; and the "make install" command took less than a minute.
      [root@numa conf]# /usr/sbin/config -g FIREWALL
      [root@numa conf]# cd ../../compile/FIREWALL
      [root@numa FIREWALL]# make depend
      [root@numa FIREWALL]# make
      [root@numa FIREWALL]# make install
      [root@numa FIREWALL]# shutdown -r now
  14. As the system is rebooting, go through the kernel reconfiguration process if you need to configure your 2nd generic NE2000-compatible ISA Ethernet Card.  
    1. To do this, interrupt the boot process by hitting the [space bar] when you see the following:
      Hit [Enter] to boot immediately, or any other key for command prompt.
      Booting [kernel] in 9 seconds...
    2. Then, at the '>' prompt, type 'boot -c' and hit [Enter].  This will boot into the Kernel Configuration Utility
    3. Then, at the next '>' prompt (usually "'config>") type 'v' and hit [Enter] to start the Kernel Configuration Utility in visual mode.  This will drop you off at the step 6 of the first section of this document (Installing FreeBSD-STABLE).  This time, you'll see support for your 2nd Ethernet card, ed1.  Configure the IRQ and Port, then save and continue booting
  15. After the system boots, you should have a completely working firewall.  Make sure you add a copy of your public SSH key to the /root/.ssh/authorized_keys file so that you can SSH to your firewall to perform maintenance. And since you modified the authorized_keys file, you'll need to update your tripwire database, as well, so you don't get notified of policy violations.
Questions or Comments? E-mail: Marty Schlacter