O O Ø O O O O
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
- Boot OpenBSD (or Greenbox) CD and Follow instructions in CD Sleeve.
-
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 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- 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)
-
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)
- After Install, reboot.
-
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.
- Do a man afterboot. Read carefully.
-
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 fiTo 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
- edit /etc/myname. Make sure it is right (pdofw).
- edit /etc/mygate. Make sure it is your default gateway.
-
edit /etc/hostname.{interfacename} for each card
e.g. hostname.fxp0 -
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
-
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
-
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
- 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 -
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.
-
Modify /etc/rc.conf to:
pf=YES
inetd=YES
all others = NO - Comment everything out of /etc/inetd.conf. You will be adding the ftp-proxy back in later.
-
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.securelevelNote: 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
- Make directory /usr/ports/packages/distfiles
-
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 -
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. - Grab
identity.pubfiles for any maintenance accounts necessary. Create a file called~fwadmin/.ssh/authorized_keys. Place the contents of all relevant identity.pub files in here. -
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.
- Send a test mail to root (locally). This message should arrive in your maintenance account mailbox (or wherever you have it forwarded to.)
-
Check cron jobs for any bizarreness:
/etc/daily, /etc/daily.local
/etc/weekly, /etc/weekly.local
/etc/monthly, /etc/monthly.local -
Add the Firewall Summary script, such as /etc/ipfsummary: (yeah, I need to update this for pf)
-
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
-
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
-
Configure
/etc/ssh/sshd_configto 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!
-
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
- Reboot, and check dmesg for any weirdness. Boot should be clean.
-
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
-
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.
-
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).
-
Add any local ports/packages you might need.
- /usr/ports/sysutils/mergemaster
- Port of a FreeBSD utility that makes upgrating /etc significantly easier.
- Test onsite before shipping to final destination.
Additional Configuration
- Installing dnscache
- Installing a squid HTTP proxy
- Installing and Configuring the postfix mail server
- Installing Anti-Spam protection
- Installing webmail
To Do
- systrace policies for all daemons
- generic pf.conf + ftp-proxy configuration