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

added new arg for host regex, fix warnings, and errors #1163

Merged
merged 3 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion samples/hosts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,12 @@ This variation will retrieve a list of hosts that haven't checked in to CrowdStr
python3 stale_sensors.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -d 30 -t testtag
```

This variation leverages a regular expression to match the host "SDKDEMO3".

```shell
python3 stale_sensors.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET -d 30 -p "^SDK.*3$"
```

You can reverse the list sort with the `-r` or `--reverse` argument.

```shell
Expand All @@ -762,7 +768,8 @@ Command-line help is available via the `-h` argument.

```shell
% python3 stale_sensors.py -h
usage: stale_sensors.py [-h] -k CLIENT_ID -s CLIENT_SECRET [-m MSSP] [-g] [-d DAYS] [-r] [-x] [-t TAG]
usage: stale_sensors.py [-h] -k CLIENT_ID -s CLIENT_SECRET [-m MSSP] [-g] [-d DAYS] [-r] [-x] [-t TAG] [-c] [-o OUTPUT_FILE] [-q]
[-f {windows,mac,linux,k8s}] [-p HOSTFILTER]

CrowdStrike Unattended Stale Sensor Environment Detector.

Expand All @@ -786,6 +793,10 @@ results for the US-GOV-1 region, pass the '-g' argument.
- ray.heffer@crowdstrike.com; 03.29.22 - Added new argument for Grouping Tags (--grouping, -g)
- @morcef, jshcodes@CrowdStrike; 06.05.22 - More reasonable date calcs, Linting, Easier arg parsing
Easier base_url handling, renamed grouping_tag to tag
- jshcodes@Crowdstrike; 11.02.22 - Added CSV output options and cleaner date outputs.
- nmills@forbarr; 22.05.24 - Fixed deprecation warning on date function,
Added new arg to accept hostname pattern
Batch the call to hide_hosts to avoid API error

optional arguments:
-h, --help show this help message and exit
Expand All @@ -799,6 +810,14 @@ optional arguments:
-r, --reverse Reverse sort (defaults to ASC)
-x, --remove Remove hosts identified as stale
-t TAG, --tag TAG Falcon Grouping Tag name for the hosts
-c, --csv Export results to CSV
-o OUTPUT_FILE, --output_file OUTPUT_FILE
File to output CSV results to. Ignored when "-c" is not specified.
-q, --quotes Quote non-numeric fields in CSV output.
-f {windows,mac,linux,k8s}, --filter-by-os {windows,mac,linux,k8s}
OS filter (windows, macos, linux)
-p HOSTFILTER, --host-pattern HOSTFILTER
filter hostnames by regex
```

### Example source code
Expand Down
56 changes: 37 additions & 19 deletions samples/hosts/stale_sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@
- @morcef, jshcodes@CrowdStrike; 06.05.22 - More reasonable date calcs, Linting, Easier arg parsing
Easier base_url handling, renamed grouping_tag to tag
- jshcodes@Crowdstrike; 11.02.22 - Added CSV output options and cleaner date outputs.
- nmills@forbarr; 22.05.24 - Fixed deprecation warning on date function,
Added new arg to accept hostname pattern
Batch the call to hide_hosts to avoid API error
"""
import csv
import re
from argparse import ArgumentParser, RawTextHelpFormatter
from datetime import datetime, timedelta, timezone
from dateutil import parser as dparser
Expand All @@ -35,7 +39,6 @@
"Please execute `python3 -m pip install crowdstrike-falconpy` and try again."
) from no_falconpy


def parse_command_line() -> object:
"""Parse command-line arguments and return them back as an ArgumentParser object."""
parser = ArgumentParser(
Expand Down Expand Up @@ -74,7 +77,8 @@ def parse_command_line() -> object:
'-d',
'--days',
help='Number of days since a host was seen before it is considered stale',
required=False
required=False,
default=10
)
parser.add_argument(
'-r',
Expand Down Expand Up @@ -130,9 +134,15 @@ def parse_command_line() -> object:
required=False,
dest="osfilter"
)
parser.add_argument(
"-p", "--host-pattern",
help="filter hostnames by regex",
default=r".*",
required=False,
dest="hostfilter"
)
return parser.parse_args()


def connect_api(key: str, secret: str, base_url: str, child_cid: str = None) -> Hosts:
"""Connect to the API and return an instance of the Hosts Service Class."""
return Hosts(client_id=key, client_secret=secret, base_url=base_url, member_cid=child_cid)
Expand All @@ -156,6 +166,10 @@ def get_hosts(date_filter: str, tag_filter: str, os_filter: str) -> list:
if os_filter == "K8s":
os_filter = "K8S"
filter_string = f"{filter_string} + platform_name:'{os_filter}'"
x = falcon.query_devices_by_filter_scroll(
limit=5000,
filter=filter_string
)["body"]["resources"]
return falcon.query_devices_by_filter_scroll(
limit=5000,
filter=filter_string
Expand All @@ -164,7 +178,7 @@ def get_hosts(date_filter: str, tag_filter: str, os_filter: str) -> list:

def calc_stale_date(num_days: int) -> str:
"""Calculate the 'stale' datetime based upon the number of days provided by the user."""
today = datetime.utcnow()
today = datetime.now(timezone.utc)
return str(today - timedelta(days=num_days)).replace(" ", "T")


Expand Down Expand Up @@ -212,14 +226,15 @@ def hide_hosts(id_list: list) -> dict:
# List to hold our identified hosts
stale = []
# For each stale host identified
try:
for host in get_host_details(get_hosts(STALE_DATE, args.tag, args.osfilter)):
# Retrieve host detail
stale = parse_host_detail(host, stale)
except KeyError as api_error:
raise SystemExit(
"Unable to communicate with CrowdStrike API, check credentials and try again."
) from api_error
pattern = args.hostfilter
if args.hostfilter != r".*":
print(f"Pattern is: {re.escape(pattern)}")

for host in get_host_details(get_hosts(STALE_DATE, args.tag, args.osfilter)):
# Retrieve host detail
if 'hostname' in host:
if re.findall(pattern, host['hostname']):
stale = parse_host_detail(host, stale)

# If we produced stale host results
if stale:
Expand All @@ -245,12 +260,15 @@ def hide_hosts(id_list: list) -> dict:
else:
# Remove the hosts
host_list = [x[1] for x in stale]
remove_result = hide_hosts(host_list)
if remove_result["status_code"] == 202:
for deleted in remove_result["body"]["resources"]:
print(f"Removed host {deleted['id']}")
else:
for deleted in remove_result["body"]["errors"]:
print(f"[{deleted['code']}] {deleted['message']}")
batch_size = 50
for i in range(0, len(host_list), batch_size):
batch = host_list[i:i + batch_size]
remove_result = hide_hosts(batch)
if remove_result["status_code"] == 202:
for deleted in remove_result["body"]["resources"]:
print(f"Removed host {deleted['id']}")
else:
for deleted in remove_result["body"]["errors"]:
print(f"[{deleted['code']}] {deleted['message']}")
else:
print("No stale hosts identified for the range specified.")
Loading