Published: Tue 04 September 2018
By Dan Kolb
In email .
Cron defaults to sending job outputs to the owner's mail, or the mail set in a MAILTO
variable, or direct to syslog when sendmail
is not installed. If the server does not have a mail server running, or there are issues such as the server being in a network or configured to specifically not send email, or is unable to send email to a particular server or service, this can cause a problem. In order to get around the issue of mail not being accepted by some third parties as I described in a previous post , emails sent by cron can instead use an Simple Mail Transport Protocol (SMTP) client to send through an external Mail Transfer Agent (MTA).
mailx
After a bit of searching, I found mailx
provides a method for connecting to an external SMTP server with simple configuration. According to the man page, mailx is an intelligent mail processing system [...] intended to provide the functionality of the POSIX mailx command, and offers extension for MIME, IMAP, POP3, SMTP and S/MIME
.
Installation
Installation was completed on a CentOS 7 VPS instance. mailx
is available in the base
repository and can be installed with a simple yum
command
mailx configuration
Installation creates a default /etc/mail.rc
file. You can then review the man page via man mailx
to review further configuration options. Since the plan is to use it for SMTP, searching for smtp
provides relevant options.
I'm using Gmail, and the documentation from Google for email client configuration provided the required SMTP host:TLS-Port combination of smtp.gmail.com:587
.
For the smtp-auth-password, I can't use my own password since I've got 2-Step-Verification enabled on my account. The server simply wouldn't be able to send email if I had to verify it and provide a code each time. Gmail allows a method around this of using App Passwords for email clients that cannot use two factor authentication. Creating an app password is just a couple of steps. Each server or client using an App Password should have its own unique password. A unique app password for each application requiring one helps to provide logs of its use, as well as easily revoking the app password if needed.
We can test our configuration as we go along with the following command:
# echo "mailx test" | mailx -s "Test Email" <EMAIL_ADDRESS>
The first round of doing that gave an error:
# smtp-server: 530 5.7.0 Must issue a STARTTLS command first. 207-v6sm21173418oie.14 - gsmtp
"/root/dead.letter" 11/308
. . . message not sent.
Easy enough of a resolution, another look at the man page or quick grep shows us that we need to include smtp-use-starttls
# man mailx | grep - ie starttls | grep - i smtp
smtp - use - starttls
Causes mailx to issue a STARTTLS command to make an SMTP session SSL / TLS encrypted . Not all servers support this command ; because of common implementation defects , it cannot be automatically
There are two possible methods to get SSL / TLS encrypted SMTP sessions : First , the STARTTLS command can be used to encrypt a session after it has been initiated , but before any user -
related data has been sent ; see smtp - use - starttls above . Second , some servers accept sessions that are encrypted from their beginning on . This mode is configured by assigning
After updating the configuration, I found another error.
# Missing "nss-config-dir" variable.
"/root/dead.letter" 11 / 308
. . . message not sent .
To resolve that, I just looked for an nss*
in /etc/
(from knowing that SSL information/certs are located there) and added that in the configuration.
# find / etc - type d - name "nss*"
/ etc / pki / nssdb
Then I got yet another error:
# Error in certificate : Peer 's certificate issuer is not recognized.
Continue ( y / n ) ? SSL / TLS handshake failed : Peer 's certificate issuer is not recognized.
"/root/dead.letter" 11/308
. . . message not sent.
Time for a bit more sleuthing. For whatever reason, the certificate issuer was not recognized and asked for manual intervention. After some searching around I figured it might be due to Google's new(ish) CA, but trying to add it to the PKI trusted CAs directly didn't help. Eventually I found a page for adding these certs directly , but in order to just get the configuration running I opted for laziness and to set ssl-verify
to ignore, with the intention of adding this as an ansible role at a later point.
Finally, we have the configuration below.
# cat /etc/mail.rc
set from=<YOUR_EMAIL_ADDRESS>
set smtp-use-starttls
set nss-config-dir=/etc/pki/nssdb/
set ssl-verify=ignore
set smtp-auth=login
set smtp=smtp://smtp.gmail.com:587
set smtp-auth-user=<YOUR_GMAIL_USER>
set smtp-auth-password=<YOUR_APP_PASSWORD>
Running the testing command with these configuration settings results in a new email showing up in our inbox.
cron configuration
In order of for cron to use mailx
, we need to do two things. First, cron will only send mail if the the MAILTO
is set. We can add that directly into crontab
with crontab -e
, and adding the MAILTO
variable. After, we'll see it included in crontab -l
.
And to test this, we should set up a cron job that provides output (also using crontab -e
)
# crontab -l
MAILTO="<YOUR_EMAIL>"
* * * * * /usr/sbin/ip a
We also need to set crond to use mailx
by editng the crond
configuration to specify using /usr/bin/mailx
to send mail, with the -t
flag sent to mailx
to use the To:
header to address the email. After editing /etc/sysconfig/crond
, restart crond.
# cat / etc / sysconfig / crond
# Settings for the CRON daemon .
# CRONDARGS = : any extra command - line startup arguments for crond
CRONDARGS =- m "/usr/bin/mailx -t"
# systemctl restart crond
Testing configuration
The crontab should now send the output of ip a
to <YOUR_EMAIL>
every minute. Once you've verified, be sure remove that job to prevent flooding your inbox.
If you don't see a new email, take a look at the system logs to see entries from the crond
service in reversed order (newest entries first).
# journalctl -r --unit crond
Because of the certificate issue noted above, and because mailx
strips headers before sending mail, the following output may be included in the journald logs even on a successful mail.
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Error in certificate : Peer ' s certificate issuer is not recognized .
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <USER=root>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <LOGNAME=root>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <PATH=/usr/bin:/bin>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <HOME=/root>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <SHELL=/bin/sh>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <MAILTO=<YOUR_EMAIL>>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <LANG=en_US.UTF-8>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <XDG_RUNTIME_DIR=/run/user/0>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "X-Cron-Env: <XDG_SESSION_ID=71363>"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "Precedence: bulk"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "Auto-Submitted: auto-generated"
Sep 03 19 : 35 : 01 < YOUR_HOST > crond [ 12378 ] : Ignoring header field "Content-Type: text/plain; charset=UTF-8"
And looking at the email source we should see something like the following (note, I did not include all output in the example below) :
Return - Path : < YOUR_EMAIL >
Received : from < YOUR_HOST > ( [ <YOUR_IP_ADDRESS> ] )
by smtp . gmail . com with ESMTPSA id < REDACTED >
for < YOUR_EMAIL >
( version = TLS1_2 cipher = ECDHE - RSA - AES128 - GCM - SHA256 bits = 128 / 128 );
Mon , 03 Sep 2018 12 : 35 : 01 - 0700 ( PDT )
Message - ID : < MESSAGE_ID >
From : "(Cron Daemon)" < YOUR_EMAIL >
X - Google - Original - From : "(Cron Daemon)" < root >
Date : Mon , 03 Sep 2018 19 : 35 : 01 + 0000
To : < YOUR_EMAIL >
Subject : Cron < root @YOUR_HOST > / usr / sbin / ip a
User - Agent : Heirloom mailx 12.5 7 / 5 / 10
MIME - Version : 1.0
Content - Type : text / plain ; charset = us - ascii
Content - Transfer - Encoding : 7 bit
1 : lo : < LOOPBACK , UP , LOWER_UP > mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link / loopback 00 : 00 : 00 : 00 : 00 : 00 brd 00 : 00 : 00 : 00 : 00 : 00
inet 127.0.0.1 / 8 scope host lo
valid_lft forever preferred_lft forever
inet6 :: 1 / 128 scope host
valid_lft forever preferred_lft forever
...
Now we can be sure our cron jobs mail us through an external SMTP server that will successfully deliver to our third party service. And with mailx
configured we can easily add an email component for any scripts we might want to run.
Of note , Google App suite does provide access to a SMTP relay that specifically that allows sending emails to email addresses either inside or outside of your domain. There are some limitations on the number of emails that can be sent based on the number of licenses in your account, but for my purposes and imposed limits, configuring mailx
was a suitable solution.