SMS

The gateway uses SMS Text messages for sending passwords to end-users. The SMS queue shows the SMS Text messages sent by the gateway which are not yet handled by the configured remote SMS transport. If an SMS Text message cannot be delivered, the “Last Error” column should provide information on why it cannot be delivered. An SMS transport must be configured before SMS Text messages can be sent.

The gateway sends passwords to end users via SMS text messages. These messages appear in the SMS Queue until they are processed by the configured SMS transport (the service or connection your system uses to deliver texts).

If a message cannot be delivered, the “Error Message column” explains why (for example, invalid phone number, carrier rejection, expired message, or network timeout). Use this information to correct the issue and resend if needed.

Before any SMS text messages can be sent, an SMS transport must be configured and active. If you do not see messages leaving the queue, verify that:

  • An SMS transport is configured and enabled.

  • Recipient phone numbers are in the correct international format (E.164, such as +15551234567).

  • Your SMS provider account has sufficient balance or quota.

  • Network connectivity and credentials for the SMS provider are valid.

SMS Queue

Send SMS

A test SMS can be sent from the actions menu Actions ‣ Send SMS

SMS transports

An SMS transport is the connection between the gateway and an SMS provider. The following transports are built in:

Transport

Description

Clickatell Connect/HTTP

Sends the SMS Text message with the Clickatell Connect HTTP API

eCall

Sends the SMS Text message with the eCall HTTP API

Twilio

Sends the SMS Text message with the Twilio REST API

Scriptable

Sends the SMS Text message with an external script (see Scriptable SMS transport)

The transports are configured on the settings page under Settings ‣ SMS ‣ Transports. The settings of a transport are global settings: they cannot be set per domain or per user.

Only one transport is active at a time. The active transport is selected with the “SMS Active Transport” setting (Settings ‣ SMS ‣ Settings ‣ SMS Active Transport) or with the CLI:

ciphermail-cli property global set --name sms-active-transport --value twilio

The names of all registered transports can be listed with:

ciphermail-cli sms transports

Hint

The transport name is not case sensitive. A name which contains a space, like Clickatell Connect/HTTP, must be quoted on the command line.

Note

Configuring a transport does not activate it. Only the transport selected with “SMS Active Transport” is used to send SMS Text messages.

Clickatell Connect/HTTP

This transport sends the SMS Text message with the Clickatell Connect HTTP API (https://platform.clickatell.com/messages/http/send).

The API key is created in the Clickatell Platform portal for the HTTP API integration which should deliver the messages.

The following setting is available under Settings ‣ SMS ‣ Transports ‣ Clickatell/Connect:

Clickatell Connect API Key

The authentication key which unlocks the Clickatell integration service (property sms-transport-clickatell-connect-api-key).

Example configuration with the CLI:

ciphermail-cli property global set --name sms-transport-clickatell-connect-api-key --value "<api-key>"
ciphermail-cli property global set --name sms-active-transport --value "Clickatell Connect/HTTP"

eCall

This transport sends the SMS Text message with the eCall HTTP API (https://url.ecall.ch/api/sms).

The following settings are available under Settings ‣ SMS ‣ Transports ‣ ECall:

Ecall account name

The account name (user name) of the eCall gateway account (property sms-transport-ecall-account-name).

Ecall account password

The password of the eCall gateway account (property sms-transport-ecall-account-password).

Example configuration with the CLI:

ciphermail-cli property global set --name sms-transport-ecall-account-name --value "<account-name>"
ciphermail-cli property global set --name sms-transport-ecall-account-password --value "<password>"
ciphermail-cli property global set --name sms-active-transport --value ecall

Twilio

This transport sends the SMS Text message with the Twilio REST API (https://api.twilio.com/2010-04-01/Accounts/<AccountSid>/Messages.json). Twilio is the default transport.

The following settings are available under Settings ‣ SMS ‣ Transports ‣ Twilio:

From

The sender of the SMS Text message: a phone number in international format (E.164, such as +15551234567) or an alphanumeric sender ID (property sms-transport-twilio-from).

Messaging Service SID

The Messaging Service to send the message with, a unique 34-character identifier which starts with MG (property sms-transport-twilio-messaging-service-sid). Use a Messaging Service instead of a fixed sender number if you want Twilio to select the sender number from a pool.

Account SID

The identifier of the Twilio account (property sms-transport-twilio-account-sid). The Account SID is always required because it is part of the API URL.

Auth Token

The authentication token of the Twilio account (property sms-transport-twilio-auth-token).

API Key SID

The identifier of a Twilio API key (property sms-transport-twilio-api-key-sid). API keys are revokable credentials which can be created on the Twilio dev tools page.

API Key Secret

The secret which belongs to the API Key SID (property sms-transport-twilio-api-key-secret).

Important

“From” or “Messaging Service SID” (or both) must be set. If neither is set, the SMS Text message cannot be sent.

Important

There are two ways to authenticate with Twilio:

  • With an API key: set “API Key SID” and “API Key Secret”.

  • With the account credentials: set “Auth Token”.

If “API Key SID” is set, the API key is used and “API Key Secret” must be set as well. If “API Key SID” is not set, “Auth Token” must be set. Using an API key is advised because an API key can be revoked without changing the account credentials.

Example configuration with the CLI, using an API key and a fixed sender number:

ciphermail-cli property global set --name sms-transport-twilio-account-sid --value "<account-sid>"
ciphermail-cli property global set --name sms-transport-twilio-api-key-sid --value "<api-key-sid>"
ciphermail-cli property global set --name sms-transport-twilio-api-key-secret --value "<api-key-secret>"
ciphermail-cli property global set --name sms-transport-twilio-from --value "+15551234567"
ciphermail-cli property global set --name sms-active-transport --value twilio

Scriptable SMS transport

This transport sends the SMS Text message with an external script, which makes it possible to use an SMS provider for which there is no dedicated transport. The script receives the phone number and the message from the gateway and is responsible for handing the message over to the provider (for example with a REST API call).

Configuring the scriptable SMS transport requires three steps:

  1. Create the script which sends the SMS Text message.

  2. Enable the scriptable SMS transport with Ansible.

  3. Make the scriptable transport the active SMS transport.

How the script is called

The gateway calls the configured command with the phone number as a command line option and the message on standard input:

/opt/ciphermail/scripts/send-sms.sh --phone-number "+15551234567"

The script must therefore meet the following requirements:

  • The script must be executable and the ciphermail-backend user must be allowed to execute it (the back end runs as the ciphermail-backend user).

  • The message must be read from standard input.

  • The phone number is passed with the --phone-number option. The gateway normalizes and validates the phone number before the script is called, so it is always in international format (E.164, such as +15551234567).

  • The script must exit with exit code 0 if the SMS Text message was sent, and with a non-zero exit code if it was not sent.

Note

If the script exits with a non-zero exit code, the SMS Text message stays in the SMS queue and is retried about once a minute. Anything the script writes to standard error is stored as the “Error message” of the queued message, so write a helpful error message to standard error. A message which cannot be sent within 24 hours is discarded.

Attention

By default the script must finish within 30 seconds. If it takes longer, the process is terminated and the SMS Text message is treated as not sent. Do not wait or retry in a loop inside the script; return a non-zero exit code and let the gateway retry.

Create the script

Create the script using the Cockpit Terminal (or use an SSH client) and make it executable:

sudo vi /opt/ciphermail/scripts/send-sms.sh
sudo chmod +x /opt/ciphermail/scripts/send-sms.sh

Hint

The directory /opt/ciphermail/scripts/ can be used for your own scripts.

The following example script sends the SMS Text message with the Twilio REST API. It handles the command line options, reads the message from standard input, logs to syslog and reports a failure with a non-zero exit code. Use it as a starting point for your own script. Click the title to expand the script.

Bash example script (send-sms.sh)
#!/bin/bash

set -e
set -o pipefail

# set a sane/secure path
PATH='/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin'
# it's almost certainly already marked for export, but make sure
export PATH

# remove all aliases (start with \ to prevent unalias from being aliased)
\unalias -a

# clean command hash
hash -r

# set a sane/secure IFS (note this is bash & ksh93 syntax only--not portable!)
IFS=$' \t\n'

SCRIPT_NAME=$(basename "$0")

# the error exit codes
GENERAL_EXIT_CODE=100

usage()
{
    echo "Usage: $SCRIPT_NAME [option...]" >&2
    echo "" >&2
    echo "    -h              show usage" >&2
    echo "    --help          show usage" >&2
    echo "    --phone-number  phone number to send SMS to" >&2

    exit 1
}

exit_with_error()
{
    echo "$1" >&2

    local exit_code="$2"

    if [ -z "$exit_code" ]; then
        exit_code=$GENERAL_EXIT_CODE;
    fi

    exit "$exit_code"
}

if [ "$#" -eq 0 ]; then usage; fi

GETOPT_TEMP=$(getopt -o h --long "help,phone-number:" \
    -n "$SCRIPT_NAME" -- "$@")

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around $GETOPT_TEMP: they are essential!
eval set -- "$GETOPT_TEMP"

while true ; do
    case "$1" in
        -h|--help) usage ;;
        --phone-number) phone_number=$2 ; shift 2 ;;
        --) shift ; break ;;
        *) echo "Internal error!" ; exit 1 ;;
    esac
done

if [[ -z $phone_number ]]; then
  exit_with_error "phone_number not set"
fi

echo "sending SMS to $phone_number" | logger -t ciphermail-backend

# Read message body from stdin (also kept for logging/debugging below)
message="$(cat)"

TWILIO_ACCOUNT_SID=<replace>
TWILIO_AUTH_TOKEN=<replace>
TWILIO_FROM_NUMBER=<replace>

resp_file="$(mktemp)"
http_code="$(
  curl -sS -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Messages.json" \
    -u "${TWILIO_ACCOUNT_SID}:${TWILIO_AUTH_TOKEN}" \
    --data-urlencode "To=${phone_number}" \
    --data-urlencode "From=${TWILIO_FROM_NUMBER}" \
    --data-urlencode "Body=${message}" \
    -o "${resp_file}" \
    -w "%{http_code}"
)"

# Twilio returns 201 Created on success for Message creation
if [[ "${http_code}" != "201" ]]; then
  {
    echo ""
    echo "Twilio SMS send failed (HTTP ${http_code}). Response body:"
    cat "${resp_file}"
  } | logger -t ciphermail-backend

  rm -f "${resp_file}"
  exit_with_error "SMS could not be sent (HTTP ${http_code})"
fi

rm -f "${resp_file}"

exit 0

Attention

The example script contains the credentials of the SMS provider. Make sure the script can only be read by the users who need it.

Enable the scriptable SMS transport

The scriptable SMS transport is only registered if the command to run is configured. Create an Ansible override file using the Cockpit File Browser (or use an SSH client):

sudo vi /etc/ciphermail/ansible/group_vars/all/override.yml

Paste the following content:

---
ciphermail_backend__scriptable_sms_transport_command: '/opt/ciphermail/scripts/send-sms.sh'

Then run the playbook:

sudo cm-run-playbook

Hint

The command may contain fixed command line options, for example '/opt/ciphermail/scripts/send-sms.sh --provider example'. The --phone-number option is always added by the gateway. Setting ciphermail_backend__scriptable_sms_transport_command to false (the default) disables the scriptable SMS transport.

Activate the scriptable SMS transport

After the back end has restarted, the transport is available under the name Scriptable. To make it the active transport, set the “SMS Active Transport” setting (Settings ‣ SMS ‣ Settings ‣ SMS Active Transport) or use the CLI:

ciphermail-cli property global set --name sms-active-transport --value scriptable

The available transports can be listed with:

ciphermail-cli sms transports

Test the scriptable SMS transport

Send a test SMS Text message:

ciphermail-cli sms send --message test --phone-number <phone-number>

Check the SMS queue:

ciphermail-cli sms get all

If the message was sent, it is removed from the queue. If the queue still contains the message, the lastError of the message (the “Error message” column on the SMS queue page) explains why it could not be sent. Additional information can be found in the back-end log:

sudo journalctl -u ciphermail-backend -r