Sendmail

From Jonathan Gardner's Tech Wiki
Jump to: navigation, search


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:

  1. Email from outside to someone@jonathangardner.net goes to 3rd party
  2. Email from inside to someone@jonathangardner.net goes to 3rd party
  3. Email from inside to someone@localhost goes to 3rd party
  4. Email from <host>.jonathangardner.net to someone@<host>.jonathangardner.net (from host to same host) goes to 3rd party
  5. Email from inside to someone goes to 3rd party.
  6. System messages from the machines go to root@jonathangardner.net.
  7. Email from inside to some other domain is delivered appropriately.

Testing all of these cases are easy.

  1. I can test mail coming from outside by sending email from another account.
  2. I can test mail from inside to someone@localhost by running the command: mail someone@localhost
  3. I can test mail from inside to someone@jonathangardner.net by running the command: mail someone@jonathangardner.net
  4. I can test mail from inside to someone@<host>.jonathangardner.net by running the command mail someone@<host>.jonathangardner.net.
  5. I can test mail from inside to someone by running the command mail someone.
  6. 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:

  1. 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.
  2. Route all email to <this host>.jonathangardner.net to jonathangardner.net of the same user.
  3. Route all email to localhost to jonathangardner.net of the same user.
  4. Route all email to jonathangardner.net appropriately.
  5. 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.

mail

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.

See Also