Sendmail
Introduction
Sendmail is a nightmare to setup. I am going to catalog what I do. Perhaps there are others who do this who will find my experience useful.
Note that I am using Software Development Best Practices. I will write the tests first and then try to pass all the tests.
Networking Layout
I have several home machines running Linux. I am also using a 3rd party to handle all of my incoming email. Here are the scenarios I need to handle:
- Email from outside to someone@jonathangardner.net goes to 3rd party
- Email from inside to someone@jonathangardner.net goes to 3rd party
- Email from inside to someone@localhost goes to 3rd party
- Email from <host>.jonathangardner.net to someone@<host>.jonathangardner.net (from host to same host) goes to 3rd party
- Email from inside to someone goes to 3rd party.
- System messages from the machines go to root@jonathangardner.net.
- Email from inside to some other domain is delivered appropriately.
Testing all of these cases are easy.
- I can test mail coming from outside by sending email from another account.
- I can test mail from inside to someone@localhost by running the command: mail someone@localhost
- I can test mail from inside to someone@jonathangardner.net by running the command: mail someone@jonathangardner.net
- I can test mail from inside to someone@<host>.jonathangardner.net by running the command mail someone@<host>.jonathangardner.net.
- I can test mail from inside to someone by running the command mail someone.
- I can test mail from inside to someone at some other domain by running the command mail someone@example.com.
Here's a shell script that will do all of the above from one of my machines.
#!/bin/bash FROM=$USER@$HOSTNAME for TO in jgardner \ jgardner@localhost \ jgardner@$HOSTNAME \ jgardner@jonathangardner.net \ someone@example.com; do echo "Test to $TO from $FROM" | mail -s "Test to $TO from $FROM" $TO; done;
For other machines, I just have to test from another email host and client.
Sendmail Configuration
Sendmail is going to be setup by all of the hosts to do the following:
- Do not accept incoming email under any circumstances.
- Don't listen on port 25 or 443
- Don't allow traffic to port 25 or 443 except on the the loopback interface (-i lo). See iptables.
- Route all email to <this host>.jonathangardner.net to jonathangardner.net of the same user.
- Route all email to localhost to jonathangardner.net of the same user.
- Route all email to jonathangardner.net appropriately.
- Route other email as well.
Configuring Sendmail
Most of the configuration lives in /etc/mail. You have to have sudo permissions to edit stuff here.
There should be a Makefile in that directory as well. When you modify the files, there has to be some work before the modifications are compiled into the format sendmail understands. The sendmail restart process handles this.
To restart, simply do the following as root:
service sendmail restart
This will rebuild the configuration files and restart sendmail to take the changes into effect.
Don't Accept Incoming Email
I don't allow incoming email. No one should be using my home machines for email since I am using a 3rd party.
The relevant line in /etc/mail/sendmail.mc file looks like this: (You may have to remove the leading dnl before the commands.)
dnl # dnl # The following causes sendmail to only listen on the IPv4 loopback address dnl # 127.0.0.1 and not on any other network devices. Remove the loopback dnl # address restriction to accept email from the internet or intranet. dnl # DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')dnl dnl # dnl # The following causes sendmail to additionally listen on the IPv6 loopback dnl # device. Remove the loopback address restriction listen to the network. dnl # DAEMON_OPTIONS(`port=smtp,Addr=::1, Name=MTA-v6, Family=inet6')dnl dnl # dnl # enable both ipv6 and ipv4 in sendmail: dnl # DAEMON_OPTIONS(`Name=MTA-v4, Family=inet, Name=MTA-v6, Family=inet6')
Note that it listens on 127.0.0.1. Only connections to localhost are going to be received. For everyone else, it's as if nothing is listening on port 25. (See /etc/services to see how "smtp" gets translated to 25.) Note that we don't need port 443--we won't need SSL for connections from localhost.
NOTE: I've had some problems getting IPv6 to work, and frankly, I don't need it--yet. I commented out the last two DAEMON_OPTIONS because they are really unnecessary.
You can double-check that it is listening to only 127.0.0.1:25 by using netstat:
netstat -pan | grep :25
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 8275/sendmail tcp 0 0 ::1:25 :::* LISTEN 8275/sendmail
This is half of the equation. Let's also configure iptables so that we won't listen to ports 25 or 443.
iptables -L -v
Should give the output with lines like this:
740 83428 ACCEPT 0 -- lo any anywhere anywhere
This says that any traffic coming over the loopback interface (the one 127.0.0.1 is bound to--try route -n to verify) will be accepted.
You should not see output like:
170 10180 ACCEPT tcp -- any any anywhere anywhere state NEW tcp dpt:smtp
If you do, that means that incoming TCP connections to the smtp port is going to be accepted. You'll want to remove such a rule if it exists (and it shouldn't by default.)
Routing Email
We want to first setup sendmail so that it won't accept any email (except for itself).
In the sendmail.mc file, comment out these lines with 'dnl':
dnl # dnl # Also accept email sent to "localhost.localdomain" as local email. dnl # dnl LOCAL_DOMAIN(`localhost.localdomain')dnl
This line would have allowed email to localhost to be delivered as if directed to your host. That's not what you want.
domaintable
The last bit is to setup a domaintable. This will refer emails to a particular domain and route it to another domain.
To figure out what domain you are running as, run this command:
hostname
That will print out what your computer thinks its domain name is, and what sendmail will assume is your domain name.
Now edit /etc/mail/domaintable and add the line:
<hostname> jonathangardner.net
Now we need to make sure that domaintable is being read. Make sure this line is in sendmail.mc. If not, add it:
FEATURE(domaintable)dnl
With those changes in place, your email should be routed properly.
Sending through another emailer
If you have a direct connection to the internet (you have an IP address that isn't assigned by DHCP), and your ISP doesn't mind you sending emails, then you don't need to do anything. However, if you have problem getting your email to go through, then you'll need to do this part.
You can set up a SMART_HOST, that is, a host through which all of the outgoing email will go. To do so, just add the line:
define(`SMART_HOST', `your.isp.mailrouter.example.com')dnl
That's it.
Debugging
You're going to have problems. Here's how to debug:
Check local mail
You can check local mail with the mail command.
This will show you any mail in your local account. Type 'h' to learn how to use this tool.
Check mail logs
The mail logs live at /var/log/maillog. Read this as messages pass through your system to see how the messages are routed.
Check system messages
Don't forget to check system messages at /var/log/messages. There may be some tips in there about what is going wrong.
Walk through the process, every step of the way
Finally, walk through the process. Check network connections. Check that ports are open where they need to be, etc... Check that sendmail is running and listening to the hosts. There are a million things that can go wrong so don't assume anything.