Alex headshot

AlBlue’s Blog

Macs, Modularity and More

Migration off of Google

And so it ends. My migration off of Google is now complete (at least, for the Bandlem e-mails; my gmail address is still much alive and subscribed to many open-source mailing lists). What started a transition in 2009 off of Yahoo! mail onto Google Mail has now happened from Google Mail. In both cases, I got what I paid for but the recent kerfuffle with Google was the tipping point for me.

On a side note, I’ll be glad to get rid of the ever-present ‘All Mail’ that lined my Google IMAP folders, resulting me in marking (on average) ever e-mail as read twice.

Tech Details

For those of you who are interested, here’s what I moved off onto. I now use OSX Server (you may have expected this), which includes postfix for mail and dovecot for IMAP connections. Fortunately, an out-of-the-box setup is almost trivial, but if you want to support IPv6 then you have to do some additional work to get going.

IPv6

WORLD IPV6 DAY is 8 June 2011 – The Future is Forever

Firstly, you have to have IPv6 support, such as a router with native IPv6 or a tunnel (I use SixXS for this purpose). Getting IPv6 up and running is relatively easy, but OSX Server makes it much more difficult than it needs to be (at least, on 10.6).

Firewall

The OSX Server firewall only configures IPv4 routes, and by default effectively disables IPv6 traffic outside the local network. Whilst this is a sane choice for a default install, it needs some tweaking to work properly if you want to run with a decent stack.

Note that the firewall GUI will blow away your settings each time you update it, so the choice comes down to either (a) not using the OSX firewall app, or (b) setting up something to monitor the changes and run your script on demand. You can also throw away whatever the OSX firewall says it should use, and instead create a startup script that monitors the state of changes, and re-applies the firewall for you.

Apple makes the information in a random help document but doesn’t exactly make it clear. In essence, there is a magic setting which stops the OSX app firewall from blowing the IPv6 rules away:

/etc/ipfilter/ip_address_groups.plist
1
2
3
4
<key>IPv6Mode</key>
<string>NoRules</string>
<key>IPv6Control</key>
<true/>

The true says to synchronize the Firewall with ipfw so that when the IPv4 firewall stops and starts, so does the ip6fw as well.

The NoRules basically means ‘get off my lawn’. It won’t do anything with the ip6fw executable, but will start and stop it. As a result, you’ll need to configure the firewall in an appropriate script or launch daemon. Although there’s no good way of calling the configuration at startup, you can watch the /etc/ipfilter/ipfilter.conf.apple which will get re-generated whenever Firewall re-generates the rules, and then use that to manually update the content:

/Library/LaunchDaemon/com.bandlem.ip6fw.plist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.bandlem.ip6fw</string>
    <key>RunAtLoad</key>
    <true/>
    <key>ProgramArguments</key>
    <array>
        <string>/sbin/ip6fw</string>
        <string>-q</string>
        <string>/etc/ipfilter/ip6fw.conf</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/etc/ipfilter/ip6fw.conf</string>
        <string>/etc/ipfilter/ip6fw.conf.apple</string>
    </array>
</dict>
</plist>

If you load this script (either by rebooting, or by doing sudo launchctl load /Library/LaunchDaemons/com.bandlem.ip6fw.plist) then it should start a monitoring thread on both of those files. If you change the ip6fw.conf one, it will automatically reload the file (just be sure that you know what you’re doing!) and if the Apple Firewall manager updates the Apple file, then the corresponding ip6fw will get reloaded as well.

There are some incompatibilities between the two files (other than the obvious filters for fe80:/64 vs 10.0.0.0/24) – for example, ip6fw doesn’t understand about either src-port or dst-port as a keyword (the values are next to the addresses), and nor does it understand about keep-state. So it’s not possible (in general) to drive the other.

(As a side note; the omission of keep-state appears to block the ability for running a DNS server over IPv6 behind the firewall, since responses come back on UDP packets that aren’t associated with port 53. I haven’t figured out how to solve this yet, so if you know, drop me a line via @alblue.)

Generally, a set of defaults for IPv6 might look like this:

/etc/ipfilter/ip6fw.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Get rid of any previous rules
flush
# For some reason the firewall defaults to 'allow any to any', so override that
add 65000 deny ipv6 from any to any

# Ping etc.
add 01000 allow ipv6-icmp from any to any
# Your local subnet
add 1200 allow all from 2001:0db8:85a3::/64 to 2001:0db8:85a3:/64
# Allow established TCP connections
add 12300 allow tcp from any to any established
# Allow any outbound requests
add 12301 allow tcp from any to any out
add 12302 allow udp from any to any out
# DNS support
add 12303 allow tcp from any to any 53
add 12303 allow tcp from any 53 to any
add 12303 allow udp from any to any 53
add 12303 allow udp from any 53 to any
# Fragmented UDP packets
add 12304 allow udp from any to any in frag
# Allow inbound HTTP
add 12309 allow tcp from any to any 80 setup
# Allow inbound SSH
add 12310 allow tcp from any to any 22 setup
# Allow inbound HTTPS
add 12311 allow tcp from any to any 443 setup

Note the TCP ‘setup’ commands, which essentially allow inbound connections only when the TCP connection is initiated. Thereafter, any connections in the ‘established’ state (rule 12300) are permitted. The only other rule pertains to DNS on port 53, but you can add others as well.

More information about the rules are listed in the ip6fw man page.

Mail (Postfix/SMTP)

Since postfix comes with compiled-in support for IPv6, setting the value isn’t too difficult. However, the Apple Mail server only supports setting values in IPv4 (dotted decimal) form, which somewhat limits the use for setting the values.

The two changes toy need to make are:

/etc/postfix/main.cf
1
2
3
inet_protocols = all
smtp_bind_address6 = 2001:0db8:85a3::c3b0
mynetworks = 127.0.0.0/8,[::1]/128,[2001:0db8:85a3::]/64

The inet_protocols = all configures it for both IPv6 and IPv4. Note that since the IPv6 address has :: characters in it, they need to be surrounded by the double quotes. Also, the netmask is larger – typically a /64 subnet – but obviously configure to taste.

Mail (Dovecot/IMAP)

Dovecot provides the IMAP functionality in OSX server, and also comes with compiled support for IPv6. However, it doesn’t listen on any IPv6 addresses by default which means it’s a no-go for IPv6 mail clients.

/etc/dovecot/dovecot.cf
1
2
3
4
5
6
protocol imap {
...
  # Listen on both IPv4 (*) and IPv6 ([::]) addresses
  listen = *,[::]
...
}

The listen keyword isn’t present by default, but the protocol imap part is. I put the listen at the top and with a comment with # in to remind me (plus, useful for showing in git diffs when you have /etc stored in a Git repository).

DNS

The OSX DNS server supports IPv6 records (AAAA) but unfortunately, doesn’t have a way to set any of them. This presents a bit of a problem when it comes to both forward and reverse domain name lookups.

Fortunately, it is possible to do. The way that OSX stores its records for the DNS service is with /etc/named.conf pointing to an include of /etc/dns/publicView.conf.apple file, which in turn defines a list of zones at /var/named/ – although the OSX managed data is in /var/named/zones/. The /var/named/ zone file contains an include for /var/named/zones/ corresponding file name as an include, which provides the SOA record. Fortunately, this means we can inject additional entries in the /var/named/ zone after the OSX include line:

/var/named/db.example.zone
1
2
3
4
;THE FOLLOWING INCLUDE WAS ADDED BY SERVER ADMIN. PLEASE DO NOT REMOVE.
$INCLUDE /var/named/zones/db.example.com.zone.apple

osx.example.com. IN AAAA 2001:0db8:85a3::c3b0

Thus Server Admin manages the IPv4 addresses whilst we can add additional IPv6 addresses manually to this list. But what about reverse lookups?

Reverse IPv6 lookups use the ip6.arpa space, in much the same way that reverse IPv4 lookups use the in-addr.arpa space. And in the same way that IPv4 addresses are reversed (a lookup of 1.2.3.4 results in a PTR lookup of 4.3.2.1.in-addr.arpa), the same is true with IPv6 as well. However, instead of using the colon separated addresses (including the :: variable zeros), IPv6 expands the address an byte at a time. Thus, a lookup of 2001:0db8:85a3::c3b0 resuts in a PTR lookup of 0.b.3.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.3.a.5.8.8.b.d.0.1.0.0.2.ip6.arpa

This might seem excessive, but it is the only way to ensure that leading zeros are represented, and furthermore allows the records to be broken down by TLD. All example records will start with 2001, so a top-level authority will look up a record 1.0.0.2.ip6.arpa which then gives an NS of the 8.b.d.0 record, and so the recursion continues.

Ultimately you will probably have the reverse domain requirements of a /64 or /48 and as such, you can create a zone which represents that reverse lookkup, such as 0.0.0.0.3.a.5.8.8.b.d.0.1.0.0.2. Fortunately, although this looks like a weird zone, we can create this zone in Server Manager – although it doesn’t match the built-in regexp so treats it as a forward list rather than a reverse list. But it does give us this file:

/var/named/zone/db.0.0.0.0.3.a.5.8.8.b.d.0.1.0.0.2.ip6.arpa.zone.apple
1
2
3
4
;THE FOLLOWING INCLUDE WAS ADDED BY SERVER ADMIN. PLEASE DO NOT REMOVE.
$INCLUDE /var/named/zones/db.0.0.0.0.3.a.5.8.8.b.d.0.1.0.0.2.ip6.arpa.zone.apple

0.b.3.c.0.0.0.0.0.0.0.0.0.0.0.0 PTR osx.example.com.

So whilst we can’t edit the file in any meaningful sense in Server Admin, we can use it to wire in the zone as a master for the server and set up any additional options, such as transfers and the like. I’ve found adding zones this way to be much more preferable than trying to add an additional view in the bind configuration; for some reason, the views never seem to work out when I tried the various permutations.

Other config

These notes aren’t IPv6 specific, but are things that I had to do when setting up OSX as a server. However, they’re related so I leave them here for anyone else who is interested in setting them up.

Postfix and Greylisting

The postfix server in OSX sets up an automatic greylisting process, whereby each MTA that connects to the server is immediately told to go away with a 450 code. This is intended to be a transient condition in the SMTP server, such that any MTA should re-try after a certain time-out.

This works when the sending MTA is not a one-off spambot (which typically doesn’t bother) and when there is a single sending MTA (small organisations). The IP address of the attempt gets recorded, which then unlocks the IP address for the re-delivery of the message, provided that it arrives within the appropriate window.

Where this doesn’t work is when someone is sending you a mail from Yahoo or GMail. These have SMTP farms which take it in turns to throw your message at the MTA, each with its own IP address. It likely also records the number of failures, so after cycling through a few of the zombie net it is fairly likely ultimately to mark the address as a failure and give up (and maybe even flag it up for future unavailability).

You can fix this in one of two ways:

  1. Remove the greylisting checks
  2. Implement sender address checks

The first is easiest, but also opens your door to more drive-by spam from fake MTAs. In the main.cf there’s a property smtpd_recipient_restrictions which lists what the checks are.

The check_policy_service unix:private/policy implements the greylisting policy. Removing these two words will disable the greylisting from happening.

You can also leave this in, and add a sender access which allows some common domains. To do this, check_sender_access hash:/etc/postfix/sender_access into the main.cf as follows:

/etc/postfix/main.cf
1
2
3
4
5
6
7
8
9
10
...
# Place the following on one line
smtpd_recipient_restrictions =
 permit_sasl_authenticated
 permit_mynetworks
 reject_unauth_destination
 check_sender_access hash:/etc/postfix/sender_access
 check_policy_service unix:private/policy
 permit
...

We also need to create a file /etc/postfix/sender_access as follows:

/etc/postfix/sender_access
1
2
3
gmail.com OK
yahoo.com OK
...

Whenever you change this file, you need to tell postfix about it; the way you do that is to invoke the shell commands:

1
2
$ sudo postmap /etc/postfix/sender_access
$ sudo postfix reload

Of course, keeping the sender access list up-to-date and monitoring the logs is the only way to tell if everything is OK.

SSL and Mail

OSX Server Admin allows you to define SSL access for mail hosts. There is an option to use SSL for SMTP as well as IMAP. The options are:

  • Don’t use
  • Use
  • Require

The don’t use is not recommended, and means that all communication is unencrypted. Of course, once mail goes to the outside world then it will be unecrypted anyway; but for local-to-local messages it can still make sense.

The require option sounds like a good choice. However, if you enable SSL with require, it forces all clients to switch via StartTLS. This confuses Yahoo! Mail! badly because it tries to send messages via port 587 and then refusing to switch to SSL mode – though that may be an artefact of the self-signed certificate I’m currently using.

The only option that makes sense – unless you’re going to blacklist Yahoo! Mail! – is to use the Use option, and ensure that clients that you care about always switch to SSL when they need to.

Summary

Moving off of Google Mail was a technical challenge, from setting up my own server to configuring all the nuances of IPv6 support. An unrelated part is how I moved over all the GMail from Google to my servers; this basically involved deleting huge quantities of old mail, and then moving the rest via a Mail.app configured to point to both IMAP servers. Once that had been done, it was a case of switching over the MX records and letting the spam begin!