--- title: Chapter 1. Introduction prev: books/ipfw-primer/ipfw-preface next: books/ipfw-primer/ipfw-operation description: Introduction about IPFW tags: ["introduction", "IPFW", "firewall"] showBookMenu: true weight: 20 path: "/books/ipfw-primer/ipfw-introduction/" --- [[ipfw-introduction]] = Introduction :doctype: book :toc: macro :toclevels: 4 :icons: font :sectnums: :sectnumlevels: 4 :sectnumoffset: 1 :partnums: :source-highlighter: rouge :experimental: :images-path: books/ipfw-primer/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] This book is about one of the native firewalls included with FreeBSD, man:ipfw[8] - the Internet Protocol FireWall. *ipfw* is designed to operate on a FreeBSD host with multiple network interfaces, to filter out unwanted traffic and pass through desired traffic. It does this based on a collection of rules (numbered, text based statements) that are entered into the system from the command line. indexterm:[rules] This usage model is different from many other firewall products that employ Graphical User Interfaces (GUIs), or separate control programs. indexterm:[GUI] All *ipfw* statements are entered into the user shell, typically by a user with root privileges or access to root privilege by means of programs that elevate normal user privileges such as man:sudo[8] or man:doas[1]. indexterm:[privilege] indexterm:[sudo] indexterm:[doas] *ipfw* reads network traffic from the interfaces it knows about and processes them inside the FreeBSD kernel. *ipfw* itself is a kernel module that can be either compiled into the kernel or loaded at run time. indexterm:[module, ipfw] It includes a number of other kernel modules (*ipfw_nat*, *ipfw_nptv6*, etc.) many of which we discuss in this book. indexterm:[module, ipfw_nat] indexterm:[module, ipfw_nptv6] A bird's-eye view of *ipfw* operation notes that: . Rules are organized into a sorted list based on a rule number . Packets entering the kernel from a network interface or leaving the kernel via a network interface are checked against the ruleset indexterm:[ruleset] . Rules are checked one by one and the first rule that matches the packet characteristics wins - that is, it accepts the packet for processing, either allowing transit through the firewall, denying transit, or moving the packet into userspace for specialized processing. The book makes frequent reference to the man:ipfw[8] manual page and the reader is advised to become familiar with the manual page alongside this book. There is also a section on *ipfw* in the link:https://docs.freebsd.org/en/books/handbook/firewalls/#firewalls-ipfw[FreeBSD Handbook Page on IPFW]. The intent with this book is to provide examples and informative material beyond the manual page and handbook to increase understanding and usage of *ipfw*. Throughout this book are many examples of using *ipfw* with virtual machines to simulate actual hardware. indexterm:[virtual machine] indexterm:[QEMU] These examples were developed with link:https://www.qemu.org[QEMU] version 9.0.2. It is, of course, entirely possible to perform all the examples in this book with real hardware. QEMU provides a way to perform the examples without spending any money for hardware. In either case, some setup is required. [NOTE] ==== Note that QEMU command syntax with some of the examples may have changed slightly by the time this book becomes available. Use the latest QEMU release where possible, and check the QEMU documentation if the examples in this book do not work correctly. ==== Also used are a number of scripts that allow easy man:if_bridge[4] and man:tap[4] setup, virtual machine setup, and data transfer from external VMs to or through a firewall VM. indexterm:[bridge] indexterm:[tap] In the early examples, data transfer is accomplished with the *netcat* program, specifically the version distributed with the link:https://www.nmap.org[nmap] package (www.nmap.org). indexterm:[netcat] indexterm:[nmap] This version, man:ncat[1], has the best coverage of features that are used throughout the book. indexterm:[ncat] A familiarity with the man page for man:ncat[1] is helpful, but not required. All scripts used in this book are found in crossref:ipfw-appendix-b[appendix-b,Appendix B] and published under the BSD 3-clause license. indexterm:[scripts] The scripts are also available on the GitHub link:https://github.com/jimmyb-gh/ipfw-primer[IPFW Primer] page. indexterm:[github] When copy/pasting examples, be aware that some desktop copy/paste functions add an extra space (or multiple spaces) to the end of a line, messing up the Unix continuation character convention ' ... \' at the end of a line. You should ensure that the paste function does not introduce extra spaces at the end of the line. [NOTE] ==== The lab examples in this book involve passing data between interfaces on the host system. A running firewall on the host such as *pf*, *ipfw*, or *ipfilter* (also known as *ipf*) may interfere with data transfer, so ensure that any host system firewall is disabled. In addition, take any necessary steps to ensure that this does not compromise the security of the host. ==== [[ipfw-introduction-quickstart]] == Quick Start This section details the steps to quickly begin using two QEMU virtual machines, one named "*firewall*" and the other named "*external1*". indexterm:[Quick Start] indexterm:[QEMU] These are the first two of several that will be used throughout this book. Additional detail, along with setup instructions for all virtual machines, is provided in crossref:ipfw-appendix-a[appendix-a, Appendix A]. [[ipfw-introduction-quick00]] .Setting Up the Initial Virtual Machines image::ipfw-introduction001.png[Setting Up the Initial Virtual Machines] The initial setup is that shown in crossref:ipfw-introduction[ipfw-introduction-quick00,Figure {counter:figure}]. indexterm:[virtual machine] [[qemu-vm-installation-process]] === QEMU VM Installation Process Follow the steps below to install and configure the QEMU virtual machines for this example. indexterm:[QEMU] [.procedure] ==== . On the FreeBSD host, install the necessary packages - man:qemu[1], man:sudo[8] (or man:doas[1]). Sudo, (or doas) is necessary for running the virtual machines as these QEMU configurations open a separate console window through SDL. indexterm:[SDL] The examples in this book use sudo. + [subs=+quotes] ---- # *pkg install qemu sudo* ---- + indexterm:[QEMU, pkg install] indexterm:[sudo] + Configure sudo as desired. + . Create a directory layout for virtual machines and scripts, and download an install ISO for FreeBSD. + [subs=+quotes] ---- % *mkdir -p ~/ipfw/VM ~/ipfw/SCRIPTS ~/ipfw/ISO* % *cd ~/ipfw/ISO* % *fetch https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/##/FreeBSD-##-RELEASE-amd64-dvd1.iso* ---- + indexterm:[SCRIPTS] indexterm:[ISO] indexterm:[fetch] + . Create the bridge and tap devices for the virtual machines (VMs) to use. + [subs=+quotes] ---- # *ifconfig tap0 create* # *ifconfig tap1 create* # *sysctl net.link.tap.up_on_open=1* net.link.tap.up_on_open: 0 -> 1 # *sysctl net.link.tap.user_open=1* net.link.tap.user_open: 0 -> 1 # *ifconfig bridge0 create* # *ifconfig bridge0 addm tap0 addm tap1 addm _hostintf_* #<--- replace _hostintf_ with host network interface (em0, bge0, etc.)# # *ifconfig bridge0 up* ---- + indexterm:[tap] indexterm:[bridge] + A script for creating and managing bridge and tap devices is introduced in the next section. + . Create two new VM image files and install FreeBSD on one. + [subs=+quotes] ---- % *cd ~/ipfw/VM* % *qemu-img create -f qcow2 -o preallocation=full firewall.qcow2 8G* % *qemu-img create -f qcow2 -o preallocation=full external1.qcow2 8G* % *cd ~/ipfw/ISO* % # Link a shorter name to the ISO image. % *ln -s FreeBSD--RELEASE-amd64-dvd1.iso fbsd.iso* % *cd ~/ipfw/SCRIPTS* Copy the below text into a file (say, [.filename]#firewall.sh#) and run: % *sudo /bin/sh firewall.sh* ------- #!/bin/sh # firewall.sh /usr/local/bin/qemu-system-x86_64 -monitor stdio \ -cpu qemu64 \ -vga std \ -m 4096 \ -smp 4 \ -cdrom ../ISO/fbsd.iso \ -boot order=cd,menu=on \ -blockdev driver=file,aio=threads,node-name=imgright,filename=../VM/firewall.qcow2 \ -blockdev driver=qcow2,node-name=drive0,file=imgright \ -device virtio-blk-pci,drive=drive0,bootindex=1 \ -netdev tap,id=nd0,ifname=tap0,script=no,downscript=no,br=bridge0 \ -device e1000,netdev=nd0,mac=02:69:70:66:77:00 \ -name \"Firewall\" exit ------- ---- + indexterm:[qemu-img] indexterm:[qcow2] indexterm:[-blockdev] indexterm:[-device] indexterm:[-netdev] + The FreeBSD installer should boot. Perform a standard installation of FreeBSD. + During the installation note the following: + * Select to use UFS as the filesystem. indexterm:[UFS] ZFS does not perform well with small memory sizes. indexterm:[ZFS] + * In this Quick Start, use DHCP for networking. indexterm:[DHCP] If desired, configure IPv6 if supported by the local LAN. + * When adding the default user, ensure they are a member of the _wheel_ group. + Once the installation completes, the virtual machine reboots into the newly installed FreeBSD image. + . Login as root and update the system if desired. + . Repeat the above step to create another QEMU script file, and perform another installation with these changes: + [subs=+quotes] ---- Copy the below text into a file (say, [.filename]#external1.sh#) and run: % *sudo /bin/sh external1.sh* ------- #!/bin/sh # external1.sh /usr/local/bin/qemu-system-x86_64 -monitor stdio \ -cpu qemu64 \ -vga std \ -m 4096 \ -smp 4 \ -cdrom ../ISO/fbsd.iso \ -boot order=cd,menu=on \ -blockdev driver=file,aio=threads,node-name=imgleft,filename=../VM/external1.qcow2 \ -blockdev driver=qcow2,node-name=drive0,file=imgleft \ -device virtio-blk-pci,drive=drive0,bootindex=1 \ -netdev tap,id=nd0,ifname=tap1,script=no,downscript=no,br=bridge0 \ -device e1000,netdev=nd0,mac=02:20:65:78:74:31 \ -name \"External1\" exit ------- ---- + . As above, login and update the system if desired. + . On both virtual machines (and all later installed VMs) , install the packages listed below The `nmap` package brings in the version of man:ncat[1] used by scripts on the *firewall* and *external* VMs. indexterm:[nmap] `nginx`, `lynx`, `cmdwatch`, `hping3`, and `iperf3` will be used in later chapters. indexterm:[nginx] indexterm:[lynx] indexterm:[cmdwatch] indexterm:[hping3] indexterm:[iperf3] + [subs=+quotes] ---- # *pkg install nmap nginx lynx cmdwatch hping3 iperf3* ---- + . Finally, download link:https://raw.githubusercontent.com/jimmyb-gh/ipfw-primer/main/ipfw/SCRIPTS/VM_SCRIPTS/IPFW_root_bin.tgz[IPFW_root_bin.tgz] file to *_both_* VMs. This tar file has a number of scripts needed for the virtual machines. + Move the tarzip file into [.filename]#/root# and extract the contents: + [subs=+quotes] ---- # *fetch https://raw.githubusercontent.com/jimmyb-gh/ipfw-primer/main/ipfw/SCRIPTS/VM_SCRIPTS/IPFW_root_bin.tgz* # # *mv IPFW_root_bin.tgz /root* # # *cd /root* # # *tar xvzf IPFW_root_bin.tgz* ... files are extracted into /root/bin # # *chmod +x /root/bin/***.sh* ---- + indexterm:[VM_SCRIPTS] (End installation procedure.) ==== For this Quick Start, it is Ok to use `DHCP` for both VMs. indexterm:[Quick Start] indexterm:[DHCP] In later examples there will be multiple external VMs using the *203.0.113.0/24* network and other private networks, all set up the same way and attached via man:tap[4] interfaces to one or more man:if_bridge[4] interfaces on the FreeBSD host. indexterm:[networks, private] indexterm:[bridge] indexterm:[tap] To ensure you have the first two VMs setup correctly, ping the *firewall* VM from the *external1* VM and vice-versa. Communications should be successful. If not, check the above installation details and troubleshoot any network issues. It should be possible ping in both directions, and even man:ssh[1] from one VM to the other. [TIP] ==== If you are having difficulty getting QEMU set up correctly, check the link:https://docs.freebsd.org/en/books/handbook/virtualization/#qemu-virtualization-host-guest[QEMU virtualization section in the FreeBSD Handbook]. ==== [TIP] ==== If the mouse is clicked in the QEMU console window, QEMU will “grab” the mouse. If this happens,type, kbd:[Ctl]+kbd:[Alt]+kbd:[G] to release the mouse. ==== indexterm:[mouse, grab] [TIP] ==== If suddenly, the QEMU console window is full screen, you have accidentally typed kbd:[Ctl]+kbd:[Alt]+kbd:[F]. If this happens, retype kbd:[Ctl]+kbd:[Alt]+kbd:[F] to restore the desktop screen. ==== indexterm:[full screen] === Disabling Syslog Messages to the Console in the Virtual Machines You may find it advantageous (even necessary) to stop syslog messages from being sent to the console (either the QEMU console, or the serial port). indexterm:[syslog] indexterm:[console, virtual] indexterm:[console, serial] To configure syslog to stop logging to the console, configure a file to receive console messages: [subs=+quotes] ---- # *touch /var/log/console.log* # # *chmod 0600 /var/log/console.log* ---- indexterm:[console.log] Then, as *root*, modify the line in [.filename]#/etc/syslog.conf# to read (instead of [.filename]#/dev/console#): indexterm:[/etc/syslog.conf] [subs=+quotes] ---- **.err;kern.warning;auth.notice;mail.crit /var/log/console.log* ---- And, if necessary: indexterm:[syslogd] [subs=+quotes] ---- # *service syslogd restart* ---- All messages previously bound for the console, will be directed to [.filename]#/var/log/console.log# instead. indexterm:[console, virtual] Before continuing, we have one more piece to add to each VM - a serial console. indexterm:[console, serial] We will need a serial console to examine the state of each VM independent of the main console. [[ipfw-introduction-serialconsole]] === Adding and Managing Serial Console Access to the VMs *Adding a serial console to FreeBSD* To add a serial console to each FreeBSD VM, start up the VM and edit the file [.filename]#/boot/loader.conf# and add *console=“comconsole”* to allow use of the serial console. indexterm:[/boot/loader.conf] indexterm:[comconsole] Reboot the VM to begin using the serial console. Note that FreeBSD diverts boot I/O to the serial console, so until the FreeBSD operating system is completely ready, output to the QEMU window will be limited. *Adding a serial device to QEMU* Adding a serial console to QEMU is fairly straightforward. indexterm:[add a serial console] indexterm:[console, serial] A single configuration line is added to the QEMU configuration to provide a serial device that is actually accessed over a man:telnet[1] session. indexterm:[telnet] The QEMU manual page, man:qemu[1], describes how the *-serial* keyword works in detail. indexterm:[serial keyword] QEMU redirects the serial port I/O to a TCP port on the host system at VM startup, and the QEMU monitor allows a man:telnet[1] connection on the configured port on the host. indexterm:[QEMU] indexterm:[QEMU, monitor] indexterm:[telnet] Once the FreeBSD system starts booting and recognizes the console directive in [.filename]#/boot/loader.conf# it redirects I/O to the serial console. The QEMU monitor detects this and manages the necessary character I/O on that serial port to the TCP port on the host. It is important to note that the this serial redirect over TCP takes place outside the virtual machine. There is no interaction with any network on the virtual machine and thus it is not subject to any firewall rules. Think of it just like a "dumb terminal" sitting on an RS-232 serial port on a real machine. indexterm:["dumb terminal"] *Management of serial console windows on the FreeBSD host* However, before we start adding serial devices, we need to plan how to manage them on the host. indexterm:[serial devices] Each QEMU VM generates a console window, and each serial device also needs its own window, potentially doubling the number of windows used. Possible solutions are: * Separate windows for each QEMU VM (doubles the number of windows) * Use tabbed windows (available on XFCE and some other desktops) * Use a terminal multiplexer such as man:tmux[1] or man:screen[1] indexterm:[tmux] indexterm:[screen] Our solution uses the multiplexer approach with the man:tmux[1] program for window management. crossref:ipfw-appendix-d[appendix-d,Appendix D] provides details on using both man:tmux[1] and man:screen[1]. indexterm:[tmux] indexterm:[screen] The following figures and descriptions use man:tmux[1]. Install *tmux* on the FreeBSD host with: indexterm:[tmux] [subs=+quotes] ---- # *pkg install tmux* ---- and review the file *swim.sh* in the SCRIPTS directory. indexterm:[swim.sh] The figure below shows the use of the *swim.sh* tmux session manager. Run *sh swim.sh* in the SCRIPTS directory to start up the session manager. indexterm:[session] [[ipfw-tmux-session-introduction]] .Starting Up tmux(1) Session Manager image::ipfw-introduction020.png[.Starting Up tmux(1) Session Manager] The figure shows five named windows in one session (session [0]) with the *tmux* status line in green at the bottom: indexterm:[session] * *0:bash* - a terminal window of the user running *swim.sh* * *1:firewall* - a terminal window to access the *firewall* VM * *2:external1* - a terminal window to access the *external1* VM * *3:external2* - a terminal window to access the *external2* VM * *4:external3* - a terminal window to access the *external3* VM The current window is marked with the '***' character on the status bar. indexterm:[status bar] [NOTE] ==== Uncomment additional window lines in *swim.sh* as you need them. ==== indexterm:[swim.sh] *Simplified tmux(1) Usage* *tmux* uses kbd:[Ctl]+kbd:[b] as its control key. indexterm:[control key] To move from window to window use kbd:[Ctl]+kbd:[b] kbd:[n] to move to the next window or kbd:[Ctl]+kbd:[b] kbd:[p] to move to the previous window. Use kbd:[Ctl]+kbd:[b] kbd:[?] for a list of all key bindings. indexterm:[binding, key] Type *tmux kill-server* in any session window to completely leave *tmux*. indexterm:[tmux] Consult the *tmux* manual page man:tmux[1] for more usage details. *Accessing the QEMU Serial Consoles* To access the VM serial consoles, move to the indicated window and telnet to the port on the local host for that VM: indexterm:[console, serial] [subs=+quotes] ---- Move to the *external1* window in *tmux*, then ~/ipfw/SCRIPTS % telnet localhost 4410 Trying ::1... Connected to localhost. Escape character is '^]'. FreeBSD/amd64 (external1) (ttyu0) login: ---- indexterm:[telnet] [TIP] ==== You can exit out of the *telnet* session by pressing kbd:[Ctl]+kbd:[\]] then pressing kbd:[q] like this: ---- login: (type Ctl+]) telnet> q Connection closed. ~/ipfw/SCRIPTS % ---- ==== Edits for the *firewall* VM to use the serial console are shown below: [subs=+quotes] ---- \#!/bin/sh ## firewall.sh #echo# #echo "NOTE!!! QEMU telnet server running!"# #echo "To access QEMU, telnet to localhost 4450"# #echo# /usr/local/bin/qemu-system-x86_64 -monitor none \ #-serial telnet:localhost:4450,server=on,wait=off \# -cpu qemu64 \ -vga std \ -m 4096 \ -smp 4 \ -cdrom ../ISO/fbsd.iso \ -boot order=cd,menu=on \ -blockdev driver=file,aio=threads,node-name=imgright,filename=../VM/firewall.qcow2 \ -blockdev driver=qcow2,node-name=drive0,file=imgright \ -device virtio-blk-pci,drive=drive0,bootindex=1 \ -netdev tap,id=nd0,ifname=tap0,script=no,downscript=no,br=bridge0 \ -device e1000,netdev=nd0,mac=02:69:70:66:77:00 \ -name \"Firewall\" #&# exit ------- ---- indexterm:[telnet] indexterm:[QEMU] Edits for the *external1* VM to use the serial console: [subs=+quotes] ---- ------- \#!/bin/sh ## external1.sh #echo# #echo "NOTE!!! QEMU telnet server running!"# #echo "To access QEMU, telnet to localhost 4410"# #echo# /usr/local/bin/qemu-system-x86_64 -monitor none \ #-serial telnet:localhost:4410,server=on,wait=off \# -cpu qemu64 \ -vga std \ -m 4096 \ -smp 4 \ -cdrom ../ISO/fbsd.iso \ -boot order=cd,menu=on \ -blockdev driver=file,aio=threads,node-name=imgleft,filename=../VM/external1.qcow2 \ -blockdev driver=qcow2,node-name=drive0,file=imgleft \ -device virtio-blk-pci,drive=drive0,bootindex=1 \ -netdev tap,id=nd0,ifname=tap1,script=no,downscript=no,br=bridge0 \ -device e1000,netdev=nd0,mac=02:20:65:78:74:31 \ -name \"External1\" #&# exit ------- ---- You should now have two QEMU VMs (*firewall* and *external1*) started, and have serial console sessions available through the *tmux* sessions as shown below. indexterm:[tmux] Configure the FreeBSD *host*, *firewall* VM, and *external1* VM with DHCP addressing as shown in the figure at the beginning on this Quick Start session. indexterm:[Quick Start] You should have full connectivity between your FreeBSD *host*, the *firewall* VM and the *external1* VM. [[ipfw-startup-with-serial-console]] .External1 and Firewall VMs Startup with Serial Console image::ipfw-introduction025.png[.Firewall and External1 VMs Startup with Serial Console] Scripts used by the two QEMU VMs are discussed in the next section.