Saturday, October 23, 2010

postfix, mac os X. also fetchmailrc

Continuing the Linux→Mac migration story started here... I had to get something set up to fetch mail automatically. What do you use to fetch mail?
$ type fetchmail
fetchmail is /usr/bin/fetchmail
$ 
Yeah. But I'm getting ahead of myself.

To fetch mail from my ISP, I need to authenticate across the wire. But this is a LAN; I don't want to authenticate with plaintext, and I'm not so sure about pop3s or whatever. How do you spell relief? S-S-H-T-U-N-N-E-L. What I'm gonna do is this:

$ ssh -f shell.my.isp.com -L 60110:popserver.my.isp.com:110 sleep 1200 
Every couple of minutes, I see if the port is still open locally and restart the tunnel if needed. I'll use fetchmail from port 60110 periodically.

How do you fetchmail from an alternate port? Here's what my .fetchmailrc looks like.
poll see.postman.fetchmailrc.invalid proto pop3 port 60110 user ISPUSERNAME pass ISPPASSWORD is postman here mda "/usr/sbin/sendmail -i -f %F %T"
What does all that stuff mean?

  • poll see.postman.fetchmailrc.invalid
    That "see.postnam.fetchmailrc.invalid" thing is an alias for 127.0.0.1 (i.e., localhost). The ".invalid" suffix means sendmail wouldn't try to send anything on it. (I'm not sure this is actually needed any more, but some time in the previous millennium I think, I hit upon this ".invalid" thing as a way to stop some Bad Thing from happening.)
  • proto pop3 port 60110
    This is how I tell fetchmail to use a different port. Since I'm just grabbing the email off my ISP's server (and deleting it as soon as I can deliver locally), there's no need for anything more sophisticated than POP.
  • user ISPUSERNAME pass ISPPASSWORD
    remote username/password
  • is postman here
    deliver remote email to user "postman" on the local host. The "postman" (not "postmaster") has a .procmailrc that figures out who the email is probably for (the lovely Carol, or one of the kids, or me -- actually the ex-teen no longer gets email here) and sends it accordingly. By the way, postman has this in ".forward":
    "|/usr/bin/procmail"
  • mda "/usr/sbin/sendmail -i -f %F %T"
    By default fetchmail will try to deliver to port 25. But the postfix daemon seems to want to stay alive for just a minute. This "mda" incantation says to send mail using this program, rather than trying to connect to port 25.
By the way, I also had to tweak postfix a bit. I messed with the plist file in /System/Library/LaunchDaemons/org.postfix.master.plist, but after learning about the 60-second timeout, I'm not sure that was needed. The below certainly is, though, for outbound mail -- I mean mail sent with mail(1) -- to go anywhere:
$ diff /etc/postfix/main.cf{.install,}
70a71
> myhostname = MYDOMAINNAME
76,77c77,78
< #
< #mydomain = domain.tld
---
> 
> mydomain = MYDOMAINNAME
307a309,310
> # collin 2010-10-23
> relayhost = [MY_ISP.MAILSERVER.HOST]

Getting it all to start on boot

Those details were fascinating (at least they were to me), but how about getting the ssh tunnel kicked off when the box boots?

On a Linux distro with sysV-based startup, you'd put something into /etc/init.d or something; Mac OS X doesn't do that sort of thing. Instead there are files in /System/Library/LaunchDaemons/ . Following some web advice, I created this file:

$ cat /System/Library/LaunchDaemons/collin.postman.tunnel.plist 
## NOTE: owned by root and perms no greater than 0644 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>collin.postman.tunnel</string> <key>Program</key> <string>/Users/postman/tunnel.sh</string> <key>RunAtLoad</key> <true/> </dict> </plist> $
(where /Users/postman/tunnel.sh is the shell script that creates the tunnel if needed, runs fetchmail, etc.)

Then to make the system aware of this...

$ launchctl load /System/Library/LaunchDaemons/collin.postman.tunnel.plist 
$ launchctl start collin.postman.tunnel
And basically, it should work.

UPDATE! 2010-11-07

A few gotchas that threw me off... discovered somewhat (!) later. Easy one first. To determine whether the tunnel was active, tunnel.sh said
 if netstat -an -finet|grep LISTEN | grep 60110 > /dev/null; then
Trouble is, the default path doesn't have a "netstat" in it. Solved by adding this to the script:
PATH=$PATH:/usr/sbin

The next one was a bit harder. As I mentioned before, I followed somebody's plist instructions. Which mostly worked, except for two things:

  1. On startup I'd get this message about how dovecot might use 640 FDs and we have a limit of 256. Ick. I tweaked the parameters it said to, and got the 640 down to something like 528 -- still too high. I may actually fix this someday, but in the meanwhile I'm just becoming root, saying "ulimit -n 1024" , and then spawning dovecot in the same root shell. Ick.
  2. Every 10 seconds or so, syslog got a "dovecot already running" spam. Every 10 seconds! This I addressed by rtfm and changing the plist file to look like this:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
       "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
            <key>Label</key>
            <string>com.diymacserver.dovecot</string>
            <key>ProgramArguments</key>
            <array>
                    <string>/opt/local/sbin/dovecot</string>
                    <string>-F</string>
            </array>
            <key>RunAtLoad</key>
            <true/>
            <key>KeepAlive</key>
            <true/>
    </dict>
    </plist>
    See that "-F" added to ProgramArguments? The manual says that daemons spawned this way must not daemonize but stay in the foreground. "-F" is how we tell dovecot not to say "if (fork()) exit(0);"

No comments: