Osquery

Osquery is an operating system instrumentation framework for Windows, OS X (macOS), Linux, and FreeBSD. Zentral can act as a remote server for Osquery, for configuration, query runs, file carvings, and log collection.

Zentral configuration

To activate the osquery module, you need to add a zentral.contrib.osquery section to the apps section in base.json.

Query tables

Zentral parses the SQL of a query to find the osquery tables that the query reads. The names are in lower case, and sorted. A query that reads no table, a compliance check that only returns a status, has an empty list of tables.

The query page and the query list of the web console show the tables. The run list shows them too, and Zentral finds them in the SQL that the run froze when it started, so a later change to the query does not change them. Zentral computes the tables when it shows them, and does not store them.

Zentral refuses a query with SQL that it cannot parse, and gives an error for the sql attribute. The error gives the position of the problem in the SQL when sqlglot reports one. An import of a standard pack fails if it has such a query, and the errors give the name of every query that does not parse. Correct the SQL and import the pack again.

Audit events

Zentral publishes a zentral_audit event when a user creates, updates or deletes an Osquery object from the web console or from the API: automatic table constructions, configurations, configuration packs, enrollments, file categories, packs, pack queries, queries, and distributed query runs. Each event carries the value of the object before and after the change, with the user, the source IP and the request that made it.

Most of these objects are managed with the Zentral Terraform provider: the automatic table constructions, the configurations, the configuration packs, the enrollments, the file categories, the packs and the queries. For these objects, the event records a change that did not come from the Terraform configuration.

Distributed query runs are not managed with Terraform. A run executes SQL on the machines, and the event records that action.

Two effects of a change have no event of their own:

  • A change to a configuration increases the version of each of its enrollments. The event of the configuration records the change.
  • A change to a pack query sets the update time of its pack. The event of the pack query records the change.

The “Halt current runs” option of a new run stops the other runs of the same query. Each stopped run has its own event.

Standard pack imports

An import of a standard pack — a PUT on /api/osquery/packs/<slug>/, or an upload in the web console — changes several objects at once. It publishes one osquery_pack_update event that reports the import, and one zentral_audit event for each object it changed. They share one event UUID, and the report is the first of them, so a reader can gather the whole import from any one of its events.

The report answers what the audit events cannot: what the import did as a whole. Its result is created, updated, present or deleted, and present says that the import ran and changed nothing. Its query_results counts the pack queries the import created, updated, deleted, or left as they were.

The report does not carry the difference between the two values of an object. The audit event of that object carries the whole value before and after the change.

HTTP API

Requests

Authentication

API requests are authenticated using a token in the Authorization HTTP header:

Authorization: Token the_token_string

See API authentication for how to create a service account, issue a token for it and set an expiry.

Content type

Zentral will parse the body of the request based on the Content-Type HTTP header:

  • Content-Type: application/json
  • Content-Type: application/x-osquery-conf
  • Content-Type: application/yaml

Methods

Use PUT to update an object, and send all its attributes. PATCH is not available, and gives a 405 Method Not Allowed.

Paginated lists

The list endpoints answer with a page and not with an array:

{
  "count": 132,
  "next": "https://zentral.example.com/api/osquery/queries/?limit=50&offset=50",
  "previous": null,
  "results": []
}

Use the limit and the offset query parameters to select a page. The default limit is 50, and the maximum limit is 500. Read next until it is null to get all the objects.

/api/osquery/atcs/

List all ATCs.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewAutomaticTableConstruction"
  • Optional filter parameter:
    • name: name of the ATC.
    • configuration_id: primary key of the configuration.

Examples:

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/atcs/" \
  |python3 -m json.tool
$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/atcs/?name=Santa+rules" \
  |python3 -m json.tool

Response:

[
    {
        "id": 1,
        "name": "Santa rules",
        "description": "Access the Google Santa rules.db",
        "table_name": "santa_rules",
        "query": "SELECT * FROM rules;",
        "path": "/var/db/santa/rules.db",
        "columns": [
            "identifier",
            "state",
            "type",
            "custommsg",
            "timestamp"
        ],
        "platforms": [
            "darwin"
        ],
        "created_at": "2023-01-30T09:39:35.965003",
        "updated_at": "2023-01-30T09:39:35.965011"
    }
]

Add a new ATC.

  • method: POST
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"createAutomaticTableConstruction"

Example:

atc.json

{
	"name": "Access example",
	"description": "Access the example example.db",
	"table_name": "example_table",
	"query": "SELECT * FROM example;",
	"path": "/var/db/example/example.db",
	"columns": [
		"one",
		"two",
		"three"
	],
	"platforms": [
		"darwin",
        "linux"
	]
}
$ curl -X POST \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/atcs/" \
  -d @atc.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "Access example",
    "description": "Access the example example.db",
    "table_name": "example_table",
    "query": "SELECT * FROM example;",
    "path": "/var/db/example/example.db",
    "columns": [
        "one",
        "two",
        "three"
    ],
    "platforms": [
        "darwin",
        "linux"
    ],
    "created_at": "2023-01-31T08:59:14.097316",
    "updated_at": "2023-01-31T08:59:14.097333"
}

/api/osquery/atcs/<int:pk>/

Get a ATC.

method: GET Content-Type: application/json PBAC action: Osquery::Action::"viewAutomaticTableConstruction" <int:pk>: the primary key of the ATC.

Example

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/atcs/2/" \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "Access example",
    "description": "Access the example example.db",
    "table_name": "example_table",
    "query": "SELECT * FROM example;",
    "path": "/var/db/example/example.db",
    "columns": [
        "one",
        "two",
        "three"
    ],
    "platforms": [
        "darwin",
        "linux"
    ],
    "created_at": "2023-01-31T08:59:14.097316",
    "updated_at": "2023-01-31T08:59:14.097333"
}

Update a ATC.

  • method: PUT
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"updateAutomaticTableConstruction"
  • <int:pk>: the primary key of the ATC.

Example

atc_update.json

{
	"name": "Access example",
	"description": "Access the example example.db on all platforms",
	"table_name": "example_table",
	"query": "SELECT * FROM example;",
	"path": "/var/db/example/example.db",
	"columns": [
		"one",
		"two",
		"three",
		"four"
	],
	"platforms": [
		"darwin",
        "linux",
        "windows",
        "freebsd"
	]
}
$ curl -X PUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/atcs/2/" \
  -d @atc_update.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "Access example",
    "description": "Access the example example.db on all platforms",
    "table_name": "example_table",
    "query": "SELECT * FROM example;",
    "path": "/var/db/example/example.db",
    "columns": [
        "one",
        "two",
        "three",
        "four"
    ],
    "platforms": [
        "darwin",
        "linux",
        "windows",
        "freebsd"
    ],
    "created_at": "2023-01-31T08:59:14.097316",
    "updated_at": "2023-01-31T09:05:08.326755"
}

Delete a ATC.

  • method: DELETE
  • PBAC action: Osquery::Action::"deleteAutomaticTableConstruction"
  • <int:pk>: the primary key of the ATC.

Example

$ curl -X DELETE \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/atcs/2/" 

Response (204 No Content)

/api/osquery/configurations/

List all Configurations.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewConfiguration"
  • Optional filter parameter:
    • name: Name of the configuration.

Examples:

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configurations/" \
  |python3 -m json.tool
$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configurations/?name=example" \
  |python3 -m json.tool

Response:

[
    {
        "id": 1,
        "name": "example",
        "description": "",
        "inventory": true,
        "inventory_apps": true,
        "inventory_ec2": false,
        "inventory_interval": 600,
        "options": {
            "config_refresh": 120
        },
        "created_at": "2023-01-06T13:05:02.535763",
        "updated_at": "2023-01-30T09:40:23.912582",
        "file_categories": [],
        "automatic_table_constructions": [
            1
        ]
    }
]

Add a new Configuration.

  • method: POST
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"createConfiguration"
  • Required fields:
    • name: Name of the configuration.

Example:

configuration.json

{
	"name": "example2",
	"description": "description of example2",
	"inventory": true,
	"inventory_apps": true,
	"inventory_ec2": false,
	"inventory_interval": 600,
	"options": {
		"config_refresh": 120
	},
	"file_categories": [
		1
	],
	"automatic_table_constructions": [
		1
	]
}
$ curl -X POST \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configurations/" \
  -d @configuration.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "example2",
    "description": "description of example2",
    "inventory": true,
    "inventory_apps": true,
    "inventory_ec2": false,
    "inventory_interval": 600,
    "options": {
        "config_refresh": 120
    },
    "created_at": "2023-02-01T11:37:00.622052",
    "updated_at": "2023-02-01T11:37:00.622077",
    "file_categories": [
        1
    ],
    "automatic_table_constructions": [
        1
    ]
}

/api/osquery/configurations/<int:pk>/

Get a Configuration.

method: GET Content-Type: application/json PBAC action: Osquery::Action::"viewConfiguration" <int:pk>: The primary key of the configuration.

Example

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configurations/2/" \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "example2",
    "description": "description of example2",
    "inventory": true,
    "inventory_apps": true,
    "inventory_ec2": false,
    "inventory_interval": 600,
    "options": {
        "config_refresh": 120
    },
    "created_at": "2023-02-01T11:37:00.622052",
    "updated_at": "2023-02-01T11:37:00.622077",
    "file_categories": [
        1
    ],
    "automatic_table_constructions": [
        1
    ]
}

Update a Configuration.

  • method: PUT
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"updateConfiguration"
  • <int:pk>: The primary key of the configuration.
  • Required fields:
    • name: Name of the configuration.

Example

configuration_update.json

{
	"name": "example2",
	"description": "description of example2 updated",
	"inventory": true,
	"inventory_apps": true,
	"inventory_ec2": false,
	"inventory_interval": 800,
	"options": {
		"config_refresh": 120
	},
	"file_categories": [],
	"automatic_table_constructions": []
}
$ curl -X PUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configurations/2/" \
  -d @configuration_update.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "example2",
    "description": "description of example2 updated",
    "inventory": true,
    "inventory_apps": true,
    "inventory_ec2": false,
    "inventory_interval": 800,
    "options": {
        "config_refresh": 120
    },
    "created_at": "2023-02-01T11:37:00.622052",
    "updated_at": "2023-02-01T11:39:12.664992",
    "file_categories": [],
    "automatic_table_constructions": []
}

Delete a Configuration.

  • method: DELETE
  • PBAC action: Osquery::Action::"deleteConfiguration"
  • <int:pk>: The primary key of the configuration.

Example

$ curl -X DELETE \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/configurations/2/" 

Response (204 No Content)

/api/osquery/configuration_packs/

List all Configuration Packs.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewConfigurationPack"
  • Optional filter parameter:
    • pack_id: primary key of the pack.
    • configuration_id: primary key of the configuration.

Examples:

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configuration_packs/" \
  |python3 -m json.tool
$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configuration_packs/?pack_id=2" \
  |python3 -m json.tool

Response:

[
    {
        "id": 1,
        "configuration": 2,
        "pack": 2,
        "tags": [
            1
        ]
    }
]

Add a new Configuration Pack.

  • method: POST
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"createConfigurationPack"
  • Required fields:
    • pack: primary key of an existing pack.
    • configuration: primary key of an existing configuration.
  • Optional fields:
    • tags: list of primary keys of existing tags.

Example:

configurationpack.json

{
	"configuration": 1,
	"pack": 2,
	"tags": [
		2
	]
}
$ curl -X POST \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configuration_packs/" \
  -d @configurationpack.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "configuration": 1,
    "pack": 2,
    "tags": [
        2
    ]
}

/api/osquery/configuration_packs/<int:pk>/

Get a Configuration Pack.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewConfigurationPack"
  • <int:pk>: The primary key of the configuration pack.

Example

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configuration_packs/2/" \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "configuration": 1,
    "pack": 2,
    "tags": [
        2
    ]
}

