Creating alerts using Common Alerting Protocol

17.05.2016.

Common Alerting Protocol is the major standard used for formating and dispatching public alerts. This blog post focuses on example how to create and post valid CAP alert using standard Linux tools. The goal is to provide straightfoward and easy way to convert any Linux box into a sensor box. It can be used to create, sign and post CAP alerts using simple equipment like Raspberry Pi and any sensor or detector. Additionaly it can be used to adopt/rewrite incoming alerts from older versions of the protocol (v1.0 or v1.1) with additional elements required by needed CAP profiles.

CAP is in essence a valid XML document conforming to a CAP schema. As a prerequisite, we need to install XML parsing and crytography tools. Depending upon Linux variant, different commands might be needed. Under Debian/Raspbian/Ubuntu derivatives, the following commands would help:

apt-get -y install xmlstarlet xmlsec1

For RHEL/CentOS the following installation is needed:

yum -y install xmlsec1 xmlsec1-openssl libxslt-devel install xmlstarlet openssl

Since libraries for formatting and parsing CAP messages are not widely available for all programming platforms, the 1st step is to create a CAP template. As a starting point we can use any example from the online CAP validator page. Note that these examples are usually embedded into RSS/Atom feed, so the first step is to strip Atom elements and extract <alert> element into a template file. The template should have look as follows:

<?xml version = "1.0" encoding = "UTF-8"?>
<alert xmlns = "urn:oasis:names:tc:emergency:cap:1.2">
 <identifier>...</identifier>   
...
</alert>

Next, check whether a template is a valid XML, and that it conforms with CAP schema:

xmlstarlet val template.cap
wget https://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2.xsd
xmlstarlet val --xsd ../checkalert/tmp/CAP-1.2.xsd template.cap

The following issue is obtaining a valid certificate for signing CAP files. Although it is not mandatory, it is highly recommended that any CAP message is signed so that its authenticity and integrity can be verified. Obtaining certificates (whether self-signed or CA verified certificates has been explained well in numerous Internet resources. Here is a command for quick generation of private key and self-signed certificate:

openssl req -x509 -newkey rsa:2048 -nodes -keyout cap_submitter.key -out cap_submitter.crt -days 365

The last step is to add a signature placeholder into XML template. This is needed by the XML signing utility to locate proper element and provide instructions how to treat input XML. Insert the following XML snippet just before the </alert> tag:

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
  <SignedInfo>
    <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <Reference URI="">
      <Transforms>
        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      </Transforms>
      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
      <DigestValue />
    </Reference>
    </SignedInfo>
  <SignatureValue />
  <KeyInfo>
    <X509Data />
  </KeyInfo>
</Signature>
</alert>

After all prerequisites have been prepared, we can create script that shall use CAP template, replace it with proper values.

# perform loop
CAP_TEMPLATE=template.cap
CAP_WORKFILE=newalert.cap
# define key variables
CAP_ID=`generate_cap_identifier`
CAP_STARTTIME=`date '+%Y-%m-%dT%T%:z'`
CAP_ENDTIME=`date -d '+1hour' '+%Y-%m-%dT%T%:z'`
CAP_HEADING="Alert condition on the `hostname` was generated"
#    populate CAP template
CAP_STR=`cat $CAP_TEMPLATE`
CAP_STR=`echo $CAP_STR | sed -e "s/###CAP_ID###/$CAP_ID/g"`
CAP_STR=`echo $CAP_STR | sed -e "s/###CAP_STARTTIME###/$CAP_STARTTIME/g"`
CAP_STR=`echo $CAP_STR | sed -e "s/###CAP_ENDTIME###/$CAP_ENDTIME/g"`
CAP_STR=`echo $CAP_STR | sed -e "s/###CAP_SENDER###/$CAP_SENDER/g"`
CAP_STR=`echo $CAP_STR | sed -e "s/###CAP_HEADING###/$CAP_HEADING/g"`
cat > $CAP_WORKFILE <<EOF
$CAP_STR
EOF

The next part of the script signs the prepared CAP alert:

CAP_STR=`xmlsec1 --sign  --privkey-pem cap_submitter.key,cap_submitter.crt $CAP_WORKFILE`

cat > $CAP_WORKFILE <<EOF
$CAP_STR
EOF

End the last part is to send/notify upstream CAP server about the new alert. Here details depend upon upstream CAP server. For our CAP.CAP solution, the easiest method is simple HTTP POST call which dispatches the server:

curl -f -X POST -v --header "Content-Type:application/xml" --data-binary @$CAP_WORKFILE  $CAP_SERVER_URL
SUBMIT_SUCCESS=$?

This is a barebone script, that can be easily adapted to handle multiple receiving CAP servers (to provide high availability and geographic redundancy), and easy modification of templates, handling of alert updates and cancellations etc. Since CAP is essentially XML, it is also straightforward to use any programming language which supports XML parsing, generation and signing.