Using Axonshell to Understand REST API Interactions in Axonius

Intended Audience:

  • Developers in restricted environments that do not have the ability to utilize 3rd party packages in their production code environment.
  • Developers who do not utilize Python in their production environment
  • Developers who want to understand the Rest APIs used in the Axonius_api_client workflows

We understand that not everyone has the ability to use to the Axonius_api_client, here at Axonius, we always have a way to solve problems according to YOUR needs.

The following steps assume some familiarity with the CLI and our current API documentation. We also assume you are already authenticated to Axonius. Below are some helpful links on these topics. 

If you have any additional questions please reach out to your TAM or support@axonius.com
 
Sending a request to fetch assets with a filter and specific fields to Axonius is the perfect workflow to examine. It will help us understand what endpoints are called, the methods used, and what payloads may be required when sending the request.
 

The steps for the workflow are as followed:

  1. Fetch the fields and filter of a Saved Query
  2. Get a count of all devices using fields and filter
  3. Apply fields, filter, and page attributes to payload
  4. Fetch all Devices or Users with fields and filter

We can enable logging while using Axonshell to see the calls being made, we can also enable logging of the request body or payload if any was sent with the request.

First, lets look at the request made when looking up a Saved Query.

axonshell --log-console devices saved-query get --name "test123"

Options used:

  • --log-console - allows logs to be generated on the CLI
  • devices saved-query get - return details about a saved query

Scrolling through the CLI we come across a log that tells us what method was used and what endpoint was used to gather the data that is available from a saved query.

DEBUG    api_endpoint    ApiEndpoint(method='get', path='api/V4.0/{asset_type}/views/saved')

lets look at the raw response of a GET request to api/V4.0/{asset_type}/view

curl --request GET 'https://10.20.1.226/api/devices/views/saved' \
--header 'api-key: key here' \
--header 'api-secret: Secret Here'

a lot of data is returned, but we can clearly see our saved query in the output

{

            "attributes": {
                "always_cached": false,
                "asset_scope": false,
                "created_by": "{\"user_name\": \"admin\", \"source\": \"internal\", \"first_name\": \"administrator\", \"last_name\": \"\", \"deleted\": false, \"permanent\": false, \"is_first_login\": false, \"last_updated\": null}",
                "date_fetched": "62bdfd5e4b569c81f6b54c06",
               "description": null,
               "folder_id": "62bdf2711f50fc7d9e48a274",
                "is_referenced": false,
                "last_run_time": "2022-07-06T22:48:59.811000+00:00",
                "last_updated": "2022-06-30T19:45:34.993000+00:00",
                "module": "devices",
                "name": "test123",
                "predefined": false,
                "private": false,
                "query_type": "saved",
                "tags": [],
                "timestamp": "2022-06-30 19:45:34.993000",
                "updated_by": "{\"user_name\": \"admin\", \"source\": \"internal\", \"first_name\": \"administrator\", \"last_name\": \"\", \"deleted\": false, \"permanent\": false, \"is_first_login\": false, \"last_updated\": null}",
                "used_in": [],
                "user_id": "62bdf28a4b569c81f6b546fb",
                "uuid": "62bdfd5e4b569c81f6b54c06",
                "view": {
                    "assetConditionExpressions": [],
                    "assetExcludeAdapters": [],
                    "colExcludedAdapters": [],
                    "colFilters": [],
                    "fields": [
                        "adapters",
                        "specific_data.data.name",
                        "specific_data.data.hostname",
                        "specific_data.data.last_seen",
                        "specific_data.data.network_interfaces.mac",
                        "specific_data.data.network_interfaces.ips",
                        "specific_data.data.os.type",
                        "labels"
                    ],
                    "query": {
                       "filter": "(\"specific_data.data.hostname\" == regex(\"test\", \"i\"))",
                        "meta": {
                            "uniqueAdapters": false
                        },
                        "onlyExpressionsFilter": "(\"specific_data.data.hostname\" == regex(\"test\", \"i\"))",
                        "search": null
                    },
                    "sort": {
                        "desc": true,
                        "field": ""
                    }
                }
            },
            "id": "62bdfd5e4b569c81f6b54c06",
            "type": "views_details_schema"
        }

In the portion of the response from the request to api/V4.0/{asset_type}/view we can clearly identify the fields and filter that is assosiated with this saved query.

Now that we understand how to log with Axonshell, lets look at the rest of the workflow from a single Axonshell command.

 axonshell --log-console --log-request-body devices get-by-saved-query --name "test123"

The Raw log to identify which endpoint was used looks like:

07/07/2022 12:03:08 AM UTC DEBUG    [axonius_api_client.api.api_endpoint.ApiEndpoint:load_request:/Users/cristian.cordero/.pyenv/versions/3.10.1/lib/python3.10/site-packages/axonius_api_client/api/api_endpoint.py:169] 
ApiEndpoint(method='post', path='api/V4.0/{asset_type}/count', request_schema_cls=<class 'axonius_api_client.api.json_api.assets.CountRequestSchema'>, request_model_cls=<class 'axonius_api_client.api.json_api.assets.CountRequest'>,
response_schema_cls=None, response_model_cls=<class 'axonius_api_client.api.json_api.assets.Count'>, http_args={}, http_args_required=[], request_as_none=False, response_as_text=False, log_level='debug')
Loading request with load_cls <class 'axonius_api_client.api.json_api.assets.CountRequest'> kwargs {'use_cache_entry': False, 'filter': '("specific_data.data.hostname" == regex("test", "i"))'}

