Skip to content

Shorewall, Ipsets, Switches, and Time-based Rules.

26 April 2019

After a long while away from it, I returned to using a small internet appliance (a Protectli Vault) running linux (Ubuntu) as a router and firewall because none of the commercial routers I looked at offered me the ease of management and flexibility I was looking for. Thinks like static DHCP were difficult to manage on all of them, and firewall rules were not flexible or powerful. So once I had Ubuntu server installed on the Vault, I installed Shorewall and began to configure iptables to my liking. Ipset is a great extension to Iptables that makes several of the things I like to do with my firewall easier. Shorewall’s documentation for how to use Ipsets with Shorewall is typical of Shorewall in that it is good, but limited in scope, likely because the target audience is one that would already have a fair amount of experience. In more complicated cases, though, there was some experimentation for me to do, and I thought I’d lay out a few things I’ve done.

I have youngish kids who have devices with wifi, but no data plans through wireless carriers. They have laptops issued by their schools, tablets they use at home, etc. Some of the kids sometimes think it’s a good idea to sneak their devices into their bedrooms and watch YouTube videos, etc late into the night, then they are cross and slow to get up for school the next day. I care a *little* less about this on the weekend when they don’t have to get up so early. So, I wanted to block network access for their devices during times they should be in bed. However, sometimes (due to our schedules), they aren’t quite done with their homework at bedtime, and I wanted an easy way to extend their network access for a defined period of time. And I wanted to do this without having to restart the firewall all the time. So, to review, what I needed was to write rules that:

  1. Blocked traffic for certain devices based on time and day of week, but which I could also,
  2. Easily designate should not apply to devices they would normally apply to for a given period of time.

The facilities in Iptables (and Shorewall) I need for this are:

  1. Ipsets – (Documentation for using Ipsets with Shorewall here)
  2. Time matching – (see the Shorewall documentation for its Rules file, scroll about 4/5s of the way down the page, look for “TIME”, unfortunately no IDs to make anchors, also see this page for a more helpful description, again there are now anchors, so scroll most of the way to the bottom, or search for “TIME columns”)
  3. Switches – I discovered later that I need a way to use different rules based on if it was Daylight Saving Time where I live. Enter the “Switch” facility (Again in the Rules file documentation, SWITCH section, two sections below TIME)
  4. Writing the actual rules

Ipsets

I decided to use MAC addresses for my kids devices in case any of them decided to try to set their own IP address manually. I recognize this isn’t fool-proof, and nothing is, but I think it’s enough for now. When my kids are old enough to figure out how to change their MAC addresses, and how that might help them, I’ll have bigger fish to fry. Ipset documentation is here. I won’t be going into an exhaustive discussion of Ipsets, just detailing the features relevant to what I do.

Ipset allows different types of sets, and the ones that are relevant to what I’m doing are set types hash:mac and list:set. The man page is here, and unfortunately has no IDs to make anchors. You can find info about the hash:mac set type about halfway down the page, and list:set is the last set type listed.

Briefly, hash:mac allows you to create a set of mac addresses that are stored in memory as a hash. When matching against the set, it matches the specified address (src or dst) of the packet against all of the entries in the hash in a very efficient manner, using a single rule, rather than a separate rule for each MAC address. hash:mac is only useful for devices on the same network as the firewall, but that suits my needs well here.

list:set is used to create groups of other sets, and match against all the groups’ members in one set. So I can add set1, set1, set3, and set4 to a superset called allsets, for example, and match against any of their members at the same time. When doing this, ipset creates a superset of all members, and matches against them with an OR (eg – if an entry is a member of any one of the subsets it will match, it need not be a member of all the subsets).

There are some optional features that can be turned on for each set: comments and timeouts. Comments allow me to set a comment that will be displayed when I list the contents of the set to remind me what I’m looking at. I use it to specify which device is which, (eg – a comment may be “Farnsworth tablet”). There are restrictions on the characters allowed in a comment. Timeout lets you set the duration that an entry will remain in the set before being automatically removed, in seconds. So a timeout of 3600 would leave the item on the set for an hour.

So, you start by creating the set. The command to create the set looks basically like this:

