Internet Bandaid   [RSS Feed]

Archive for February 3rd, 2010

PHP Mail() with Postfix on Ubuntu

without comments

I have a newer guide for 2015-12-04. Click here.

I set up my own LAMP VPS on linode.com.  Although linode provides a good guide on how to set up the LAMP environment, it doesn’t explain how to enable the PHP mail() function.  Additionally, it doesn’t explain how to prevent the server’s outgoing mail from ending up in the spam folder of popular web mail like Gmail, Yahoo and Hotmail.  I’ll explain my steps to getting PHP’s mail() function to work, without having to install a full blown mail server.  This means the bare minimum installation to get your PHP mail function to do the following:

- send out-going mail
- encrypt out-going mail
- reduce likelihood out-going mail ends up in spam folder (by using SPF for google, DKIM for yahoo and hotmail)

I’m not interested in hosting my own incoming mail box since I’ve got Google Apps to handle my mail (see linode guide for details).

Install Postfix

Postfix will be your mail server.  Install it by simply typing into shell:

sudo apt-get install postfix

Linux may have sendmail as the default Mail Transfer Agent (MTA) instead of PostFix.   I’ve decided to remove sendmail so it doesn’t conflict with Postfix by doing the following

sudo apt-get autoremove sendmail
/etc/init.d/postfix restart

That should set your Postfix as your MTA.   And now you should be able to use PHP’s mail() function to send out mail, although it MAY end up in spam boxes.

Install TLS Encryption

You’d want to encrypt your outgoing mail.  Issue the following commands (this is an excerpt from ubuntu site):

touch smtpd.key
chmod 600 smtpd.key
openssl genrsa 1024 > smtpd.key
openssl req -new -key smtpd.key -x509 -days 3650 -out smtpd.crt # has prompts
openssl req -new -x509 -extensions v3_ca -keyout cakey.pem -out cacert.pem -days 3650 # has prompts
sudo mv smtpd.key /etc/ssl/private/
sudo mv smtpd.crt /etc/ssl/certs/
sudo mv cakey.pem /etc/ssl/private/
sudo mv cacert.pem /etc/ssl/certs/
sudo postconf -e 'smtp_tls_security_level = may'
sudo postconf -e 'smtpd_tls_security_level = may'
sudo postconf -e 'smtpd_tls_auth_only = no'
sudo postconf -e 'smtp_tls_note_starttls_offer = yes'
sudo postconf -e 'smtpd_tls_key_file = /etc/ssl/private/smtpd.key'
sudo postconf -e 'smtpd_tls_cert_file = /etc/ssl/certs/smtpd.crt'
sudo postconf -e 'smtpd_tls_CAfile = /etc/ssl/certs/cacert.pem'
sudo postconf -e 'smtpd_tls_loglevel = 1'
sudo postconf -e 'smtpd_tls_received_header = yes'
sudo postconf -e 'smtpd_tls_session_cache_timeout = 3600s'
sudo postconf -e 'tls_random_source = dev:/dev/urandom'
sudo postconf -e 'myhostname = server1.example.com' # remember to change this to yours

In the last command, make sure you replace server1.example.com with your domain name.

If you have multiple domain names, add them to /etc/postfix/main.cf under

mydestination = domain1.com, domain2.com, domain3.com

Install SPF

Installing SPF will reduce the likelihood your emails go to recipient’s spam folder.

sudo apt-get install python-policyd-spf python-spf

Add this line to the end of /etc/postfix/main.cf

spf-policyd_time_limit = 3600s

Also add the following

smtpd_recipient_restrictions =
     ...
     permit_sasl_authenticated
     permit_mynetworks
     reject_unauth_destination
     check_policy_service unix:private/policy-spf
     ...

Where the triple dots denote the possible existence of other settings.

Now you should restart your postfix by typing

sudo /etc/init.d/postfix restart

Add the following TXT record in your DNS Manager to utilize SPF

v=spf1 a mx ~all

And that should be it.

Install PostfixDKIM

This guide is based on Ubuntu guide and someone’s answer on Serverfault.

sudo aptitude install dkim-filter

Open up /etc/dkim-filter.conf and edit it so that it’s like below:

# Log to syslog
Syslog                  yes
# Required to use local socket with MTAs that access the socket as a non-
# privileged user (e.g. Postfix)
#UMask                  002
# dkim-milter (2.5.2.dfsg-1ubuntu1) hardy:
# Disable new umask option by default (not needed since Ubuntu default
# uses a TCP socket instead of a Unix socket).

# Attempt to become the specified userid before starting operations.
#UserID                 105 # 'id postfix' in your shell

# Sign for example.com with key in /etc/mail/dkim.key using
# selector '2007' (e.g. 2007._domainkey.example.com)
# Domain won't really matter because that will be specified in the KeyList file
Domain                  yourdomain.com
#KeyFile                 /etc/mail/dkim.key # See bellow how to generate and set up the key
Selector                mail

# Common settings. See dkim-filter.conf(5) for more information.
AutoRestart             yes
Background              yes
Canonicalization        simple
DNSTimeout              5
Mode                    sv
SignatureAlgorithm      rsa-sha256
SubDomains              no
#UseASPDiscard          no
#Version                rfc4871
X-Header                no

#InternalHosts          /etc/mail/dkim-InternalHosts.txt
# The contents of /etc/mail/dkim-InternalHosts.txt should be
#   127.0.0.1/8
#   192.168.1.0/24
#   other.internal.host.domain.tld
# You need InternalHosts if you are signing e-mails on a gateway mail server
# for each of the computers on your LAN.

###############################################
# Other (less-standard) configuration options #
###############################################
#
# If enabled, log verification stats here
Statistics              /var/log/dkim-filter/dkim-stats
#
# KeyList is a file containing tuples of key information. Requires
# KeyFile to be unset. Each line of the file should be of the format:
#    sender glob:signing domain:signing key file
# Blank lines and lines beginning with # are ignored. Selector will be
# derived from the key's filename.
KeyList                /etc/mail/mail
#
# If enabled, will generate verification failure reports for any messages
# that fail signature verification. These will be sent to the r= address
# in the policy record, if any.
#ReportInfo             yes
#
# If enabled, will issue a Sendmail QUARANTINE for any messages that fail
# signature verification, allowing them to be inspected later.
#Quarantine             yes
#
# If enabled, will check for required headers when processing messages.
# At a minimum, that means From: and Date: will be required. Messages not
# containing the required headers will not be signed or verified, but will
# be passed through
#RequiredHeaders        yes

Add the following lines to /etc/postfix/main.cf

# DKIM
milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

Create the public and private keys with these lines

openssl genrsa -out private.key 1024
openssl rsa -in private.key -out public.key -pubout -outform PEM
cp private.key /etc/mail/mail

Add a TXT record to your DNS like so

mail._domainkey.yourserverdomain.com. IN TXT "k=rsa; t=y; p=yourpubkey"

Where yourdomain.com is the domain you want to authenticate against, and yourpubkey is the contents of public.key WITHOUT the

-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----

Create a new file called /etc/mail/dkim_domains.key and put into it

*:yourserverdomain.com:/etc/mail/mail

Again, replace yourserverdomain.com with the domain your want to authenticate against, which should be the same value as the one chosen in your TXT record.

Then open up your /etc/dkim-filter.conf again and change the KeyList line to the following:

KeyList /etc/mail/dkim_domains.key

That should tell DKIM to point to the right dkim_domains.key file.

Then restart dkimfilter and postfix with

> /etc/init.d/dkim-filter restart
> /etc/init.d/postfix restart

Now you’re done install postfix dkim

CHECK TO MAKE SURE IT WORKS!

Send a few emails with PHP mail() function.  If all works properly, you should get emails in your gmail, yahoo or hotmail inbox as opposed to spam folder.  Additionally, if you inspect for header information, take notice of the text I bolded:

Delivered-To: lai.john@gmail.com
Received: by 11.111.11.11 with SMTP id lkdjfljasdlfj;
        Sun, 17 Apr 2011 07:59:31 -0700 (PDT)
Received: by 11.111.11.11 with SMTP id b5kjlj95vcx.140.130304353492;
        Sun, 17 Apr 2011 07:59:30 -0700 (PDT)
Return-Path: <john@yourserverdomain.com>
Received: from yourserverdomain.com (providersomething.com [173.255.230.136])
        by mx.google.com with ESMTPS id b35si3672371vcm.157.2011.04.17.07.59.29
        (version=TLSv1/SSLv3 cipher=OTHER);
        Sun, 17 Apr 2011 07:59:29 -0700 (PDT)
Received-SPF: pass (google.com: domain of john@yourserverdomain.com designates 222.222.222.222 as permitted sender) client-ip=222.222.222.222;
Authentication-Results: mx.google.com; spf=pass (google.com: domain of john@yourserverdomain.com designates 222.222.222.222 as permitted sender) smtp.mail=john@yourserverdomain.com; dkim=neutral (bad format) header.i=@yourserverdomain.com
Received: by yourserverdomain.com (Postfix, from userid 33)
	id 73D6A74DAE; Sun, 17 Apr 2011 09:59:29 -0500 (EST)
DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=yourserverdomain.com; s=mail;
	t=1303052369; bh=/Ab0giHZitYQbDhFszoqQRUkgqueaX9zatJttIU/plc=;
	h=To:Subject:From:Message-Id:Date;
	b=GHUBp+kiQRGVpecyYusQRuPWWDBwPZmTOsomecrazykey0GE=
To: john@gmail.com
Subject: test
X-PHP-Originating-Script: 0:mail.php
From: john@anydomainatall.com
Message-Id: <20110417145929.73D6A74DAE@yourserverdomain.com>
Date: Sun, 17 Apr 2011 09:59:29 -0500 (EST)

Notice the SPF pass  (as opposed to something negative like fail), and the existence of a DKIM-Signature (as opposed to something inconclusive like nothing).  All should be good now.

Other useful resources:

Set up SPF Records

SPF, DKIM or SenderId?

Written by John Lai

February 3rd, 2010 at 2:10 pm