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

Discover for AWS Usage Examples #117

Merged
merged 40 commits into from
Apr 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c488171
Update label_request.yml
jshcodes Apr 2, 2021
1d13967
Sample code: Sample Uploads API
jshcodes Apr 2, 2021
bcb75a4
Code sample: Sample Uploads API
jshcodes Apr 2, 2021
78efe40
Sample config.json file
jshcodes Apr 2, 2021
c74cc9c
Documentation updates
jshcodes Apr 2, 2021
6a35d1f
Documentation updates
jshcodes Apr 2, 2021
9292c75
Documentation updates
jshcodes Apr 2, 2021
35eab6d
Documentation updates
jshcodes Apr 2, 2021
b3a2ac8
Documentation updates
jshcodes Apr 2, 2021
f4d5497
Documentation updates
jshcodes Apr 2, 2021
d0dd5e7
Documentation updates
jshcodes Apr 2, 2021
19ac67c
Documentation updates
jshcodes Apr 2, 2021
086e476
Documentation updates
jshcodes Apr 2, 2021
2af4478
Documentation updates
jshcodes Apr 2, 2021
f750d7b
Uploading simple example of containing and uncontaining a host via API
Apr 2, 2021
bbd9017
Documentation updates
jshcodes Apr 2, 2021
72b237e
Documentation updates
jshcodes Apr 2, 2021
5d44b68
Documentation updates
jshcodes Apr 2, 2021
aaa7412
Update labeler.yml
jshcodes Apr 3, 2021
d9b8f02
Update labeler.yml
jshcodes Apr 3, 2021
660ffa0
Update wordlist.txt
jshcodes Apr 3, 2021
ea336d2
Linting
jshcodes Apr 3, 2021
6149b51
Update linting.yml
jshcodes Apr 3, 2021
301dc98
Sample Uploads sample adjustments
jshcodes Apr 3, 2021
e594b27
Merge branch 'ver_0.4.4' of https://github.com/CrowdStrike/falconpy i…
jshcodes Apr 3, 2021
cb1f683
Added samples to bandit analysis
jshcodes Apr 3, 2021
87d206f
Update bandit.yml
jshcodes Apr 3, 2021
668d35b
Merge branch 'ver_0.4.4' of https://github.com/CrowdStrike/falconpy i…
jshcodes Apr 3, 2021
51d8d03
Documentation updates
jshcodes Apr 3, 2021
ed4e3f0
Documentation updates
jshcodes Apr 3, 2021
b13be05
Documentation updates
jshcodes Apr 3, 2021
7778d56
Falcon Discover example
jshcodes Apr 4, 2021
c4dbb01
Update bandit.yml
jshcodes Apr 4, 2021
7019038
Adjustments
jshcodes Apr 4, 2021
fe10c3d
Update test_uber_api_complete.py
jshcodes Apr 4, 2021
c5e5574
Update test_uber_api_complete.py
jshcodes Apr 4, 2021
6069722
Update labeler.yml
jshcodes Apr 4, 2021
7cb1d16
Update test_uber_api_complete.py
jshcodes Apr 4, 2021
f42ada1
Merge remote-tracking branch 'upstream/main' into ver_0.4.4
jshcodes Apr 4, 2021
6485486
Comment update
jshcodes Apr 4, 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
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