Sample Templates
This section contains sample templates that illustrate the following:
- Session management using login and logout templates
- Viewing event types
- XML parsing and handling
- Steps execution through backtracking and loops in a template
- Operations using namespace variables
- IP-related XC operations
- WAPI integration
Note: Some of the templates are specific for Rapid7 servers.
Session Management Template
Following is a sample session management template for REST API endpoint:
{
"name": "session",
"version": "2.0",
"type": "REST_ENDPOINT",
"vendor_identifier": "Rapid7",
"path": "/api/1.1/xml",
"keepalive": true,
"login_template": "login",
"logout_template": "logout",
"session_duration": 60000
}
Note: The login_template and logout_template lines specify the templates to be executed for login and logout.
The session_duration line specifies the length of the session. You must upload the login and logout templates before configuring a session management template that refers them.
Following is a sample session management template for DXL endpoint:
{"name": "session_tmplate",
"version": "3.0",
"type": "DXL_ENDPOINT",
"vendor_identifier": "
DXL",
"dxl_keep_alive_interval": 60,
"dxl_topic": "/outbound/session"
}
Login Template
{
"name": "login",
"version": "2.0",
"vendor_identifier": "Rapid7",
"type": "REST_EVENT",
"event_type": ["SESSION"],
"content_type": "text/xml",
"quoting": "XMLA",
"steps": [
{
"body": "${XC:ASSIGN:{H:Authorization}:{S:}}",
"operation": "NOP",
"name": "login: clear basic auth"
},
{
"parse": "XMLA",
"operation": "POST",
"no_connection_debug": false,
"name": "login: request",
"body_list": [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<LoginRequest user-id=\"${UT::USERNAME}\" password=\"${UT::PASSWORD}\"/>"
]
},
{
"operation": "CONDITION",
"name": "login: errorcheck",
"condition": {
"statements": [
{
"op": "!=",
"right": "${P:A:PARSE[[name]]}",
"left": "LoginResponse"
},
{
"op": "!=",
"right": "1",
"left": "${P:A:PARSE{{success}}}"
}
],
"condition_type": "OR",
"else_eval": "${XC:COPY:{S:SESSID}:{P:PARSE{{session-id}}}}",
"error": true
}
}
]
}
Note: The else_eval line copies the session-id from the parsed reply (if successful); otherwise, it returns an error.
Logout Template
{
"name": "logout",
"version": "2.0",
"type": "REST_EVENT",
"vendor_identifier": "Rapid7",
"event_type": ["SESSION"],
"content_type": "text/xml",
"quoting": "XMLA",
"steps": [
{
"parse": "XMLA",
"operation": "POST",
"no_connection_debug": false,
"name": "logout: request",
"body_list": [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<LogoutRequest session-id=\"${S::SESSID}\"/>"
]
},
{
"operation": "CONDITION",
"name": "logout: errorcheck",
"condition": {
"statements": [
{
"op": "!=",
"right": "${P:A:PARSE[[name]]}",
"left": "LogoutResponse"
},
{
"op": "!=",
"right": "1",
"left": "${P:A:PARSE{{success}}}"
}
],
"condition_type": "OR",
"error": true
}
}
]
}
Action Template
Following is a sample action template for DXL endpoint:
{
"vendor_identifier": "DXL",
"version": "3.0",
"name": "action",
"type": "DXL_EVENT",
"event_type": [
"FIXED_ADDRESS_IPV4"
],
"dxl_topic": "/outbound/action",
"steps": [
{
"operation": "DXL_SEND_EVENT",
"name": "send_ip",
"body": "address ${E:A:values{ipv4addr}}",
"dxl_topic": "/outbound/step"
}
]
}
Following is a sample action template for REST API endpoint:
{
"name": "action",
"vendor_identifier": "Rapid7",
"version": "2.0",
"content_type": "text/xml",
"action_type": "add network or remove IP",
"quoting": "XMLA",
"type": "REST_EVENT",
"event_type": [ "FIXED_ADDRESS_IPV4", "FIXED_ADDRESS_IPV6" ],
"steps": [
{
"name": "check operation type",
"operation": "CONDITION",
"condition": {
"statements": [
{
"op": "!=",
"right": "${E:A:operation_type}",
"left": "INSERT"
},
{
"op": "!=",
"right": "${E:A:operation_type}",
"left": "DELETE"
}
],
"condition_type": "AND",
"stop": true
}
},
{
"name": "send SiteListingRequest",
"operation": "POST",
"body_list": [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<SiteListingRequest session-id=\"${S::SESSID}\" />"
],
"parse": "XMLA"
},
{
"operation": "CONDITION",
"name": "send SiteListingRequest (error check)",
"condition": {
"statements": [
{
"op": "!=",
"right": "${P:A:PARSE[[name]]}",
"left": "SiteListingResponse"
},
{
"op": "!=",
"right": "1",
"left": "${P:A:PARSE{{success}}}"
}
],
"condition_type": "OR",
"error": true,
"else_eval": "${XC:COPY:{L:site_list}:{P:PARSE}"
}
},
{
"operation": "CONDITION",
"name": "check whether site list is empty",
"condition": {
"statements": [{
"op": "==",
"right": "${L:L:site_list}",
"left": "0"
}],
"condition_type": "AND",
"stop": true
}
},
{
"operation": "VARIABLEOP",
"name": "get the next site",
"variable_ops": [{
"operation": "POP",
"type": "COMPOSITE",
"destination": "L:a_site",
"source": "L:site_list"
}]
},
{
"operation": "CONDITION",
"name": "check site name",
"condition": {
"statements": [{
"op": "!=",
"right": "${L:A:a_site{{name}}}",
"left": "${E:A:values{extattrs}{r7_site}{value}}"
}],
"condition_type": "AND",
"next": "check whether site list is empty",
"else_eval": "${XC:COPY:{L:site_id}:{L:a_site{{id}}}}"
}
},
{
"parse": "XMLA",
"operation": "POST",
"name": "send SiteConfigRequest",
"body_list": [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<SiteConfigRequest session-id=\"${S::SESSID}\" site-id=\"${L:A:site_id}\"/>"
]
},
{
"operation": "CONDITION",
"name": "send SiteConfigRequest (error check)",
"condition": {
"statements": [
{
"op": "!=",
"right": "${P:A:PARSE[[name]]}",
"left": "SiteConfigResponse"
},
{
"op": "!=",
"right": "1",
"left": "${P:A:PARSE{{success}}}"
}
],
"condition_type": "OR",
"else_eval": "${XC:COPY:{L:Site}:{P:PARSE{SiteConfigResponse}}}",
"error": true
}
},
{
"operation": "CONDITION",
"name": "check operation type again",
"condition": {
"statements": [{
"op": "==",
"right": "${E:A:operation_type}",
"left": "INSERT"
}],
"condition_type": "AND",
"eval":
"${XC:COPY:{L:network}:{E:values{network}}}${XC:NETWORKTORANGE:{L:network}:{L:range
}}",
"next": "insert network"
}
},
{
"operation": "CONDITION",
"name": "remove ip",
"condition": {
"statements": [{
"op": "==",
"right": "${E:A:event_type}",
"left": "FIXED_ADDRESS_IPV4"
}],
"condition_type": "AND",
"eval":
"${XC:COPY:{L:ip}:{E:values{ipv4addr}}}${XC:REMOVEIP:{L:ip}:{L:Site{Hosts}}}",
"else_eval":
"${XC:COPY:{L:ip}:{E:values{ipv6addr}}}${XC:REMOVEIP:{L:ip}:{L:Site{Hosts}}}"
}
},
{
"operation": "CONDITION",
"name": "jump to send",
"condition": {
"statements": [{
"op": "==",
"right": "",
"left": ""
}],
"condition_type": "AND",
"next": "send SiteSaveRequest"
}
},
{
"operation": "VARIABLEOP",
"name": "insert network",
"variable_ops": [{
"operation": "PUSH",
"type": "COMPOSITE",
"source": "L:range",
"destination": "L:Site{Site}{Hosts}"
}]
},
{
"parse": "XMLA",
"operation": "POST",
"name": "send SiteSaveRequest",
"body_list": [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<SiteSaveRequest session-id=\"${S::SESSID}\">",
"${L:x:Site}",
"</SiteSaveRequest>"
]
},
{
"operation": "CONDITION",
"name": "send SiteSaveRequest (error check)",
"condition": {
"statements": [
{
"op": "!=",
"right": "${P:A:PARSE[[name]]}",
"left": "SiteSaveResponse"
},
{
"op": "!=",
"right": "1",
"left": "${P:A:PARSE{{success}}}"
}
],
"condition_type": "OR",
"error": true
}
},
{
"operation": "CONDITION",
"name": "check operation type once more",
"condition": {
"statements": [{
"op": "!=",
"right": "${E:A:operation_type}",
"left": "INSERT"
}],
"condition_type": "AND",
"stop": true
}
},
{
"operation": "PUT",
"name": "add an attribute with WAPI",
"transport": {"path": "${E:A:values{_ref}}"},
"wapi": "v2.6",
"body": "{\"extattrs+\": {\"r7_added\": {\"value\": \"Added to the Rapid7 ${UT:A:TIME}\"}}}"
}
]
}
Action Template: Check Operation Type
{
"name": "check operation type",
"operation": "CONDITION",
"condition": {
"statements": [
{
"op": "!=",
"right": "${E:A:operation_type}",
"left": "INSERT"
},
{
"op": "!=",
"right": "${E:A:operation_type}",
"left": "DELETE"
}
],
"condition_type": "AND",
"stop": true
}
Note: The "check operation type
" step checks the operation type. If it is neither INSERT nor DELETE, the template execution stops.
Action Template: Get the List of Sites
{
"name": "send SiteListingRequest",
"operation": "POST",
"body_list": [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<SiteListingRequest session-id=\"${S::SESSID}\" />"
],
"parse": "XMLA"
},
{
"operation": "CONDITION",
"name": "send SiteListingRequest (error check)",
"condition": {
"statements": [
{
"op": "!=",
"right": "${P:A:PARSE[[name]]}",
"left": "SiteListingResponse"
},
{
"op": "!=",
"right": "1",
"left": "${P:A:PARSE{{success}}}"
}
],
"condition_type": "OR",
"error": true,
"else_eval": "${XC:COPY:{L:site_list}:{P:PARSE}"
}
}
Note: The "send SiteListingRequest
" and "send SiteListingRequest (error check)
" steps request the list of sites on a Rapid7 server. If the response is successful, it copies the list to the L:site_list variable
.
Action Template: Locate the Site ID by Name
{
"operation": "CONDITION",
"name": "check whether site list is empty",
"condition": {
"statements": [{
"op": "==",
"right": "${L:L:site_list}",
"left": "0"
}],
"condition_type": "AND",
"stop": true
}
},
{
"operation": "VARIABLEOP",
"name": "get the next site",
"variable_ops": [{
"operation": "POP",
"type": "COMPOSITE",
"destination": "L:a_site",
"source": "L:site_list"
}]
},
{
"operation": "CONDITION",
"name": "check site name",
"condition": {
"statements": [{
"op": "!=",
"right": "${L:A:a_site{{name}}}",
"left": "${E:A:values{extattrs}{r7_site}{value}}"
}],
"condition_type": "AND",
"next": "check whether site list is empty",
"else_eval": "${XC:COPY:{L:site_id}:{L:a_site{{id}}}}"
}
}
NOTES:
- The "
check whether site list is empty
", "get the next site
", and "check site name
" steps form a loop for finding the site with specific name in the list of sites. - The "
check whether site list is empty
" step checks to see if the list is empty. If it is empty, the site with the specific name is not found and the execution of the template stops. - In the "
get the next site
" step, one element is copied to theL:a_site
variable. - In the "
check site name
" step, the "name
" attribute of a site (${L:A:a_site{{name}}}
) is compared to the value of the "r7_site
" extensible attribute of the fixed address in(${E:A:values{extattrs}{r7_site}{value}})
. If they are the same, the site ID is stored to theL:site_id variable (${XC:COPY:{L:site_id}:{L:a_site{{id}}}})
. If they are not the same, the execution is continued by the "check whether site list is empty
" step.
Action Template: Get Site Configuration
{
"parse": "XMLA",
"operation": "POST",
"name": "send SiteConfigRequest",
"body_list": [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<SiteConfigRequest session-id=\"${S::SESSID}\" site-id=\"${L:A:site_id}\"/>"
]
},
{
"operation": "CONDITION",
"name": "send SiteConfigRequest (error check)",
"condition": {
"statements": [
{
"op": "!=",
"right": "${P:A:PARSE[[name]]}",
"left": "SiteConfigResponse"
},
{
"op": "!=",
"right": "1",
"left": "${P:A:PARSE{{success}}}"
}
],
"condition_type": "OR",
"else_eval": "${XC:COPY:{L:Site}:{P:PARSE{SiteConfigResponse}}}",
"error": true
}
}
Note: Once site-id
is known, steps "send SiteConfigRequest"
and "send SiteConfigRequest (error check)"
request and store the site configuration.
Action Template: Distinguish Between INSERT and DELETE
{
"operation": "CONDITION",
"name": "check operation type again",
"condition": {
"statements": [{
"op": "==",
"right": "${E:A:operation_type}",
"left": "INSERT"
}],
"condition_type": "AND",
"eval":
"${XC:COPY:{L:network}:{E:values{network}}}${XC:NETWORKTORANGE:{L:network}:{L:range
}}",
"next": "insert network"
}
}
Note: The "check operation type again
" step determines the operation type. If the operation is "INSERT
", the network of the inserted fixed address is copied to the L:network variable (${XC:COPY:{L:network}:{E:values{network}}}
) and transformed to a Rapid7 range to the L:range variable (${XC:NETWORKTORANGE:{L:network}:{L:range}})
. After the range is stored, the template execution jumps to the "insert network
" step.
Action Template: Delete an IP Address
{
"operation": "CONDITION",
"name": "remove ip",
"condition": {
"statements": [{
"op": "==",
"right": "${E:A:event_type}",
"left": "FIXED_ADDRESS_IPV4"
}],
"condition_type": "AND",
"eval":
"${XC:COPY:{L:ip}:{E:values{ipv4addr}}}${XC:REMOVEIP:{L:ip}:{L:Site{Hosts}}}",
"else_eval":
"${XC:COPY:{L:ip}:{E:values{ipv6addr}}}${XC:REMOVEIP:{L:ip}:{L:Site{Hosts}}}"
}
},
{
"operation": "CONDITION",
"name": "jump to send",
"condition": {
"statements": [{
"op": "==",
"right": "",
"left": ""
}],
"condition_type": "AND",
"next": "send SiteSaveRequest"
}
}
NOTES:
- If the operation is not "
INSERT
" (i.e. "DELETE
"), the "remove ip
" step is executed. The step determines the type of fixed address. The corresponding address ({E:values{ipv4addr}
} or{E:values{ipv6addr}}
) is copied to theL:ip
variable, and then theL:ip
address is removed from the list of hosts in the site(${XC:REMOVEIP:{L:ip}:{L:Site{Hosts}}})
. - The step "
jump to send
" skips the "inserting
" step and jumps directly to the "send SiteSaveRequest
" step.
Action Template: Add an IP Range
{
"operation": "VARIABLEOP",
"name": "insert network",
"variable_ops": [{
"operation": "PUSH",
"type": "COMPOSITE",
"source": "L:range",
"destination": "L:Site{Site}{Hosts}"
}]
}
The step "insert network" pushes the L:range to the list of site’s "Hosts".
Action Template: Saving New Configuration
{
"parse": "XMLA",
"operation": "POST",
"name": "send SiteSaveRequest",
"body_list": [
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<SiteSaveRequest session-id=\"${S::SESSID}\">",
"${L:x:Site}",
"</SiteSaveRequest>"
]
},
{
"operation": "CONDITION",
"name": "send SiteSaveRequest (error check)",
"condition": {
"statements": [
{
"op": "!=",
"right": "${P:A:PARSE[[name]]}",
"left": "SiteSaveResponse"
},
{
"op": "!=",
"right": "1",
"left": "${P:A:PARSE{{success}}}"
}
],
"condition_type": "OR",
"error": true
}
}
Note: The "send SiteSaveRequest
" and "send SiteSaveRequest (error check)
" steps save new site configuration to the Rapid7 server.
Action Template: Add Extensible Attributes Using WAPI
{
"operation": "CONDITION",
"name": "check operation type once more",
"condition": {
"statements": [{
"op": "!=",
"right": "${E:A:operation_type}",
"left": "INSERT"
}],
"condition_type": "AND",
"stop": true
}
},
{
"operation": "PUT",
"name": "add an attribute with WAPI",
"transport": {"path": "${E:A:values{_ref}}"},
"wapi": "v2.6",
"body": "{\"extattrs+\": {\"r7_added\": {\"value\": \"Added to the Rapid7 ${UT:A:TIME}\"}}}"
}
NOTES:
- The "
check operation type once more
" step determines the operation type. If the operation is not "INSERT
" (i.e. "DELETE
"), the template execution stops. - For the "
INSERT
" operation, the last step "add an attribute with WAPI
" is executed. This step adds an extensible attribute "r7_added
" to the fixed address through RESTful API. The value of the attribute has the current timestamp (${UT:A:TIME}
).
Action Template: Add a Host Record
The following sample template, if assigned to a DHCP network notification rule, will insert a host record for any added network that matches the rule, with a hostname and domain name set by extensible attributes in the network. Detailed explanations about this sample are included in Action Template with Comments: Add a Host Record.
{
"version": "1.0",
"name": "Insert host record",
"comment": "Will automatically insert a host record for new network insertions, assumes the network has a 'Zone' extensible attribute, optionally a 'Hostname' extensible attribute as well",
"type": "REST_EVENT",
"event_type": [
"NETWORK_IPV4"
],
"action_type": "Insert a host record",
"vendor_identifier": "WAPI 2.3",
"transport": {
"content_type": "application/json",
},
"steps":
[
{
"name": "stop if it is not a network insert",
"operation": "CONDITION",
"condition": {
"condition_type": "AND",
"statements": [
{
"left": "${E:A:operation_type}",
"op": "!=",
"right": "INSERT"
}
],
"stop": true
}
},
{
"name": "stop if we don't have the zone EA set, else save it",
"operation": "CONDITION",
"condition": {
"condition_type": "AND",
"statements": [
{
"left": "${E:A:values{extattrs}{Zone}{value}}",
"op": "==",
"right": ""
}
],
"stop": true,
"else_eval": "${XC:COPY:{L:ZONE}:{E:values{extattrs}{Zone}{value}}}"
}
},
{
"name": "get the hostname or use a default value",
"operation": "CONDITION",
"condition": {
"condition_type": "AND",
"statements": [
{
"left": "${E:A:values{extattrs}{Hostname}{value}}",
"op": "!=",
"right": ""
}
],
"eval":
"${XC:COPY:{L:HOSTNAME}:{E:values{extattrs}{Hostname}{value}}}",
"else_eval": "${XC:ASSIGN:{L:HOSTNAME}:{S:defaulthostname}}"
}
},
{
"name": "insert the host record with the next available IP",
"operation": "POST",
"transport": {
"path": "record:host"
},
"body_list": [
"{",
"\"name\": \"${L:A:HOSTNAME}.${L:A:ZONE}\",",
"\"ipv4addrs\": [{\"ipv4addr\":
\"func:nextavailableip:${E:A:values{network}}\"}],",
"\"comment\": \"Inserted via outbound\"",
"}"
]
}
]
}
Action Template with Comments: Add a Host Record
The comments (labeled as Note:) embedded in the sample template explain the operation for each section of the template.
Note that the execution of this sample template will cause a single POST request to be sent with the following body for an insertion of a 10.0.0.0/24 network with the Zone extensible attribute set to test.com and the Hostname extensible attribute set to name
:
{"name": "name.test.com","ipv4addrs": [{"ipv4addr":
"func:nextavailableip:10.0.0.0/24"}],"comment": "Inserted via a template"}
Note: The preamble of the template specifies the version, version, name and other relevant fields.
{
"version": "1.0",
"name": "Insert host record",
"comment": "Will automatically insert a host record for new network insertions,assumes the network has a 'Zone' extensible attribute, optionally a 'Hostname' extensible attribute as well",
"type": "REST_EVENT",
Note: The event_type
field specifies that this template is used for IPv4 network events only.
"event_type": [
"NETWORK_IPV4"
],
Note: The action_type
and vendor_identifier
fields describe the template type and the vendor type.
"action_type": "Insert a host record",
"vendor_identifier": "WAPI 2.3",
Note: The following specifies that the template is going to send JSON to the server.
"transport": {
"content_type": "application/json",
},
Note: The following are steps that will be executed sequentially.
"steps":
[
Note: The first step will stop the execution, without an error, if the network event received is not an insertion – it could be a modify, for example.
{
"name": "stop if it is not a network insert",
"operation": "CONDITION",
"condition": {
Note: You can only one statement: either AND or OR would work.
"condition_type": "AND",
"statements": [
Note: The match is to check if operation_type
in the event is different from INSERT. It is a good practice to put the event variable on the left side so if it is not present in the event, the template would not fail.
{
"left": "${E:A:operation_type}",
"op": "!=",
"right": "INSERT"
}
],
Note: The directive that stops the execution if the condition matches.
"stop": true
}
},
Note: The second step will stop the execution if the inserted network does not have the Zone extensible attribute configured. If it has the extensible attribute, it will be put in a temporary local variable for easier access later on.
{
"name": "stop if we don't have the zone EA set, else save it",
"operation": "CONDITION",
"condition": {
Note: Similar to the previous section, we have only one statement, so either AND or OR would work. The condition ensures that the Zone extensible attribute is set to a value.
"condition_type": "AND",
"statements": [
{
Note: As previously mentioned, non-existent variable access in the left side of a condition will not cause an error, but instead return an empty value.
"left": "${E:A:values{extattrs}{Zone}{value}}",
"op": "==",
"right": ""
}
],
Note: If the extensible attribute is empty or nonexistent, the operation should stop here.
"stop": true,
Note: Otherwise, it will copy the zone value to the local ZONE variable.
"else_eval": "${XC:COPY:{L:ZONE}:{E:values{extattrs}{Zone}{value}}}"
}
},
Note: This step is similar to the zone step above. However, if the host name is not set, it will put a default host name in a local variable to provide the default.
{
"name": "get the hostname or use a default value",
"operation": "CONDITION",
"condition": {
"condition_type": "AND",
"statements": [
{
"left": "${E:A:values{extattrs}{Hostname}{value}}",
"op": "!=",
"right": ""
}
],
Note: This is executed if the extensible attribute is present, by copying its value to HOSTNAME.
"eval": "${XC:COPY:{L:HOSTNAME}:{E:values{extattrs}{Hostname}{value}}}",
Note: Otherwise, the following is executed if the extensible attribute is empty or not present, by assigning the defaulthostname
string instead.
"else_eval": "${XC:ASSIGN:{L:HOSTNAME}:{S:defaulthostname}}"
}
},
Note: This step will finally contact the endpoint and in this case insert the host.
{
"name": "insert the host record with the next available IP",
Note: This defines the HTTP operation to use.
"operation": "POST",
Note: The endpoint is configured starting with https://master_ip/.... The endpoint template overrides the path with /wapi/v2.3/ so by default all template requests would go to https://master_ip/wapi/v2.3/. In this step, we want to insert a host, so record:host
is appended to the URI above (no override is set here) to arrive to the valid RESTful URI https://master_ip/wapi/v2.3/record:host.
"transport": {
"path": "record:host"
},
Note: This is the text that will be sent to the server in the POST’s BODY.
"body_list": [
"{",
Note: This references the local variables that were previously assigned.
"\"name\": \"${L:A:HOSTNAME}.${L:A:ZONE}\",",
Note: This option signifies that the RESTful API will use the next available IP in the network as the address for this host.
"\"ipv4addrs\": [{\"ipv4addr\":
\"func:nextavailableip:${E:A:values{network}}\"}],",
Note: This is the comment stating that the action is done through the RESTful API template.
"\"comment\": \"Inserted via outbound\"",
"}"
]
}
]
}