Linux Mail Server:


The Scenario


My boss approached me asking about how hard (read expensive) it would be to get "email in the office". What he wanted was everyone in the office (around 8 people) to have internet email but also local email. My budget was essentially zero. Anything I had to spend money on meant sitting down for a half-hour and explaining why we needed it. Not wanting to have addresses like "DavidEncon@aol.com" I inquired of my personal ISP (Monmouth Internet) about virtual domain hosting. It was cheap enough and we would get 10 email accounts with it. Cool. One problem, it only carried one dial-up account with it (we can't get DSL or Cable access in our area and a T1 or fraction thereof would be too expensive). I didn't want to have to buy 7 more dialup accounts or run phone wire throughout the office. I didn't trust the users to check to make sure only one person was on at a time. I also didn't want one person responsible for getting the email and distributing in around. I started looking into Linux which I had been running at home for about two years.


The Plan


I had a Pentium 90 that was going to get thrown out. I took that machine and planned to load Slackware on it (I run Slackware at home). It would dial into our ISP, retrieve the email through fetchmail and deliver it locally to user accounts on the Linux box. The Windows Workstations would then retrieve the user's email through Eudora Light (or something) through POP3. Eudora would use the Linux machine (mail from here on) as the SMTP server. Sendmail would route the mail either locally or to our ISP as a smart relay.


These are the basic steps:

  1. Install Linux (Slackware 8.1)

  2. Configure local networking (ifconfig etc.)

  3. Add users

  4. Configure dial-up networking (ppp)

  5. Configure mail retrieval (fetchmail)

  6. Configure MTA (sendmail)

  7. Set up scripts and cron jobs to tie it all together


The Execution


Installing Linux:


Slackware has an installer which uses ncurses menus to aid the process. I won't go into too much detail here. Install almost all of the A disk set and most of the N and it should get you going. I would recommend not installing software you don't need. Our box was pretty limited in what we wanted it to do so I only installed the bare minimums. This is a good idea because the default installation enables things that could be a security hazard if they aren't configured properly. The less software installed, the less you have to watch for bugs and vulnerabilities.


Configure local networking:


Network cards are ridiculously cheap now, and many computers are coming with them. Even Windows 95 has networking capabilities built in. Switches are cheap now, too, so getting a peer to peer network set up in your office (if you don't already have one) is not hard. If you do have one already, it's just a matter of adding the mail server to the existing network.


Read the Ethernet HOWTO. It explains pretty much everything you need to know. Unless you need to have the network on the internet, use private addresses. Most routers are configured to drop these adresses if they should get out by mistake. If you like, you can set up dhcp to assign the workstation addresses, but I used static IP addresses in our workstations.


DNS isn't absolutely necessary, but without it, each machine has to have a hosts file which will need to be updated every time the network changes. I set up a caching DNS with a local zone file that does not update any master zone servers. Read the DNS HOWTO for more information. The important part of the DNS, however, is to make sure you don't clobber the DNS entry your ISP has for your MX record. You want that to stay as your ISP's mail server.


Add users:


This is simple. Slackware has a utility which makes this easy. It creates the /home directories, makes the entries in /etc/passwd and prompts you for a password. Most distributions have utilities that make this easy.


Configure dial-up networking:


Slackware has a utility to make this easy. It prompts you for the entries you need to connect to your ISP. It's really not very different than configuring Windows Dial-up Networking. The only real difference is that I added a command to the startup scripts to enable "dial on demand". Any time the box wants to talk to the outside world, it starts the modem connection. This can be a hassle if services want an internet connection, but for the most part they are easy to disable once you find them. I have the idle timer in ppp set to disconnect after 60 seconds of inactivity. I recommend recompiling pppd for packet filtering as someone trying to get into the box, even if your firewall blocks the connection, resets the idle timer and keeps the connection live. It's very easy to do. Take a look at the PPP Active Filtering HowTo.


Once you have the connection active, I highly recommend a firewall. Our box is only on line for 2 minutes every half-hour, but I have caught more than one spammer trying to relay through our box. This is a box with no MX record pointing to it from outside our office, so nobody knows it's even running an MTA, but that doesn't stop them from looking for one.


The ip-up script is executed everytime the modem connects. I have fetchmail and sendmail -q running from this script.



Configure mail retrieval:


Our ISP sets up a virtual domain so you can have up to 10 email boxes in addition to the main account email box. Anything sent to *@enconmech.com will land in the main account box unless there is an additional box set up with that address. I set up 8 of the boxes for our users and everything else goes to the main box. If you don't have that option, or if you run out of email boxes with your ISP, you can set up a "catch all" account on the Linux box and use procmail to forward the mail to the correct users.


Configuring fetchmail consists of typing a config file with the ISP's mail server and the protocol to use. Under that, you simply add the user ids you set up on the ISP mail boxes with passwords and a map to who they are on your mail server. The format is:


user someuser with password somesecret is ouruser here


This will direct mail sent to someuser@ourdomain.com to ouruser on our box. Make sure this file is only readable by root because the passwords are human readable.


Fetchmail can run as a daemon, but I have had problems with it timing out while waiting for the modem to negotiate the connection (if ppp is in demand mode). Placing fetchmail -f /etc/fetchmailrc in the ip-up file will run fetchmail whenever the ppp connection comes up. Then just start the ppp connection and it will retrieve the mail. The problem this can cause, however, is that, if fetchmail is already running (downloading a large message for instance), starting fetchmail again kills the first process and starts over. This can get you in an endless loop. My solution for that was to call a script called checkmail from ip-up which first checks if a file called /var/run/fetchmail.pid exists. If so, the script drops out. Otherwise, it calls fetchmail.


Configuring the MTA:


Sendmail can be pretty intimidating. There are numerous books written on how to configure it. I picked sendmail because it came with Slackware, but there are other mail transport agents available. Qmail and Postfix are both supposed to be good, but I have no experience with them.


The out-of-the-box sendmail configuration worked great for delivering local mail as well as mail retrieved from our ISP. But I had to tweak a few things to make it deliver mail to the internet. You have to modify the default configuration file and add a few things. This is my configuration which gets run through m4 to create the sendmail configuration file (sendmail.cf). I will explain what each option means.







include(`../m4/cf.m4')

VERSIONID(`Encon smtp and local mail config')dnl

DOMAIN(generic)dnl

OSTYPE(linux)

FEATURE(nouucp,'reject')dnl

MASQUERADE_AS(enconmech.com)dnl

FEATURE(`masquerade_envelope')dnl

FEATURE(`masquerade_entire_domain')dnl

FEATURE(`accept_unresolvable_domains')dnl

FEATURE(`use_cw_file')dnl

FEATURE(`relay_entire_domain')dnl

FEATURE(`nocanonify')dnl

FEATURE(`stickyhost')dnl

define(`SMART_HOST',`smtp.monmouth.com')dnl

define(`SMTP_MAILER_FLAGS',`e')dnl

define(`confCON_EXPENSIVE',`True')dnl

define(`confREFUSE_LA',`30')dnl

MAILER(local)dnl






include(`../m4/cf.m4')

This says to include the directives in the specified file. You have to add this line or the configuration file won't compile correctly



VERSIONID(`Encon smtp and local mail config')dnl

This version id shows up in the sendmail.cf file once you run it through the m4 macro processor. It makes it easy to find the mc file used to make the cf file later.


DOMAIN(generic)dnl

OSTYPE(linux)

The domain and ostype directives includes some default values.


FEATURE(nouucp,'reject')dnl

We aren't accepting email via uucp. This tells sendmail to reject any uucp transactions.


MASQUERADE_AS(enconmech.com)dnl

FEATURE(`masquerade_envelope')dnl

FEATURE(`masquerade_entire_domain')dnl

This makes sendmail send all the email as if it came from a user on one machine. So instead of seeing tsgilvary@shannon.enconmech.com the headers only read tsgilvary@enconmech.com.



FEATURE(`accept_unresolvable_domains')dnl

This feature tells sendmail to accept for delivery any mail that does not have a resolvable domain. Since our server is not connected all the time, and DNS (except locally) won't be available most of the time, we need this option or users sending mail get an error. It can also cause caching DNS to initiate the modem connection to try to resolve the domain. This option is dangerous because of spam, but I'm only allowing access from the local LAN so it shouldn't be a problem.


FEATURE(`use_cw_file')dnl

This feature tells sendmail to look in /etc/mail/local-host-names for other names this server is known by. This lets mail from a local user to somebody@enconmech.com be delivered locally.


FEATURE(`relay_entire_domain')dnl

This allows relaying from any machine claiming to be from our domain. Again, this is a dangerous option because a spammer could spoof the domain name, but as I'm only allowing access from the LAN it shouldn't be a problem.


FEATURE(`nocanonify')dnl

I added 'nocanonify' to keep sendmail from connecting to the internet everytime someone in the office sent mail. Our email is being relayed through our ISP's MTA before it gets delivered, so it isn't a problem.


FEATURE(`stickyhost')dnl



define(`SMART_HOST',`smtp.monmouth.com')dnl

This sets up our ISP's smtp server to be our smart relay. Any mail that isn't delivered locally will go through monmouth.com to get delivered. Otherwise, sendmail attempts to connect to the mail server for the domain the mail is being sent to.


define(`SMTP_MAILER_FLAGS',`e')dnl

define(`confCON_EXPENSIVE',`True')dnl

These two tell sendmail that the SMTP (ie not local) mailer is "expensive" and that it should queue messages for it instead of delivering them immediately. It will hold the mail in queue until you execute sendmail -q which tells sendmail to process the queue. I have this command in the ip-up script which gets executed when the modem makes a connection to the internet. This way, sendmail doesn't cause the modem to dial out everytime someone in the office sends email. My checkmail script automatically checks for outgoing mail every 5 minutes and starts the connection if there is any.


define(`confREFUSE_LA',`30')dnl

I put this in because we run spamassassin which is a resource hog (at least on a P-90 ;-). Sendmail will stop accepting connections if the load average gets too high. Setting this number high keeps sendmail accepting connections in all but the worst circumstances. First, this is the main function of the server. I don't have anything more pressing for it to do than sending mail. Second, if sendmail doesn't process the mail, it creates a race condition with spamassassin: spamassassin continues to use resources until the mail is delivered but the mail can't be delivered until some resources are freed. I also limited the number of children spamd can spawn.


MAILER(local)dnl

MAILER(smtp)dnl

These include the two mailers in the cf file.


This file is processed using the command:

m4 < encon.smtp.mc > encon.smtp.cf

look through it briefly, copy it to /etc/mail/sendmail.cf, send sendmail a SIGHUP and you're on your way.


After all that was set, it was just a matter of getting it all working together. I call a script every five minutes from a cron job. This script checks to see if there is any out going mail (/var/spool/mqueue). If there is, it initiates the connection immediately and flushes the queue. The script also checks the mail once every five minutes if we're online anyway, or initiates a connection every 20 minutes if we aren't. An entry in ip-up sends the mail queue and calls the checkmail script with a value of 1 minute. The script checks to see if there is another fetchmail running before starting another one as long downloads (attachments) will keep fetchmail open for longer than 20 minutes and calling it again kills the first one .

FAC=mail
PRI=info
if (( $# != 1 )); then
	echo "usage: checkmail minutes" > /dev/stderr
	exit 1
else
	checktime=$1
fi

# how long since we last checked the mail?
lastcheck=$(( (`date +%s`-`cat /var/checkmail/mail-checked`) / 60 ))
logger -i -p $FAC.$PRI -t checkmail Mail last checked $lastcheck minutes ago

#check to see if ppp is running already
if [[ -f /var/run/ppp0.pid ]]; then 

	# if it is, is the connection live
	# modify /etc/ppp/ip-up to write a "1" to the /var/ppp/ip-up file
	if [[ $(grep 1 /var/ppp/ip-up) ]]; then

		# if so, send the queue since we're online already
		sendmail -q

		# if the mail was checked less than five minutes ago, don't bother
		if (($lastcheck > 5)) && [[ ! -f /var/run/fetchmail.pid ]]; then

			logger -i -p $FAC.$PRI -t checkmail Checking mail

			if [[ $(fetchmail -a -f /etc/fetchmailrc) > 2 ]]; then

				#update the time the mail was checked
				echo `date +%s` > /var/checkmail/mail-checked

			fi

		fi
	else
		# if not, check to see if there's any outgoing mail in the
		# queue and how long it's been since we checked incoming
		# mail
		if [[ $(ls /var/spool/mqueue) ]] || (($lastcheck > $checktime)); then
			logger -i -p $FAC.$PRI -t checkmail Starting mail connection

			# this ping will fail but start the connection
			# /etc/ppp/ip-up calls this script which will run
			# the above loop
			ping -c1 -w1 mail.monmouth.com > /dev/null  2>&1

		fi
	fi
else
	# start the ppp connection which will call this script
	# and run the above loop
	# /var/ppp/restart must contain "1" for this to work
	# set to "0" (or anything but "1") to disable auto restart

	if [[ $(grep 1 /var/ppp/restart) ]]; then
		ppp-go -d 1>> /var/log/ppplog 2>> /var/log/ppplog
	fi

fi

Our email server is working. Every one has their own email account which means our receptionist doesn't have to download and sort the mail. People can email everyone in the office and on the internet from the same mail client. This is important for two reasons: 1) I don't have to support two different mail clients or worse, explain to people that Eudora - local is for here and Eudora - Internet is for outside; and 2) I don't have to explain the difference between local email and remote email. I didn't have to purchase 8 dialup accounts (and run phone lines) that were only going to get used for minutes a day, but I don't have to worry about my users violating our terms of service by being online at the same time. We have our own domain name which just looks more professional than an AOL account ;-). And it didn't cost me a dime. I had the hardware lying around and the software was all free. We only have to pay for the virtual domain hosting and that is roughly $50 a month.


Add-ons:


Once you have the mail box running with Linux, you can add Samba to allow file sharing and print spooling from the Windows machines to the Linux box. I added a Zip drive and run a script every night which backs up our Quickbooks files, and once a week backs up reception's My Documents folder.


Since all the mail is coming through one machine, you can set up something like SpamAssassin to do spam filtering, or virus scanning software to catch viruses through email. I especially like that idea as it catches the virus before it gets to a Windows machine and quarantines it on a machine that it most likely can't infect.



Maintenance:


The most important part of keeping the box secure is maintenance. I go through our logs once a week just to make sure there is no funny business going on. I'm also on Slackware's security mailing list to make sure I know about any vulnerabilities that need patching. Apply the patches. This is where not installing unnecessary services comes in handy. I ignore any Apache vulnerabilities because I don't have Apache installed. You can't be caught by a vulnerability you didn't know about if the software isn't on the machine.