Scrolling just a little past this log we see the payload used in this POST request to api/V4.0/{asset_type}/count:

07/07/2022 12:03:09 AM UTC DEBUG    [axonius_api_client.http.Http:_do_log_request:/Users/cristian.cordero/.pyenv/versions/3.10.1/lib/python3.10/site-packages/axonius_api_client/http.py:289] REQUEST BODY:
{
  "data": {
    "type": "entities_count_schema",
    "attributes": {
      "use_cache_entry": false,
      "filter": "(\"specific_data.data.hostname\" == regex(\"test\", \"i\"))"
    }
  }
}

lets look at the raw response from a POST Request to api/V4.0/{asset_type}/count:

curl --request POST 'https://10.20.1.226/api/devices/count' \
--header 'api-key: key here' \
--header 'api-secret: Secret Here' \
--data-raw '{
  "data": {
    "type": "entities_count_schema",
    "attributes": {
      "use_cache_entry": false,
      "filter": "(\"specific_data.data.hostname\" == regex(\"test\", \"i\"))"
    }
  }
}'

we get a count of devices:

{
    "data": {
        "attributes": {
            "value": 10
        },
        "type": "int_value_schema"
    }
}

the next log to look at is the log for fetching the assests themselves !
after the log for the POST request to api/V4.0/{asset_type}/count: We see a log for a POST request to the endpoint api/V4.0/{asset_type}.

DEBUG    api_endpoint    ApiEndpoint(method='post', path='api/V4.0/{asset_type}',
request_schema_cls=<class 'axonius_api_client.api.json_api.assets.AssetRequestSchema'>,
request_model_cls=<class 'axonius_api_client.api.json_api.assets.AssetRequest'>,
response_schema_cls=None, response_model_cls=<class 'axonius_api_client.api.json_api.assets.AssetsPage'>,
http_args={}, http_args_required=[], request_as_none=False, response_as_text=False, log_level='debug')
Loading request with load_cls <class 'axonius_api_client.api.json_api.assets.AssetRequest'>
kwargs {'always_cached_query': False, 'use_cache_entry': False, 'include_details': False, 'include_notes': False,
'get_metadata': True, 'use_cursor': True, 'filter': '("specific_data.data.hostname" == regex("test", "i"))',
'cursor_id': None, 'sort': None, 'excluded_adapters': {}, 'field_filters': {}}

just a little further we see a log for the payload sent

{
  "data": {
    "type": "entity_request_schema",
    "attributes": {
      "fields": {
        "devices": [
          "adapters",
          "specific_data.data.name",
          "specific_data.data.hostname",
          "specific_data.data.last_seen",
          "specific_data.data.network_interfaces.mac",
          "specific_data.data.network_interfaces.ips",
          "specific_data.data.os.type",
          "labels"
        ]
      },
      "page": {
        "limit": 2000,
        "offset": 0
      },
      "include_notes": false,
      "always_cached_query": false,
      "field_filters": {},
      "excluded_adapters": {},
      "get_metadata": true,
      "use_cache_entry": false,
      "filter": "(\"specific_data.data.hostname\" == regex(\"test\", \"i\"))",
      "include_details": false
    }
  }
}

When we send the POST request to api/V4.0/{asset_type}, the devices or users will be logged to the console.
lets take a look at one of the entries from the POST to api/V4.0/{asset_type}

curl --location --request POST 'https://10.20.1.226/api/devices' \
--header 'Content-Type: text/plain' \
--header 'api-key: key here' \
--header 'api-secret: Secret Here' \
--data-raw '{
  "data": {
    "type": "entity_request_schema",
    "attributes": {
      "fields": {
        "devices": [
          "adapters",
          "specific_data.data.name",
          "specific_data.data.hostname",
          "specific_data.data.last_seen",
          "specific_data.data.network_interfaces.mac",
          "specific_data.data.network_interfaces.ips",
          "specific_data.data.os.type",
          "labels"
        ]
      },
      "page": {
        "limit": 2000,
        "offset": 0
      },
      "include_notes": false,
      "always_cached_query": false,
      "field_filters": {},
      "excluded_adapters": {},
      "get_metadata": true,
      "use_cache_entry": false,
      "filter": "(\"specific_data.data.hostname\" == regex(\"test\", \"i\"))",
      "include_details": false
    }
  }
}'

One of the devices returned:

{
          "attributes": {
                "adapter_list_length": 1,
                "adapters": [
                    "active_directory_adapter"
                ],
                "internal_axon_id": "09409796ec51ea160e0c48c86a6dc338",
               "specific_data.data.hostname": "EC2AMAZ-7ASERO.Domain.test.test",
                "specific_data.data.last_seen": "Wed, 06 Jul 2022 18:51:03 GMT",
               "specific_data.data.name": "EC2AMAZ-7ASERO",
                "specific_data.data.network_interfaces.ips": [
                   "10.154.246.54"
                ],
                "specific_data.data.os.type": "Windows"
            },
            "type": "entities_schema"
        }

With this final API call we have reviewed how you can use Axonshell to understand workflows you may want to accomplish with other tools or languages. No matter what programming language you choose to interface with, we want to be able to support you development efforts. Axonius was built on api first principles so everything you do through the User Interface, there is a API call (or workflows of API calls) that can support your efforts.

- Cris

0

Comments

0 comments

Please sign in to leave a comment.

Didn't find what you were looking for?

New post