In SOM we use Slack for our internal communications and to receive notifications from different channels. Since I have recently done some changes in a server I maintain, I decided to forward the different server notifications to Slack.
Server notifications are usually sent via local e-mail to root, who can read them from the /var/mail/root
file. Notifications are also tipically forwarded to an external e-mail address by configuring the /etc/aliases
file. Thus, notifications can be easily forwarded to Slack by using regular e-mails.
However, although sending e-mails to Slack is available in all plans (including the free one), the Email app is only available for paid plans. That implies that users using a free plan can only receive e-mails via direct messages from slackbot, and such e-mails cannot be automatically redirected, organized or shared in topic-specific channels participated by other users.
Nevertheless, the Incoming WebHooks app will allow us to easily send automated messages to any Slack channel using a simple shell script.
To make this work, you must login in Slack and install the Incoming WebHooks app. Once you have selected the channel in which you want to receive the notifications, you just need to configure your server using the Webhook URL provided by the app as follows.
First, the local e-mail account for root needs to be configured so that the MTA runs a specific shell script every time a local e-mail for root is sent. This can be achieved by using a pipe in the root entry at the /etc/aliases
file as shown below:
# Person who should get root's mail root: root,admin@example.com,| /opt/local/server-notifications/notify-slack.sh
The entry above states that a e-mail sent to root will be first sent locally to /var/mail/root
(for archiving purposes); second, will forwarded to admin@example.com
; and third, the script /opt/local/server-notifications/notify-slack.sh
will be executed receiving the raw e-mail via its stdin
. Remember that changes in /etc/aliases
must be reloaded using the newaliases
command.
Next, a possible (quick & dirty) script processing the raw e-mail is shown. It makes use of a temporal file in favor of an in-memory variable to avoid problems with line endings (also a dirty solution). The script processes the e-mail and extracts the most important fields using sed
. Finally, the data is sent to Slack using the curl
command in a JSON payload. It is noteworthy that the Webhook URL is configured at the beginning using the webhook_url
variable.
#!/bin/bash # URL to the Slack API # Incoming WebHooks App URL webhook_url="https://hooks.slack.com/services/..." site_name="My Site Name" site_url="https://example.com" admin_mail="admin@example.com" # Main function function main { # We will use a temp file for simplicity temp_file=$(mktemp) # Remove temp file on exit or when receiving other signals trap 'rm -f -- "$temp_file"' INT TERM HUP EXIT # Slashes need to be escaped to avoid problems in the payload cat | sed -e 's/\\/\\\\/g' -e 's/"/\\\"/g' > $temp_file # Extract important data from raw e-mail body=$(cat $temp_file | sed '1,/^$/d') subject=$(cat $temp_file | sed -n -e 's/^Subject: //p') from=$(cat $temp_file | sed -n -e 's/^From: //p') to=$(cat $temp_file | sed -n -e 's/^To: //p') date=$(cat $temp_file | sed -n -e 's/^Date: //p') # Try to send message via webhook result=$(curl -X POST --data-urlencode "$(payload)" "$webhook_url") # If the webhook does not return ok, send a default message if [ "$result" != "ok" ]; then curl -X POST --data-urlencode "$(default_payload)" "$webhook_url" fi } # Create payload using the data parsed from the raw e-mail function payload { cat << EOF payload={ "attachments" : [ { "title" : "$subject", "text" : "\`\`\`\n$body\n\`\`\`", "color": "#36a64f", "footer" : "See the raw contents in $admin_mail's email or locally at $site_name:/var/mail/root", "mrkdwn_in" : [ "text" , "fields" ], "fields": [ { "title": "From", "value": "$from", "short": true }, { "title": "To", "value": "$to", "short": true }, { "title": "Date", "value": "$date", "short": false }, ], }, { "fallback" : "footer", "color": "#000099", "footer" : "$site_name", "footer_link" : "$site_url", "footer_icon" : "https://platform.slack-edge.com/img/default_application_icon.png", "ts" : "$(date +%s)", }, ], } EOF } # Build a default payload whis basic data function default_payload { cat << EOF payload={ "title" : "Script error", "text" : "Unable to post e-mail into Slack.\nPlease check $admin_mail's <https://accounts.google.com/ServiceLogin?service=mail&passive=false&Email=$admin_mail&continue=https://mail.google.com/mail/u/$admin_mail/|e-mail> or locally at $site_name:/var/mail/root to see the raw message contents.", "attachments" : [ { "fallback" : "footer", "color": "#000099", "footer" : "$site_name", "footer_link" : "$site_url", "footer_icon" : "https://platform.slack-edge.com/img/default_application_icon.png", "ts" : "$(date +%s)", }, ], } EOF } # Call the main function main "$@"
Finally, the configuration above can be tested by running the following command:
shell$ echo "Test body" | mail -s "Test subject" root@localhost
If everything is properly configured, you should receive a message from incoming-webhook in your favorite channel!