ipset create <set-name> <set-type> <options>

I wanted to create several sets, one for each child’s devices, and I didn’t want to have devices drop off the list automatically.

ipset create kid1-devices hash:mac comment
ipset create kid2-devices hash:mac comment
ipset create kid3-devices hash:mac comment
ipset create kid4-devices hash:mac comment

I also wanted to be able to limit devices belonging to my kids’s guests that I allow on the network

ipset create kid-guests hash:mac comment

I want to be able to add devices to the block list one at a time, or I may want to add groups of devices to the block list (say, the set of all a specific child’s devices). The easiest way to do this was to create two different types of set: A hash:mac for adding single devices, or groups of devices that I don’t have in another set; and a list:set for adding other sets. I also want to be able to treat the older kids differently than the younger kids, because in my case, the older kids have different bedtimes, and I trust them more to abide by the rules. For now, I treat the kids guests like little kids. For sets of type list:set, I really don’t need a comment unless I’m trying to keep try of why someone was added temporarily.

ipset create bigkids list:set
ipset create littlekids list:set
ipset create individualdevices hash:mac comment

I want to create a white-list that temporarily allows devices through, so it needs a timeout, and I want to default the timeout to one hour, and I want to be able to add devices individually or as sets.

ipset create tempWLdevices hash:mac comment timeout 3600
ipset create tempWLset list:set timeout 3600

Now that all the sets are created, I need to add members to the set. The general format for adding to a set is:

ipset add <set-name> <device> <options>

To add my oldest child’s devices to the set for those devices:

ipset add kid1-devices 00:11:22:33:44:55 comment "Farnsworth tablet"
ipset add kid1-devices 66:77:88:AA:BB:CC comment "Farnsworth laptop"
ipset add kid1-devices DD:EE:FF:00:11:22 comment "Farnsworth DS"

I did this with all the kids devices, including friends’ devices that are commonly here. Then I needed to add the sets to the appropriate supersets

ipset add bigkids kid1-devices
ipset add bigkids kid2-devices
ipset add littlekids kid3-devices
ipset add littlekids kid4-devices
ipset add littlekids individualdevices
ipset add littlekids kid-guests

I next want to only have to specify one whitelist set in rules, so I add the set of individual whitelisted devices to the whitelist superset, and I don’t want it to be automatically removed, so I set a timeout of 0 seconds.

ipset add tempWLset tempWLdevices timeout 0

I want to whitelist Farnsworth’s devices later because his bedtime is in 5 minutes but he has an hour of homework left to do using his schools CMS, I add him to the tempWLset, and I don’t need to specify a timeout because the default timeout I set of one hour (3600 seconds) when I created the set will work.

ipset add tempWLset kid1-devices

If, on the other hand, he’s watching a video that will be over in 15 minutes, and I won’t want to give him a full hour, I can do this, instead:

ipset add tempWLset kid1-devices timeout 900

And, finally, if I just wanted to allow his school laptop for 30 minutes, and not his tablet or DS, I could instead do this:

ipset add tempWLdevices 00:11:22:33:44:55 comment "Farnsworth tablet" timeout 1800

Because we included tempWLdevices in tempWLset, I only ever need to specify tempWLset in a Shorewall rule.

Shorewall lets you use an ipset in the place of a src or dst address. To do this, from the page on Shorewall and Ipsets

Support for ipsets was introduced in Shorewall version 2.3.0. In most places where a host or network address may be used, you may also use the name of an ipset prefaced by “+”.

Example: “+Mirrors”

To generate a negative match, prefix the “+” with “!” as in “!+Mirrors”.

Beginning with Shorewall 4.4.14, multiple source or destination matches may be specified by placing multiple set names in ‘+[…]’ (e.g., +[myset,myotherset]). When so enclosed, the set names need not be prefixed with a plus sign.

This is one of the places I had to do some guessing, and I’ll explain it more below when I talk about writing the relevant rules themselves.

Time Matching

Now, I only want the rules I’m going to write involving these sets to apply between certain hours. Iptables has a time match extension that Shorewall supports, so we’re good to go, except (as I’ll detail below more) linux time handling is a little obnoxious. But, before we look at how this can get a little complicated, let’s look at the general format, relevant portions from the man page for the Shorewall Rules file:

TIME – timeelement[,timelement…]

May be used to limit the rule to a particular time period each day, to particular days of the week or month, or to a range defined by dates and times. Requires time match support in your kernel and iptables.

timeelement may be:

timestart=hh:mm[:ss]
Defines the starting time of day.
timestop=hh:mm[:ss]
Defines the ending time of day.
utc
Times are expressed in Greenwich Mean Time.
weekdays=ddd[,ddd]…
where ddd is one of MonTueWedThuFriSat or Sun

Also helpful reading here is the iptables documentation for the time matching extension, find the excerpt on time matching about 1/2 to 2/3s of the way down the page.

This is a nice feature that lets a rule apply only during a specific time period. I’ll discuss more below about why, but in my opinion (and the opinion of the iptables developers) it is best to use UTC for this. So, for example, I live in the Mountain Time Zone in the US, and have an offset from UTC of -6 to -7 hours, depending on whether or not I’m in Daylight Saving Time (MST has an offset of -7, MDT of -6). For me, that means that if I want to set a blocked period for 9pm to 6am (the older kids), I set the start time for 03:00 UTC and the stop time for 12:00 if it’s MDT, or 04:00 and 13:00 if it’s MST. If I want the block time to be 7pm to 7am (the younger kids and guests), I set the start time for 01:00 and the stop time for 13:00 if it’s MDT, or 02:00 and 14:00 if it’s MST.

For the younger kids, I want the rules to apply every day of the week, and can stop here; but for the older kids, I’m only applying these rules on school nights. To do that, I use the weekdays entry. You have to take into account what day of the week it is in UTC, not your local time. So, in my case, when it’s 7pm Sunday, in UTC that is 01:00 Monday. So for the older kids, the days I’m interested in are Monday-Friday.

If your local time is such that your time span crosses a date (say from 23:00 UTC to 05:00 UTC the next day), you’ll want to look at the contiguous option, which is discussed more in this Shorewall page, and this Iptables page.

So, for the older kids, the rule will have a time string that looks like this:

timestart=03:00&timestop=12:00&weekdays=Mon,Tue,Wed,Thu,Fri&utc

or this:

timestart=04:00&timestop=13:00&weekdays=Mon,Tue,Wed,Thu,Fri&utc

for MDT and MST, respectively.

I looked for a way to not have to have two rules, because the last thing I wanted to do was manually change the rule every time I change my clocks, or write a script to edit my configuration files every time the time changed. If I could use local time, I thought that would eliminate the need for two rules, because local time gets updated for the Daylight Saving change. Unfortunately, there’s no clean way to do that as far as I can tell because, as is detailed in the Iptables documentation for the time match extension:

About kernel timezones: Linux keeps the system time in UTC, and always does so. On boot, system time is initialized from a referential time source. Where this time source has no timezone information, such as the x86 CMOS RTC, UTC will be assumed. If the time source is however not in UTC, userspace should provide the correct system time and timezone to the kernel once it has the information.

Local time is a feature on top of the (timezone independent) system time. Each process has its own idea of local time, specified via the TZ environment variable. The kernel also has its own timezone offset variable. The TZ userspace environment variable specifies how the UTC-based system time is displayed, e.g. when you run date(1), or what you see on your desktop clock. The TZ string may resolve to different offsets at different dates, which is what enables the automatic time-jumping in userspace. when DST changes. The kernel’s timezone offset variable is used when it has to convert between non-UTC sources, such as FAT filesystems, to UTC (since the latter is what the rest of the system uses).

The caveat with the kernel timezone is that Linux distributions may ignore to set the kernel timezone, and instead only set the system time. Even if a particular distribution does set the timezone at boot, it is usually does not keep the kernel timezone offset – which is what changes on DST – up to date. ntpd will not touch the kernel timezone, so running it will not resolve the issue. As such, one may encounter a timezone that is always +0000, or one that is wrong half of the time of the year. As such, using –kerneltz is highly discouraged.

And if you needed one more reason to dislike Daylight Saving Time, this could be it. Though, incidentally, it also leaves me somewhat bothered with the haphazard way linux systems seem to handle actual user-useful times. But that’s another matter. The end result is that I decided to use UTC, and needed a way to switch between alternate versions of the rule, depending on whether I was in Daylight Saving Time or not. Enter Switches.

Switches

Shorewall’s documentation has this to say about switches:

There are times when you would like to enable or disable one or more rules in the configuration without having to do a shorewall reload or shorewall restart. This may be accomplished using the SWITCH column in shorewall-rules (5) or shorewall6-rules (5). Using this column requires that your kernel and iptables include Condition Match Support and you must be running Shorewall 4.4.24 or later. See the output of shorewall show capabilities and shorewall version to determine if you can use this feature.

The SWITCH column contains the name of a switch. Each switch is initially in the off position. You can turn on the switch named switch1 by:

echo 1 > /proc/net/nf_condition/switch1
You can turn it off again by:

echo 0 > /proc/net/nf_condition/switch1
If you simply include the switch name in the SWITCH column, then the rule is enabled only when the switch is on. If you precede the switch name with ! (e.g., !switch1), then the rule is enabled only when the switch is off. Switch settings are retained over shorewall restart.

Shorewall requires that switch names:

begin with a letter and be composed of letters, digits, underscore (‘_’) or hyphen (‘-‘); and
be 30 characters or less in length.
Multiple rules can be controlled by the same switch.

The bit I marked above is not precisely correct. I was having some unexpected behavior and contacted the shorewall users list and was given a clearer explanation of this:

Switch values are retained over a ‘shorewall reload’ and are saved over
‘shorewall restart’ only if RESTART=reload in shorewall.conf. If
RESTART=restart, then ‘shorewall restart’ does a ‘shorewall stop’
followed by a ‘shorewall start’. So, unless the switch also appears in
the ‘stoppedrules’, its setting will be lost.

This description is what I find to be true. If I run a “shorewall restart” I lose the value of the switch. There are reasons I don’t want to put the switch in stoppedrules and want to maintain the difference between restart and reload. So, for my rules, I use a switch “is_dst”, which is set to 1 if I am currently in Daylight Time, and 0 if I am in Standard time. I adjust the variable with a small bash script:

#!/bin/bash
currtz="$(perl -e 'print ((localtime)[8]);')"
isdst="$(cat /proc/net/nf_condition/is_dst)"
if [ "$currtz" == "$isdst" ]; then
echo "No time zone change"
exit 1
else
echo "Time offset has changed"
echo $currtz > /proc/net/nf_condition/is_dst
fi

Perl’s localtime function returns several values, the 9th is “isdst”, and is 1 if it is currently Daylight time in the system’s locale, and 0 if it is not.  I store this in the currtz variable. I then store the current value of the switch (kept in /proc/net/nf_condition/<switch-name>, in this case, /proc/net/nf_condition/is_dst) in the isdst variable. If the two variables are the same, the script does nothing. If they are different, it prints out that the time offset has changed and writes the new value to /proc/net/nf_condition/is_dst, updating the value of the switch.

I run this script several times on the Second Sundays in March and November (when we current change DST), and once at system startup by adding the following to the root user’s crontab

0 0,1,2,3,4,5,6 8-14 3,11 0 /path/to/script
@reboot /path/to/script

And I also run this script every time Shorewall starts/restarts to make sure the value is set correctly when the firewall starts, by adding the following to /etc/shorewall/started

/path/to/script

(Yes, it’s pretty simple).

Writing the Actual Rules

Now we have all the relevant pieces together, and it’s time to write the rules themselves. The format of the Shorewall rules file is:

ACTION         SOURCE          DEST            PROTO   DEST    SOURCE          ORIGINAL        RATE            USER/   MARK    
                                                       PORT    PORT(S)         DEST            LIMIT           GROUP

CONNLIMIT TIME HEADERS SWITCH HELPER (this is a continuation of the first line, but I broke it to fit it in a standard browser width)