Update a Configuration Pack.

  • method: PUT
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"updateConfigurationPack"
  • <int:pk>: The primary key of the configurationpack.
  • Required fields:
    • pack: primary key of an existing pack.
    • configuration: primary key of an existing configuration.
  • Optional fields:
    • tags: list of primary keys of existing tags.

Example

configurationpack_update.json

{
	"configuration": 1,
	"pack": 1,
	"tags": [
		1
	]
}
$ curl -X PUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/configuration_packs/2/" \
  -d @configurationpack_update.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "configuration": 1,
    "pack": 1,
    "tags": [
        1
    ]
}

Delete a Configuration Pack.

  • method: DELETE
  • PBAC action: Osquery::Action::"deleteConfigurationPack"
  • <int:pk>: The primary key of the configuration pack.

Example

$ curl -X DELETE \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/configuration_packs/2/" 

Response (204 No Content)

/api/osquery/file_categories/

List all FileCategories.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewFileCategory"
  • Optional filter parameter:
    • name: name of the FileCategory.
    • configuration_id: primary key of the configuration.

Examples:

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/file_categories/" \
  |python3 -m json.tool
$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/file_categories/?name=example" \
  |python3 -m json.tool

Response:

[
    {
        "id": 1,
        "name": "example",
        "slug": "example",
        "description": "example description",
        "file_paths": [],
        "exclude_paths": [],
        "file_paths_queries": [],
        "access_monitoring": false,
        "created_at": "2023-01-31T11:48:53.014319",
        "updated_at": "2023-01-31T11:48:53.014332"
    }
]

Add a new FileCategory.

  • method: POST
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"createFileCategory"

Example:

file_category.json

{
	"name": "example2",
	"slug": "example2",
	"description": "example2 description",
	"file_paths": ["/usr/example2"],
	"exclude_paths": ["/home/you/exclude1", "/home/me/exclude2"],
	"file_paths_queries": [],
	"access_monitoring": true
}
$ curl -X POST \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/file_categories/" \
  -d @file_category.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "example2",
    "slug": "example2",
    "description": "example2 description",
    "file_paths": [
        "/usr/example2"
    ],
    "exclude_paths": [
        "/home/you/exclude1",
        "/home/me/exclude2"
    ],
    "file_paths_queries": [],
    "access_monitoring": true,
    "created_at": "2023-01-31T14:09:46.079654",
    "updated_at": "2023-01-31T14:09:46.079664"
}

/api/osquery/file_categories/<int:pk>/

Get a FileCategory.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewFileCategory"
  • <int:pk>: the primary key of the FileCategory.

Example

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/file_categories/2/" \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "example2",
    "slug": "example2",
    "description": "example2 description",
    "file_paths": [
        "/usr/example2"
    ],
    "exclude_paths": [
        "/home/you/exclude1",
        "/home/me/exclude2"
    ],
    "file_paths_queries": [],
    "access_monitoring": true,
    "created_at": "2023-01-31T14:09:46.079654",
    "updated_at": "2023-01-31T14:09:46.079664"
}

Update a FileCategory.

  • method: PUT
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"updateFileCategory"
  • <int:pk>: the primary key of the FileCategory.

Example

file_category_update.json

{
    "name": "example2 updated",
    "description": "example2 description updated",
    "file_paths": [
        "/usr/bin/example2"
    ],
    "exclude_paths": [
        "/home/you/exclude1"
    ],
    "file_paths_queries": [],
    "access_monitoring": false
}
$ curl -X PUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/file_categories/2/" \
  -d @file_categories_update.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "example2 updated",
    "slug": "example2-updated",
    "description": "example2 description updated",
    "file_paths": [
        "/usr/bin/example2"
    ],
    "exclude_paths": [
        "/home/you/exclude1"
    ],
    "file_paths_queries": [],
    "access_monitoring": false,
    "created_at": "2023-01-31T11:48:53.014319",
    "updated_at": "2023-01-31T14:13:39.306239"
}

