Building a Firewall with OpenBSD

$Revision: 1.3 $ $Date: 2003/08/24 02:26:32 $

Overview

This is an updated version of a document I have been using since the OpenBSD 2.3 days. Back then, we used to sell a managed firewall product that we called a Greenbox. This document started life as our own standardized build instructions for the firewall, and gradually grew into something we would ship off to clients who didn't want to pay us to do their dirty work. I still find it useful, so every couple of releases, I try and update it.

The siteXX.tgz mechanism

This document contains instructions for installing and configuring an OpenBSD firewall machine manually. Though I try to keep these instructions up to date, I rarely do manual configurations anymore. Instead, I tend to build custom installation CDs using the siteXX.tgz mechanism.

Firewall configuration

  1. Boot OpenBSD (or Greenbox) CD and Follow instructions in CD Sleeve.
  2. If the machine is not being used as a mail drop, the following settings are recommended

    a    /         64M
    b    swap      128M
    d    /tmp      128M
    e    /var      300M
    f    /usr      500M
    g    /var/log  300M
    h    /home     200M
    

    If you are including a source tree (and compiler), then add the following

    g    /usr/src  800M
    j    /usr/obj  800M
    

    Set the system host name to:
    shortname + 'fw'
    eg. pintday.org = pdofw

  3. Configure NIC cards. Name the inside card MachineName + -good, and the outside one MachineName + -evil. If there is a DMZ, it gets -dmz
    eg.

         pdofw-good
         pdofw-evil
    
  4. Use a strong password for root. Use lower case, upper case, symbols, and numbers. 8 char. Note this password and store in fireproof safe (i.e. follow local strong password recovery procedures)
  5. Select Packages:
    • base33 (Core Operating System)
    • etc33 (Default config files)
    • bsd (GENERIC Kernel)
    • man33 (Man Pages)
    • comp33 (if compiling is going to be necessary)
    • site33 (site-specific files - only for customized builds)
  6. After Install, reboot.
  7. If the firewall will be tracking -stable, then update to the latest -stable sources:

      # export CVSROOT=anoncvs@anoncvs.ca.openbsd.org:/cvs
      # export CVS_RSH=/usr/bin/ssh
      # cd /usr
      # cvs -q get -rOPENBSD_3_3 -P src
    

    Note: If your CD has the source tree on it, it will be slightly faster to untar the source, then update via cvs:

      # cd /
      # tar xzf /mnt/3.3/src.tar.gz
      # tar xzf /mnt/3.3/srcsys.tar.gz
      # cd /usr/src
      # cvs -q up -rOPENBSD_3_3 -Pd
    

    Once the latest source is available, upgrade as usual.

  8. Do a man afterboot. Read carefully.
  9. Check datetime (date command). Ensure rdate is configured properly on boot. i.e. Edit rc.conf to contain:

      rdate_flags="-ancv ntp.cpsc.ucalgary.ca"
    

    NOTE: For this to work correctly, you probably want to amend the default (restrictive) firewall rules in /etc/rc to pass SNTP traffic. i.e.

    if [ "X${pf}" != X"NO" ]; then
            RULES="block in all\nblock out all"
            RULES="$RULES\npass in proto tcp from any to any port 22 keep state"
            RULES="$RULES\npass out proto udp from any to any port 123 keep state" 
            case `sysctl vfs.mounts.nfs 2>/dev/null` in
            *[1-9]*)
                    # don't kill NFS
                    RULES="$RULES\npass in proto udp from any port { 111, 2049 } to any"
                    RULES="$RULES\npass out proto udp from any to any port { 111, 2049 }"
                    ;;
            esac
            echo $RULES | pfctl -f - -e
    fi
    

    To keep the time in sync while the firewall is running, add the following to root's crontab:

    # Sync date/time with NTP server
    0       0       *       *       *       /usr/sbin/rdate -ncva ntp.cpsc.ucalgary.ca | logger -t NTP
    
  10. edit /etc/myname. Make sure it is right (pdofw).
  11. edit /etc/mygate. Make sure it is your default gateway.
  12. edit /etc/hostname.{interfacename} for each card
    e.g. hostname.fxp0
  13. edit /etc/hosts. Add MachineName-good and -evil entries. Make sure the canonical name is there, and attached to the loopback interface. If you have a mail gateway, put it in there as well. eg.

    127.0.0.1        localhost    pdofw   pdofw.pintday.org
    207.228.105.254  pdofw-evil pdofw-evil.pintday.org
    10.1.1.1        pdofw-good pdofw-good.pintday.org
    10.2.2.2        mailgw.pintday.org
    
  14. Edit /etc/sysctl.conf. uncomment the following values:

    net.inet.ip.forwarding=1
    net.inet.esp.enable=1       # Default for most modern OpenBSD versions
    machdep.kbdreset=1
    ddb.panic=0
    vm.swapencrypt=1
    
  15. Create a file called /etc/myroutes. In this file, place all the inside networks that the firewall will be responsible for routing to. Put them in the form:
    gateway network/netmask
    

    eg.

    10.0.0.1 10.0.3.0/24
    10.0.0.1 10.0.5.0/25
    
  16. Load up /etc/netstart in vi. Add the following code to handle the static routes contained in /etc/myroutes:
    # Add static routes as required.
    # file format is:
    #  gw network/mask
    if [ -f /etc/myroutes ]; then
    {
    	set -- `sed -e 's/#.*$//' /etc/myroutes |grep -v '^$'`
    	while [ $# -ge 2 ]; do
    		route -n add -net $2 $1
    		shift 2
    	done
    }
    fi
    
  17. edit /etc/motd to:

    OpenBSD 3.3 (GENERIC) #0: Mon Jun 12 14:15:00 MDT 2000
    
    This is a private computer system.
    Unauthorized use prohibited.
    Your session may be monitored and logged.
    

    Note: The first two lines must be the banner line and a blank line, or else the /etc/rc script will overwrite the entire file.

  18. Modify /etc/rc.conf to:
    pf=YES
    inetd=YES
    all others = NO

  19. Comment everything out of /etc/inetd.conf. You will be adding the ftp-proxy back in later.
  20. Check the following files. Remove all unneccesary services. Comment out anything that isn't necessary
    /etc/rc.conf (should be done in previous step)
    /etc/rc.conf.local (should be empty)
    /etc/inetd.conf (should be done in previous step)
    /etc/netstart
    /etc/rc.local
    /etc/rc.securelevel

    Note: For the record, I do not agree with the logic of having /etc/rc.conf and /etc/rc.conf.local. I have seen far too many OpenBSD administrators make changes to rc.conf (out of habit), forgetting that rc.conf.local would override it. Depending on the change (and the thoroughness of the sysadmin) these errors can go undetected for quite a while. I recommend removing the rc.conf.local glue entirely and putting all configuration directives in one place: rc.conf

  21. Make directory /usr/ports/packages/distfiles
  22. Note: If you are used to filename completion and uparrow history from the bash shell and want to customize ksh to do the same, add this code to your user's .profile (and optionally, the system skeleton files in /etc/skel:

    
    PS1=${LOGNAME}@`uname -n`:'${PWD}$ '
    export PS1
    
    # emacs keybindings
    set -o emacs
    export VISUAL=emacs
    	
    # bash/zsh compatibility
    set -o markdirs
    bind '^I'=complete-list
    bind '^V'=quote
    
    
  23. Add the fwadmin account (or whatever account you will use to administer the firewall). use adduser. Make a strong password and store as above. Add it to its own group, and group wheel

    Using Sudo: To avoid getting into the habit of using root for everything, sudo is a useful tool. You can, for instance, create an /etc/sudoers file containing the commands you regularly run as root. eg:

    %wheel    ALL=(ALL)    ALL
    fwadmin   ALL=(ALL)    ALL
    

    Now instead of su'ing to root for your maintenance, you can simply place a sudo before the command you wish to run. Eg.

    $ whoami
    fwadmin
    $ sudo whoami
    Password:       (This is your fwadmin Account password, not root!)
    root
    $
    
    IMPORTANT SUDO NOTE: Adding vi, cp, chmod, and so on to /etc/sudoers is equivlalent to giving that user account root access, as it will be able to modify any file on the system! Protect your maintenance account like you would root.
  24. Grab identity.pub files for any maintenance accounts necessary. Create a file called ~fwadmin/.ssh/authorized_keys. Place the contents of all relevant identity.pub files in here.
  25. Edit /etc/mail/aliases to point mail aliases to appropriate account (fwadmin, usually). Do a newaliases after.

    root: fwadmin
    manager: fwadmin
    dumper: fwadmin
    

    Note: If you are not going to be checking the account regularly, you should create a .forward file in the maintenance user's account.

  26. Send a test mail to root (locally). This message should arrive in your maintenance account mailbox (or wherever you have it forwarded to.)
  27. Check cron jobs for any bizarreness:
    /etc/daily, /etc/daily.local
    /etc/weekly, /etc/weekly.local
    /etc/monthly, /etc/monthly.local

  28. Add the Firewall Summary script, such as /etc/ipfsummary: (yeah, I need to update this for pf)

  29. Now, make the security script aware of these changes:

    --- /etc/changelist.old	Tue Jun 27 13:38:08 2000
    +++ /etc/changelist	Tue Jun 27 13:38:54 2000
    @@ -31,6 +31,7 @@
     /etc/ifaliases
     /etc/inetd.conf
     /etc/ipf.rules
    +/etc/pfsummary
     /etc/ipnat.rules
     /etc/locate.rc
     /etc/mail.rc
    
    --- /etc/mtree/special.old	Tue Jun 27 10:57:51 2000
    +++ /etc/mtree/special	Tue Jun 27 10:59:03 2000
    @@ -60,6 +60,7 @@
     rc.securelevel	type=file mode=0644 uname=root gname=wheel
     rc.shutdown	type=file mode=0644 uname=root gname=wheel
     security	type=file mode=0644 uname=root gname=wheel
    +pfsummary	type=file mode=0644 uname=root gname=wheel
     resolv.conf	type=file mode=0644 uname=root gname=wheel optional
     resolv.conf.tail type=file mode=0644 uname=root gname=wheel optional
     shells		type=file mode=0644 uname=root gname=wheel
    
  30. Enable the firewall log summary script. Add this code to /etc/daily:

    /usr/bin/zcat /var/log/ipflog.0.gz | /bin/cat - /var/log/ipflog | \
      /usr/bin/perl /etc/pfsummary yesterday - 2>&1 | \
      mail -s "$host daily firewall output" root
    
  31. Configure /etc/ssh/sshd_config to allow only RSA authenticated sessions. (ie. no passwords allowed) Eg.

    RhostsAuthentication no
    RhostsRSAAuthentication no
    PasswordAuthentication no
    PermitEmptyPasswords no
    RSAAuthentication yes
    

    Restart sshd with a kill -HUP for the changes to take effect. Test the changes by ssh'ing from a remote machine before logging out!

  32. Edit /etc/newsyslog.conf. Change all relevant log files to roll over after a suitably long time.
    Eg.

    # logfilename           owner.group     mode ngen size time [ZB]
    /var/cron/log		root.wheel	600  3    10   *     Z
    /var/log/aculog		uucp.dialer	640  7    *    24    Z
    /var/log/authlog	root.wheel	640  7    *    208   Z
    /var/log/daemon				640  7    *    208    Z
    /var/log/lpd-errs			640  7    10   *     Z
    /var/log/maillog			600  7    *    208   Z
    /var/log/messages			644  5    30   *     Z
    /var/log/secure				600  7    *    208   Z
    /var/log/wtmp				644  7    *    208   ZB
    /var/log/xferlog			640  7    250  *     Z
    /var/log/ppp.log			640  7    250  *     Z
    /var/log/pflog                          600  31   *    24    ZB /var/run/pflogd.pid
    
  33. Reboot, and check dmesg for any weirdness. Boot should be clean.
  34. Build a custom kernel if desired. Brief overview:

    # cd /usr/src/sys/arch/i386/conf
    # cp GENERIC FIREWALL
    # (edit FIREWALL)
    # config FIREWALL
    # cd ../compile/FIREWALL
    # make clean && make depend && make
    # sudo make install
    

    Tips:

    • If you don't need it, comment it out
    • If your custom kernel starts acting weird, try GENERIC before reporting any problems
  35. Reboot again. boot should still be clean. If your kernel panics, you have done something wrong. reboot, and at the prompt, type:

    boot> boot hd0a:/obsd
    

    Now, fix your mistake and try again.

  36. Create Policy documents, /etc/pf.conf

    Good Trick: When changing a ruleset remotely, do the following:

    
    # cp /etc/pf.conf /etc/pf.test
    # pfctl -F rules -Rf /etc/pf.test; sleep 20; pfctl -F rules -Rf /etc/pf.conf
    
    (Type a few characters to make sure you get echos and then type ^C)
    
    # cp /etc/pf.test /etc/pf.conf
    

    The advantage here is that you have a chance to correct syntax errors before they go into effect. If you do something really stupid and cut yourself off, then the old configuration goes back into effect after 10 seconds (unless you've done something dumb, like blow out the state table in the process, or returned a RST to your SSH connection).

  37. Add any local ports/packages you might need.

    /usr/ports/sysutils/mergemaster
    Port of a FreeBSD utility that makes upgrating /etc significantly easier.
  38. Test onsite before shipping to final destination.

Additional Configuration

To Do

pintday.org » Fresh every Tuesday.