Firewall with IDS in OpenBSD 3.2

You are here

Create an invisible firewall.

OpenBSD and its packet filter are free and have a good history of tight security.


    Difference in OpenBSD releases

    PF in OpenBSD has gone through a history of changes. OpenBSD through version 2.9 had IPF, written by Darren Reed. One day Darren made some confusion on the OpenBSD mailing lists about the licensing of his IPF software in operating systems. The OpenBSD authors didn't like it and someone decided they would make their own packet filter, just for OpenBSD. The packet filter candidate from Daniel Hartmeier was released on and was accepted by the core developers into the OpenBSD kernel. The official release of PF was in OpenBSD 3.0. Between the 3.0 release and 3.2, PF stayed relatively unchanged.

    The addition of PF made the rulesets from IPF incompatible with those from IPF. In other words, rules from OpenBSD 2.9 or before won't work with PF on a OpenBSD 3.0 or newer machine. Also note that PF started getting a large clump of changes in OpenBSD 3.3, where the kernel developers decided to merge ALTQ (traffic shaping) and PF so the packet filter could also do traffic shaping. I'd link to ALTQ docs, but by the time I wrote this, they had already started being merged in the snapshot sources of OpenBSD with the PF documentation.



    There are two ways you can get an install started, by CD or Floppy. I'll assume that since you're using OpenBSD, you want something free, so that rules out the possibility that you bought a OpenBSD release CD from the website, even though you should have.

    Floppy Method

    Download rawrite. The link, unless dead, should go to a rawrite for windows that has a GUI. All other versions you'll find on the internet work with a DOS prompt. Whether from a DOS window or a GUI, you'll need a copy of rawrite to write a copy of the boot image (OpenBSD 3.2 link) to a floppy disk. Open rawrite and write the floppy32.fs file to the floppy disk.

    Notice other releases will have floppy image filenames that match the version release. For example, OpenBSD 3.3 will have a floppy33.fs.

    The aforesaid will cover most hardware configurations. If you know you have some weird hardware in the machine you're going to be installing OpenBSD on, there are actually two other floppy images with different hardware support. From the documentation:

    • floppy32.fs (Desktop PC) supports many PCI and ISA NICs, IDE and simple SCSI adapters and some PCMCIA support.
    • floppyB32.fs (Servers) supports many RAID controllers, and some of the less common SCSI adapters. However, support for many standard SCSI adapters and many EISA and ISA NICS has been removed.
    • floppyC32.fs (Laptops) supports the Cardbus and PCMCIA devices found in many laptops.

    In almost all cases, you'll want the link above.

    CD-R(W) method

    This method will require you to download the install files before installation. Using Bulletproof FTP or CuteFTP might be a good idea here. What you will want is to create a directory called "3.2", or whatever version number you download and go to the version directory for that release of OpenBSD. For version 3.2, that would be Don't forget OpenBSD has many FTP mirrors such as

    Here is the directory structure of the files you need to download.
    Again, note that if you are installing OpenBSD 3.3 or newer, the filenames won't end with 32, but rather 33 or 34, and so on.

    The same tools that you can use on Linux or BSD are available in Windows to make ISO files. OpenBSD doesn't release ISO files to OpenBSD because they need CD sales to support the full time developers. Thanks to official Windows ports of
    , just grab a copy of cdrtools from the official cdrecord website. Sometimes the windows binaries of cdrtools get moved on the ftp server, so you might have to hunt around.

    Extract the cdrtools files to

    Then here's what you need to do to make the ISO file:
      Start menu > Run...
      (run `command` for Win95\98\ME or `cmd` for NT\2k\XP)
      cd c:\
      cd openbsd
        -V "OpenBSD3.2"
        -b 3.2/I386/cdrom32.fs
        -c boot.catalog
        -o c:/OpenBSD3.2.iso
        -A "OpenBSD 3.2 Install"
    The period at the end is necessary.

    When the ISO is done, use Roxio Easy CD Creator 5 or your favorite burning program to burn it. If you don't have it, cdrecord is in the cdrtools distribution. I haven't tried it, but mkisofs works, so cdrecord probably does too. Documentation is all over the internet for cdrecord.

Install OpenBSD

    From here, it is only better to refer you to the official installation document. It is well written and should get you through the installation process, whether you bought a CD, created one, or made a floppy.

    Install Notes

    • If you did the floppy install, during the install, you'll have the option to get the installation files from FTP and that is what you will do.
    • To make an invisible firewall, you might ask yourself how it will be invisible if you have to configure a network device during the install. Don't worry about that now. Configure a device because we will need it both to grab operating system updates and to have an interface to use to get updates later if needed. You should have three interfaces in your machine for this document. Two will be invisible, and one will be for administration. Just configure one device during install, and leave the other two alone. If you want to configure all of them, you can do that too, because later, they will just be re-configured to be invisible.
    • However you partition your drive, it is a good idea to leave 2gb for the
    • When you're done with the install, type "reboot".
    • When the system comes back up to a login prompt, the administrator account is "root" and the password is whatever you set during the installation.
    • Configuring your machine with DHCP will be fine for the start. Later in this document will be instructions on how to set a static IP address. If you already know what static IP address you will use, go ahead and set it.
    • The network interface cards in the machine are numbered. For example, the a 3com NIC will use the
      kernel driver. The card furthest from the CPU is card 0, and each card closer increments by one, so if you have 4 NICs, the one closest to the CPU would be xl3.
    • This tutorial assumes you use 3com 905 NIC. Other network cards will have other kernel driver names in the kernel. When you start up your system, you can do
                # dmesg > dmesg
                # grep -e "..:..:..:..:.." dmesg
                # rm dmesg
      and the resulting lines should show the NICs, starting with the kernel driver and ending with the adapter MAC address.
    • If you're a Windows user and don't have much experience with BSD or Linux, creating a swap partition is mandatory. It is where all the extra memory processes go when you run out of RAM and is the equivalent of the Windows pagefile. Making it at least twice the size of the amount of space you have in RAM is good.