Delete a FileCategory.

  • method: DELETE
  • PBAC action: Osquery::Action::"deleteFileCategory"
  • <int:pk>: the primary key of the FileCategory.

Example

$ curl -X DELETE \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/file_categories/2/" 

Response (204 No Content)

/api/osquery/packs/

List all Packs.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewPack"
  • Optional filter parameter:
    • name: Name of the pack.
    • configuration_id: primary key of the configuration.

Examples:

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/packs/" \
  |python3 -m json.tool
$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/packs/?name=Default" \
  |python3 -m json.tool

Response:

[
    {
        "id": 1,
        "name": "Default",
        "slug": "default",
        "description": "",
        "discovery_queries": [],
        "shard": null,
        "event_routing_key": "",
        "created_at": "2023-01-13T07:06:51.000733",
        "updated_at": "2023-01-13T07:06:51.000743"
    }
]

Add a new Pack.

  • method: POST
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"createPack"
  • Required fields:
    • name: Name of the pack.

Example:

pack.json

{
	"name": "Example",
	"description": "description of the example",
	"discovery_queries": ["SELECT 1 FROM users WHERE username like 'www%';"],
	"shard": 50
}
$ curl -X POST \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/packs/" \
  -d @pack.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "Example",
    "slug": "example",
    "description": "description of the example",
    "discovery_queries": [
        "SELECT 1 FROM users WHERE username like 'www%';"
    ],
    "shard": 50,
    "event_routing_key": "",
    "created_at": "2023-02-02T07:30:42.133421",
    "updated_at": "2023-02-02T07:30:42.133434"
}

/api/osquery/packs/<int:pk>/

Get a Pack.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewPack"
  • <int:pk>: The primary key of the pack.

Example

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/packs/2/" \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "Example",
    "slug": "example",
    "description": "description of the example",
    "discovery_queries": [
        "SELECT 1 FROM users WHERE username like 'www%';"
    ],
    "shard": 50,
    "event_routing_key": "",
    "created_at": "2023-02-02T07:30:42.133421",
    "updated_at": "2023-02-02T07:30:42.133434"
}

Update a Pack.

  • method: PUT
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"updatePack"
  • <int:pk>: The primary key of the pack.
  • Required fields:
    • name: Name of the pack.

Example

pack_update.json

{
	"name": "Example Updated",
	"description": "description of the example updated",
	"discovery_queries": ["SELECT 1 FROM users WHERE username like 'www%';"],
	"shard": 30
}
$ curl -X PUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H "Content-Type: application/json" \
  "https://$ZTL_FQDN/api/osquery/packs/2/" \
  -d @pack_update.json \
  |python3 -m json.tool

Response:

{
    "id": 2,
    "name": "Example Updated",
    "slug": "example-updated",
    "description": "description of the example updated",
    "discovery_queries": [
        "SELECT 1 FROM users WHERE username like 'www%';"
    ],
    "shard": 30,
    "event_routing_key": "",
    "created_at": "2023-02-02T07:30:42.133421",
    "updated_at": "2023-02-02T07:32:55.258776"
}

Delete a Pack.

  • method: DELETE
  • PBAC action: Osquery::Action::"deletePack"
  • <int:pk>: The primary key of the pack.

Example

$ curl -X DELETE \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/packs/2/" 

Response (204 No Content)

/api/osquery/packs/<slug:slug>/

Create or update a standard Osquery pack.

  • method: PUT, DELETE

This endpoint is designed to create or update a standard Osquery pack.

Examples

pack.json

{
  "name": "First pack",
  "platform": "darwin",
  "queries": {
    "Leverage-A_1": {
      "query" : "select * from launchd where path like '%UserEvent.System.plist';",
      "interval" : "3600",
      "version": "1.4.5",
      "description" : "(http://www.intego.com/mac-security-blog/new-mac-trojan-discovered-related-to-syria/)",
      "value" : "Artifact used by this malware"
    },
    "Leverage-A_2": {
      "query" : "select * from file where path = '/Users/Shared/UserEvent.app';",
      "interval" : "3600",
      "version": "1.4.5",
      "description" : "(http://www.intego.com/mac-security-blog/new-mac-trojan-discovered-related-to-syria/)",
      "value" : "Artifact used by this malware"
    }
  }
}

PUT the pack.json file to Zentral:

$ curl -XPUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d @pack.json \
  "https://$ZTL_FQDN/api/osquery/packs/first-pack-slug/" \
  |python3 -m json.tool

You should get a response close to this one:

{
  "pack": {
    "pk": 1,
    "slug": "first-pack-slug"
  },
  "result": "created",
  "query_results": {
    "created": 2,
    "deleted": 0,
    "present": 0,
    "updated": 0
  }
}

If you PUT the same file again, you will get this answer:

{
  "pack": {
    "pk": 1,
    "slug": "first-pack-slug"
  },
  "result": "present",
  "query_results": {
    "created": 0,
    "deleted": 0,
    "present": 2,
    "updated": 0
  }
}

If you make a DELETE request on the same URL, the pack and all its rules will be deleted:

$ curl -XDELETE \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H 'Content-Type: application/json' \
  "https://$ZTL_FQDN/api/osquery/packs/first-pack-slug/" \
  |python3 -m json.tool

You should get a response close to this one:

{
  "pack": {
    "pk": 1,
    "slug": "first-pack-slug"
  },
  "result": "deleted",
  "query_results": {
    "created": 0,
    "deleted": 2,
    "present": 0,
    "updated": 0
  }
}

If the pack is in the osquery format (broken JSON), with line-wrapping characters, or comments, use the application/x-osquery-conf content type.

pack.conf (Real examples are available in the osquery repository.)

{
  // Do not use this query in production!!!
  "platform": "darwin",
  "queries": {
    "WireLurker": {
      "query" : "select * from launchd where \
        name = 'com.apple.periodic-dd-mm-yy.plist';",
      "interval" : "3600",
      "version": "1.4.5",
      "description" : "(https://github.com/PaloAltoNetworks-BD/WireLurkerDetector)",
      "value" : "Artifact used by this malware - 🔥"
      # 🧨
    }
  }
}
$ curl -XPUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H 'Content-Type: application/x-osquery-conf' \
  --data-binary @pack.conf \
  "https://$ZTL_FQDN/api/osquery/packs/second-pack-slug/" \
  |python3 -m json.tool

You should get a response close to this one:

{
  "pack": {
    "pk": 2,
    "slug": "second-pack-slug"
  },
  "result": "created",
  "query_results": {
    "created": 1,
    "deleted": 0,
    "present": 0,
    "updated": 0
  }
}

You can also use a YAML payload, with the application/yaml content type.

pack.yml

---
# Do not use this query in production!!!

