creating FreeBSD packages without ports: part 2

I believe I now have an effective FreeBSD package manifest creator. It will be included in the next release of libDwm.

I need this for the cases where I want to build a FreeBSD package for distribution without using ports. I have my own needs for this, but there are other cases where it makes sense to have this kind of functionality. For example, when you need to distribute data or configuration files. Heck, even packaging your shell initialization and other rc files from your home directory. Or in my case, packaging small things on a Raspberry Pi where I don’t have ports installed.

The program inside libDwm is called ‘mkfbsdmnfst’. It’s in libDwm because I intend to use it for packaging libDwm and the support classes are in libDwm.

It will read a template manifest file before reading files from a staging directory. This template file is in manifest format, though it also allows comments. Having the template allows the stuff that doesn’t change to be put in one place. Another benefit is that you can prepopulate parts of the ‘files:’ section. For example, when you have a binary that you want to be setuid root when the package is installed, you can use a line like this in the ‘files:’ section:

/usr/local/sbin/evilsetuidprog:{uname:root,gname:wheel,perm:04555}

I check that all files in the manifest exist in the staging directory, so that you will receive an error message if you have a manifest with files that don’t exist.

More in a later post after I’ve written the manpage.

creating FreeBSD packages without ports

This weekend I spent some time working on creating FreeBSD packages of my software.

Background…

Way back when, I would use epm. It worked up until FreeBSD switched to pkgng, which was a long time ago. It looks like the author of epm has no interest in updating epm to create native packages on FreeBSD. Probably in no small part because pkgng changed things dramatically. It does make me wonder why epm is still in the ports tree, since its primary facility has not worked on FreeBSD for a long time.

At any rate, pkgng’s ‘pkg create …’ needs a manifest file in order to do what I need. It’s a relatively simple file, though it appears that it’s overly forgiving of missing/present quotes and commas (i.e. the grammar isn’t very rigorous). I’ll blame YAML here.

While I’d like to use libpkg to do what I need, the important data structures are in a private header file. Which presumably means it’s subject to change. And there appears to be no good way to get at the contents of a manifest without using the private header file. Though I’ve yet to look at using libucl to parse a manifest file.

I wrote my own FreeBSDPkg::Manifest class and helper classes, along with a lexer/parser for manifest files using flex and bison. The parser can populate a Manifest object from a manifest file. The manifest can then be manipulated as desired, and emitted to an ostream. For my own software packages, this will allow me to create a skeletal manifest file from the build, then further populate it with my files, then create a native FreeBSD package. What I have today works on libDwm, and the next release of libDwm will include the classes and some supporting applications.

My own version of ‘what’, geared toward SVN users

Some of you older folks like me may remember the ‘what’ program. Actually, most software developers probably know about it, but aren’t aware of its history. It came from SCCS, a now ancient source code control system.

The gist of the ‘what’ program: it searches files for strings that start with ‘@(#)’. It is generally available in the base install of all UNIX systems, including OS X.

I’ve continued to use it for some things. Why? There are occasions where a user of my software is using a binary whose origin they do not know. Typically this happens when I give them something from a trunk for testing, but it also happens when they compile and install from a tag but don’t use any package management for the installation (perhaps they installed in their home directory on a host where they don’t have administrator privileges).

Since I embed ‘@(#)’ strings in my source code that get compiled into binaries, I can usually get a pretty good idea of what version a user is having problems with by having them run ‘what’ on the binary, even if it’s from a trunk.

However, I’ve never been fond of the output of ‘what’. The way to get useful information into all compiled files with a typical revision control system (CVS in the old days, Subversion today) is via expanded keywords. I don’t care to see the keywords themselves, and in most cases I don’t care to see the whole repository path. Not to mention the decorations (@(#), $, etc.). I need tag/branch/trunk information, and revision numbers.

When I was using CVS as my repository system, I embedded “@(#) $Name:$ $Id:$” strings in my compilable source files. I actually used this string as the argument to the constructor of my CvsTag class, which allowed me to programmatically retrieve version information for the compilation unit. Today, using Subversion, I do something similar using my SvnTag class. However, Subversion has no equivalent to $Name:$. So in my sources, I’m now converting to using a custom keyword called DwmPath (very unoriginal), whose expansion is used in the constructor of SvnTag instances. It contains the relative path of the file in the repository and the revision number. Custom keywords are available in Subversion 1.8 and above.

What my ‘what’ replacement does is similar to ‘what’ in that it searches for strings that start with ‘@(#)’. I still embed these start characters to remain compatible with ‘what’. But if I find the string contains a Subversion keyword DwmPath or Header or HeadURL, I pluck out just that information for display, minus the keywords themselves. And if the path is typical (under a trunk, branches or tags directory), I will do more processing. For a trunk path, I’ll show the revision number of the last revision of the file. For a tag path, I’ll show the tag name. For a branch, I’ll show the branch name and ‘(branch)’ to distinguish it from tags of the same name.

With a ‘-s’ (short) argument on the command line, I’ll shorten the file path to just its basename.

Some example output from the trunk of libDwm:

dwm@kiva:/home/dwm/svn/dwm/libDwm/trunk/apps/dwmwhat% ./dwmwhat dwmwhat
trunk (8407) dwm/libDwm/trunk/apps/dwmwhat/dwmwhat.cc
trunk (8388) dwm/libDwm/trunk/src/DwmOptArgs.cc
trunk (8388) dwm/libDwm/trunk/src/DwmStringUtils.cc
trunk (8402) dwm/libDwm/trunk/src/DwmSvnTag.cc

And the shortened output on the dwmwhat program itself:

dwm@kiva:/home/dwm/svn/dwm/libDwm/trunk/apps/dwmwhat% ./dwmwhat -s dwmwhat
trunk (8407) dwmwhat.cc
trunk (8388) DwmOptArgs.cc
trunk (8388) DwmStringUtils.cc
trunk (8402) DwmSvnTag.cc

And of course it still works on programs that don’t have Subversion keywords:

% dwmwhat /usr/bin/php
@(#)$File: apprentice.c,v 1.191 2013/02/26 21:02:48 christos Exp $
@(#)ADF Database
@(#)SunPHIGS
@(#)$File: apptype.c,v 1.13 2011/09/07 21:57:15 christos Exp $
@(#)$File: ascmagic.c,v 1.85 2012/08/09 16:33:15 christos Exp $
@(#)$File: cdf.c,v 1.53 2013/02/26 16:20:42 christos Exp $
@(#)$File: cdf_time.c,v 1.12 2012/05/15 17:14:36 christos Exp $
@(#)$File: compress.c,v 1.70 2012/11/07 17:54:48 christos Exp $
@(#)$File: encoding.c,v 1.7 2012/01/24 19:02:02 christos Exp $
@(#)$File: fsmagic.c,v 1.67 2013/03/17 15:43:20 christos Exp $
@(#)$File: funcs.c,v 1.61 2012/10/30 23:11:51 christos Exp $
@(#)$File: is_tar.c,v 1.37 2010/11/30 14:58:53 rrt Exp $
@(#)$File: magic.c,v 1.78 2013/01/07 18:20:19 christos Exp $
@(#)$File: print.c,v 1.76 2013/02/26 18:25:00 christos Exp $
@(#)$File: readcdf.c,v 1.33 2012/06/20 21:52:36 christos Exp $
@(#)$File: readelf.c,v 1.97 2013/03/06 03:35:30 christos Exp $
@(#)$File: softmagic.c,v 1.165 2013/03/07 02:22:24 christos Exp $

It’s probably worth noting that to make this speedy, I resorted to using mmap(). My first pass read the file using getdelim(), which worked but was 4 times slower than using mmap(). I consider it very usable now. On a fairly large library file, it’s speedy enough for me. It is single threaded, but on my dual Xeon L5640 machine:

dwm@kiva:/home/dwm/svn/dwm/libDwm/trunk% ls -l lib/libDwm.a 
-rw-r--r--  1 dwm  dwm  18616988 Apr 18 00:40 lib/libDwm.a
dwm@kiva:/home/dwm/svn/dwm/libDwm/trunk% /usr/bin/time dwmwhat -s lib/libDwm.a
trunk (8383) DwmBZ2IO.cc
trunk (8384) DwmCgi.ll
trunk (8386) DwmConditionVariable.cc
trunk (8387) DwmCvsTag.cc
trunk (8388) DwmDaemonUtils.cc
trunk (8388) DwmDateTime.cc
trunk (8388) DwmDirectoryEntry.cc
trunk (8388) DwmDescriptor.cc
trunk (8388) DwmEngFormat.cc
trunk (8388) DwmExecutor.ll
trunk (8388) DwmFileLogger.cc
trunk (8388) DwmFileRoller.cc
trunk (8388) DwmGZIO.cc
trunk (8388) DwmGroup.cc
trunk (8388) DwmHostPinger.cc
trunk (8388) DwmIO.cc
trunk (8388) DwmIpv4Address.cc
trunk (8388) DwmIpv4IcmpDestinationUnreachable.cc
trunk (8388) DwmIpv4IcmpEchoReply.cc
trunk (8388) DwmIpv4IcmpEchoRequest.cc
trunk (8388) DwmIpv4IcmpMessage.cc
trunk (8388) DwmIpv4IcmpRedirect.cc
trunk (8388) DwmIpv4IcmpSourceQuench.cc
trunk (8388) DwmIpv4IcmpTimeExceeded.cc
trunk (8388) DwmIpv4PacketHeader.cc
trunk (8388) DwmIpv4Prefix.cc
trunk (8388) DwmIpv4TcpHeader.cc
trunk (8388) DwmIpv4TcpPacket.cc
trunk (8388) DwmIpv4TcpPayload.cc
trunk (8388) DwmIpv4UdpHeader.cc
trunk (8388) DwmIpv4UdpPacket.cc
trunk (8388) DwmIpv4UdpPayload.cc
trunk (8388) DwmIpv4Utils.cc
trunk (8388) DwmIpv6Address.cc
trunk (8388) DwmIpv6Prefix.cc
trunk (8388) DwmLocalInterfaces.cc
trunk (8388) DwmMacAddress.cc
trunk (8388) DwmMplsLabel.cc
trunk (8388) DwmMplsLabelStack.cc
trunk (8388) DwmMutex.cc
trunk (8388) DwmOptArgs.cc
trunk (8388) DwmPacer.cc
trunk (8388) DwmPassword.cc
trunk (8388) DwmPathUtils.cc
trunk (8388) DwmPcap.cc
trunk (8388) DwmPingDestination.cc
trunk (8388) DwmPingDestinationEntry.cc
trunk (8388) DwmPthreadLocker.cc
trunk (8388) DwmPthreadReadWriteLock.cc
trunk (8388) DwmPthreadSignal.cc
trunk (8388) DwmRegistry.ll
trunk (8388) DwmRusage.cc
trunk (8388) DwmSignal.cc
trunk (8388) DwmSocket.cc
trunk (8388) DwmStringUtils.cc
trunk (8388) DwmStrptime.cc
trunk (8402) DwmSvnTag.cc
trunk (8388) DwmSysLogger.cc
trunk (8388) DwmTermios.cc
trunk (8388) DwmTimeInterval.cc
trunk (8388) DwmTimeUtil.ll
trunk (8388) DwmTimeValue.cc
trunk (8388) DwmTypeName.cc
trunk (8388) DwmUnitAssert.cc
trunk (8388) DwmXmlAttribute.cc
trunk (8388) DwmXmlElement.cc
trunk (8388) DwmProcessInfo.cc
trunk (8388) DwmProcessTable.cc
        0.08 real         0.08 user         0.00 sys

tcsh completions for svn URLs

For quite some time, I’ve wanted tcsh completions for URLs when using svn (the Subversion command-line client). I finally wrote my own recently.

There were solutions available on the web, but they only worked with file:// URLs. I suspect most developers are rarely using file:// URLs. Typically the repository is on another machine with some type of remote access. In my case, via svn+ssh:// URLs. I was unable to find anything that worked with remote URLs.

Since tcsh 6.17 and above expose a COMMAND_LINE environment variable that contains the current command line, it wasn’t rocket science to write a simple C++ program to complete svn URLs. The source code for my simple solution is available here: svnutils_tcsh.tgz

If you compile this and put the resulting cmpl_svn_url binary in your bin directory, you can then have an svn completion in your .tcshrc that contains this:

complete svn ...
             'C@file:///@`'"${HOME}/bin/cmpl_svn_url"'`@@' \
             'C@svn+ssh://@`'"${HOME}/bin/cmpl_svn_url"'`@@' \
             'C@http://@`'"${HOME}/bin/cmpl_svn_url"'`@@' \
             'C@https://@`'"${HOME}/bin/cmpl_svn_url"'`@@' \
             ...

Obviously this will be slow if access to the Subversion server is slow. That’s not the case for my home repository since it’s on a local gigabit ethernet connection. But even when slow, it’s handy for new checkouts, creating branches or tags, listing the repository or ‘svn cat’ to see a file in the repository.

My personal repository migrated from CVS to Subversion

This was a long time coming, but I’ve finally migrated my personal source code repository from CVS to Subversion. I had to make many passes at it with cvs2svn to get it laid out reasonably well, but it’s done.

The new subversion repository is in one of my ZFS pools (mirrored), hence I feel a little more secure about its longevity. And it’s not as if I don’t have backups. I wrote a new script to snapshot and send/receive the repository nightly using an incremental scheme. A full snapshot is sent every 4 weeks, an incremental versus the full is sent every week, and an incremental versus the weekly is sent every night. These are received in a ZFS pool on another host. I’ll likely svnadmin dump the repository monthly as well to send off-site.

Why did it take me so long to do this migration? Well, there are things I still like about CVS. One of them is $Name:$ substitution for tags, which has no equivalent in Subversion. Yes, there’s svnversion, but that’s not the same thing nor is it even close to the same thing. And when you’ve long been using this feature (my CvsTag class, for example), it’s painful to give it up. Fortunately I now have an SvnTag class in libDwm that provides at least some of the functionality I need. Namely being able to emit a tag or branch name from within a program by parsing the HeadURL substitution with a user-provided regular expression at compile time.

Another reason was simply the amount of work required. I have about 1.1 million lines of C, C++ and java code in my repository, spread across projects going back 25 years.

I won’t write about all of the upsides of using Subversion versus CVS. It’s a topic that’s been beaten to death, and if there were no upsides, I would not have migrated. Obviously, the constant-time and space remote copy mechanism (used for branches and tags) is a huge boon for frequent branches and tags of large projects. As is the ability to delete branches that are no longer active. These things alone were enough motivation for me to switch.

A friend asked me, “Why not switch to GIT?”. The simple answer is that I don’t have enough experience with it yet. I’ve used it, but I’m a novice. And my needs don’t match GITs primary features. I don’t need a full history of my projects on the machine on which I’m working, and my personal repository is for personal use. Since my Subversion repository is on my local gigabit ethernet, I don’t suffer much for speed. And since I own and administer the machine hosting the repository, I’m free to have the backup scheme I need.

llvm/clang libclangTooling: making progress on unit test generator

I’ve spent a decent amount of time for my day job creating a tool to generate unit/component test skeletons from existing source code. I’ve made reasonable progress in the last couple of weeks.

Background… this isn’t the first time I’ve run into code without unit tests. Any of us that’ve had to deal with legacy code have found small to large chunks of code with no unit tests. I think of this as flying blind from a development perspective. It’s generally impossible for integration testing and software validation to find all problems. This is of course true of unit tests as well, but unit tests can find issues that other testing cannot, and ideally before code is committed to a repository.

At any rate, the point of my tool is to speed up the creation of unit tests for legacy code that doesn’t have any unit tests written.

The first pass was relatively easy after finding my way around the libclangTooling APIs. Generating skeletons from a blank slate (no existing tests) is easy.

I did get hung up for a bit with an existing pattern from a tool we already had. That pattern combined the code to be tested with the unit tests in a single translation unit. It turns out that this complicates things significantly if you really want to produce test code that compiles without intervention in most cases. I’ve since decided that it’s smarter to produce a translation unit that #includes the code to be tested. This accomplishes the main purpose of the previous pattern (bring module-static variables into scope) without touching the code under test. This separates the concerns of unit tests versus original source code, so there are no questions about whether or not the original source code matches what is being tested: by #including it, it is unquestionably unmodified. Yes, it’s unconventional to #include a .c or

The hard part came when I decided that I needed to be able to automatically update the unit test code. This is basically a sanity check sort of thing… if someone adds a new function to a translation unit and doesn’t write a test for the new function, at a minimum I want my tool to warn me. Better, it should generate a new unit test that fails until someone populates it, and should call it from main() in the test driver so that no one will miss the fact that there’s a failing unit test.

Conversely, the tool should warn about tests for functions that no longer exist and hence have unit test functions that will not compile and link. Or better, delete the unit test for the function that no longer exists.

I’m now doing both of these things. I add new unit test skeletons that don’t exist, while leaving the existing ones alone. I remove unit tests for functions that have disappeared.

I wound up doing this with 2 separate passes of ClangTool::run(), each using a different class derived from ASTFrontendAction and processing different files. The first pass processes the code under test, the second pass processes the unit test code.

So far so good with respect to functions under test and unit test functions. I now need to to the same for the extern global variables and stub functions that I generate as part of the first pass.

mcblock examples

I recently wrote about the creation of a new utility I created to help manage my pf rules called mcblock. Thus far the most useful part has been the automation of rule addition by grokking logs.

For example, it can parse auth.log on FreeBSD and automatically add entries to my pf rule database. And before adding the entries, it can show you what it would do. For example:

# bzcat /var/log/auth.log.0.bz2 | mcblock -O - 
109.24.194.41        194 hits
  add 109.24.194/24 30 days
103.25.133.151         3 hits
  add 103.25.133/24 30 days
210.151.42.215         3 hits
  add 210.151.42/24 30 days

What I’ve done here is uncompress auth.log.0.z2 to stdout and pipe it to mcblock to see what it would do. mcblock shows that it would add three entries to my pf rule database, each with an expiration 30 days in the future. I can change the number of days with the -d command line option:

# bzcat /var/log/auth.log.0.bz2 | mcblock -d 60 -O -
109.24.194.41        194 hits
  add 109.24.194/24 60 days
103.25.133.151         3 hits
  add 103.25.133/24 60 days
210.151.42.215         3 hits
  add 210.151.42/24 60 days

By default, mcblock uses a threshold of 3 entries from a given offending IP address in a log file. This can be changed with the -t argument:

# bzcat /var/log/auth.log.0.bz2 |  mcblock -t 1 -O - 
109.24.194.41        194 hits
  add 109.24.194/24 30 days
103.25.133.151         3 hits
  add 103.25.133/24 30 days
210.151.42.215         3 hits
  add 210.151.42/24 30 days
31.44.244.11           2 hits
  add 31.44.244/24 30 days

If I’m happy with these actions, I can tell mcblock to execute them:

# bzcat /var/log/auth.log.0.bz2 | mcblock -t 1 -A -

And then look at one of the entries it added:

# mcblock -s 31.44.244/24
31.44.244.0/24     2015/08/21 - 2015/09/20

This particular address space happens to be from Russia, and is allocated as a /23. So let’s add the /23:

# mcblock -a 31.44.244/23

And then see what entries would match 31.44.244.11:

# mcblock -s 31.44.244.11
31.44.244.0/23     2015/08/21 - 2015/09/20

The /24 was replaced by a /23. Let’s edit this entry to add the registry and the country, and extend the time period:

# mcblock -e 31.44.244/23
start time [2015/08/21 04:37]: 
end time [2015/09/20 04:37]: 2016/02/21 04:37
registry []: RIPE
country []: RU
Entry updated.

And view again:

# mcblock -s 31.44.244.11
31.44.244.0/23     2015/08/21 - 2016/02/21 RIPE     RU

mcblock: new code for pf rule management from a ‘lazy’ programmer

Good programmers are lazy. We’ll spend a good chunk of time writing new/better code if we know it will save us a lot of time in the future.

Case in point: I recently completely rewrote some old code I use to manage the pf rules on my gateway. Why? Because I had been spending too much time doing things that could be done automatically by software with just a small bit of intelligence. Basically codifying the things I’ve been doing manually. And also because I’m lazy, in the way that all good programmers are lazy.

Some background…

I’m not the type of person who fusses a great deal about the security of my home network. I don’t have anything to hide, and I don’t have a need for very many services. However, I know enough about Internet security to be wary and to at least protect myself from the obvious. And I prefer to keep out the hosts that have no need to access anything on my home network, including my web server. And a very long time ago, I was a victim of an SSH-v1 issue and someone from Romania set up an IRC server on my gateway while I was on vacation in the Virgin Islands. I don’t like someone else using my infrastructure for nefarious purposes.

At the time, it was almost humorous how little the FBI knew about the Internet (next to nothing). I’ll never forget how puzzled the agents were at my home when I was explaining what had happened. The only reason I had called them was because the perpetrator managed to get a credit card number from us (presumably by a man-in-the-middle attack) and used it to order a domain name and hosting services. At the time I had friends with fiber taps at the major exhanges and managed to track down some of his traffic and eventually a photo of him and his physical address (and of course I had logged a lot of the IRC traffic before I completely shut it down). Didn’t do me any good since he was a Russian minor living in Romania. The FBI agents knew nothing about the Internet. My recollection is hazy, but I think this was circa 1996. I know it was before SSH-v2, and that I was still using Kerberos where I could.

Times have changed (that was nearly 20 years ago). But I continue to keep a close eye on my Internet access. I will never be without my own firewall with all of the flexibility I need.

For a very long time, I’ve used my own software to manage the list of IP prefixes I block from accessing my home network. Way back when, it was hard: we didn’t have things like pf. But all the while I’ve had some fairly simple software to help me manage the list of IP prefixes that I block from accessing my home network and simple log grokking scripts to tell me what looks suspicious.

Way back when, the list was small. It grew slowly for a while, but today it’s pretty much non-stop. And I don’t think of myself as a desirable target. Which probably means that nearly everyone is under regular probing and weak attack attempts.

One interesting thing I’ve observed over the last 5 years or so… the cyberwarfare battle lines could almost be drawn from a very brief lesson on WWI, WWII and the Cold War, with maybe a smattering of foreign policy SNAFUs and socialism/communism versus capitalism and East versus West. In the last 5 years, I’ve primarily seen China, Russia, Italy, Turkey, Brazil and Columbia address space in my logs with a smattering of former Soviet block countries, Iran, Syria and a handful of others. U.S. based probes are a trickle in comparison. It’s really a sad commentary on the human race, to be honest. I would wager that the countries in my logs are seeing the opposite directed at them: most of their probes and attacks are likely originating from the U.S. and its old WWII and NATO allies. Sigh.

Anyway…

My strategy

For about 10 years I’ve been using code I wrote that penalizes repeat attackers by doubling their penalty time each time their address space is re-activated in my blocked list. This has worked well; the gross repeat offenders wind up being blocked for years, while those who only knock once are only blocked for a long enough time to thwart their efforts. Many of them move on and never return (meaning I don’t see more attacks from their address space for a very long time). Some never stop, and I assume some of those are state-sponsored, i.e. they’re being paid to do it. Script kiddies don’t spend years trying to break into the same tiny web site nor years scanning gobs of broadband address space. Governments are a different story with a different set of motivations that clearly don’t go away for decades or even centuries.

The failings

The major drawback to what I’ve been doing for years: too much manual intervention, especially adding new entries. It doesn’t help that there is no standard logging format for various externally-facing services and that the logging isn’t necessarily consistent from one version to the next.

My primary goal was to automate the drudgery, replace the SQL database in the interest of having something lighter and speedier, while leveraging code and ideas that have worked well for me. I created mcblock as a simple set of C++ classes and a single command-line application to serve the purpose of grokking logs and automatically adding to my pf rules.

Automation

  • I’m not going to name all the ways in which I automatically add offenders, but I’ll mention one: I parse auth.log.0.bz2 every time newsyslog rolls over auth.log. This is fairly easy on FreeBSD, see the entry regarding the R flag and path_to_pid_cmd_file in the newsyslog.conf(5) manpage. Based on my own simple heuristics, those who've been offensive will be blocked for at least 30 days. Longer if they're repeat offenders, and I will soon add policy to permit more elaborate qualifications. What I have today is fast and effective, but I want to add some feeds from my probe detector (reports on those probing ports on which I have nothing listening) as well as from pflog. I can use those things today to add entries or re-instantiate expired entries, but I want to be able to extend the expiration time of existing active entries for those who continue to probe for days despite not receiving any response packets.
  • My older code used an SQL database, which was OK for most things but made some operations difficult on low-power machines. For example, I like to be able to automatically coalesce adjacent networks before emitting pf rules; it makes the pf rules easier to read. For example, if I already have 5.149.104/24 in my list and I add 5.149.105/24, I prefer emitting a single rule for 5.149.104/23. And if I add 5.149.105/24 but I have an inactive (expired) rule for 5.149.104/22, I prefer to reactivate the 5.149.104/22 rule rather than add a new rule specifically for 5.149.105/24. My automatic additions always use /24's, but once in a while I will manually add wider rules knowing that no one from a given address space needs access to anything on my network or the space is likely being used for state-sponsored cyberattacks. Say Russian government address space, for example; there's nothing a Russian citizen would need from my tiny web site and I certainly don't have any interest in continuous probes from any state-sponsored foreign entity.
  • Today I'm using a modified version of my Ipv4Routes class template to hold all of the entries. Modified because my normal Ipv4Routes class template uses a vector of unordered_map under the hood (to allow millions of longest-match IPv4 address lookups per second), but I need ordering and also a smaller memory footprint for my pf rule generation. While it's possible to reduce the memory footprint of unordered_map by increasing the load factor, it defeats the purpose (slows it down) when your hash key population isn't well-known and you still wind up with no ordering. Ordering allows the coalescing of adjacent prefixes to proceed quickly, so my modified class template uses map in place of unordered_map. Like my original Ipv4Routes class template, I have separate maps for each prefix length, hence there are 33 of them. Of course I don't have a use for /0, but it's there. I also typically don't have a use for the /32 map, but it's also there. Having the prefix maps separated by netmask length makes it easy to understand how to find wider and narrower matches for a given IP address or prefix, and hence write code that coalesces or expands prefixes. And it's more than fast enough for my needs: it will easily support hundreds of thousands of lookups per second, and I don't need it to be anywhere near as fast as it is. But I only had to change a couple of lines of my existing Ipv4Routes class template to make it work, and then added the new features I needed.
  • I never automatically remove entries from the new database. That's because historical information is useful and the automation can re-activate an existing but expired entry that might be a wider prefix than what I would allow automation to do without such information. While heuristics can do some of this fairly reliably, expired entries in the database serve as additional data for heuristics. If I've blocked a /16 before, seeing nefarious traffic from it again can (and usually should) trigger reactivation of a rule for that /16. And then there are the things like bogons and private space that should always be available for reactivation if I see packets with source addresses from those spaces coming in on an external interface.
  • Having this all automated means I now spend considerably less time updating my pf rules. Formerly I would find myself manually coalescing the database, deciding when I should use a wider prefix, reading the daily security email from my gateway to make sure I wasn't missing anything, etc. Since I now have unit tests and a real lexer/parser for auth.log, and pf entries are automatically updated and coalesced regularly, I can look at things less often and at my leisure while knowing that at least most of the undesired stuff is being automatically blocked soon after it is identified.

Good programmers are lazy. A few weekends of work is going to save me a lot of time in the future. I should've cobbled this up a long time ago.

depot’s backup space is now ZFS mirror

Last night I installed an HGST Deskstar NAS 4TB drive in depot to pair with the existing HGST Deskstar 4TB drive. I saved the existing data to a ZFS pool on kiva, then wiped the existing HGST Deskstar drive: unmounted the filesystem, deleted the partition, deleted the partitioning scheme.

If you’re doing this for the first time on FreeBSD 10.1 or later, don’t forget to enable ZFS (loading of the kernel module) and tell the system to mount ZFS pools at boot.

Enable ZFS kernel module at boot by adding to /boot/loader.conf:

zfs_load="YES"

Tell the system to mount ZFS pools at boot by adding to /etc/rc.conf:

zfs_enable="YES"

If you haven’t rebooted after changing /boot/loader.conf, you can load the kernel module manually:

# kldload zfs

Before getting started, I changed the default ashift setting to be more amenable to 4k drives:

# sysctl vfs.zfs.min_auto_ashift=12

I then created my ZFS pool. First I created the GPT partitioning scheme on each drive:

# gpart create -s gpt ada0
# gpart create -s gpt ada4

I then created a partition on each, leaving 1 gigabyte of space unused:

# gpart add -t freebsd-zfs -l gpzfs1_0 -b1M -s3725G ada0
# gpart add -t freebsd-zfs -l gpzfs1_1 -b1M -s3725G ada4

I then created the pool:

# zpool create zfs1 mirror /dev/gpt/gpzfs1_0 /dev/gpt/gpzfs1_1

I created my filesystem heirarchy. For now I only need my backups mount point. Since FreeBSD now has lz4_compress enabled by default, I can use lz4 compression. lz4 is considerably faster than lzjb, especially on incompressible data.

# zfs create -o compression=lz4 zfs1/backups

I then copied back the original data that was on the single HGST Deskstar 4TB drive. Since I had disabled TimeMachine on my desktop computer in order to move TimeMachine backups to the ZFS mirror, I re-enabled TimeMachine on my desktop and manually asked it to perform a backup. It worked fine and completed in less than 2 minutes since I hadn’t changed much on my desktop machine.

First ZFS pool now on kiva

I finally got around to creating the first ZFS pool on my new-to-me server (kiva). At the moment, this particular pool is for backups of other machines.

I am using the 4TB HGST Deskstar drive I bought a little bit ago, and a 4TB HGST Deskstar NAS I bought today. Once installed in hot-swap bays, they showed up as da1 and da2.

I created the GPT partitioning scheme on each:

# gpart create -s gpt da1
# gpart create -s gpt da2

I created a partition on each, leaving 2 gigabytes of space unused. It’s not uncommon for a replacement drive to have slightly less space, and I don’t want to be trapped in a jam if one of the drives fails and I need to use a different type of drive as a replacement. 2 gigabytes seems like a lot of space, but in the grand scheme of this ZFS host, it’s nothing. On FreeBSD, there is no performance penalty for using partitions for ZFS versus using whole disks. This allows me to wait to buy a replacement disk, which means I don’t have spare disks sitting around with their warranty period ticking away without the drives being used. I would always have spare drives on hand in a production environment, but at home it makes sense (especially for backups) to wait for a drive to have some trouble before purchasing its replacement. 4TB drives are readily available locally.

# gpart add -t freebsd-zfs -l gpzfs1_0 -b1M -s3724G da1
# gpart add -t freebsd-zfs -l gpzfs1_1 -b1M -s3724G da2

So now I see:

% gpart show da1
=>        34    7814037101  da1  GPT  (3.6T)
          34          2014       - free -  (1.0M)
        2048    7809794048    1  freebsd-zfs  (3.6T)
  7809796096       4241039       - free -  (2.0G)

% gpart show da2
=>        34    7814037101  da2  GPT  (3.6T)
          34          2014       - free -  (1.0M)
        2048    7809794048    1  freebsd-zfs  (3.6T)
  7809796096       4241039       - free -  (2.0G)

I created the pool:

# zpool create zfs1 mirror /dev/gpt/gpzfs1_0 /dev/gpt/gpzfs1_1

I created my filesystem heirarchy. For now I only need my backups mount point. Since FreeBSD now has lz4_compress enabled by default, I can use lz4 compression. lz4 is considerably faster than lzjb, especially on incompressible data.

# zfs create -o compression=lz4 zfs1/backups

And since I had not yet enabled ZFS on kiva, I added to /boot/loader.conf:

zfs_load="YES"

And added to /etc/rc.conf:

zfs_enable="YES"

After copying over 38 gigabytes of backups from another host, I have this:

% zpool list -v
NAME               SIZE  ALLOC   FREE  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
zfs1              3.62T  23.9G  3.60T         -     0%     0%  1.00x  ONLINE  -
  mirror          3.62T  23.9G  3.60T         -     0%     0%
    gpt/gpzfs1_0      -      -      -         -      -      -
    gpt/gpzfs1_1      -      -      -         -      -      -

lz4 compression yielded a 37% reduction in disk space for these backups. That’s quite reasonable.

A friend asked me why I was using a mirror. The simple answer is that it’s more reliable than raidzN, and more easily expanded. This machine has 12 hot-swap drive bays, and I don’t expect to need all of them anytime soon (if ever). While a raidzN is more space-efficient, it’s not easily expanded and when one drive from a batch fails, others are often not far behind. Resilvering a raidzN is hard on all of the drives involved, and it’s not uncommon to have another disk fail during a resilvering. Resilvering a raidzN is slower than resilvering a mirror, and array performance suffers dramatically during resilvering of a raidzN. If/when I need to add more space to the pool, I can simply buy two more drives and add another mirror to the pool.

It’s worth noting that ZFS is not a substitute for backups. Here I am using ZFS to store backups of other machines, and it’s very useful for this use case.