Update to -stable

    Why, why and where

    Although OpenBSD has a better than average record of remote and local security vulnerabilities, sometimes someone still discovers a flaw. The OpenBSD errata page is usually updated with patches for vulnerabilities or stability flaws. For purposes of explaining how to do an operating system upgrade, we'll skip the method that would use the src.tar.gz and srcsys.tar.gz files from the OpenBSD install tree. If you want to use the src.tar.gz and srcsys.tar.gz files, the patch branches page provides some information and links to get started in that direction.

    Method 1a: Installing kernel and system binary sources from CD

    If you have the official CD, you will only have one src.tar.gz file which contains srcsys.tar.gz.
       # mkdir /usr/src
       # mount /dev/cd0a /mnt
       # cd /mnt
       # cp src.tar.gz srcsys.tar.gz /usr/src
       # cd /usr/src
       # tar -xzf src.tar.gz
       # tar -xzf srcsys.tar.gz

    Method 2a: Installing kernel and system binary sources from FTP

    If you have src.tar.gz and srcsys.tar.gz
       # mkdir /usr/src
       # mount /dev/cd0a /mnt
       # cd /mnt
       # cp src.tar.gz srcsys.tar.gz /usr/src
       # cd /usr/src
       # tar -xzf src.tar.gz
       # tar -xzf srcsys.tar.gz

    Parts 1b and 2b: Updating kernel and system binary sources from CVS

    You must update your kernel and system sources if you installed them from the release tar files to patch security holes and fix reliability problems.
       # cd /usr
       # cvs -d -q up -rOPENBSD_3_2 -Pd src
    During this process, it might look like your system stalled out downloading updates. Most likely it hasn't. The CVS process must still check each file in the source tree to make sure it matches the server. By doing the tar file first and then CVS, you save having to download each individual file and instead just check against a CVS version number. Files that have security updates will have a newer CVS version than the copy on your machine. When the cvs command sees the version difference, it will patch the file on your system to match the one on the server.

    Method 3: Downloading kernel and system binary sources from CVS

    Don't do this step if you already did the stuff above with the tar files. This method will download the contents of src.tar.gz and srcsys.tar.gz and put them in /usr/src for you complete with up-to-date patches.
       # cd /usr
       # cvs -d -q get -rOPENBSD_3_2 -P src
    Say yes when it wants to confirm the SSH fingerprint. Note the OPENBSD_3_2 corresponds to the version number. OpenBSD 3.0 would have OPENBSD_3_0 for downloading the stable kernel source. Downloading with CVS will take a while, so while you wait, you can get started on downloading ports.

    Try hitting
    . You didn't just log out, you switched to another console. Log in again on the second console. You can switch back to the original console by hitting
    . You can use consoles with the F1, F2, F3, F4, and F6 keys. The other function keys are reserved by the operating system for other background tasks. Now you can multitask.

Install Ports

    What are they?

    Ports are specially packaged software editions for OpenBSD. They are maintained especially for OpenBSD and available from most OpenBSD regional mirrors. Often ports are created when software packages don't compile by default on OpenBSD. The port maintainers massage the source code of the software to make it work with OpenBSD. In some cases, they even make security audits to make the source more secure.

    Method 1: Downloading ports from CVS

    If you're already downloading the kernel and system sources, don't forget you can hit
    and download ports in the second terminal.
       # cd /usr
       # setenv CVSROOT
       # cvs -d $CVSROOT -q get -rOPENBSD_3_2 -P ports
    It is also possible to download ports that correspond to the major OpenBSD version release. In most cases, there is no reason to do so because the most recent imports to the CVS server will likely have security updates to software packages since the major release of OpenBSD, therefore the
    option was left off of the example.

    Even if you're on a T1, downloading sources and ports will take a while. Get up and strech. Get something to drink. Go to the bathroom. Make some phone calls. Check your email. You're cheering for src to finish first because that's what you'll need to work with first.

    Method 2: Install ports from CD

    This is often the choice if you already have ports.tar.gz downloaded and don't want to wait for it again. If you have the official CD from, ports.tar.gz is on the last CD. If you created your own cd, you know where it is.
       # mount /dev/cd0a /mnt
       # cd /mnt/3.2
       # cp ports.tar.gz /usr
       # cd /usr
       # tar -xzf ports.tar.gz

    Method 3: Ports from FTP

       # cd /usr
       # lynx
       # tar -xzf ports.tar.gz
    lynx will ask you some questions. The sequence of answers is 'D' for download, '[enter]' to save to disk, '[enter]' again to accept the default filename, 'q' to quit, and 'y' to say you really want to quit.

    Updating packages

    No matter which method you used to istall, there are some packages you will probably want to update individually. Since the OpenBSD 3.2 release, MySQL has has some security patch releases and Snort has had a new release with a newer rule parsing method. If you already set the CVSROOT and haven't rebooted, you don't have to set it again until you reboot.
       # cd /usr
       # setenv CVSROOT
       # cvs -d $CVSROOT -q up -Pd ports/net/snort
       # cvs -d $CVSROOT -q up -Pd ports/databases/mysql

Updating the system kernel, binaries, and libraries

    You need to update the system kernel. Don't skip this part. You need to compile your updated kernel source and compile your system binaries and libraries again before moving on. Don't skip steps. Why? Any programs from this point that you compile against security vulnerable kernel hooks or system libraries could have the vulnerabilities linger even after you compile a new kernel, binaries, and libraries. Compile the kernel, reboot, and recompile the system files.

    Compile a new kernel

       # cd /usr/src/sys/arch/i386/conf
       # config GENERIC
       # cd ../compile/GENERIC
       # make depend && make
          (this step will take a while)
       # cp /bsd /bsd.old
       # cp bsd /
    Then reboot. You must reboot before moving on to make use of the newly patched kernel.
       # shutdown -r now
    The -r is for reboot. If you want to shutdown a machine, use -h for halt.

    Compile new system files (binaries and libraries)

        # cd /usr/src
        # rm -r /usr/obj/*
        # make obj && make build
    You're recompiling everything installed on your system except your kernel, which you already did. This process will take a long time on an old machine. Rebooting when you're done isn't mandatory, but you should do it for good measure.

