Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
3a37fc2
Packaging updates, Licensing fixes and minor README cleanup (#7)
jshcodes Dec 7, 2020
0e6edea
Broken link fixes in README.md (#8)
jshcodes Dec 7, 2020
e1d5102
Restructure the README (#9)
shawndwells Dec 8, 2020
cd142b9
Create GitHub Issue Templates (#10)
shawndwells Dec 8, 2020
e92668a
Uber class custom headers and Uber class UploadSampleV2 fix (#13)
jshcodes Dec 8, 2020
41b4c51
Update PIP metadata for package status (#15)
jshcodes Dec 8, 2020
bd62c8c
Minor typo fix in README.md (#16)
jshcodes Dec 8, 2020
0b49d59
Initial web template (#19)
shawndwells Dec 9, 2020
9d5b2db
Set theme jekyll-theme-slate
shawndwells Dec 9, 2020
f1c7e32
removing web template (#20)
shawndwells Dec 9, 2020
ecb9c60
Update to reflect enabling of GitHub Discussions (#18)
shawndwells Dec 9, 2020
9cf008c
Non-JSON content response fix for Uber class. README.md updates. Init…
jshcodes Dec 18, 2020
68ac9a3
Begin test scripts (#24)
shawndwells Dec 18, 2020
08eb681
PyTest Workflow integration (#25)
jshcodes Dec 18, 2020
d7f7bc2
PyTest workflow update for working folder (#26)
jshcodes Dec 18, 2020
abf0000
Update README with github actions status badge (#27)
isimluk Dec 22, 2020
85d5782
Fix to reduce test flakiness in test_authorization.py (#28)
jshcodes Dec 24, 2020
9cbaed5
Updated service class unit tests (#31)
jshcodes Dec 26, 2020
c900816
Service class updates (#33)
jshcodes Dec 28, 2020
6433582
Coverage badge generation, additional unit tests (#41)
jshcodes Jan 11, 2021
41f134d
MSSP targeting token authentication fix (#48)
jshcodes Jan 19, 2021
72b737c
MSSP targeting credential dictionary change (Uber class only) (#50)
jshcodes Jan 19, 2021
b3a82cf
Configurable SSL verification options, Disable Python 3.5 testing (#54)
jshcodes Feb 12, 2021
9532aed
initial gitbook scaffolding (#61) (#62)
shawndwells Mar 1, 2021
9485189
Version 0.4 Update (#64)
jshcodes Mar 2, 2021
9e879ff
Unit test updates (#65)
jshcodes Mar 2, 2021
1d825a8
v0.4.1 - action_name, missing methods, CSPM class (#67)
jshcodes Mar 11, 2021
302c5c8
v0.4.2 Update - New endpoints, Missing UpdateDeviceTags method (#73)
jshcodes Mar 17, 2021
c9a738a
Unit test adjustments (Post v0.4.2 merge) (#75)
jshcodes Mar 17, 2021
7218d3d
Configure dependabot to focus on python (#77)
isimluk Mar 17, 2021
0ff17f5
Requirements.txt updates (#83)
jshcodes Mar 17, 2021
f768475
Bump urllib3 from 1.26.2 to 1.26.4 (#82)
dependabot[bot] Mar 17, 2021
807859d
Bump requests from 2.25.0 to 2.25.1 (#81)
dependabot[bot] Mar 17, 2021
7bc9b63
Bump ini from 1.3.5 to 1.3.8 (#84)
dependabot[bot] Mar 18, 2021
70e9c1f
Bump pug-code-gen from 2.0.2 to 2.0.3 (#85)
dependabot[bot] Mar 18, 2021
1e4df2c
Create CODE_OF_CONDUCT.md (#88)
jshcodes Mar 22, 2021
8c4e4c9
Adding SECURITY.md (#89)
jshcodes Mar 23, 2021
2bf4197
Create CONTRIBUTING.md (#92)
jshcodes Mar 25, 2021
2370db0
Create codeql-analysis.yml (#93)
jshcodes Mar 26, 2021
e7056b4
Update CODE_OF_CONDUCT.md (#94)
jshcodes Mar 26, 2021
b2c4638
Update linting.yml - Adding bandit to workflow (#95)
jshcodes Mar 26, 2021
fd6d975
Update CONTRIBUTING.md (#97)
jshcodes Mar 27, 2021
8289e5e
Update pull_request_template.md (#98)
jshcodes Mar 27, 2021
88205de
Update CONTRIBUTING.md (#99)
jshcodes Mar 28, 2021
f25421b
v0.4.3 Update - Sample_Uploads service class, Fixes, Documentation (#96)
jshcodes Mar 30, 2021
47bff9b
Update README.md (#101)
jshcodes Mar 30, 2021
15ec033
Bump y18n from 4.0.0 to 4.0.1 (#100)
dependabot[bot] Mar 30, 2021
857d6d3
Update README.md (#103)
jshcodes Mar 30, 2021
0e5181a
Documentation updates (#104)
jshcodes Mar 30, 2021
148af62
Update CONTRIBUTING.md (#105)
jshcodes Mar 31, 2021
929753f
Update README.md (#107)
jshcodes Apr 1, 2021
1c6cc02
Create python-publish.yml (#106)
jshcodes Apr 1, 2021
3c02bac
Update linting.yml (#108)
jshcodes Apr 1, 2021
0cd6976
Add spell checking workflow (#110)
jshcodes Apr 2, 2021
0b19fa7
Pull request automatic labelling (#111)
jshcodes Apr 2, 2021
dfd78af
Workflow updates (#112)
jshcodes Apr 2, 2021
1b79f0c
Create dev-deploy.yml (#109)
jshcodes Apr 2, 2021
69ed0bc
Usage Examples (#116)
jshcodes Apr 3, 2021
efef8ed
Discover for AWS Usage Examples (#117)
jshcodes Apr 4, 2021
b411eff
[DEPLOY] Version 0.4.4 Update - Sensor Download Service Class, Fixes …
jshcodes Apr 5, 2021
00afc8c
[DEPLOY] Version 0.4.5 update - Custom IOA, Quick Scan Service Classe…
jshcodes Apr 12, 2021
36312f4
Quick Scan / Sandbox usage sample (#127)
jshcodes Apr 13, 2021
5f0efc2
Quick Scan sample documentation (#128)
jshcodes Apr 13, 2021
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
Prev Previous commit
Next Next commit
Discover for AWS Usage Examples (CrowdStrike#117)
* Update label_request.yml

Labeller fails on forks, removing workflow from dev branches.

* Sample code: Sample Uploads API

* Code sample: Sample Uploads API

* Sample config.json file

* Documentation updates

* Documentation updates

* Documentation updates

* Documentation updates

* Documentation updates

* Documentation updates

* Documentation updates

* Documentation updates

* Documentation updates

* Documentation updates

* Uploading simple example of containing and uncontaining a host via API

* Documentation updates

* Documentation updates

* Documentation updates

* Update labeler.yml

Added code samples label tagging

* Update labeler.yml

* Update wordlist.txt

* Linting

* Update linting.yml

* Sample Uploads sample adjustments

* Added samples to bandit analysis

* Update bandit.yml

* Documentation updates

* Documentation updates

* Documentation updates

* Falcon Discover example

* Update bandit.yml

Bandit analysis of samples no longer stops the build

* Adjustments

* Update test_uber_api_complete.py

* Update test_uber_api_complete.py

* Update labeler.yml

* Update test_uber_api_complete.py

* Comment update

Co-authored-by: Shane Shellenbarger <soggysec@gmail.com>
  • Loading branch information
jshcodes and Shane Shellenbarger authored Apr 4, 2021
commit efef8ed4ab66e2ce999c987649d6f604229950f8
1 change: 1 addition & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ unit testing:

code samples:
- samples/*.py
- samples/discover_aws/*.py
- samples/real_time_response/*.py
- samples/sample_uploads/*.py
14 changes: 10 additions & 4 deletions samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,23 @@ _Coming Soon_
_Coming Soon_

### Falcon Discover
_Coming Soon_
| Service Class | Uber Class |
| :--- | :--- |
| [Register, delete, update and check accounts](discover_aws/manage_discover_accounts_service.py) | [Register, delete, update and check accounts](discover_aws/manage_discover_accounts_uber.py) |


### Hosts
_Coming Soon_

### Real Time Response
+ [Quarantine a host](real_time_response/quarantine_hosts.py)
| Service Class | Uber Class |
| :--- | :--- |
| [Quarantine a host](real_time_response/quarantine_hosts.py) | |

### Sample Uploads
+ [Upload, Retrieve and then Delete a file (Service Class)](sample_uploads/sample_uploads_service.py)
+ [Upload, Retrieve and then Delete a file (Uber Class)](sample_uploads/sample_uploads_uber.py)
| Service Class | Uber Class |
| :--- | :--- |
| [Upload, Retrieve and then Delete a file](sample_uploads/sample_uploads_service.py) | [Upload, Retrieve and then Delete a file](sample_uploads/sample_uploads_uber.py) |

## Suggestions
Got a suggestion for an example you'd like to see? Let us know by posting a message to our [discussion board](https://github.com/CrowdStrike/falconpy/discussions).
Expand Down
260 changes: 260 additions & 0 deletions samples/discover_aws/manage_discover_accounts_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
# ____ _ _ ____ _ ___ ______
# / ___| | ___ _ _ __| | / ___|___ _ __ _ __ ___ ___| |_ / \ \ / / ___|
# | | | |/ _ \| | | |/ _` | | | / _ \| '_ \| '_ \ / _ \/ __| __| / _ \ \ /\ / /\___ \
# | |___| | (_) | |_| | (_| | | |__| (_) | | | | | | | __/ (__| |_ / ___ \ V V / ___) |
# \____|_|\___/ \__,_|\__,_| \____\___/|_| |_|_| |_|\___|\___|\__| /_/ \_\_/\_/ |____/
#
# This is a modified version of the script falcon_discover_accounts, a troubleshooting script
# posted to our Cloud-AWS repository at https://github.com/CrowdStrike/Cloud-AWS.
#
# This solution demonstrates accepting user input to query the CrowdStrike Falcon Discover API
# to register, update and delete AWS accounts. An additional check function loops through all
# accounts registered, and returns configuration detail to assist with troubleshooting setup.
#
# This example leverages the Cloud Connect AWS Service Class and legacy authentication.
#
import argparse
import json
import sys
# Falcon SDK - Cloud_Connect_AWS and OAuth2 API service classes
from falconpy import cloud_connect_aws as FalconAWS
from falconpy import oauth2 as FalconAuth


# =============== FORMAT API PAYLOAD
def format_api_payload(rate_limit_reqs=0, rate_limit_time=0):
# Generates a properly formatted JSON payload for POST and PATCH requests
data = {
"resources": [
{
"cloudtrail_bucket_owner_id": cloudtrail_bucket_owner_id,
"cloudtrail_bucket_region": cloudtrail_bucket_region,
"external_id": external_id,
"iam_role_arn": iam_role_arn,
"id": local_account,
"rate_limit_reqs": rate_limit_reqs,
"rate_limit_time": rate_limit_time
}
]
}
return data


# =============== ACCOUNT VALUE
def account_value(id, val, accts):
# Returns the specified value for a specific account id within account_list
returned = False
for item in accts:
if item["id"] == id:
returned = item[val]
return returned


# =============== CHECK ACCOUNTS
def check_account():

# Retrieve the account list
account_list = falcon_discover.QueryAWSAccounts(parameters={"limit": int(query_limit)})["body"]["resources"]
# Log the results of the account query to a file if logging is enabled
if log_enabled:
with open('falcon-discover-accounts.json', 'w+') as f:
json.dump(account_list, f)
# Create a list of our account IDs out of account_list
id_items = []
for z in account_list:
id_items.append(z["id"])
q_max = 10 # VerifyAWSAccountAccess has a ID max count of 10
for index in range(0, len(id_items), q_max):
sub_acct_list = id_items[index:index + q_max]
temp_list = ",".join([a for a in sub_acct_list])
access_response = falcon_discover.VerifyAWSAccountAccess(ids=temp_list)
if access_response['status_code'] == 200:
# Loop through each ID we verified
for result in access_response["body"]["resources"]:
if result["successful"]:
# This account is correctly configured
print(f'Account {result["id"]} is ok!')
else:
# This account is incorrectly configured. We'll use our account_value function to
# retrieve configuration values from the account list we've already ingested.
account_values_to_check = {
'id': result["id"],
'iam_role_arn': account_value(result["id"], "iam_role_arn", account_list),
'external_id': account_value(result["id"], "external_id", account_list),
'cloudtrail_bucket_owner_id': account_value(result["id"], "cloudtrail_bucket_owner_id", account_list),
'cloudtrail_bucket_region': account_value(result["id"], "cloudtrail_bucket_region", account_list),
}
# Use the account_value function to retrieve the access_health branch,
# which contains our api failure reason.
try:
print('Account {} has a problem: {}'.format(result["id"],
account_value(result["id"],
"access_health",
account_list
)["api"]["reason"]
))
except Exception:
# The above call will produce an error if we're running
# check immediately after registering an account as
# the access_health branch hasn't been populated yet.
# Requery the API for the account_list when this happens.
account_list = falcon_discover.QueryAWSAccounts(
parameters={"limit": f"{str(query_limit)}"}
)["body"]["resources"]
print('Account {} has a problem: {}'.format(result["id"],
account_value(result["id"],
"access_health",
account_list
)["api"]["reason"]
))
# Output the account details to the user to assist with troubleshooting the account
print(f'Current settings {json.dumps(account_values_to_check, indent=4)}\n')
else:
try:
# An error has occurred
print("Got response error code {} message {}".format(access_response["status_code"],
access_response["body"]["errors"][0]["message"]
))
except Exception:
# Handle any egregious errors that break our return error payload
print("Got response error code {} message {}".format(access_response["status_code"], access_response["body"]))
return


# =============== REGISTER ACCOUNT
def register_account():
# Call the API to update the requested account.
register_response = falcon_discover.ProvisionAWSAccounts(parameters={}, body=format_api_payload())
if register_response["status_code"] == 201:
print("Successfully registered account.")
else:
print("Registration failed with response: {} {}".format(register_response["status_code"],
register_response["body"]["errors"][0]["message"]
))

return


# =============== UPDATE ACCOUNT
def update_account():
# Call the API to update the requested account.
update_response = falcon_discover.UpdateAWSAccounts(body=format_api_payload())
if update_response["status_code"] == 200:
print("Successfully updated account.")
else:
print("Update failed with response: {} {}".format(update_response["status_code"],
update_response["body"]["errors"][0]["message"]
))

return


# =============== DELETE ACCOUNT
def delete_account():
# Call the API to delete the requested account, multiple IDs can be deleted by passing in a comma-delimited list
delete_response = falcon_discover.DeleteAWSAccounts(ids=local_account)
if delete_response["status_code"] == 200:
print("Successfully deleted account.")
else:
print("Delete failed with response: {} {}".format(delete_response["status_code"],
delete_response["body"]["errors"][0]["message"]
))

return


# =============== MAIN

# Configure argument parsing
parser = argparse.ArgumentParser(description="Get Params to send notification to CRWD topic")
# Fully optional
parser.add_argument('-q', '--query_limit', help='The query limit used for check account commands', required=False)
parser.add_argument('-l', '--log_enabled', help='Save results to a file?', required=False, action="store_true")
# Optionally required
parser.add_argument('-r', '--cloudtrail_bucket_region', help='AWS Region where the S3 bucket is hosted',
required=False)
parser.add_argument('-o', '--cloudtrail_bucket_owner_id', help='Account where the S3 bucket is hosted',
required=False)
parser.add_argument('-a', '--local_account', help='This AWS Account', required=False)
parser.add_argument('-e', '--external_id', help='External ID used to assume role in account', required=False)
parser.add_argument('-i', '--iam_role_arn',
help='IAM AWS IAM Role ARN that grants access to resources for Crowdstrike', required=False)
# Always required
parser.add_argument('-c', '--command', help='Troubleshooting action to perform', required=True)
parser.add_argument("-f", "--falcon_client_id", help="Falcon Client ID", required=True)
parser.add_argument("-s", "--falcon_client_secret", help="Falcon Client Secret", required=True)
args = parser.parse_args()

# =============== SET GLOBALS
command = args.command
# Only execute our defined commands
if command.lower() in "check,update,register,delete":
if command.lower() in "update,register":
# All fields required for update and register
if (args.cloudtrail_bucket_owner_id is None or
args.cloudtrail_bucket_region is None or
args.local_account is None or
args.external_id is None or
args.iam_role_arn is None):
parser.error("The {} command requires the -r, -o, -a, -e, -i arguments to also be specified.".format(command))
else:
cloudtrail_bucket_region = args.cloudtrail_bucket_region
cloudtrail_bucket_owner_id = args.cloudtrail_bucket_owner_id
local_account = args.local_account
external_id = args.external_id
iam_role_arn = args.iam_role_arn
elif command.lower() in "delete":
# Delete only requires the local account ID
if args.local_account is None:
parser.error("The {} command requires the -l argument to also be specified.".format(command))
else:
local_account = args.local_account
else:
parser.error("The {} command is not recognized.".format(command))
# These globals exist for all requests
falcon_client_id = args.falcon_client_id
falcon_client_secret = args.falcon_client_secret
log_enabled = args.log_enabled
if args.query_limit is None:
query_limit = 100
else:
query_limit = args.query_limit

# =============== MAIN ROUTINE
# Authenticate using our provided falcon client_id and client_secret
try:
authorized = FalconAuth.OAuth2(creds={'client_id': falcon_client_id, 'client_secret': falcon_client_secret})
except Exception:
# We can't communicate with the endpoint, return a false token
authorized.token = lambda: False
# Try to retrieve a token from our authentication, returning false on failure
try:
token = authorized.token()["body"]["access_token"]
except Exception:
token = False

# Confirm the token was successfully retrieved
if token:
# Connect using our token and return an instance of the API gateway object
falcon_discover = FalconAWS.Cloud_Connect_AWS(access_token=token)
try:
# Execute the requested command
if command.lower() == "delete":
delete_account()
elif command.lower() == "register":
register_account()
elif command.lower() == "update":
update_account()
else:
check_account()
except Exception as e:
# Handle any previously unhandled errors
print("Command failed with error: {}.".format(str(e)))
# Discard our token before we exit
authorized.revoke(token)
else:
# Report that authentication failed and stop processing
print("Failed to retrieve authentication token.")

# Force clean exit
sys.exit(0)
Loading