Horizon3.ai API
The Horizon3.ai API provides programmatic access to a subset of functionality available through the Horizon3.ai portal. At a a high level, this API allows for:
- scheduling an automated pentest, known as an operation
- monitoring the status of the operation while it is running
- retrieving an operation report after it’s complete
The API can be used for a variety of use cases such as periodically scheduling assessments of your environment or kicking off an operation as part of a continuous integration build pipeline.
- Pre-Requisites
- API Access
- Client Infrastructure
- Host/VM Requirements
- Outbound Network Access
- Inbound Network Access
- Container Privileges
- Create an Operation Template
- API Specification
- Authentication
- Scheduling an Operation
- Launching an Operation
- Monitoring the Status of an Operation
- Operation Life Cycle
- Querying the Operation State
- Reporting
- Example Script
Pre-Requisites
Prior to using the Horizon3.ai API, make sure to complete the following steps:
- Getting an API Key
- Setting up a host/VM to host the Horizon3.ai Docker container, called NodeZero
- Creating an operation template These steps are described in greater detail below.
API Access
An API key is required to access the API. The key is tied to an existing user account with Org Admin privileges in the Horizon3.ai portal.
At this time, API keys are manually provisioned by Horizon3.ai. If you don’t have an API key, please contact your Horizon3.ai representative to acquire one.
In addition, if you have an API key and need to revoke it and/or generate a new one, please contact your Horizon3.ai representative Please download the PDF to view it
Client Infrastructure
Running a Horizon3.ai operation requires running a Docker container, called NodeZero, on a Linux VM or host. The NodeZero container receives instructions from the Horizon3.ai cloud in order carry out the operation.
The NodeZero container may open ports on the host/VM in order to conduct certain types of attacks. The container automatically shuts down after the operation is complete.
A NodeZero container is used only once per operation. Once an operation is complete, the NodeZero container can be removed.
Host/VM Requirements
- Linux:
- Ubuntu >= 16.04
- Redhat >= 7
- 2 CPUs
- 4GB RAM
- 10GB free disk space
- Docker >= v17.0.7
Outbound Network Access
- Uninterrupted network access to the following endpoints over TCP 443 during the entire operation
- cognito-identity.us-east-2.amazonaws.com
- us-east-2.queue.amazonaws.com
- *.s3.us-east-2.amazonaws.com
It is possible to run NodeZero through a proxy if necessary. Contact your Horizon3.ai representative for more details.
Inbound Network Access
The following ports should be opened on the NodeZero host/VM to allow traffic in:
- TCP 21, 23, 25, 53, 80, 88, 110, 135, 139, 143, 389, 443, 445, 587, 1433, 3389, 8080
- UDP 69
This is required on the NodeZero host. This does not pertain to perimeter firewalls.
Container Privileges
The NodeZero container runs with the following elevated privileges:
- root user inside the container
- --network=host privileges
- --cap-add=SYS_ADMIN
- --security-opt apparmor=unconfined
These privileges are required to carry out certain types of automated attacks.
Create an Operation Template
Prior to scheduling an operation, the user to whom the API key belongs must first create an operation template in the Horizon3.ai portal. An operation template describes the scope that the client wants to scan and the operation configuration. The scope consists of IP addresses and subnets that the client wants to scan. The configuration describes which attack behaviors are turned on or off during the operation.
See Template Docs for details
API Specification
The API is hosted at api.horizon3ai.com. It provides endpoints for authentication, scheduling an op, monitoring the status of an op, and reporting. The current API version is v1, and all endpoints in the v1 API are accessible at api.horizon3ai.com/v1.
At a high level, to run an operation, the following sequence of steps should be followed:
- Authenticate using an API key.
- Schedule an operation using the operation template you configured earlier.
- Download a launch script.
- Run the launch script on the NodeZero Linux host to launch the operation. This is done outside of the API.
- While the operation is running, its state can be monitored using the API. The operation is complete when its state is “done.”
- Download an operation report.
These steps are described in further detail below.
Authentication
A client first authenticates by using its API key against the auth endpoint. The auth API validates the API key and returns a JSON Web Token (JWT) that clients use to invoke other APIs, as described below.
The JWT expires after an hour. Clients must retrieve a new JWT from the auth endpoint after an hour.
Example Request
curl \
-X POST https://api.horizon3ai.com/v1/auth \
-H "Content-Type: application/json" \
-d @- <<HERE
{
"key": "<API key>"
}
HERE
Example response
{"token": "<token>"}
Status Codes
- 200: Success: The response contains the JWT in the token field.
- 400: Bad Request: Missing Content-Type header
- 401: Unauthorized: Malformed request payload or unauthorized API key
- 5xx: Internal Server Error
Launching an Operation
After a client schedules an operation, the Horizon3.ai service begins the process of provisioning cloud infrastructure to support the operation. This process can take a few minutes. When the provisioning process completes, the client should download an operation launch script using the graphql
query shown below. The nodezero_script_url
field in the response returns a short-lived download URL that should be used by the client to directly download a NodeZero launch script. The URL is valid for up to 12 hours.
The launch script is a bash script that should be run on the NodeZero host. The script validates NodeZero system requirements, downloads the NodeZero Docker image, and runs the NodeZero container.
When an operation is initially scheduled, the download URL for the launch script will not be immediately available. The client can poll periodically
on the graphql
endpoint until a download URL is returned. Alternatively, the client can poll for the operation status as described in the next
section. The script will always be available by the time an operation enters the “ready” state.
Example Request
curl \
-X POST https://api.horizon3ai.com/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>"
-d @- <<HERE
{
"query": "
query {
op(op_id:\"e11b0a7c-fd24-4d48-b4c9-de8220f99847\") {
nodezero_script_url
}
}
"
}
HERE
Example Response
{
"data": {
"op": {
"nodezero_script_url": "<link>"
}
}
}
Status Codes
- 200: Success. The
nodezero_script_url
field in the response will contain a link to download the script using an HTTPS GET. If the operation is not yet ready, or if the operation is already running or complete, thenodezero_script_url
field in the response will have a value ofnull
. - 400: Bad Request: Malformed request. The
errors
field inside the response will contain a descriptive message. - 401: Unauthorized: Invalid or expired JWT
- 403: Forbidden:
op_id
in the GET request is invalid or not authorized for access. Theerrors
field inside the response will contain a descriptive message. - 5xx: Internal Server Error
Monitoring the Status of an Operation
Operation Life Cycle
It’s helpful to understand the life cycle of an operation. An operation always starts off in an initial state of “scheduled”, and it proceeds until it reaches a terminal state of either “Done”, “Canceled”, or “Error.” In between the initial state and a terminal state, there are other states that an operation can assume. These are described in the table below, roughly in the order that the operation proceeds.
State | Description |
---|---|
scheduled | This is the first state an operation enters, Immediately after an API client or end-user schedules an operation. |
provisioning | The Horizon3.ai cloud service is actively setting up infrastructure to support the operation. |
ready | The operation is ready to begin. The Horizon3.ai cloud service is waiting for the NodeZero container to start. The operation launch script is available for client download. |
running | The NodeZero container has connected to the Horizon3.ai cloud service, and the operation is in progress. |
complete | The Horizon3.ai service has finished executing all attack sequences against the client environment via NodeZero. |
destroying | The Horizon3.ai service is actively tearing down infrastructure that was provisioned for the operation. |
post-processing | The Horizon3.ai service is processing the results of the operation and preparing an operation report. |
done | The operation report is available in the portal or via the API. |
canceled | An end-user has manually canceled the operation from the Horizon3.ai portal. An operation can only be canceled while it is in the “ready” or “running” state. After it is canceled, the operation moves into the “complete” state and ends at “canceled” instead of “done.” The operation report for everything that was collected prior to cancellation is available in the portal or via the API. |
error | Unspecified error |
Querying the Operation State
The following graphql
query against the graphql
API endpoint can be used to retrieve the state of an operation. Knowing the state of an operation is helpful for several reasons:
- If a client has just scheduled an operation, a client can poll on the operation state until it reaches “ready.” When the operation is “ready”, the client can get a link to the launch script to start the operation.
- While an operation is “running”, a client can poll the operation state until it reaches “done” or “canceled” to know when the operation report is available for download.
A successful response contains the status of the operation in the op_state field.
Example Request
curl \
-X POST https://api.horizon3ai.com/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>"
-d @- <<HERE
{
"query": "
query {
op(op_id:\"e11b0a7c-fd24-4d48-b4c9-de8220f99847\") {
op_state
}
}
"
}
HERE
Example Response
{
"data": {
"op": {
"op_state": "done"
}
}
}
Status
- 200: Success. The response will contains the operation status in the op_state field.
- 400: Bad Request: Malformed request. The errors field inside the response will contain a descriptive message.
- 401: Unauthorized: Invalid or expired JWT
- 403: Forbidden: op_id in the GET request is invalid or not authorized for access. The errors field inside the response will contain a descriptive message.
- 5xx: Internal Server Error
Reporting
After an operation reaches the “done” or “canceled” state, a client can download a summary report using the following graphql
query against the graphql
API endpoint. The endpoint returns a set of summary statistics containing the number of weaknesses found, credentials discovered, and hosts scanned.
Example Request
curl \
-X POST https://api.horizon3ai.com/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>"
-d @- <<HERE
{
"query": "
query {
op(op_id:\"e11b0a7c-fd24-4d48-b4c9-de8220f99847\") {
op_name
op_state
scheduled_timestamp_iso
launched_timestamp_iso
completed_timestamp_iso
duration_hms
in_scope_hosts_count
confirmed_weaknesses_count
confirmed_credentials_count
}
}
"
}
HERE
Example Response
{
"data": {
"op": {
"completed_timestamp_iso": "2020-11-13T23:23:43",
"duration_hms": "00:21:45",
"in_scope_hosts_count": 10,
"launched_timestamp_iso": "2020-11-13T23:01:59",
"op_name": "my op name",
"op_state": "done",
"confirmed_credentials_count": 11,
"confirmed_weaknesses_count": 7,
"scheduled_timestamp_iso": "2020-11-13T22:14:17"
}
}
}
Status
-200: Success. The response contains summary data for the operation. If the operation was never launched or is not yet done, certain fields inside the response may have null values.
- 400: Bad Request: Malformed request. The errors field inside the response will contain a descriptive message.
- 401: Unauthorized: Invalid or expired JWT
- 403: Forbidden: op_id in the GET request is invalid or not authorized for access. The errors field inside the response will contain a descriptive message.
- 5xx: Internal Server Error
Example Script
The following bash script demonstrates all of the above API actions, from scheduling an operation to retrieving the operation report after the operation is done.
NOTE: This script does not handle error conditions or token renewal if the operation extends past an hour.
#!/bin/bash
#
# requires: curl, jq
#
# User defined parameters, set in the environment
API_KEY="$API_KEY"
OPERATION_TEMPLATE_NAME="$OP_TEMPLATE"
OPERATION_NAME="$OP_NAME"
# Static
API_URL="https://api.horizon3ai.com"
AUTH_URL="$API_URL"/v1/auth
GQL_URL="$API_URL"/v1/graphql
CONTENT_TYPE_HEADER="Content-Type: application/json"
function authenticate {
curl -s -S \
-X POST "$AUTH_URL" \
-H "$CONTENT_TYPE_HEADER" \
-d "{\"key\": \"$API_KEY\"}"
}
function schedule_op_template {
curl -s -S \
-X POST "$GQL_URL" \
-H "$CONTENT_TYPE_HEADER" \
-H "$AUTHZ_HEADER" \
-d @- <<EOF
{
"query": "
mutation {
schedule_op_template(
op_template_name:\"$OPERATION_TEMPLATE_NAME\"
op_name:\"$OPERATION_NAME\"
) {
op {
op_id
}
}
}
"
}
EOF
}
function fetch_op_state {
curl -s -S \
-X POST "$GQL_URL" \
-H "$CONTENT_TYPE_HEADER" \
-H "$AUTHZ_HEADER" \
-d @- <<EOF
{
"query": "
query {
op(op_id:\"$OP_ID\") {
op_state
}
}
"
}
EOF
}
function fetch_nodezero_script_url {
curl -s -S \
-X POST "$GQL_URL" \
-H "$CONTENT_TYPE_HEADER" \
-H "$AUTHZ_HEADER" \
-d @- <<EOF
{
"query": "
query {
op(op_id:\"$OP_ID\") {
nodezero_script_url
}
}
"
}
EOF
}
function fetch_op_report {
curl -s -S \
-X POST "$GQL_URL" \
-H "$CONTENT_TYPE_HEADER" \
-H "$AUTHZ_HEADER" \
-d @- <<EOF
{
"query": "
query {
op(op_id:\"$OP_ID\") {
op_name
op_state
scheduled_timestamp_iso
launched_timestamp_iso
completed_timestamp_iso
duration_hms
in_scope_hosts_count
confirmed_weaknesses_count
confirmed_credentials_count
}
}
"
}
EOF
}
function poll_op_for_status {
while [ 1 ]; do
op_state=`fetch_op_state | jq -r .data.op.op_state`
if [ "$op_state" = "$WAIT_STATE" ]; then
echo "op_state $op_state == $WAIT_STATE! breaking loop."
break
fi
echo "op_state $op_state != $WAIT_STATE ..."
sleep 15
done
}
# Step 1: Authenticate to API to get short-lived session token
TOKEN=`authenticate | jq -r .token`
echo "Authenticated to API"
export AUTHZ_HEADER="Authorization: Bearer $TOKEN"
# Step 2: Schedule an operation
export OP_ID=`schedule_op_template | jq -r .data.schedule_op_template.op.op_id`
echo "Scheduled operation id: $OP_ID"
# Step 3: Wait for op to hit "ready" state
export WAIT_STATE="ready"
poll_op_for_status
echo "Operation is ready"
# Step 4: Get the link to the NodeZero launch script
SCRIPT_URL=`fetch_nodezero_script_url | jq -r .data.op.nodezero_script_url`
echo "Launching NodeZero..."
# Step 5: Launch the NodeZero container
curl "$SCRIPT_URL" | bash &
# Step 6: Wait til the op is "done"
export WAIT_STATE="done"
poll_op_for_status
echo "Operation is done"
# Step 7: Download the op report
REPORT=`fetch_op_report | jq -r .data.op`
echo "Operation report: $REPORT"