Not every column needs to be specified, but if you want to specify a column that appears later in the list than ones you want to leave unspecified, you fill the unspecified columns with “-“. When the entry for a column is wider than the column, you can end the entry with “\” to break the rule up onto multiple lines to keep it more readable.

I use the REJECT action for this. I want to block traffic in both directions, so I use two rules, one where the ipsets are the source, and one where the ipsets are the destination. I want to block everything, so protocol is set to “ALL”, I leave Dest Port, Source Port, Original Dest, Rate Limit, User/Group, Mark, Connlimit, and Headers unspecified by filling them with “-“.

You specify an ipset by designating the zone, then adding a colon and the ipset name. Ipset names are prefixed with +. You can negate/invert a match with “!” Also, you can include multiple ipsets in a rule. There are two ways to do this, and they have different meanings. Using mine as an example, “loc” is the firewall zone in which these sets reside:

“loc:+bigkids” would match anything in any of the subsets of the bigkids set.

“loc:!+bigkids” would match anything that is NOT in any of the subsets of the bigkids set.

“loc:+bigkids,+littlekids” would match anything in EITHER bigkids OR littlekids, including anything that was in both. But an entry need not match on both sets.

“loc:+[bigkids,littlekids]” would match any entry that is in BOTH bigkids AND littlekids, and only those entries that match on both sets.

And here’s where the whitelisting works:

“loc:+[bigkids,!tempWLset]” will match anything that is in bigkids BUT NOT in the temporary whitelist. This lets me cause a packet not to match by adding it to the whitelist.

Finally, you can mix both types of combining:

“loc:+[littlekids,!tempWLset],+[kid-guests,!tempWLset]” will match EITHER addresses that are BOTH in littlekids and NOT in tempWLset, OR addresses that are BOTH in kid-guets and NOT in tempWLset.

 

Which brings us to the rules as I finally got them written out, in the “ALL” section of the Shorewall Rules file:

# Kids can't use devices in the middle of the night, series of rules, one for when it's DST and one for when it's Standard Time
# (Necessary because the only clean way linux handles dates/times is with reference to UTC with adjustments calculated locally
# tempallow sets there to allow temporary whitelisting easily.
#
# Starting with the big kids. Off-times are 21:00 to 06:00, only on school nights.
#
REJECT:info loc:+[bigkids,!tempWLset]\
net all - - - - - - - timestart=03:00&timestop=12:00&weekdays=Mon,Tue,Wed,Thu,Fri&utc\
- is_dst
REJECT:info loc:+[bigkids,!tempWLset]\
net all - - - - - - - timestart=04:00&timestop=13:00&weekdays=Mon,Tue,Wed,Thu,Fri&utc\
- !is_dst
REJECT:info net loc:+[bigkids,!tempWLset]\
all - - - - - - - timestart=03:00&timestop=12:00&weekdays=Mon,Tue,Wed,Thu,Fri&utc\
- is_dst
REJECT:info net loc:+[bigkids,!tempWLset]\
all - - - - - - - timestart=04:00&timestop=13:00&weekdays=Mon,Tue,Wed,Thu,Fri&utc\
- !is_dst

# Next for the little kids and guest kids. Off-times are 19:00 to 07:00
REJECT:info loc:+[littlekids,!tempWLset],+[guest-kids,!tempWLset]\
net all - - - - - - - timestart=01:00&timestop=13:00&utc\
- is_dst
REJECT:info net loc:+[littlekids,!tempWLset],+[guest-kids,!tempWLset]\
all - - - - - - - timestart=01:00&timestop=13:00&utc\
- is_dst
REJECT:info loc:+[littlekids,!tempWLset],+[guest-kids,!tempWLset]\
net all - - - - - - - timestart=02:00&timestop=14:00&utc\
- !is_dst
REJECT:info net loc:+[littlekids,!tempWLset],+[guest-kids,!tempWLset]\
all - - - - - - - timestart=02:00&timestop=14:00&utc\
- !is_dst

Both sections above have two pair of rules. The first pair blocks outgoing traffic, the second pair incoming. In each pair of rules, the one with the switch of “is_dst” matches during Daylight Saving Time, and the one with the switch of “!is_dst” matches when it isn’t during Daylight Saving Time.

