29 September 2006

Fine-tune your system clock

These days it's really pretty easy to just run ntpd and have your system clock almost perfectly accurate all the time. But, you may prefer not to run ntpd. You may have security concerns with it or resource concerns or network problems. Or, maybe you're just running a work station and you don't want to waste a time server's resources. Here is an interesting little trick to take the major drift out of your system clock.

First, we still need ntpd or ntpdate to get a baseline of how much our system clock is drifting. I usually use ntpdate to get this baseline. (For that matter, I usually continue to use ntpdate after tuning because you just cannot remove all of the drift.) After running ntpdate once a day for a few days and directing all the output to a log file (with a cron job like this perhaps: "25 3 * * * /usr/sbin/ntpdate 192.43.244.18 >> /var/log/ntp 2>&1"), I would check the log file and see something like this:

5 Jun 03:24:56 ntpdate[4971]: step time server 192.43.244.18 offset -4.676964 sec
6 Jun 03:24:56 ntpdate[7630]: step time server 192.43.244.18 offset -4.677364 sec
8 Jun 03:25:02 ntpdate[1135]: step time server 192.43.244.18 offset 1.038868 sec
9 Jun 03:24:57 ntpdate[3474]: step time server 192.43.244.18 offset -4.688533 sec
10 Jun 03:24:56 ntpdate[5884]: step time server 192.43.244.18 offset -4.694951 sec
11 Jun 03:24:56 ntpdate[8205]: step time server 192.43.244.18 offset -4.710710 sec
12 Jun 03:24:57 ntpdate[10432]: step time server 192.43.244.18 offset -4.712613 sec
13 Jun 03:24:56 ntpdate[12957]: step time server 192.43.244.18 offset -4.685443 sec


This is a real example, though old. And, it seems someone rebooted the machine once to play some game. So, I'm definitely throwing out the 8 June number. I'll average the remaining 7 figures and get -4.692368. Next, you need to find out what timer your machine is using and what its default frequency is. That's pretty easy. You can either look in "/var/run/dmesg.boot" or type "sysctl machdep". The two alternatives I've seen are the "i8254" (on older machines) and the acpi_timer. Many machines have both, in which case it is probably actually using the acpi_timer for its system clock -- and that's the case in this example. The default frequency for the acpi_timer is 3579545 and that has been giving us about 86404.7 seconds in a day, when there are really 86400. The adjustment we need to make to our clock might be counter-intuitive -- to slow it down you must raise the timer's frequency. (And, vice-versa.) To find your new, improved frequency you can use this formula:



new-frequency
3579545
=
86400
(86400 - 4.692368)



And, then our new frequency ends up being 3579739.41, which must be rounded to an integer. You can apply this frequency by changing the sysctl variable. As root, I entered the command "sysctl machdep.acpi_timer_freq=3579739" ... and my clock hasn't been the same since then. Take a look:

21 Jun 03:25:01 ntpdate[8299]: adjust time server 192.43.244.18 offset -0.009996 sec
22 Jun 03:25:01 ntpdate[10609]: adjust time server 192.43.244.18 offset -0.001996 sec
23 Jun 03:25:01 ntpdate[12909]: adjust time server 192.43.244.18 offset 0.007902 sec


Also, to make this change persistent across reboots you should edit the file "/etc/sysctl.conf" and place a line in it like this "machdep.acpi_timer_freq=3579739" (but with the frequency you found for your machine :) ). That's pretty much it!

The formula I used above to find the new frequency is the result of experimentation. Instead of adding the average offset to the denominator on the right hand side, you could also subtract it from the numerator and get pretty much the same result. (Only because the offsets are so small compared to 86400.) Also, if you're going to do this, please continue to run ntpdate and verify that you got it right! I did get it backwards once and doubled my clock's drift for a day. (Whoops.)

(Postscript. You can use ntpd fine after you've tuned your clock this way. In fact, I'm sure there must be a way to adjust your timer frequency based on an ntpd calculated drift, but I haven't tried to find it. IP's have been changed to protect the innocent -- I don't actually use 192.43.244.18 for ntp services. You should try to find a public server close to you. I used the "HTML Equation Preprocessor" at http://www.miracosta.edu/home/sschaefer/eqsyntax.html to get the HTML table code for the equation above.)

[ tags: , , , ]

Labels:

17 September 2006

A fancy home firewall

If you've got a home network or are running any sort of network server that you want open to the internet, then the bare bones home firewall won't work for you. You need something a little fancier.

Before we get to the script and rules there are a couple of requirements. I am using ppp to connect to the internet and that is an important part of this puzzle because I am using ppp's own NAT (network address translation) functionality. If you're at home, you're probably using ppp to connect as well. It's typically used for dial-up, DSL, and cable connections where your provider is also assigning you an address via DHCP. I can't go into all of the nitty-gritty of setting up ppp here. (For that matter, I have to assume that the private/home lan is already properly set-up as well.) But, once ppp's set-up and working, if you want a home network behind your FreeBSD machine to be able to share that connection, you should enable its NAT capability by placing 'ppp_nat="YES"' into /etc/rc.conf. (If you have some sort of direct internet connection and are not running ppp, you might have to use natd and divert ports. The rules below probably will not work for you.)

Your machine still won't be forwarding the home lan traffic until you also ensure that it's set to act as a gateway. (Not enabled by default. A part of the secure-and-sane-defaults philosophy.) To do that place 'gateway_enable="YES"' into /etc/rc.conf.

These changes to rc.conf won't become active unless you reboot. But, who wants to reboot and lose all of that impressive uptime? If you want to enable ppp's NAT immediately, kill the running instance then restart it and include the '-nat' option. To allow your machine to act as a gateway immediately, use sysctl. As root, type 'sysctl net.inet.ip.forwarding=1'. You're ready. Now the rules/script (as before, this should be placed in a file somewhere that we will reference in rc.conf):

#!/bin/sh

FWCMD='/sbin/ipfw'
MYNET='192.168.0.0/24' #choose carefully [note 1]
TRUST='216.136.204.117 or 111.222.333.0/25' #choose very carefully! [note 2]

INSIDE='sis0' #don't get this wrong :) [note 3]

$FWCMD add 00100 allow ip from any to any via lo0
$FWCMD add 00200 deny ip from any to 127.0.0.0/8
$FWCMD add 00300 deny ip from 127.0.0.0/8 to any
$FWCMD add 00500 allow ip from any to any via $INSIDE

# game server ... [note 4, note 5]
$FWCMD add 01101 set 2 allow udp from any to me 28000
$FWCMD add 01102 set 2 allow udp from me 28000 to any
# off by default; type 'ipfw set enable 2' to turn it on
$FWCMD set disable 2

$FWCMD add 04000 check-state

$FWCMD add 05000 allow udp from me to any keep-state
$FWCMD add 05100 allow udp from $MYNET to any keep-state
$FWCMD add 06000 deny udp from any to me

# so you can log in from some place else [note 5]
$FWCMD add 07000 allow tcp from \{ $TRUST \} to me 22
$FWCMD add 07100 allow tcp from me 22 to \{ $TRUST \}

$FWCMD add 07500 allow tcp from me to any keep-state
$FWCMD add 07600 allow tcp from $MYNET to any keep-state
$FWCMD add 08000 deny tcp from any to me

# so you may VPN out, for example
$FWCMD add 09000 allow gre from me to any keep-state
$FWCMD add 09100 allow gre from $MYNET to any keep-state
$FWCMD add 09900 deny gre from any to me

$FWCMD add 50000 allow icmp from me to any keep-state
$FWCMD add 51000 allow icmp from $MYNET to any keep-state
$FWCMD add 52000 allow icmp from \{ $TRUST \} to me
# don't make it easy for them
$FWCMD add 53000 deny icmp from any to me icmptype 8
$FWCMD add 54000 allow log icmp from any to me
#$FWCMD add 65535 deny ip from any to any


Note 1. 192.168.0.0/24 is the private network in this example. You could choose a different address range for this, but it must be RFC-1918 specified private IP space, which is any sub-range of 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16. (You can read RFC 1918 here.)

Note 2. I will use the shell variable TRUST to list all the addresses and address ranges that I trust. In this example, that means I'm going to allow them to log in via ssh/port 22 (via rule 07000). Allowing incoming connections is a huge departure from the bare bones firewall. Anyone with access to a machine with an address listed in TRUST can now try to log into your box. I'm using an or-block here just to show what's possible and make the example more interesting. Such arrangements might be used to allow you to log into your box from your friend's house or work, for example. (Strict security would require that only addresses for machines that you control are listed here. If you're doing anything else, you'd better at least be watching your logs carefully for login attempts.) Also see note 5.

Note 3. Elsewhere, we're using the handy ipfw magic of 'me' to specify any IP address configured on the local machine. But, before that we want to keep the home lan wide open to the FreeBSD firewall/router. This is applied in rule 00500. ipfw processes rules sequentially, so with this rule near the beginning, we can be sure that traffic between machines on our own LAN and the firewall machine is uninhibited. Then, for the remainder of the script we can focus on how to protect ourselves from the public internet.

A security purist might say that the "allow any to any" in rule 00500 is too liberal. My own rule of thumb is that the purpose of a firewall is protect you from the world -- not to protect the world from you! But, I'm envisioning a small home LAN here. If you are running an ISP or otherwise have untrusted or unknown machines on your LAN then, yes, you should have rules to prevent abuse coming from your LAN!!

Note 4. If you want to run a public game server, you don't know who is going to be connecting. So, you've got to allow any address and you've got to put these rules before the later blocks which will deny unsolicited traffic. If you were running a web server instead of a game server, you could change these rules by changing the port (28000 => 80) and the protocol ('udp' => 'tcp').

I also use the 'set' option and then disable the set. The rules are then inactive but may be easily enabled all at once (with 'ipfw set enable 2'). If you have a public service always running, you don't need this and can just comment out the disable line ('$FWCMD set disable 2').

(P.S. A public game server can be a big security risk and doesn't typically have all security features of something like apache. You should seriously look into using a jail or a chroot environment.)

Note 5. Here we allow the trusted addresses to reach our ssh port and login. By having both an in rule and an out rule what we've done is to make this traffic stateless. In fact, we did the same thing with the game server. This does a couple of interesting things. First, it prevents the use of more system resources for more dynamic rules for traffic you're expecting anyway. Usually that's negligible, but in the case of a public server it's not at all! It's a vulnerability to allow unknown addresses to cause dynamic rules to be allocated. (To be clear, that would be the case if we had just allowed the in traffic here and let the out traffic be handled by the later 'keep-state' rules.)

Second, it gives you nice in/out counters for the bytes and packets of that traffic. You can see those using the command 'ipfw -at list'. ... Watching and counting different kinds of traffic will hopefully be the subject of a future post, too.

[ tags: , , ]

Labels:

01 September 2006

A bare bones home firewall

Here we're going to look at a simple firewall using FreeBSD's own 'ipfw'. This is a bare bones, protect-this-machine-only firewall and will work fine if you have no network behind your FreeBSD machine and no special needs:

#!/bin/sh
/sbin/ipfw add 00100 allow ip from any to any via lo0
/sbin/ipfw add 00200 deny ip from any to 127.0.0.0/8
/sbin/ipfw add 00300 deny ip from 127.0.0.0/8 to any

/sbin/ipfw add 04000 check-state

/sbin/ipfw add 05000 allow udp from me to any keep-state
/sbin/ipfw add 06000 deny udp from any to me

/sbin/ipfw add 07500 allow tcp from me to any keep-state
/sbin/ipfw add 08000 deny tcp from any to me

/sbin/ipfw add 50000 allow icmp from me to any keep-state
/sbin/ipfw add 52000 deny icmp from any to me icmptype 8
/sbin/ipfw add 53000 allow log icmp from any to me
#/sbin/ipfw add 65535 deny ip from any to any


I've put the blank lines in to divide up the different functional parts of the rules. The first three rules (100-300) are standard and allow internal communications on your machine and then stop any possible abuse of that restricted address space. The next rule (04000) is to allow traffic in that was already initiated by your machine via one of the later "keep-state" rules. The next four rules (in two sets of two) are the heart of the firewall and each set basicly says, "let me send traffic anywhere, create a dynamic rule (to accept the return traffic via rule 4000), then deny any other such traffic." We do this for each of the two major protocols, TCP and UDP. This will cover the vast majority of your traffic. And finally, we do almost the same thing for the ICMP traffic, however we only block unrequested, incoming ping traffic ("icmptypes 8"). Other types of ICMP traffic should generally be allowed as it may contain important network error information. And, at the end, I've left in a comment to remind myself of the default rule -- any IP traffic that wasn't already handled by the other rules will be unceremoniously denied.

To make this firewall effective on system boot, you must do just a few things. First, put it into a file (I'd recommend "/usr/local/etc/rc.ipfw"). Then, place the following lines into "/etc/rc.conf":

firewall_enable="YES"
firewall_script="/usr/local/etc/rc.ipfw"

And, that's it! Now every time you boot your machine, you'll be protected from unwanted network traffic. And, if you don't want to reboot, you can make this effective immediately by doing two things (both as user root, please). First, ensure that the "ipfw.ko" kernel module is loaded (check with "kldstat" and if it's not listed enter the command "kldload ipfw"). Next, run the script you've just created with the command "sh /usr/local/etc/rc.ipfw". Be warned, though, that if you have any active TCP sessions when you activate the firewall they might be cut off from further communication, as the firewall wasn't active to create the dynamic rules (via "keep-state" rules) when the session was started.

If you need something more from your firewall, take a look at this follow-up article, "A Fancy Home Firewall." In it, I expand on this example and explain a rule set that can allow traffic from a private network behind your FreeBSD machine, VPN traffic, and more.

[ tags: , , ]

Labels: