Sendmail Configuration for Laravel: A Comprehensive Guide
Introduction
Sendmail is a classic Mail Transfer Agent (MTA) that facilitates email delivery on Unix-based systems. While modern alternatives such as Postfix and Exim have gained popularity, Sendmail remains a crucial tool for legacy systems, deep troubleshooting, and custom setups. This guide aims to provide a comprehensive understanding of configuring Sendmail for Laravel without advocating it as the primary choice. It's a deep dive, drawing from the provided documentation, to help you master Sendmail's intricacies. This is not a recommendation to use it everywhere, but rather a valuable skill for specific situations.
Laravel offers flexibility in handling various mail drivers, including SMTP, Mailgun, Postmark, Amazon SES, and Sendmail. While Sendmail isn't typically the default choice due to its complexity and security considerations, it can be beneficial in specific cases:
- Direct SMTP control without relying on an external service.
- Integration with legacy systems already using Sendmail.
- Advanced customization and local mail handling.
Sendmail Configuration Files: The Basics
Sendmail configuration revolves around .mc
files, which are processed by the m4 macro processor to generate .cf
configuration files. It's important to know that .mc
files are human-readable, while .cf
files are not meant to be edited directly. The m4
processor compiles .mc
files into .cf
files, treating the input as a stream rather than line by line, making the order of directives crucial.
To convert an .mc
file to a .cf
file, you would typically use a command like this:
m4 ${CFDIR}/m4/cf.m4 config.mc > config.cf
${CFDIR}
usually refers to /etc/mail/
or a similar location.
Anatomy of a .mc File
The general structure of an .mc
file is important to understand. The order of the directives matters. Key elements include:
VERSIONID
: For version control information.OSTYPE
: Specifies the operating system.DOMAIN
: Sets domain-specific configurations.FEATURE
: Activates specific functionalities.- Local macro definitions.
MAILER
: Specifies which mailers to use.- Local rulesets.
Macro definitions that influence a FEATURE()
should be defined before that feature.
Essential Directives in .mc Files
-
divert(0)
: This directive restores normal output after adivert(-1)
command, which is often used to exclude unnecessary sections. -
VERSIONID
: This adds version control information to the configuration file. This is not the same as the version ID in SMTP greeting messages. -
OSTYPE(os_type)
: This macro is essential for specifying the operating system to correctly configure pathnames, flags for the local mailer, and more. If this is omitted, it will result in an error during the build process. Known operating system types can be found in theostype
directory.- Example for HP-UX:
OSTYPE(`hpux9')dnl
- Selecting the correct
OSTYPE
is crucial to prevent configuration errors.
- Example for HP-UX:
-
DOMAIN(domain_name)
: This configures domain-specific settings. You can useDOMAIN('generic')
for a basic setup or create a custom domain definition.- Example for the Computer Science Division at Berkeley:
DOMAIN(`CS.Berkeley.EDU')dnl
- Example for the Computer Science Division at Berkeley:
-
MAILER(mailer_type)
: This defines the mailers used by the system. Thelocal
mailer is always included by default.MAILER
declarations should typically be at the end of the configuration file.- Example showing how to include local and SMTP mailers:
MAILER(`local') MAILER(`smtp')
- Example showing how to include local and SMTP mailers:
-
FEATURE(feature_name, optional_args)
: This enables special features and accepts optional parameters.- The default database map type for the table features can be changed with
define('DATABASE_MAP_TYPE', 'dbm')
.
- The default database map type for the table features can be changed with
-
define(macro, value)
: This sets macros controlling various aspects of Sendmail's operation. Quoting both the macro name and the value can prevent unintended macro expansion.- Example: Setting a smart host:
define(`SMART_HOST', `smtp.yourdomain.com')
- Example: Setting a smart host:
Understanding the M4 Macro Processor
Sendmail uses m4
to compile configuration files. m4
processes input as a stream rather than line by line. The dnl
command, short for "delete through newline," is used for commenting out lines or parts of lines in .mc
files. Macros are expanded even within comments, which may lead to unexpected behaviour.
File Locations and the MAIL_SETTINGS_DIR
Macro
The MAIL_SETTINGS_DIR
macro defines where Sendmail looks for configuration files, typically /etc/mail/
. All filenames in .mc
or .cf
files should be absolute paths for consistency and security.
Operating System Definitions (ostype Files)
Each OS has predefined settings in ostype
files, such as ALIAS_FILE
and QUEUE_DIR
. Customising these ensures optimal performance. These files may be empty if the defaults are sufficient. The list of configuration-supported systems is not as broad as the list of source-supported systems.
- Examples of variables that can be set in an
ostype
file:define(`LOCAL_SHELL_DIR', `$z:/') define(`LOCAL_MAILER_QGRP', `undefined') define(`USENET_MAILER_PATH', `/usr/lib/news/inews') define(`USENET_MAILER_FLAGS', `rsDFMmn') define(`USENET_MAILER_ARGS', `-m -h -n') define(`USENET_MAILER_MAX', `100000') define(`USENET_MAILER_QGRP', `undefined') define(`SMTP_MAILER_FLAGS', `undefined') define(`RELAY_MAILER_FLAGS', `undefined') define(`SMTP_MAILER_MAX', `undefined') define(`SMTP_MAILER_MAXMSGS', `undefined') define(`SMTP_MAILER_MAXRCPTS', `undefined') define(`SMTP_MAILER_ARGS', `TCP $h') define(`ESMTP_MAILER_ARGS', `TCP $h') define(`SMTP8_MAILER_ARGS', `TCP $h') define(`DSMTP_MAILER_ARGS', `TCP $h') define(`RELAY_MAILER_ARGS', `TCP $h') define(`SMTP_MAILER_QGRP', `undefined') define(`ESMTP_MAILER_QGRP', `undefined') define(`SMTP8_MAILER_QGRP', `undefined') define(`DSMTP_MAILER_QGRP', `undefined') define(`RELAY_MAILER_QGRP', `undefined') define(`RELAY_MAILER_MAXMSGS', `undefined') define(`SMTP_MAILER_CHARSET', `undefined') define(`UUCP_MAILER_PATH', `/usr/bin/uux') define(`UUCP_MAILER_FLAGS', `undefined') define(`MAIL11_MAILER_FLAGS', `nsFx') define(`MAIL11_MAILER_ARGS', `mail11 $g $x $h $u') define(`MAIL11_MAILER_QGRP', `undefined') define(`PH_MAILER_PATH', `/usr/local/etc/phquery') define(`PH_MAILER_FLAGS', `ehmu') define(`PH_MAILER_ARGS', `phquery -- $u') define(`PH_MAILER_QGRP', `undefined') define(`CYRUS_MAILER_FLAGS', `Ah5@/:|') define(`CYRUS_MAILER_PATH', `/usr/cyrus/bin/deliver') define(`CYRUS_MAILER_ARGS', `deliver -e -m $h -- $u') define(`CYRUS_MAILER_MAX', `undefined') define(`CYRUS_MAILER_USER', `cyrus:mail') define(`CYRUS_MAILER_QGRP', `undefined') define(`CYRUS_BB_MAILER_FLAGS', `u') define(`CYRUS_BB_MAILER_ARGS', `deliver -e -m $u') define(`confEBINDIR', `/usr/libexec') define(`QPAGE_MAILER_FLAGS', `mDFMs') define(`QPAGE_MAILER_PATH', `/usr/local/bin/qpage') define(`QPAGE_MAILER_ARGS', `qpage -l0 -m -P$u') define(`QPAGE_MAILER_MAX', `4096') define(`QPAGE_MAILER_QGRP', `undefined')
Domains
Domain-dependent definitions can be collected into a file referenced by the DOMAIN
macro. This file can include local names, domain names using DD
, and site-wide features. MASQUERADE_AS
can be defined in this file if all hosts at the site masquerade behind one name. Defining a domain is optional and may not be worth the effort for single-machine setups. Domain files help in organizing "domain-dependent knowledge".
Mailers
Sendmail supports various mailers:
local
: For local and program delivery, included by default.smtp
: For Simple Mail Transfer Protocol, includingsmtp
,esmtp
,smtp8
,dsmtp
, andrelay
variations.uucp
: For UNIX-to-UNIX Copy Program, withuucp-old
,uucp-new
,uucp-dom
anduucp-uudom
options.fax
: For Facsimile transmission via HylaFAX.pop
: For Post Office Protocol.procmail
: For integrating withprocmail
.cyrus
: For local cyrus user.qpage
: For QuickPage pager interface.
MAILER
definitions should be placed at the end of the .mc
file.
- Example:
MAILER(`local') MAILER(`smtp')
Features
FEATURE
macros are used to add optional functionalities. A feature can have up to 9 optional parameters. The database map type defaults to Berkeley DB hash format but can be set with define('DATABASE_MAP_TYPE', 'dbm')
.
Common features include:
-
use_cw_file
: Reads/etc/mail/local-host-names
for class{w}
. -
use_ct_file
: Reads/etc/mail/trusted-users
for trusted users. -
redirect
: Rejects mail to addresses with.REDIRECT
. -
stickyhost
: Marks email for local addresses to prevent them from being matched against UDB. -
mailertable
: Enables overriding routing using a "mailer table". -
always_add_domain
: Includes the local domain on locally delivered mail. -
allmasquerade
: Masquerades recipient addresses when usingMASQUERADE_AS
. -
generics_entire_domain
: Searchesgenericstable
map for subdomains of class{G}
. -
virtusertable
: Enables hosting multiple virtual domains. -
virtuser_entire_domain
: Searchesvirtusertable
map for subdomains of class{VirtHost}
. -
ldap_routing
: Implements LDAP-based email routing. -
nocanonify
: Disables canonicalization of the host name. -
local_lmtp
: Uses an LMTP capable local mailer. -
enhdnsbl
: Enhanced version ofdnsbl
with specific return values. -
authinfo
: Provides a separate map for client-side authentication information. -
preserve_luser_host
: Preserves the name of the recipient host ifLUSER_RELAY
is used. -
preserve_local_plus_detail
: Preserves the+detail
portion of the local address. -
compat_check
: Enables the rulesetcheck_compat
for address pair checks with theCompat:
tag. -
no_default_msa
: Disables the default Message Submission Agent (MSA) daemon. -
msp
: Defines the config file for the Message Submission Program. -
queuegroup
: Allows selection of a queue group based on the full email address or the domain of the recipient. -
Example:
FEATURE(`use_cw_file')
Hacks
Site-dependent configurations that are not considered features are placed in the hack
subdirectory and referenced using the HACK
macro. These tend to be site-dependent.
Site Configuration
Complex sites might need local configurations, such as lists of UUCP hosts. The SITECONFIG
macro can indirectly reference site-dependent information. This section is mostly obsolete, and mailertables should be used instead.
UUCP Mailer Variations
There are differences between uucp-old
, uucp-new
, uucp-dom
, and uucp-uudom
mailers, especially regarding address handling. Sender rewriting is handled differently for different UUCP mailers.
Tweaking Rulesets
LOCAL_RULE_3
can be used to define custom rules for canonicalizing names, which are reflected in the header. The UUCPSMTP
macro is used for converting old UUCP addresses to SMTP addresses. Rulesets 1 and 2 can be tweaked using LOCAL_RULE_1
and LOCAL_RULE_2
. The LOCAL_CONFIG
macro adds lines after boilerplate option settings but before rulesets, and can be used for declaring database maps.
- Example of translating UUCP addresses:
LOCAL_RULE_3 UUCPSMTP(`decvax', `decvax.dec.com') UUCPSMTP(`research', `research.att.com')
Masquerading and Relaying
-
MASQUERADE_AS
: This is used to rewrite email addresses. -
MASQUERADE_DOMAIN
andMASQUERADE_DOMAIN_FILE
: Add elements to class{M}
. -
MASQUERADE_EXCEPTION
andMASQUERADE_EXCEPTION_FILE
: Exempt hosts or subdomains from masquerading. -
FEATURE('masquerade_envelope')
: Should be used to masquerade the envelope as well as the header addresses. -
EXPOSED_USER
andEXPOSED_USER_FILE
: Define users whose internal site names are displayed. -
LOCAL_RELAY
: Relays unqualified names. -
MAIL_HUB
: Sends mail addressed to local hostnames to a centralized hub. -
SMART_HOST
: Used for all outgoing mail to go to a central relay site. -
Example of defining masquerading and relay settings:
MASQUERADE_AS(`mail.example.com') define(`LOCAL_RELAY', `relay:mail.example.com') define(`MAIL_HUB', `relay:mailhub.example.com') define(`SMART_HOST', `smtp:smtp.example.com')
LOCAL_RELAY
applies to unqualified names, MAIL_HUB
to names qualified with the local host name, and SMART_HOST
to names qualified with other hosts or bracketed addresses.
Using LDAP for Aliases, Maps, and Classes
LDAP can be used for alias lookups using the ALIAS_FILE
option. The default LDAP schema can be used, or a custom one specified when setting ALIAS_FILE
. LDAP can also be used for maps, including access_db
, authinfo
, bitdomain
, domaintable
, genericstable
, mailertable
, uucpdomain
, and virtusertable
. The map parameters can be customized when using the FEATURE()
macro. Classes can be filled via map lookups using the @ldap:
syntax. These classes can be used with commands such as RELAY_DOMAIN_FILE()
, MASQUERADE_DOMAIN_FILE()
etc. Macros cannot be used in class declarations.
LDAP Routing
FEATURE('ldap_routing')
can be used to implement LDAP-based routing. The LDAP lookup is done on the full address and then the domain portion. LDAPROUTE_EQUIVALENT
is also used. The default behavior of FEATURE('ldap_routing')
can be modified using additional arguments.
Anti-Spam Configuration Control
Relaying is denied by default. FEATURE('promiscuous_relay')
can be used to revert to the old behavior, but should be avoided. RELAY_DOMAIN()
and RELAY_DOMAIN_FILE()
add domains or IP addresses to class {R}
. FEATURE('relay_entire_domain')
can also be used. FEATURE('relay_local_from')
should be avoided. FEATURE('relay_mail_from')
is a slightly better option. Source routing is used, and the effects of using FEATURE('loose_relay_check')
should be considered. UUCP addresses can be used to bypass anti-spam rules. FEATURE('accept_unresolvable_domains')
can be used. FEATURE('accept_unqualified_senders')
accepts senders with unqualified addresses. The access_db
feature can be enabled and used, and an access database created.
Examples of entries in the access database include: REJECT
, SKIP
, ERROR:
, OK
, RELAY
, and specific error codes. FEATURE('relay_hosts_only')
can also be used. Sender addresses can be blocked using FEATURE('blacklist_recipients')
. Tags such as From:
, To:
, and Connect:
can be used in the access database for more specific filtering. The delay_checks
feature delays the check_mail
and check_relay
rulesets and the Spam:
tag. LOCAL_RULESETS
can implement header checks.
STARTTLS
STARTTLS can be configured by setting the confCACERT_PATH
, confCACERT
, confSERVER_CERT
, confSERVER_KEY
, and confRAND_FILE
variables. SMTP STARTTLS allows relaying for authenticated senders in the RelayAuth
ruleset, using the CERTISSUER
tag. Examples of allowing relaying for certificates signed by a specific authority can be found in the sources. The tls_server
, tls_client
, and tls_rcpt
rulesets control accepted connections. The access map can be used with tags such as TLS_Srv
, TLS_Clt
, and TLS_Rcpt
. ENCR
, VERIFY
, and CN
in the TLS settings have specific meanings. Examples of enforcing encryption and authentication rules for specific domains can be found in the sources. Try_TLS
and Srv_Features
can be used to disable STARTTLS for broken MTAs. The Received:
header reveals whether STARTTLS has been used.
SMTP Authentication
The auth_authen
, auth_author
, and auth_type
macros are used for anti-relay rulesets, and Local_trust_auth
is used for trusting the AUTH=
parameter. Relaying is allowed by default for any user authenticated via trusted mechanisms defined by TRUST_AUTH_MECH()
. The authinfo
ruleset provides authentication data for client MTA.
Adding New Mailers or Rulesets
New mailers or rulesets can be introduced using MAILER_DEFINITIONS
and LOCAL_RULESETS
, respectively. Local additions for srv_features
, try_tls
, tls_rcpt
, tls_client
, and tls_server
can be made using LOCAL_SRV_FEATURES
, LOCAL_TRY_TLS
, LOCAL_TLS_RCPT
, LOCAL_TLS_CLIENT
, and LOCAL_TLS_SERVER
.
- Example of adding a custom mailer and ruleset:
MAILER_DEFINITIONS Mmymailer, ... LOCAL_RULESETS Smyruleset ...
Adding New Mail Filters
Mail filters can be added using MAIL_FILTER()
and INPUT_MAIL_FILTER()
. INPUT_MAIL_FILTER()
populates confINPUT_MAIL_FILTERS
with the name of the filter. The list of filters can be reset by setting confINPUT_MAIL_FILTERS
.
- Example:
MAIL_FILTER(`archive', `S=local:/var/run/archivesock, F=R') INPUT_MAIL_FILTER(`archive', `S=local:/var/run/archivesock, F=R') INPUT_MAIL_FILTER(`spamcheck', `S=inet:2525@localhost, F=T')
Queue Group Definitions
Queue groups can be defined using QUEUE_GROUP()
. Refer to the Sendmail documentation for more details.
Non-SMTP Based Configurations
Configuration files may have limitations for non-SMTP sites. SMART_HOST
can be used to route messages with a richer address syntax. LOCAL_NET_CONFIG
adds appropriate rules for SMTP-based sites connecting via UUCP. Anti-spam rules may need to be disabled using FEATURE('promiscuous_relay')
and FEATURE('accept_unresolvable_domains')
.
Who Am I?
The $j
macro is automatically defined to be the fully qualified domain name (FQDN). The confDOMAIN_NAME
macro can be defined in cases where gethostbyname()
does not return the FQDN.
- Example:
define(`confDOMAIN_NAME', `$w.$m')dnl
Accepting Mail for Multiple Names
Class {w}
can be augmented for hosts known by several names. This can be done by creating the /etc/mail/local-host-names
file with a list of host aliases or using LOCAL_DOMAIN()
. The virtusertable
feature can be used for different addresses in different domains.
Using Mailertables
An external database can be created using FEATURE('mailertable')
for routing information. Examples of mailertable entries include domain, host-specific, and bitnet routing. makemap
creates the database version of the mailertable. Matching rules in mailertables include matching order, wildcards, and the use of %1
to interpolate wildcarded parts. The right-hand side of a mailertable entry should always be a mailer:host
pair. Creating MX loops when using mailertables should be avoided.
Using Userdb to Map Full Names
The user database is not intended for mapping full names to login names. Aliases are recommended instead. The user database is used to locate the default maildrop. FEATURE('stickyhost')
should not be used when using the user database to map full names. makemap
builds the internal form of the user database. Using full names as email addresses is generally discouraged.
Miscellaneous Special Features
Plussed users can be used for centralized mail configuration with root mail forwarding.
Security Notes
Security depends on the user, and Sendmail 8 is more secure than previous versions. Essential security practices include:
- Ensuring the aliases file isn't writable except by trusted personnel.
- Ensuring that other files Sendmail reads are writable by trusted personnel only.
Integrating Sendmail with Laravel
To configure Laravel to use Sendmail, you need to update the .env
file and config/mail.php
:
-
Update
.env
:MAIL_MAILER=sendmail MAIL_SENDMAIL="/usr/sbin/sendmail -bs"
For Laravel 7 and above use
MAIL_MAILER
, earlier versions useMAIL_DRIVER
. -
Update
config/mail.php
:return [ 'default' => env('MAIL_MAILER', 'sendmail'), 'mailers' => [ 'sendmail' => [ 'transport' => 'sendmail', 'path' => env('MAIL_SENDMAIL', '/usr/sbin/sendmail -bs'), ], ], ];
-
Ensure that
MAIL_SENDMAIL
in your.env
is set to the correct path to the Sendmail executable. Often, this is/usr/sbin/sendmail -t -i
or/usr/sbin/sendmail -bs
. -
You may need to clear the configuration cache after making these changes
php artisan config:cache
Debugging Sendmail Issues
- Checking Sendmail Logs: Logs are typically located in
/var/log/mail.log
. Usesudo tail -f /var/log/mail.log
to monitor logs. - Testing Email Sending: You can manually test email sending with a command such as
echo "Subject: Test Email" | sendmail -v recipient@example.com
. - Common Errors and Fixes:
- Sendmail Not Found: Ensure Sendmail is installed:
sudo apt install sendmail
. - Emails Not Being Sent: Check logs for issues.
- Permissions Issues: Ensure Laravel has execute permissions on
/usr/sbin/sendmail
. proc_open()
disabled: Ensure that PHP'sproc_open()
function is not disabled for security reasons inphp.ini
.
- Sendmail Not Found: Ensure Sendmail is installed:
Sendmail Tips and Tricks
-
Redirecting System Mail: Redirect system mail so that it goes to a real person rather than the root user. This prevents the root mailbox from filling up and makes it more likely that important system messages will be seen. Define a root alias to point to both your local administrative email and a backup mail account on another server. This ensures that you will still get email even if the local system has problems.
-
Rebuilding
sendmail.cf
: When you modifysendmail.mc
, always rebuild thesendmail.cf
file using them4
processor. It is always wise to backup critical system files before rebuilding. -
Turning on SMTP-Auth: To limit mail relaying and combat spam, implement SMTP-Auth, which requires a valid username and password. This is better than older methods like pop-locks and whitelists.
Conclusion
Sendmail, despite its complexity, is a powerful and flexible MTA. When integrated correctly with Laravel, it provides direct email control, suitable for specific use cases. By following best practices in security, configuration management, and debugging, Laravel applications can reliably send emails via Sendmail.
This guide serves as a reference for developers working with Laravel and Sendmail, ensuring a robust and secure configuration.