To help make it a little easier to understand the format lineup, the rules line up with the columns as so:

#ACTION         SOURCE          DEST            PROTO   DEST    SOURCE          ORIGINAL        RATE            USER/   MARK    CONNLIMIT       TIME            HEADERS         SWITCH          HELPER
#                                                       PORT    PORT(S)         DEST            LIMIT           GROUP
REJECT:info     loc:+[burrow-bigkids,!tempallowset]\
                                net             all     -       -               -               -               -       -       -               timestart=03:00&timestop=12:00&weekdays=Mon,Tue,Wed,Thu,Fri&utc\
                                                                                                                                                                -               is_dst

It was an interesting project, and it’s running well, doing what I intend it to do.

Landmarks: Potent Potables

7 May 2017
tags:

Part 11: Potent Potables – Sundry Bits of Information

  1. Latin Phrases and Definitions,
  2. Statutes and Standards,
  3. Phil’s Favorite Phrases,
  4. Compare and Contrast Cases

Landmark Cases: Sexual Harassment, Confidentiality and Privilege, Hypnosis

7 May 2017

Part 10: Sexual Harassment, Confidentiality and Privilege, and Hypnosis.

Sexual Harassment

  1. Meritor Savings Bank v. Vinson
  2. Harris v. Forklift Systems
  3. Oncale v. Sundowner

Confidentiality/Privilege

  1. In re: Lifschutz
  2. Doe v. Roe
  3. Jaffee v. Redmond

Hypnosis

  1. State v. Hurd
  2. People v. Shirley
  3. Rock v. Arkansas

Landmark Cases: Children

6 May 2017

Part 9: Child Custody, Child Abuse and Neglect, Children’s Rights, and Juvenile Sentencing and Delinquency

Child Custody

  1. Painter v. Bannister
  2. Santosky v. Kramer

Child Abuse/Neglect

  1. Landeros v. Flood
  2. People v. Stritzinger (Also Confidentiality)
  3. Minnesota v. Andring
  4. DeShaney v. Winnebago County

Children’s Rights

  1. In re: Gault
  2. Board of Education v. Rowley
  3. Irving v. Tatro

Juvenile Sentencing and Delinquency Rights

  1. Fare v. Michael
  2. Graham v. Florida
  3. Miller v. Alabama

Landmark Cases: Death Penalty

5 May 2017

Part 8: Death Penalty

Death Penalty

  1. Estelle v. Smith,
  2. Barefoot v. Estelle,
  3. Ake v. Oklahoma,
  4. Ford v. Wainwright,
  5. Payne v. Tennessee,
  6. Lousiana v. Perry,
  7. Atkins v. Virginia,
  8. Roper v. Simmons,
  9. Clark v. Arizona,
  10. Panettie v. Quarterman

Landmark Cases: Liability, Duty to Protect, Emotional Harm, Disability

4 May 2017

Part 7: Liability, Duty to Protect, Emotional Harm, Disability

Liability to Patients

  1. Roy v. Hartogs
  2. Clites v. Iowa

Duty to Protect

  1. Tarasoff v. Board of Regents
  2. Lipari v. Sears
  3. Littleton v. Good Samaritan
  4. Morgan v. Fairfield

Emotional Harm

  1. Dillon v. Legg

Disability

  1. Bragdon v. Abbott
  2. Olmstead v. L.C. and E.W.
  3. U.S. v. Georgia

Landmark Cases: Informed Consent and Right to Refuse

4 May 2017

Part 6: Informed Consent and Right to Refuse (Psychiatric and Life-Saving Treatment)

Cases include:

Informed Consent

  1. Canterbury v. Spence
  2. Kaimowitz v. Dept of Mental Health

Right to Refuse Psychiatric Treatment

  1. Rogers v. Commissioner
  2. Rennie v. Klein
  3. Washington v. Harper
  4. Steel v. Hamilton Cty
  5. Hargrave v. Vermont

Right to Refuse Life-Saving Treatment

  1. Application of the President and Directors of Georgetown College
  2. Cruzan v. Director
  3. Washington v. Glucksberg
%d bloggers like this: