Automatic Email and Text Notifications

From TheBeard Science Project Wiki
Revision as of 10:11, 26 February 2021 by Beard (talk | contribs) (Set Up Cron)

Jump to: navigation, search

These tutorials describe my method of automatic messaging via email and text (SMS) from my Linux servers.

Required Packages

These packages are necessary for the all the following sections of this tutorial. In Debain, install them with apt-get:

apt-get install ssmtp procmail fetchmail

Directory Structure

Just for reference, here are the locations of the relevant files:

/
├── etc
│   └── ssmtp
│       └── ssmtp.conf
└── scripts
    ├── config
    │   ├── accounts.conf
    │   ├── ddns-fetchmailrc
    │   └── ddns-mailprocess.sh
    ├── ddns.sh
    ├── email.sh
    ├── external-ip.sh
    ├── notifications.sh
    └── sms.sh

And permissions:

ls -l /scripts
drwxr-xr-x 4 root config   4096 Jun 20 08:52 config
-rwxr-xr-- 1 root root      260 Jun 20 08:40 ddns.sh
-rwxr-xr-- 1 root mailers   564 Apr 23 12:18 email.sh
-rwxr-xr-- 1 root root      529 Apr 27 07:56 external-ip.sh
-rwxr-xr-- 1 root root     3834 May 15 08:32 notifications.sh
-rwxr-xr-- 1 root texters   545 Apr 23 12:24 sms.sh
ls -l /scripts/config
-rwxr-x--- 1 root mailers  147 May  1 13:01 accounts.conf
-rwx------ 1 root root     246 May 21 09:49 ddns-fetchmailrc
-rwxr-x--- 1 root config   604 Jun 20 08:52 ddns-mailprocess.sh

Create Security Groups

I use group membership to control read access to certain files. You don't have to do this, but it was useful for me.

Create the groups:

groupadd texters #allowed to sent text messages
groupadd mailers #allowed to send email messages
groupadd config  #allowed to read other config files

You can add a user to all three groups like this:

usermod -aG texters,mailers,config username

Email and Text Messaging with SSMTP

First set up the /etc/ssmtp/ssmtp.conf. I use a dedicated Gmail account (named serveraccount@gmail.com in the example below) for all of my server messaging. When I receive an email from my server, it will look like it's coming from that Gmail.

Make a backup copy of the original config:

cp /etc/ssmtp/ssmtp.conf /etc/ssmtp/ssmtp.conf.orig

Here are the contents of my modified /etc/ssmtp/ssmtp.conf. Replace the relevant values.

#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
root=serveraccount

# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
#mailhub=mail

# Where will the mail seem to come from?
rewriteDomain=gmail.com

# The full hostname
# This can be left as 'localhost'
hostname=localhost

# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
FromLineOverride=YES

mailhub=smtp.gmail.com:587
UseStartTLS=YES
UseTLS=YES
AuthMethod=LOGIN
AuthUser=serveraccount@gmail.com
AuthPass=InsertPasswordHere
TLS_CA_File=/etc/pki/tls/certs/ca-bundle.crt
FromLineOverride=YES

Since the file contains a password, make sure the file is not readable to normal users.

chown root:mail /etc/ssmtp/ssmtp.conf
chmod 640 /etc/ssmtp/ssmtp.conf

And because you don't have to remember this password, it would be a good idea to make it very long and complex (16+ characters; uppercase, lowercase, numbers, and special characters; no dictionary words).

You now need to log onto the serveraccount Gmail account and enable "less secure apps" at https://myaccount.google.com/lesssecureapps?pli=1
Gmail-less-secure-app.png

Here is more info about less secure apps: https://support.google.com/cloudidentity/answer/6260879?hl=en

You should now be able use the ssmtp command line tool from your server. Most of the time, you will need to include a subject line. It's important that it follows the format below (capital 'S' on Subject, colon, space, YourSubject, two newlines):

echo -e "Subject: Hi\n\nHello, world!" | ssmtp myaccount@yahoo.com

Create a Secure Accounts File

For some of the automatic messaging, you need to store text and email accounts in a file. To figure out the destination address to send text (SMS) messages to your cell, look on your cell provider's website for the proper account/domain name. It will probably be something like "<your-phone-number>@<provider-domain>". List of provider addresses

Contents of my /scripts/config/accounts.conf file:

#!/bin/bash
#
# Secure account information
# Permissions must be 750 root:mailers

smsto="17775551234@myprovider.net"
mailto="myaccount@yahoo.com"

Change the permissions on the file:

chown root:mailers /scripts/config/accounts.conf
chmod 750 /scripts/config/accounts.conf

Email Script

This script can be used to send an email message.

Contents of the file /scripts/email.sh:

#!/bin/bash

# Send email message:
#  Usage: $0 "<Subject>" "<Message>"


default_subject="Alert from Server (`date`)"

subject=""
message=""

case $# in
  2)
    subject="$1"
    message="$2"
  ;;
  1)
    subject="$default_subject"
    message="$1"
  ;;
  *)
    echo "Send Email Message:"
    echo "  Usage: `basename $0` \"Message\""
    echo "         `basename $0` \"Subject\" \"Message\""
    exit 1
  ;;
esac

dir="$(cd $(dirname ${BASH_SOURCE[0]})&&pwd)"
. $dir/config/accounts.conf

echo -e "Subject: $subject\n\n$message" | /usr/sbin/ssmtp $mailto

Set up permissions:

chown root:mailers /scripts/email.sh
chmod 754 /scripts/email.sh

You can create an alias so you can quickly run this from the command line:

echo "alias email='/scripts/email.sh'" >>~/.bashrc
source ~/.bashrc

Usage:

# Send message with default subject
email "This is a test message."

# Send message with a subject
email "Test Subject" "This is a test message."

Text Script

This script can be used to send a text (SMS) message.

Contents of the file /scripts/sms.sh:

#!/bin/bash

# Send SMS message:
#  Usage: $0 "<Subject>" "<Message>"


default_subject="Alert"

subject=""
message=""

case $# in
  2)
    subject="$1"
    message="$2"
  ;;
  1)
    subject="$default_subject"
    message="$1"
  ;;
  *)
    echo "Send SMS Message:"
    echo "  Usage: `basename $0` \"Message\""
    echo "         `basename $0` \"Subject\" \"Message\""
    exit 1
  ;;
esac

dir="$(cd $(dirname ${BASH_SOURCE[0]})&&pwd)"
. $dir/config/accounts.conf

echo -e "To: $smsto\nSubject: $subject\n\n$message" | /usr/sbin/ssmtp $smsto

Set up permissions:

chown root:texters /scripts/sms.sh
chmod 754 /scripts/sms.sh

You can create an alias so you can quickly run this from the command line:

echo "alias sms='/scripts/sms.sh'" >>~/.bashrc
source ~/.bashrc

Usage:

# Send message with default subject
sms "This is a test message."

# Send message with a subject
sms "Test Subject" "This is a test message."

Scheduled Notifications Script

One of my life improvement tasks was to organize chores into scheduled chore days and rely on text/email notification to remind me to do them.

This dramatically reduced the amount of time I spend working on chores, and the amount of effort I spend thinking about what needs to be done. I could barely get projects done because every day I was loading my task list with chores. Now I don't think about chores until I'm notified. When I think of new chores, new items for my inventory, or other things I need to be reminded of, I update my checklists. When I feel I can improve scheduling or consolidate chores, I update my notifications script.

This utilizes the Email Script and Text Script from the previous sections.

Contents of the file /scripts/notifications.sh:

#!/bin/bash
#
# Send regular notifications.
# Currently set to run daily at NOON.
#

# Get current datetime values
dt=(`date +%Y\ %m\ %d\ %H\ %M\ %S\ %a\ %s\ %I\ %p\ %Z`)
y=$((10#${dt[0]})) #year
M=$((10#${dt[1]})) #month
d=$((10#${dt[2]})) #day
h=$((10#${dt[3]})) #hour(24)
m=$((10#${dt[4]})) #minute
s=$((10#${dt[5]})) #second
w=${dt[6]}         #day of week (eg. Sun)
e=$((10#${dt[7]})) #epoch
t=$((10#${dt[8]})) #hour(12)
p=${dt[9]}         #meridian (AM/PM)
z=${dt[10]}        #timezone (eg. PDT)

# Schedule command when date ($1) matched regex ($2) with extra grep parameters ($3)
# Use parameter -P at $3 for perl regex: in case you need to use (str1|str2).
# Example: schedule $M$d "(115|21)" -P && sms "Subject" "Message"
# Don't use -P when you want a range of numbers including 2 or more digit numbers (ie. [1-15]).
schedule(){ echo "$1" | grep -wq $3 "$2";}
sms(){ /scripts/sms.sh "$1" "$2";}
eml(){ /scripts/email.sh "$1" "$2";}
notify(){ sms "$1" "$2";eml "$1" "$2";}


# =================================
# ==== Temporary Notifications ====
# =================================

# Dentist appointment
#schedule $M$d "58" && notify "Dentist" "Appointment 4:00pm 10/31/2017"


# =================================
# ====== Chore Notifications ======
# =================================

# LAUNDRY DAY
# Day before
schedule $w$d "Mon[1-7]" && notify "LAUNDRY DAY" "WORK: Check gas. HOME: Do laundry, put laundry away."
schedule $w$d "Mon(15|16|17|18|19|20|21)" -P && notify "LAUNDRY DAY" "WORK: Check gas. HOME: Do laundry, put laundry away."


# WEEKLY CLEANING DAY
schedule $w "Tue" && notify "WEEKLY CLEANING DAY" "WORK: Fill gas, shopping. HOME: Clean, litter, sweep/vac, fridge, trash, check meds."
# Day after
schedule $w "Wed" && notify "WEEKLY REMINDER" "Order meds if necessary."


# MONTHLY CLEANING DAY
schedule $w$d "Tue[1-7]" && notify "MONTHLY CLEANING DAY" "WORK: Car stuff. HOME: Dishes, kitchen, bathroom, dust, inventory."


# BUSINESS DAY
schedule $d "[1-4]" && notify "BUSINESS DAY" "Rent due before 5th (\$720). Fedloan, budgeting, donations, next-buy."


# TECH DAY
schedule $w$d "Mon[1-7]" && notify "TECH DAY" "WORK: Updates, phone, healthcheck, security scan, organize files. HOME: Organize Workstation/Laptop, USB drives."


# SHOPPING DAY
schedule $w$d "Wed[1-7]" && notify "SHOPPING DAY" "Shopping, carwash. Maybe do during lunch."


# GROOMING DAY
schedule $w$d "Sun[1-7]" && notify "GROOMING DAY" "Shave/trim, clip nails."
schedule $w$d "Sun(15|16|17|18|19|20|21)" -P && notify "GROOMING DAY" "Shave/trim, clip nails."


# ELIMINATION DAY
schedule $w$d "Sun(15|16|17|18|19|20|21)" -P && notify "ELIMINATION DAY" "Do at least one item on TO_ELIMINATE list."


# INVENTORY DAY
# Disabled. Inventory is now included in monthly cleaning day.
# schedule $w$d "Tue[1-7]" && notify "INVENTORY DAY" "Do home, car, and office inventory."


# =================================
# = Other Permanent Notifications =
# =================================

# Air filter replacement notifications
schedule $M$d "71" && notify "Air Filter" "Holmes HAP726: Replace HEPA filters (model:HAPF600,qty:2)"
schedule $M$d "[1|7]1" && notify "Air Filter" "Holmes HAP726: Replace Carbon filters (model:HAPF600,qty:2)"

Now set the permissions:

chown root:root /scripts/external-ip.sh
chmod 754 /scripts/external-ip.sh

Reporting External IP Address

My home server does not have a static IP address, so I use dynamic DNS. With my free DDNS service, I have to renew the domain name every 30 days. So if I forget to renew it, it will expire and I won't be able to access my server unless I know the IP address. As a contingency, I created a script that sends my external IP address to an email account every 6 hours. Then at least I would be able find out what my last IP was and use that to access my server.

Contents of the file /scripts/external-ip.sh:

#!/bin/bash

log="/var/log/external-ip.log"
ipsrc="http://checkip.dyndns.org"
date="`date`"
addr="myaccount@yahoo.com"

ip=`curl -s --connect-timeout 3 $ipsrc 2>/dev/null|grep -Po "Current IP Address: .*?\<"|tr '<' '\0'`
echo "IP: $ip"

if [ -z "$ip" ];then
	echo "$date ERROR: Could not get external IP from $ipsrc" >>$log
else
	echo -e "Subject:[`hostname`] $date $ip" | /usr/sbin/ssmtp $addr
	if [ "$?" = "0" ];then
		echo "$date SUCCESS: $ip sent to $addr" >>$log
	else
		echo "$date ERROR: Could not send to $addr" >>$log
	fi
fi

Now set the permissions:

chown root:root /scripts/external-ip.sh
chmod 754 /scripts/external-ip.sh

This script appends to a log file /var/log/external-ip.log:

touch /var/log/external-ip.log
chown root:root /var/log/external-ip.log
chmod 644 /var/log/external-ip.log

Automatic Email Download

As mentioned in a previous section, I use free dynamic DNS which requires that I renew it manually every 30 days. I need to remind myself to do this. My provider sends an email notification a few days before it expires, but I hate email and I don't want it in my face all the time, certainly not in my phone.

The best solution I found was to retrieve the email automatically and send me a text message with only the subject line in the message. I eventually created a second method of notification: it generates a file in a place that I regularly go, the folder that contains all of my to-do lists.

Contents of the file /scripts/ddns.sh:

#!/bin/bash
#
# This script is run by a cron job.

/usr/bin/fetchmail -N -d0 -f "/scripts/config/ddns-fetchmailrc" -m "/scripts/config/ddns-mailprocess.sh"

alertfile="/shares/Private/000__DDNS-ALERT__.txt"
if [ -e $alertfile ];then
	chown root:root $alertfile
	chmod 777 $alertfile
fi

Contents of the file /scripts/config/ddns-mailprocess.sh:

#!/bin/bash
#
# This script is run by /scripts/ddns.sh

mailto="17775551234@myprovider.net"
date="`date`"
log="/var/log/ddns.log"
alertfile="/shares/Private/000__DDNS-ALERT__.txt"
sms="/scripts/sms.sh"

cat /dev/stdin | grep -i "^Subject:" | sed 's/Subject\://g' | $sms "DDNS" "`cat /dev/stdin` ( $date )" &&\
echo "$date - DDNS notification sent to $mailto" | tee -a $log | sed "s/$mailto/cell\ phone/g" >$alertfile &&\
chmod 777 $alertfile

Contents of the file /scripts/config/ddns-fetchmailrc:

#
# This rc file is run by /scripts/ddns.sh
# This file MUST have root:root and chmod 700

set postmaster "username";

poll "imap.mail.yahoo.com" protocol IMAP username "myaccount@yahoo.com" password "InsertPasswordHere" is "username" here keep folder "DDNS-Mailbox" ssl

You'll notice that it creates a file /shares/Private/000__DDNS-ALERT__.txt. The "000" is just to put it at the top of the directory listing. Also notice that I make it read-write for everyone so I can delete it from my webapp.

Set up permissions on all three files, and create the log file:

touch /var/log/ddns.log
chown root:root /var/log/ddns.log
chmod 640 /var/log/ddns.log

chown root:root /scripts/ddns.sh
chmod 754 /scripts/ddns.sh

chown root:config /scripts/config/ddns-mailprocess.sh
chmod 750 /scripts/config/ddns-mailprocess.sh

chown root:root /scripts/config/ddns-fetchmailrc
chmod 700 /scripts/config/ddns-fetchmailrc

UPDATE February 2021: Yahoo now requires you to create a secure app password. See this section.

When you run ddns.sh as root it gives you a warning about running as root. This is okay.

fetchmail: WARNING: Running as root is discouraged.

If permissions on ddns-fetchmailrc are not correct, you will get this failure message:

File /scripts/config/ddns-fetchmailrc must have no more than -rwx------ (0700) permissions.

Set Up Cron

Just for reference, here are the entries I made in my /etc/crontab file:

# Crontabs to be run without 'run-parts'
#
# RUN AT NOON
0                  12           * * *   root    /scripts/ddns.sh
0                  12           * * *   root    /scripts/notifications.sh
# RUN EVERY 6 HOURS
1                  0,6,12,18    * * *   root    /scripts/external-ip.sh

Yahoo App Password

As of February 2021, Yahoo changed that way third party apps can access it. Instead of using your normal password, you need to generate an app password.

  1. Login to Yahoo, click you user icon, and click Account Info.
  2. Click Account Security.
  3. Click Manage app passwords.
  4. Click the Select your app drop-down and select Other app.
  5. Type a name for the app then click Generate.
  6. Now you can copy the password and use it in your app. Once you navigate away from this page, you will not be able to retrieve the password again. You will have to generate another password.