Download, compile, and install software from ports

The cool thing about using ports is that with one command, all the downloading, compiling, patching, installing, and cleanup is done with one command and is specifically tailored for OpenBSD. If you watch the installation, it also downloads all the libraries and dependencies that the programs you're installing might have.

    Installing text editor (nano)

    OpenBSD, and most other BSD and Linux operating systems come with VI as their default editor, however VI has a big learning curve. If you're feeling confident with your Google skills, learning VI will benefit you in the long run.

    Since VI has a big learning curve and you probably just want to get the system up, nano is a much simpler text editor which will give you the basic file editing funcitonality you'll need to get the job done.
       # cd /usr/ports/editors/nano
       # make install clean
    OpenBSD won't pick up the nano installation right away. It is not in the path. What that means is until you restart, you'll have to type the full path to the nano executable. You make the choice. Reboot or just type the full file path until the next reboot. You won't have to edit files for a bit, so it can wait.

    Compile and install Snort

    The snort intrusion detection system is available in ports. Here we will be adding a "FLAVOR" to the snort installation which changes the default install options. Normally snort writes all the intrusion hits to files, but we're going to want them stored in a MySQL database. If you're curious about the options available for snort install, you can do this:
       # cd /usr/ports/net/snort
       # make show VARNAME=FLAVORS
    The documentation for snort will explain better what each option does. This is merely an installation guide. For the purposes of this installation, do the following:
       # cd /usr/ports/net/snort
       # env FLAVOR="mysql flexresp" make install
    If you sit and watch the installation process, you will notice that MySQL will also automagicly download, get patched, configure, compile, and install. For your information, since the OpenBSD 3.2 release, MySQL has released new versions of MySQL that fix security vulnerabilties. This should not be a problem for an invisible firewall because nobody should have rights to either use the MySQL console client or connect to the MySQL socket. This will be discussed later in this paper.

    Install PHP

    If you are experienced with using the FLAVORS environment variable, you can alter the PHP install to cut install time. An example FLAVOR is shown. It excludes most of the extensions from the PHP install so you have a shorter install time and don't install a lot of software you won't use.
       # cd /usr/ports/graphics/jpeg
       # make install clean
       # cd /usr/ports/graphics/gd
       # make install clean
       # cd /usr/ports/www/php4/core
       # make install clean
       # /usr/local/sbin/phpxs -s
       # cp /usr/local/share/doc/php4/php.ini-recommended /var/www/conf/php.ini
       # cd ../extensions
       # env FLAVOR="no_x11 no_bz2 no_curl no_dba no_dbase no_domxml no_filepro \
        no_gmp no_imap no_ldap no_mcrypt no_mhash no_ncurses no_odbc no_pdf \
        no_pgsql no_shmop no_snmp no_sybase_ct no_xml no_xslt" make install clean
       # cd ../pear
       # make install clean
    As you can see, we're leaving out a lot of the functions of PHP, but we don't need them. All that should be left are the MySQL database and GD graphic library extensions. You still need to actually install them:
       # cd /usr/ports/packages/i386/www
       # pkg_add php4-mysql*
       # /usr/local/sbin/phpxs -a mysql
       # pkg_add php4-gd*
       # /usr/local/sbin/phpxs -a gd

Configure PHP

Now configure Apache to parse files ending with ".php" using the PHP extension.
   # cd /var/www/conf
   # /usr/local/bin/nano httpd.conf
If you have rebooted your machine since you installed nano, you can do this:
   # cd /var/www/conf
   # nano httpd.conf
Use the CTRL+W function to find "index.html". Add index.php and index.php3 to the DirectoryIndex line to make it look like:
  # DirectoryIndex: Name of the file or files to use as a pre-written html
  # directory index. Separate multiple entries with spaces.
  DirectoryIndex index.php index.html index.php3
Then use the CTRL+W function to find "x-httpd-php". You'll need to uncomment the two lines it finds and alter them. They should look like:
  # For example, the PHP3 module (not part of the Apache distribution)
  # will typically use:
  AddType application/x-httpd-php .php .php3 .phtml
  AddType application/x-httpd-php-source .phps
If you can't find those lines in your httpd.conf file, look harder or just add the lines as you see them above. If there are other file extensions you want to be parsed by the PHP engine, you can add them to the first AddType line too if you want. Some people add .html to obscure the engines running their website. This can be inefficient if you also have a many regular html files that do not contain PHP which will require PHP to examine the files anyway.

Save your httpd.conf with CTRL+X and follow the prompts.

Now it might be nice to test your PHP installation. I delete all the default Apache documents in the web root directory. You can skip that if you want.

   # cd /var/www/htdocs
   # rm -fr *
   # /usr/local/bin/nano phpinfo.php
You're creating a file named phpinfo.php. In it, you want to put:
<?php phpinfo(); ?>
Save it and test it:
   # apachectl start
   # lynx localhost/phpinfo.php
If you see a page that has a bunch of information about PHP, all went well. If you see just phpinfo(); then you messed up somewhere. Go back and make sure you did everything. This won't prevent you from installing Snort, but it will definately keep ACID from working, which is one of the best Snort log HTTP-based viewers.

Setup Apache SSL

