I’m slowly thinking of things that I’ve hacked together over the years that might be of interest or use to others, and this one is next on the list. I’ll say at the outset that there might be things missing here as I’m having to reconstruct the whole process from memory.
But I digress. What I wanted was a system where I could see when addresses were last used, so that I could delete ones that haven’t received messages for a while. To cut a long story short, the prerequisites for my solution are
- A Linux server which is the MX for your domain
- Postfix, already configured and working
- Apache 2, and the perl CGI, DBI and DBD::Pg modules (for the web management interface)
(I can’t remember now why I used Postgres instead of MySQL, but it was something to do with Postfix’s support, or MySQL’s abilities, or something. Dunno. But there was a reason.)
So first thing’s first: let’s get everything working before we start adding our throwaway addresses. Here’s the Postfix statement for creating the tables in which the addresses will be stored.
CREATE TABLE addresses ( username character varying(32) NOT NULL, local_user character varying(32) NOT NULL, create_time timestamp without time zone DEFAULT now(), expires timestamp without time zone, last_used timestamp without time zone, description text ); CREATE TABLE local_users ( local_user character varying(32) NOT NULL );
(I should say at this point that I’m copying all the Postgres stuff from the output of pg_dumpall, as it’s been a while since I did this and I don’t have a note of my original commands… so some of this—like the ‘without time zone’ bit—might be extraneous)
The addresses table will have a row for each throwaway address. The local_users table is just a list of real users who can receive mail on this host, and is just used for the web management interface.
Next we create a Postgres function that will look up an address (and its real destination), while updating the ‘last used’ time so that we know when it received a message.
CREATE FUNCTION lookup_address(character varying) RETURNS SETOF addresses LANGUAGE sql AS $_$ update addresses set last_used=now() where username=$1; select * from addresses where username=$1; $_$;
Finally, we sort out the database permissions.
CREATE ROLE tempaddress; ALTER ROLE tempaddress WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN PASSWORD 'md5c449f557c920b70a1c2ce82568b75f2d'; GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE addresses TO tempaddress; GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE local_users TO tempaddress;
That MD5 hash is the password ‘tempaddress’—funnily enough it’s not the password on my own server 😛
Now that unpleasantness is behind us, we can configure Postfix. First in main.cf:
alias_maps = hash:/etc/aliases, pgsql:/etc/postfix/tempaddress.cf
You might have other stuff in your alias_maps directive; if so, just add the pgsql bit to the end.
Then we create our configuration file /etc/postfix/tempaddress.cf:
hosts = localhost user = tempaddress password = tempaddress dbname = tempaddress query = select local_user from lookup_address('%s');
If you use the MD5 hash above then you can leave the password as-is, but that’s probably not a good idea.
The key bit is the query SQL command: it calls our function lookup_address which we defined earlier. That function will update the last-used time on the address in question, and return the local user that should actually receive the email. Postfix will then route the email accordingly (or reject it if Postgres finds no match).
Restart Postfix, and that’s it—the table in the database will now be queried every time an email arrives to an otherwise unknown address. Although it’s basically working now, it’s a bit of a pain in the bum to enter database commands every time we want to add or delete our throwaway addresses, so I wrote a little script to manage it all called tempaddress.pl.
It’s a standard CGI script, so bung it in your cgi-bin directory, protect it properly with authentication and so forth, change the $DBPASS variable in the script if you haven’t use the MD5 hash I gave above, and you’re ready to go. The script (and indeed all of this) was always a little hack, and no-one except me has ever used it, so apologies in advance for any strangeness.
Oh, one more thing: I never implemented a way of adding local users with the web interface, mainly because I’m the only one on my server, so I just stuck myself in the local_users table by hand… so you’ll have to do this as well.