Creating alerts using Common Alerting Protocol
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.