OpenBSD ships with an SSL-ready httpd and RSA libraries. For use with httpd(8), you must first have a certificate created. This will be kept in /etc/ssl/ with the corresponding key in /etc/ssl/private/. The steps shown here are taken in part from the ssl(8) man page. Refer to it for further information. This FAQ entry only outlines how to create an RSA certificate for web servers, not a DSA server certificate. To find out how to do so, please refer to the ssl(8) man page.

To start off, you need to create your server key and certificate using OpenSSL:
   # openssl genrsa -out /etc/ssl/private/server.key 1024
The next step is to generate a Certificate Signing Request which is used to get a Certifying Authority (CA) to sign your certificate. To do this use the command:

   # openssl req -new -key /etc/ssl/private/server.key -out /etc/ssl/private/server.csr
This server.csr file can then be given to Certifying Authority who will sign the key. One such CA is Thawte Certification which you can reach at Thawte can currently sign RSA keys for you. A procedure is being worked out to allow for DSA keys.

If you cannot afford this, or just want to sign the certificate yourself, you can use the following.

   # openssl x509 -req -days 365 -in /etc/ssl/private/server.csr \
        -signkey /etc/ssl/private/server.key -out /etc/ssl/server.crt
With /etc/ssl/server.crt and /etc/ssl/private/server.key in place, you should be able to start httpd(8) with the -DSSL flag (see the section about rc(8) in this faq), enabling https transactions with your machine on port 443.

See 10.7

Start Apache on boot

   # cd /etc
   # /usr/local/bin/nano rc.conf