platform: "darwin"
queries:
  WireLurker:
    query: >-
      select * from launchd where
      name = 'com.apple.periodic-dd-mm-yy.plist';
    interval: 3600
    version: 1.4.5
    description: (https://github.com/PaloAltoNetworks-BD/WireLurkerDetector)
    value: Artifact used by this malware - 🔥
$ curl -XPUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H 'Content-Type: application/yaml' \
  --data-binary @pack.yml \
  "https://$ZTL_FQDN/api/osquery/packs/second-pack-slug/" \
  |python3 -m json.tool

You should get a response close to this one:

{
  "pack": {
    "pk": 2,
    "slug": "third-pack-slug"
  },
  "result": "present",
  "query_results": {
    "created": 0,
    "deleted": 0,
    "present": 0,
    "updated": 1
  }
}

/api/osquery/queries/

List all queries.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewQuery"
  • Optional filter parameter:
    • name: name of the query.

Examples

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/queries/" \
  |python3 -m json.tool
$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/queries/?name=GetApps" \
  |python3 -m json.tool

Response:

[
    {
        "id": 1,
        "compliance_check_enabled": false,
        "compliance_check_id": null,
        "name": "GetApps",
        "sql": "SELECT * FROM apps;",
        "platforms": [],
        "scheduling": null,
        "minimum_osquery_version": null,
        "description": "Get list of Apps",
        "value": "",
        "version": 2,
        "created_at": "2023-01-13T07:10:12.571288",
        "updated_at": "2023-01-13T09:24:39.779067"
    }
]

Add a new query.

  • method: POST
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"createQuery"

NOTE: compliance_check_enabled: true only possible if sql query contains ztl_status.

Example

query.json

{
	"compliance_check_enabled": false,
	"name": "GetApps",
	"sql": "SELECT * FROM apps;",
    "scheduling": {
        "pack": 17,
        "interval": 120
    }
}
$ curl -X POST \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d @query.json \
  "https://$ZTL_FQDN/api/osquery/queries/" \
  |python3 -m json.tool

Response:

{
	"id": 1,
	"compliance_check_enabled": false,
	"compliance_check_id": null,
	"name": "GetApps",
	"sql": "SELECT * FROM apps;",
	"platforms": [],
    "scheduling": {
        "can_be_denylisted": true,
        "interval": 120,
        "log_removed_actions": true,
        "pack": 17,
        "shard": null,
        "snapshot_mode": false
    },
	"minimum_osquery_version": null,
	"description": "Get list of Apps",
	"value": "",
	"version": 1,
	"created_at": "2023-01-13T07:10:12.571288",
	"updated_at": "2023-01-13T09:24:39.779067"
}

/api/osquery/queries/<int:pk>/

Get a query.

  • method: GET
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"viewQuery"
  • <int:pk>: the primary key of the query.

Example

$ curl -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/queries/1/" \
  |python3 -m json.tool

Response:

{
	"id": 1,
	"compliance_check_enabled": false,
	"compliance_check_id": null,
	"name": "GetApps",
	"sql": "SELECT * FROM apps;",
	"platforms": [],
    "scheduling": null,
	"minimum_osquery_version": null,
	"description": "Get list of Apps",
	"value": "",
	"version": 1,
	"created_at": "2023-01-13T07:10:12.571288",
	"updated_at": "2023-01-13T09:24:39.779067"
}

Update a query.

  • method: PUT
  • Content-Type: application/json
  • PBAC action: Osquery::Action::"updateQuery"
  • <int:pk>: the primary key of the query.

Example

query_update.json

{
	"name": "GetUsers",
	"sql": "SELECT * FROM users;"
}
$ curl -X PUT \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -d @query_update.json \
  "https://$ZTL_FQDN/api/osquery/queries/1/" \
  |python3 -m json.tool

Response:

{
	"id": 1,
	"compliance_check_enabled": false,
	"compliance_check_id": null,
	"name": "GetUsers",
	"sql": "SELECT * FROM users;",
	"platforms": [],
    "scheduling": null,
	"minimum_osquery_version": null,
	"description": "Get list of Apps",
	"value": "",
	"version": 2,
	"created_at": "2023-01-14T07:10:12.571288",
	"updated_at": "2023-01-14T09:24:39.779067"
}

Delete a query.

  • method: DELETE
  • PBAC action: Osquery::Action::"deleteQuery"
  • <int:pk>: the primary key of the query.

Example

$ curl -X DELETE \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/queries/1/"

Response (204 No Content)

/api/osquery/runs/<int:pk>/results/export/

Trigger a Osquery run export task.

  • method: POST
  • PBAC actions:
    • Osquery::Action::"viewDistributedQueryResult"
  • optional parameter:
    • export_format: One of csv, ndjson or json. Defaults to csv.

Use this endpoint to trigger a Osquery run export task. The result of this task will be a file containing all the data collected during the run.

Example

curl -XPOST \
  -H "Authorization: Token $ZTL_API_TOKEN" \
  "https://$ZTL_FQDN/api/osquery/runs/1/results/export/" \
  |python3 -m json.tool

Response

{
  "task_id": "b1512b8d-1e17-4181-a1c3-93a7243fddd3",
  "task_result_url": "/api/task_result/b1512b8d-1e17-4181-a1c3-93a7243fddd3/"
}