Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Support filtering on host online state #49

Merged
merged 10 commits into from
May 5, 2023
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,22 +233,22 @@ Before jumping into an RTR shell, you may wish to see which hosts you would conn

As with the `shell` command, you must specify a profile (the name of a configuration you created using the `new` command above) if you have created more than one, and you can then optionally provide as many filters as you want using succesive `-f` switches. Some examples:

List all Windows hosts that are not within the `London` site, within the one Falcon instance configured earlier.
List all online Windows hosts that are not within the `London` site, within the one Falcon instance configured earlier.

```shell
falcon host_search -f OS=Windows -f Site__NOT=London
falcon host_search -f OS=Windows -f Site__NOT=London -o online
```

List all Windows hosts not within the `075e03f5e5c04d83b4831374e7dc01c3` Group, wihtin the `MyCompany` Falcon tenant.
List all Windows hosts not within the `075e03f5e5c04d83b4831374e7dc01c3` Group, within the `MyCompany` Falcon tenant.

```shell
falcon -p MyCompany host_search -f OS=Windows -f GroupID__NOT=075e03f5e5c04d83b4831374e7dc01c3
```

List all `MyOtherCompany` Windows servers or domain controllers not within an OU called `Protected`
List all offline `MyOtherCompany` Windows servers or domain controllers not within an OU called `Protected`

```shell
falcon -p MyOtherCompany host_search -f OS=Windows -f Role=Server,DC -f OU__NOT=Protected
falcon -p MyOtherCompany host_search -f OS=Windows -f Role=Server,DC -f OU__NOT=Protected -o offline
```

List all `MyOtherCompany` Windows Workstations that have checked in to Falcon within the past 30 minutes
Expand Down
14 changes: 13 additions & 1 deletion falcon_toolkit/hosts/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import click

from caracara.common.constants import OnlineState

from falcon_toolkit.common.cli import (
get_instance,
parse_cli_filters,
Expand All @@ -29,13 +31,23 @@
required=False,
help="Filter hosts to search based on standard Falcon filters",
)
@click.option(
'-o',
'--online_state',
'online_state',
type=click.Choice(OnlineState.VALUES),
multiple=False,
required=False,
help="Filter hosts by online state",
)
def cli_host_search(
ctx: click.Context,
filter_kv_strings: List[str],
online_state: str = None,
):
"""Implement the host_search CLI command."""
instance = get_instance(ctx)
client = instance.auth_backend.authenticate()
filters = parse_cli_filters(filter_kv_strings, client)

host_search_cmd(client, filters)
host_search_cmd(client, filters, online_state)
5 changes: 4 additions & 1 deletion falcon_toolkit/hosts/host_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"""
import logging

from typing import Optional

import click
import click_spinner

Expand All @@ -16,14 +18,15 @@
def host_search_cmd(
client: Client,
filters: FalconFilter,
online_state: Optional[str],
):
"""Search for hosts that match the provided filters."""
click.echo(click.style("Searching for hosts...", fg='magenta'))

fql = filters.get_fql()

with click_spinner.spinner():
host_data = client.hosts.describe_devices(filters=fql)
host_data = client.hosts.describe_devices(filters=fql, online_state=online_state)

logging.debug(host_data)
for aid in host_data.keys():
Expand Down
30 changes: 23 additions & 7 deletions falcon_toolkit/shell/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
MutuallyExclusiveOptionGroup,
)

from caracara.common.constants import OnlineState

from falcon_toolkit.common.cli import (
get_instance,
parse_cli_filters,
Expand Down Expand Up @@ -121,6 +123,10 @@ def cli_shell( # pylint: disable=too-many-arguments,too-many-locals
instance = get_instance(ctx)
client = instance.auth_backend.authenticate()

# Show online hosts only if queueing is false
online_state = None if queueing else OnlineState.ONLINE
online_string = "" if queueing else "online "

if filter_kv_strings:
click.echo(click.style(
"Connecting to all hosts that match the provided Falcon filters",
Expand All @@ -134,7 +140,7 @@ def cli_shell( # pylint: disable=too-many-arguments,too-many-locals
click.echo(filters)
logging.info(filters)

device_ids = client.hosts.get_device_ids(filters=filters)
device_ids = client.hosts.get_device_ids(filters=filters, online_state=online_state)
elif device_id_list:
click.echo(click.style(
"Connecting to the device IDs provided on the command line",
Expand All @@ -149,7 +155,10 @@ def cli_shell( # pylint: disable=too-many-arguments,too-many-locals
if device_id:
device_ids.add(device_id)

device_ids = list(device_ids)
device_ids = client.hosts.filter_by_online_state(
list(device_ids),
online_state=online_state,
)
elif device_id_file:
click.echo(click.style(
"Connecting to the device IDs listed in a file",
Expand All @@ -166,17 +175,24 @@ def cli_shell( # pylint: disable=too-many-arguments,too-many-locals
line = line.strip()
if line:
device_ids.add(line)
device_ids = list(device_ids)
device_ids = client.hosts.filter_by_online_state(
list(device_ids),
online_state=online_state,
)
else:
click.echo(click.style(
"WARNING: Connecting to all hosts in the Falcon instance",
f"WARNING: Connecting to all {online_string}hosts in the Falcon instance",
fg='yellow',
))
logging.info("Connecting to all hosts in the Falcon instance")
device_ids = client.hosts.get_device_ids()
logging.info("Connecting to all %shosts in the Falcon instance", online_string)
device_ids = client.hosts.get_device_ids(online_state=online_state)

if not device_ids:
click.echo(click.style("No devices match the provided filters", fg='red', bold=True))
click.echo(click.style(
f"No {online_string}devices match the provided filters",
fg='red',
bold=True,
))
sys.exit(1)

device_count = len(device_ids)
Expand Down
39 changes: 20 additions & 19 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
[tool.poetry]
name = "falcon-toolkit"
version = "3.1.2"
version = "3.2.0"
description = "Toolkit to interface with CrowdStrike Falcon via the API"
license = "MIT"
authors = [
"Chris Hammond <chris.hammond@crowdstrike.com>",
"Kira Noël <kira.noel@crowdstrike.com>",
]
repository = "http://github.com/CrowdStrike/Falcon-Toolkit"
readme = "README.md"
Expand All @@ -29,7 +30,7 @@ classifiers = [

[tool.poetry.dependencies]
python = "^3.9"
caracara = "^0.2.2"
caracara = "^0.3.0"
click = "^8.1.3"
click-option-group = "^0.5.3"
click-spinner = "^0.1.10"
Expand Down