Change httpd_flags from NO to "-u -DSSL". Add the quotes too. Be careful about the comment at the end of the line (# for normal use...) spilling over to the next line. That is bad. If it does, either get it all on one line again or delete the comment. Hit CTRL+X to save the file.

. The -DSSL tells Apache to start up with SSL. A later section will discuss SSL. If you know you just want to run regular HTTP services through port 80 and don't want SSL through 443, you can leave off the -DSSL and skip the Apache SSL configuration.

Finishing MySQL Install

Check /etc/rc.conf to make sure that the following line is at the bottom:
   # cat /etc/rc.conf
The line should be there, but if for some reason it isn't, add it with nano.
should not exist. If it does or if it doesn't, do exit nano and do the following:

   # echo "mysql=YES" >> /etc/rc.conf.local
is just shorthand so you don't have to use an editor to edit a file. If the file doesn't exist, it will be created. If it does exist,
will be appended to it. You can use
to verify the contents of
is a tool that can be used to output a file right to the screen.
   # cat /etc/rc.conf.local
MySQL isn't done installing. Go back to ports.
   # cd /usr/ports/databases/p5-DBD-Msql-Mysql
   # make install clean
   # cd /usr/ports/packages/i386/databases
   # pkg_add mysql-server*
Next you need to move the configuration file for MySQL to
. In
look at the files
, and
is good for most server configurations.
   # cd /usr/local/share/mysql
   # cp my-medium.cnf /etc/my.cnf
   # /usr/local/bin/nano /etc/my.cnf
We're almost done with MySQL. Edit /etc/rc.conf and change
shlib_dirs= # extra directories for 
at the bottom of the file to read like this:

   # shlib_dirs="/usr/local/lib/mysql"
Or if you know you have multiple directories:
   # shlib_dirs="/usr/local/lib/{mysql,libmcrypt}"
Make sure the (# extra directories...) comment doesn't spill over to the next line. The following will add execute permissions to the file that starts mysql.
   # mkdir /var/run/mysql
   # chown mysql /var/run/mysql
   # chmod 755 /usr/local/share/mysql/mysql.server
exists already, that's good. If it doesn't exist it'll be created. Either way, it should be there. Add this to the bottom of

   if [ X"${mysql}" == X"YES" -a -x /usr/local/bin/safe_mysqld ]; then
	echo -n " mysqld"; /usr/local/share/mysql/mysql.server start
	/bin/sleep 1
This will start MySQL when you boot your server. Now might be a good time to reboot if you're curious to see if everything will crash and burn. If you don't want to reboot, you can do this:
   # /usr/local/share/mysql/mysql.server start
   # /usr/local/bin/mysql -u root
The second line will try to connect to MySQL. You can either connect or you can't. A connection is good. The password is blank if you did not set it before. Type
to get out of mysql. When you reboot, you should see
in the local daemons list just before logon. Now might be a good time to change the default root password to your MySQL server:
   # /usr/local/bin/mysqladmin -u root -p password 'new-password'
If it's a single user machine and you properly deny outside connections to MySQL, you might be fine leaving the root password blank. Later in this tutorial, we will configure the server to not accept connections on on the MySQL socket from anywhere other than localhost.

If you think you know what you're doing, now might be a good time to stop mysqld and move /var/mysql to another drive if you've got a multiple drive system. For example, you might have created a /misc partition during installation on a second hard drive. Then you could move /var/mysql to it and edit the datadir var in /usr/local/share/mysql/mysql.server and /etc/my.cnf to point to the new db storage directory.


Snort is a free intrusion detection system.

    Configure Snort

    Now you need to configure MySQL to have a user and table to store Snort alerts:
       # mysqladmin -u root -p create snort
       # mysqladmin -u root -p create snort_archive
       # mysql -u root -p
    If you didn't set a password before, when it asks for a password, hit enter. At the mysql prompt, type
       mysql> grant all on snort.* to snort@localhost identified by 'snort';
       mysql> grant all on snort_archive.* to snort@localhost identified by 'snort';
       mysql> exit
    will be the password in the quotes.
    says all tables in the snort database.
    says the snort user can only connect from localhost. Now add a system user for snort.
       # groupadd snort
       # adduser -batch snort snort -shell /bin/nologin -home /home
    Since this is the first time for you to create a user on the system, it will ask you for default values for accounts. Just hit enter to all of them to accept the set defaults in brackets.
       # mkdir /var/log/snort
       # chown snort /var/log/snort
    We will start Snort a lot like we started MySQL:
       # echo "snort=YES" >> /etc/rc.conf.local
       # /usr/local/bin/nano /etc/rc.local
    Now you will need to decide which interfaces in your machine will do what. Pick the one that will be on the inside of the firewall. In the example machine, we have one administration NIC with an IP address assigned, and two more, one for the outside of the firewall and one for the inside. For the sake of this example, xl1 will be the interface on the inside of the firewall. Add this to the bottom of your
       if [ X"${snort}" == X"YES" -a -x /usr/local/bin/snort ]; then
    	echo -n " snort"; /usr/local/bin/snort -D -d -c /etc/snort/snort.conf -u snort -g snort -i xl1
    line will be longer than the screen, so get it to fit on one line when it spills over to the next. If you are using VI, you don't have to worry about things like that, because when you edit a file with VI and a line spills over, it does a wordwrap instead of a line break like nano. Also note the
    -i xl1
    which corresponds to the interface on the inside of the firewall. Then we can import the Snort database information into MySQL:
       # cd /usr/ports/net/snort
       # mysql -u snort -p snort < /usr/ports/net/snort/w-snort-*/snort-*/contrib/create_mysql
       # make clean
    If you had done a
    make install clean
    make clean
    for snort already, you can do a
    make extract
    to get the sources you'll need to import the tables you need into mysql. There are a lot of rules files in
    . We should put them in a different directory.
       # mkdir /etc/snort
       # cd /usr/local/share/examples/snort
       # cp -r * /etc/snort
    Then go to the
    and edit snort.conf. The file will explain what variables do what. Defaults will probably work if you're scared to change the file. The only thing you absolutely have to change is find the mysql log line, uncomment it, and change the login information for each of the variables on the line, otherwise you won't be able to view the snort logs from ACID.

    To log to MySQL for ACID, you will need to find the database section, uncomment the line for MySQL in
    , and change the connection details. Just make sure you read the whole configuration file.

    Update Snort rules

       # mkdir /usr/local/src
       # cd /usr/local/src
       # lynx
       # tar -xzvf snortrules-stable.tar.gz
       # cp -r rules /etc/snort
       # cd /etc/snort/rules
       # mv * ..
       # cd ..
       # mv *.rules rules
    Now you must go back to
    and edit snort.conf to add the additional rules files that aren't in the distribution and point the rules location to

    Create firewall network

    If you want a NAT configuration, you'll need a LAN IP for an interface on the inside of the network. Choose a network device not in use. We'll assume that xl0 right now is configured with an external world address. Edit hostname.xl1. Nano and vi will create it if it's not there already. Put this line in it:
       inet NONE
    To create IP aliases for the same network interface, the file would look like:
       inet NONE
       inet alias NONE
       inet alias NONE
       inet alias NONE
       inet alias NONE
       inet alias NONE
    If you don't want to reboot now, you can configure the network device with the
       # ifconfig xl1 inet netmask
       # ifconfig xl1 inet alias netmask
    After you reboot, the file will automaticly do ifconfig for you.

    The other choice is creating an invisible passthru firewall. Either way, if you want extra interfaces to go to the internal network interface and have them bridged together, you'll need to create invisible interface configurations for the other NICs.
       # ifconfig xl2 up
       # echo "up" > /etc/hostname.xl2
    Make sure you don't create a hostname file for the wrong interface. If you echo "up" to the interface hostname file you're using to get on the internet, you won't be able to get on the internet until you go back and replace up with the correct internet configuration. The interface you should have configured by default during the install was

    While you're at it, now is a good time to add the second invisible interface for the firewall.
       # ifconfig xl3 up
       # echo "up" > /etc/hostname.xl3
    Now you can bridge them together. Your bridge configuration will list all the network interfaces for your internal network. For an invisible firewall, that should be two interfaces. For a NAT machine, the PCI slot number is the limit. Create /etc/bridgename.bridge0
       add xl1
       add xl2
       add xl3
       add xl4
       blocknonip xl1
       blocknonip xl2
       blocknonip xl3
       blocknonip xl4
    Again, if you don't want to reboot right now, you can use the
    command to manually create the bridge:
       # brconfig bridge0 add xl1
       # brconfig bridge0 add xl2
       # brconfig bridge0 add xl3
       # brconfig bridge0 add xl4
       # brconfig bridge0 blocknonip xl1
       # brconfig bridge0 blocknonip xl2
       # brconfig bridge0 blocknonip xl3
       # brconfig bridge0 blocknonip xl4      
       # brconfig bridge0 up

    Start Snort

    You won't have to do this all the time because the editing you did to rc.local with a similar line should start Snort automagicly on boot. This will get snort running now just to make sure it runs.
       # /usr/local/bin/snort -D -d -c /etc/snort/snort.conf -u snort -g snort -i xl1
    You'll either get a "Snort running" message, or a "FATAL ERROR". The errors are quite informational and usually tell you, you have a file in the wrong place if you get one. Get
    to sync with where files are in the
    directory if you have an error. If it says it needs a file, but you don't know where to find it
       # find / -name "filename" -print
    should spit it out on the screen if it exists. It's a console Find File equivilent from Windows. You can add asterisks for wildcards if you feel the need.

    Install ADODB database abstraction

       # mkdir /usr/local/src
       # cd /usr/local/src
       # lynx
       [ download file here and exit lynx ]
       # tar -xzf adodb*.tgz

    Install PHPlot graphing scripts

       # cd /usr/local/src
       # lynx
       [ download file here and exit lynx ]
       # tar -xzf phplot-*.tar.gz
       # lynx
       [ download file here and exit lynx ]
       # tar -xzf jpgraph-*.tar.gz

    Install ACID

       # cd /usr/local/src
       # lynx
       [ download file here and exit lynx ]
       # tar -xzf acid*.tgz
       # mkdir /var/www/phplibs
       # mv adodb /var/www/phplibs
       # mv jpgraph-x.xx /var/www/phplibs/jpgraph
       # mv phplot-x.x.x /var/www/phplibs/phplot
       # mv acid /var/www/htdocs
       # cd /var/www/htdocs/acid
       # nano acid_conf.php
    Now edit acid_conf.php to point
    , change the logon information for MySQL to use
    as the user and password with for the
    databases, and set

    Now you'll probably want to put a password on the access to Apache. Edit /var/www/conf/httpd.conf, find the directory directive for /var/www/htdocs and change AllowOverride from None to All. This will allow us to use .htaccess files to change permissions of directories in the Apache web directory. An .htaccess file in a directory provides specific instructions for permissions to that specific directory. In this example, we will create an .htaccess file in the root directory, thereby blocking off all unauthorized access.

       # htpasswd -c /var/www/passwd administrator
    Then create the file /var/www/htdocs/.htaccess
       AuthUserFile /var/www/passwd
       AuthName "firewall"
       AuthType Basic
       require valid-user

Clear console on logout

Clearing the console isn't nessesary to get your firewall up and running, but it does add an extra layer of security to sensitive information you might enter in the console. When you log out, it will automaticly clear away for you. To do this you must add a line in
. Change the current section:
   P|Pc|Pc console:\
adding the line ":cl=\E[H\E[2J:" at the end, so that it ends up looking like this:
   P|Pc|Pc console:\
Changes will be immediate. Next time you log out, the console will clear. You can get the same result by typing
at the prompt, but who wants to remember to do that every time.

Lockdown single user mode

One element of security often overlooked is physical security. The OpenBSD developers built a "feature" into OpenBSD called single user mode. Single user mode allows you, if you are at the keyboard, to boot into the system to do recovery or diagnostic work. Under normal circumstances, booting into single user mode gives you automatic root access, without asking for a password. Single user mode is also often used for password recovery when nobody can remember the root password. You can make single user mode ask for the root password.

to change the current line:
   console "/usr/libexec/getty Pc"     vt220   off secure
to insecure

   console "/usr/libexec/getty Pc"     vt220   off insecure

Deny remote root login

Root has the power to do anything to a system. Here we'll add a user that has very little power to change files on the system.
   # adduser
If you decided not to install Snort, the
command will ask for default user account values. Just hit enter to accept each of the default values in brackets. Then follow the prompts to create a user.

   Use option "-silent" if you don't want to see all warnings and questions.
   Reading /etc/shells
   Check /etc/master.passwd
   Check /etc/group
   Ok, let's go.
   Don't worry about mistakes. I will give you the chance later to correct any input.
   Enter username [a-z0-9_-]: administrator
   Enter full name []: administrator
   Enter shell csh ksh nologin sh [sh]: csh
   Uid [1002]: [ENTER]
   Login group administrator [administrator]: [ENTER]
   Login group is "administrator". Invite administrator into other groups: guest no [no]: wheel
   Enter password []: ********
   Enter password again []: ********
   Name:     administrator
   Password: ****
   Fullname: administrator
   Uid:      1002
   Gid:      1002
   Groups:   administrator wheel
   HOME:     /home/administrator
   Shell:    /bin/sh
   OK? (y/n) [y]: [ENTER]
   Added user "administrator
   Copy files from /etc/skel to /home/administrator
   Add another user? (y/n) [y]: n
Don't make the administrator password the same as the root password. If someone compromised the system, was able to read /etc/passwd and noticed that the administrator password hash is the same as the root password, you're double login protection is wasted. If you're already familiar with a particular shell, you can pick something other than csh. Default is sh, but root's default is csh.

and change
   #PermitRootLogin yes

   PermitRootLogin no
Now that you can no longer log in as root remotely, when you log in as administrator over ssh, you'll have to use the
command to become a super user. It will ask you for a password. When it does, type in the root password and you will be root. This is only possible because when you created the administrator user, you added them to the wheel group, which is where super users go. Only users in the wheel group can become a super user from
. When you're done being a super user, type
to become a regular user again. The
will make a log of when and where someone becomes a super user.

Configuring the firewall

Remember, this section was written for OpenBSD 3.3. These rules might work on other OpenBSD installations >=3.0, however that doesn't mean that they're right.

    Enable IP forwarding

    . Uncomment
    . While you're in there, you could uncomment

    Create invisible interfaces

    For this example,
    is our administration interface, which will have an IP assigned and firewall rules to allow only SSH and HTTPS connections. The invisible interfaces are
    , and

    There are some fine details of creating a bridge between network interfaces for a firewall.
    Rule 1: Always filter on one interface.
    Rule 2: Don't filter on the other interfaces.

    Remember, the computer doesn't know which interface leads to the internet and which goes to a crossover cable for a server. When you bridge interfaces, you are essentially creating one virtual interface.

    See the Create invisible interface section for the invisible device configuration.

    You also want to bridge those interfaces to make a connection between them. Create a file in
    . Add the following to it and save.
       add xl1
       add xl2
       add xl3
    You can lock things down even tighter. Type
    man brconfig
    at a prompt to get the manual for the bridge software. Some options might be to consider making the bridge
       add xl1
       add xl2
       add xl3
       blocknonip xl1
       blocknonip xl2
       blocknonip xl3
       rule pass in on xl3 dst 00:BB:A0:33:3A:D1
       rule pass out on xl3 src 00:BB:A0:33:3A:D1
       rule block in on xl3
       rule block out on xl3
    What the rules have done is block all traffic that's not associated with the computer behind the firewall that has the MAC address of 00:BB:A0:33:3A:D1. If it either isn't headed to or from the machine with 00:BB:A0:33:3A:D1, it won't get passed. If you decide to use bridge rules with MAC addresses, you'll have to maintain a current ruleset of MACs, otherwise don't use bridge rules at all. Note:Experience has shown this author that MAC filtering in this style is not 100% good 100% of the time. If you decide you want MAC address filtering, make sure you test a lot. Merely adding the interfaces should be enough for most firewalling situations.

    Note that the packet filter reads traffic on the IP level. In other words, it won't filter traffic based on MACs, just source and destination IPs by port number and traffic type. The bridge is the only place to filter by MAC and the packet filter is the only place to filter by IP.

    Configure static network interface

    If you already configured the administration static IP you want during the OS install, you can skip this section.

    To switch from DHCP to static or to fix a mess-up if you echoed "up" into the wrong hostname file, you need to edit the hostname.if file for the interface you're using. In the example, the contents of
    should be
       dhcp NONE NONE NONE
    for a DHCP environment. To change it to static, change it to match
       echo "inet NONE" > /etc/hostname.xl0
    Note that the hostname.if file doesn't contain the gateway IP. That is stored in a different file.
       echo "" > /etc/mygate
    To activate the gateway address, you'll have to restart. There are ways to activate it otherwise, but saying to restart is much simpler. You can do the research if you don't want to reboot.

    Setting up the packet filter (PF)

    First turn PF on. Edit
    and then turn PF on without having to reboot.
       # pfctl -e
    You will not get enough information about packet filtering from this tutorial to be well versed. Minimally, you need to read these two documents and understand them or you're wasting your time with this firewall.

    You can type
       # man pfctl
       # man pf.conf
    to get the manual for the packet filter right from your machine. To exit the man pages viewer, hit the "q" key or scroll all the way down to the end of the document. Page Down will get you there faster.

    Here is a ruleset you might use to start an invisible passthru firewall. It uses the slower bracketed blocks of IPs that expand into separate rules to check against incoming and outgoing traffic.
    # /etc/pf.conf
    # David Norman, OpenBSD 3.2
    # Begin Ruleset
    # not routable
    # spaces before brackets required
    spoofed="{,,, \
      ,, }"
    # IP blocks ripped from
    reserved="{,,,, \
       ,,, \
       ,,,, \
       ,,, \
       ,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,,, \
       ,,, }"
    uttnet="{,,, \
       ,,, \
    scrub in on $external all
    # Loopback device rules
    pass out quick on lo0 all keep state
    pass in quick on lo0 all keep state
    block in on { $external, $admin } all
    ## Comment this out if you're using LAN IPs
    block in from no-route to any
    ## good rule but also dangerously strict and needs IP in place of ($external)
    # block out quick on $external ! from ($external) to any
    block in quick on { $external, $admin } inet from $spoofed to any
    block in quick on { $external, $admin } inet from $reserved to any
    pass in quick on $admin inet proto tcp from $uttnet to { } port { 22, 443 } keep state
    pass out quick proto tcp all flags S/SA keep state
    pass out quick proto udp all keep state
    pass in quick on $external inet proto tcp from any to {, } port 80
    pass in quick on $external inet proto icmp all icmp-type 8 code 0 keep state
    pass out quick on $external inet proto icmp all icmp-type 8 code 0 keep state
    Here is a newer ruleset that uses tables for blocks of IPs. PF takes as long to look up an address in a table with 5 addresses as it does with a table full of 100,000 addresses.
    # /etc/pf.conf
    # Academic Computing Services
    # OpenBSD 3.3 PF ruleset
    # reload rules with `pfctl -f /etc/pf.conf`
    # rc.conf should take over after you change it there and reboot
    # not routable
    # spaces before brackets required
    table <spoofed> const { 10/8, 172.16/12, 192.168/16, \
                224/4, 240/5, }
    # IP blocks ripped from
    table <reserved> const { 0/8, 1/8, 2/8, 5/8, \
                 23/8, 27/8, 31/8, \
                 36/8, 37/8, 39/8, 41/8, \
                 42/8, 58/8, 59/8, \
                 60/8, 69/8, 70/8, \
                 71/8, 72/8, 73/8, 74/8, \
                 75/8, 76/8, 77/8, 78/8, \
                 79/8, 80/8, 81/8, 82/8, \
                 83/8, 84/8, 85/8, 86/8, \
                 87/8, 88/8, 89/8, 90/8, \
                 91/8, 92/8, 93/8, 94/8, \
                 95/8, 96/8, 97/8, 98/8, \
                 99/8, 100/8, 101/8, 102/8, \
                 103/8, 104/8, 105/8, 106/8, \
                 107/8, 108/8, 109/8, 110/8, \
                 111/8, 112/8, 113/8, 114/8, \
                 115/8, 116/8, 117/8, 118/8, \
                 119/8, 120/8, 121/8, 122/8, \
                 123/8, 124/8, 125/8, 126/8, \
                 127/8, 197/8, 201/8, 219/8, \
                 220/8, 221/8, 222/8, 223/8, \
                 240/8, 241/8, 242/8, 243/8, \
                 244/8, 245/8, 246/8, 247/8, \
                 248/8, 249/8, 250/8, 251/8, \
                 252/8, 253/8, 254/8, 255/8 }
    #set loginterface xl1
    set optimization conservative
    scrub in on $ExtIF all
    nat on $ExtIF from 10/8 to any -> $ExtIP
    # Loopback device rules
    pass out quick on lo0 all
    pass in quick on lo0 all
    # Default block everything
    block in on $ExtIF inet all
    block in on $IntIF inet from any to $IntIF 
    antispoof for lo0
    # Editor note: antispoof here on OBSD 3.2 kills talking btwn bridged interfaces
    #antispoof for { $ExtIF, $IntIF } inet
    # so I came up with a looser rule:
    block in on ! xl3 inet from to any
    # silently drop UDP broadcasts
    block in quick on $ExtIF inet proto udp from any to
    # Block any IP spoofing attempts. (Packets "from" our network
    # shouldn't be coming from the outside).
    block in quick on $ExtIF inet from  to any
    # Block all reserved private IP addresses.
    block in quick on $ExtIF inet from <reserved> to any
    # Outgoing Windows networking won't work stable over NAT
    # rules not working?
    block out quick on $ExtIF inet proto tcp from any to any port { 135, 137 >< 139, 445 }
    block out quick on $ExtIF inet proto udp from any to any port { 135, 137 >< 139, 445 }
    ## start letting some stuff through
    #  remote administration
    pass in quick on $ExtIF inet proto tcp from {,,, } to $ExtIP/32 port { ssh, https } flags S/SA modulate state
    # pings
    pass in quick on { $ExtIF, $IntIF } inet proto icmp all icmp-type 8 code 0 keep state
    # dhcp and ntp
    pass in quick on $ExtIF inet proto udp from 10/8 to any port { 68, 123 } keep state
    # Let traffic in and out
    pass out quick on $ExtIF inet proto tcp all flags S/SA keep state
    pass out quick on $ExtIF inet proto udp all keep state
    ## Let pings out and back
    pass in quick on { $ExtIF, $IntIF } inet proto icmp all icmp-type 8 code 0 keep state
    pass out quick on { $ExtIF, $IntIF } inet proto icmp all icmp-type 8 code 0 keep state

Transparent Squid

   # cd /usr/ports/www/squid
   # env FLAVOR="transparent" make install clean
   # /usr/local/sbin/squid -z
Before running
squid -z
, you might want to edit the default configuration in
. The cache directories will be created with
squid -z
so if you want your cache in a different directory than
or if you want to put your cache on a RAID striped device for extra speed, you'll want to edit some of the default options in

Setting up BIND (or other DNS) is a good idea for local DNS resolution.

NTP (Network Time Protocol) daemon

Installing NTPd allows your machine to check with atomic clock servers for the correct time.
   # cd /usr/ports/net/ntp/stable
   # make install clean
   # echo "0" > /etc/ntp.drift
Then create
with the following contents.
   server prefer minpoll 9 maxpoll 13
   driftfile /etc/ntp.drift
Optionally, you can add
restrict mask nomodify nopeer
to the bottom of the
file. If you want to let NTP through the firewall, it is port 123/udp.


Symon is a system monitor that lets you view the status of the CPU, memory, PF, NICs, and misc services running on the system. It uses PHP and a combination of a server (symux) and monitor that reports to the server (symon). It stores the data in a special type of database for continuous data collection called rrdtool.
   # cd /usr/ports/sysutils/symon
Versions of Symon 2.60 and before have an installation bug that doesn't install all the PHP scripts that are needed for viewing services from Apache, so this will bypass some of the post-installation instructions to do some manual configuration. Symon 2.61 should have a fix to the installation bug.

. Since you have already custom installed PHP, you don't want the Symon install to do the generic one again. Change
   WEB_RUNDEPENDS=     rrd:rrdtool-*:net/rrdtool php:php4->=4.2.3:www/php4/core

   WEB_RUNDEPENDS=     rrd:rrdtool-*:net/rrdtool
And then also change
   RUN_DEPENDS=     rrd:rrdtool-*:net/rrdtool  php:php4->=4.2.3:www/php4/core
   RUN_DEPENDS=     rrd:rrdtool-*:net/rrdtool

Then you can do
   # make install
   # cd w-symon-2.60/symon/symon2web
   # rm Makefile
   # mkdir /var/www/htdocs/symon
   # chmod 444 /var/www/htdocs/symon/*
   # cd /usr/ports/sysutils/symon
   # make clean
   # cd /usr/ports/packages/i386/sysutils
   # pkg_add symon-2.60.tgz
   # cd /usr/local/share/symon
   # ./ cpu0
   # ./ pf
   # ./ mem
   # ./ bridge0
   # ./ lo0
   # ./ xl0
   # ./ xl1
   # ./ xl2
   # ./ xl3
   # ./ xl4
  do this if you have an ata drive # ./ wd0
  do this if you have a scsi drive # ./ sd0
   # ./ debug
   # ./ proc_httpd
   # ./ proc_snort
   # ./ proc_sshd
   # ./ proc_mysqld
   # mkdir /var/symon
   # mkdir /var/symon/localhost
   # mv *.rrd /var/symon/localhost
   # cd /var/www/htdocs/symon
Most installations will give you an error at the end of the make. Everything actually compiled correctly. Edit
and change the $symon2web variable to

Then there are a few finishing touches to configure and start the monitor. Create
. The contents should be similar to the following:
   monitor { cpu(0),  mem, pf,  if(xl0), if(xl1),
	  if(lo0), if(xl2), io(wd0), debug,
          if(bridge0), proc(httpd), proc(sshd),
	  proc(snort), proc(mysqld)}  
		stream to 2100
Then create a configuration for the monitor server as
   mux 2100

   source {
	accept { cpu(0), mem, pf, if(xl0), if(xl1),
	         if(lo0), io(wd0), if(xl2),  debug,
                 if(bridge0), proc(httpd), proc(sshd),
		 proc(snort), proc(mysqld)}

	datadir "/var/symon/localhost"
Then set some permissions on them. While not required, setting the permissions to 444 makes the file only have read permissions, no write or execute.

   # chmod 444 /etc/symon.conf
   # chmod 444 /etc/symux.conf
To start them, symux (the server) goes first so when the monitor (symon) starts, it has a server to send data to.
   # /usr/local/libexec/symux
   # /usr/local/libexec/symon
and add this at the bottom:
   if [ -x /usr/local/libexec/symux ]; then
	echo -n ' symux';	/usr/local/libexec/symux

   if [ -x /usr/local/libexec/symon ]; then
	echo -n ' symon';	/usr/local/libexec/symon

Additional Notes

  • pftop
  • df -h
  • fewer httpd children
  • winscp

Information Systems Security Architecture Professional

Certified Secure Software Lifecycle Professional