-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GCP-EXPANDR-3608: Determine potential offending firewall rules (#32795)
* GCP-EXPANDR-3608: Determine potential offending firewall rules (#32678) * dl script * GCP-Enrich play * formatting and start unittest * more tests and RN * add screenshot * update pack README * increase test coverage * Apply suggestions from code review Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Shmuel Kroizer <69422117+shmuel44@users.noreply.github.com> * update function names * pre-commit changes * Update Packs/GCP-Enrichment-Remediation/Scripts/GCPOffendingFirewallRule/GCPOffendingFirewallRule.py Co-authored-by: Shmuel Kroizer <69422117+shmuel44@users.noreply.github.com> --------- Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com> Co-authored-by: Shmuel Kroizer <69422117+shmuel44@users.noreply.github.com> * update docker * some improve * revert * Update Packs/GCP-Enrichment-Remediation/Scripts/GCPOffendingFirewallRule/GCPOffendingFirewallRule.py --------- Co-authored-by: johnnywilkes <32227961+johnnywilkes@users.noreply.github.com> Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com> Co-authored-by: Shmuel Kroizer <69422117+shmuel44@users.noreply.github.com> Co-authored-by: Shmuel Kroizer <skroizer@paloaltonetworks.com>
- Loading branch information
1 parent
91a9fda
commit 8098ed3
Showing
11 changed files
with
715 additions
and
144 deletions.
There are no files selected for viewing
421 changes: 287 additions & 134 deletions
421
Packs/GCP-Enrichment-Remediation/Playbooks/GCP_-_Enrichment.yml
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
|
||
#### Playbooks | ||
|
||
##### GCP - Enrichment | ||
|
||
Updated the playbook to include the new **GCPOffendingFirewallRule** script. | ||
|
||
#### Scripts | ||
|
||
##### New: GCPOffendingFirewallRule | ||
|
||
New: Determine potential offending firewall rules in GCP based on port, protocol and possibly target tags (network tags). | ||
|
||
Considerations: | ||
At this time this automation only finds potential offending rules and not necessarily the rule that is matching traffic. (Available from Cortex XSOAR 6.8.0). |
138 changes: 138 additions & 0 deletions
138
...s/GCP-Enrichment-Remediation/Scripts/GCPOffendingFirewallRule/GCPOffendingFirewallRule.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import demistomock as demisto # noqa: F401 | ||
from CommonServerPython import * # noqa: F401 | ||
from typing import Any | ||
import traceback | ||
|
||
|
||
''' STANDALONE FUNCTION ''' | ||
|
||
|
||
def is_port_in_range(port_range: str, port: str) -> bool: | ||
""" | ||
Breaks a string port range (i.e. '20-25') into integers for comparison | ||
Args: | ||
port_range (str): string based port range from the GCP firewall rule. | ||
port (str): port supplied in script args. | ||
Returns: | ||
bool: whether there was a match between the supplied port in args and the supplied range. | ||
""" | ||
start, end = port_range.split('-') | ||
return int(start) <= int(port) <= int(end) | ||
|
||
|
||
def is_there_traffic_match(port: str, protocol: str, rule: dict, network_tags: list) -> bool: | ||
""" | ||
Determines if there's a match between the supplied port, protocol, and possible target tag combination | ||
and the GCP firewall rule. | ||
The function checks: | ||
if the rule is an ingress rule, | ||
if the source is from the internet (0.0.0.0/0), | ||
if the rule is enabled, and if it's an allow rule. | ||
It also checks if the target tags are relevant (if they show up in keys or tag match), and if the protocol and ports match | ||
the supplied protocol and port. | ||
Args: | ||
port (str): The port supplied in script args. | ||
protocol (str): The protocol supplied in script args. This should be a string representing a | ||
network protocol (e.g., 'tcp', 'udp', 'icmp'). | ||
rule (dict): A dictionary representing a GCP firewall rule. This is pulled from the integration command. | ||
network_tags (list): A list of network tags. This can be empty. | ||
Returns: | ||
bool: True if there's a match between the supplied port, protocol, and possible target tag combination | ||
and the GCP firewall rule. False otherwise. | ||
""" | ||
# Match rule needs to be direction ingress, source from internet (0.0.0.0/0), enabled and an allow rule. | ||
if ( | ||
rule.get('direction') == 'INGRESS' | ||
and '0.0.0.0/0' in rule.get('sourceRanges', []) | ||
and rule.get('disabled') is False | ||
and 'allowed' in rule | ||
): | ||
# Test if targetTags are relevant or not (if show up in keys or tag match) | ||
target_tags_verdict = ('targetTags' not in rule.keys() or len(set(rule.get('targetTags', [])) & set(network_tags)) > 0) | ||
for entry in rule['allowed']: | ||
# Match is all protocol AND either no target tags OR target tags match | ||
if entry.get('IPProtocol') == 'all' and target_tags_verdict: | ||
return True | ||
# Complicated because just {'IPProtocol': 'udp'} means all udp ports | ||
# therefore if protocol match but no ports, this is a match | ||
elif entry.get('IPProtocol') == protocol.lower() and 'ports' not in entry: | ||
return True | ||
# Else need to go through all ports to see if range or not | ||
elif entry.get('IPProtocol') == protocol.lower() and 'ports' in entry: | ||
for port_entry in entry.get('ports', []): | ||
if "-" in port_entry: | ||
res = is_port_in_range(port_entry, port) | ||
if res and target_tags_verdict: | ||
return True | ||
else: | ||
if port == port_entry and target_tags_verdict: | ||
return True | ||
return False | ||
|
||
|
||
''' COMMAND FUNCTION ''' | ||
|
||
|
||
def gcp_offending_firewall_rule(args: dict[str, Any]) -> CommandResults: | ||
""" | ||
Determine potential offending firewall rules in GCP based on port, protocol and possibly target tags (network tags). | ||
Args: | ||
args (dict): Command arguments from XSOAR. | ||
Returns: | ||
CommandResults: outputs, readable outputs and raw response for XSOAR. | ||
""" | ||
|
||
project_id = args["project_id"] | ||
network_url = args["network_url"] | ||
port = args["port"] | ||
protocol = args["protocol"] | ||
network_tags = args.get("network_tags", []) | ||
|
||
# Using `demisto.executeCommand` instead of `execute_command` because for | ||
# multiple integration instances we can expect one too error out. | ||
network_url_filter = f"network=\"{network_url}\"" | ||
fw_rules = demisto.executeCommand( | ||
"gcp-compute-list-firewall", {"project_id": project_id, "filters": network_url_filter} | ||
) | ||
fw_rules_returned = [ | ||
instance | ||
for instance in fw_rules | ||
if (not isError(instance) and instance.get("Contents", {}).get("id")) | ||
] | ||
if not fw_rules_returned: | ||
return CommandResults(readable_output="Could not find specified firewall info.") | ||
final_match_list = [] | ||
for rule in fw_rules_returned[0].get('Contents', {}).get('items', []): | ||
if is_there_traffic_match(port, protocol, rule, network_tags): | ||
final_match_list.append(rule['name']) | ||
if not final_match_list: | ||
return CommandResults(readable_output="Could not find any potential offending firewall rules.") | ||
|
||
return CommandResults( | ||
outputs={'GCPOffendingFirewallRule': final_match_list}, | ||
raw_response={'GCPOffendingFirewallRule': final_match_list}, | ||
readable_output=f"Potential Offending GCP Firewall Rule(s) Found: {final_match_list}", | ||
) | ||
|
||
|
||
''' MAIN FUNCTION ''' | ||
|
||
|
||
def main(): | ||
try: | ||
return_results(gcp_offending_firewall_rule(demisto.args())) | ||
except Exception as ex: | ||
demisto.error(traceback.format_exc()) # print the traceback | ||
return_error(f'Failed to execute GCPOffendingFirewallRule. Error: {str(ex)}') | ||
|
||
|
||
''' ENTRY POINT ''' | ||
|
||
|
||
if __name__ in ('__main__', '__builtin__', 'builtins'): | ||
main() |
41 changes: 41 additions & 0 deletions
41
.../GCP-Enrichment-Remediation/Scripts/GCPOffendingFirewallRule/GCPOffendingFirewallRule.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
args: | ||
- description: The project to look up firewall rules in. The project ID instead of the project number. No need to supply `projects/` before the ID (i.e., use `project-name` instead of `projects/project-name` or `projects/111111111111`). | ||
name: project_id | ||
required: true | ||
- description: The url of the network objects to lookup firewall rules in. This will be the url of the network and not just the name (i.e. https://www.googleapis.com/compute/v1/projects/<project_name>/global/networks/<network_name>). | ||
name: network_url | ||
required: true | ||
- description: Port to match traffic on for firewall rules. | ||
name: port | ||
required: true | ||
- description: Protocol to match traffic on for firewall rules. | ||
name: protocol | ||
required: true | ||
- description: Network tags on GCP VM instance to match rules based on target tag (optional). | ||
isArray: true | ||
name: network_tags | ||
comment: |- | ||
Determine potential offending firewall rules in GCP based on port, protocol and possibly target tags (network tags). | ||
Considerations: | ||
- At this time this automation only find potential offending rules and not necessarily the rule that is matching traffic. | ||
commonfields: | ||
id: GCPOffendingFirewallRule | ||
version: -1 | ||
dockerimage: demisto/python3:3.10.13.87159 | ||
enabled: true | ||
engineinfo: {} | ||
name: GCPOffendingFirewallRule | ||
outputs: | ||
- contextPath: GCPOffendingFirewallRule | ||
description: One or more potential offending firewall rules in GCP based on port, protocol and possibly target tags (network tags). | ||
runas: DBotWeakRole | ||
runonce: false | ||
script: '' | ||
scripttarget: 0 | ||
subtype: python3 | ||
tags: [] | ||
type: python | ||
fromversion: 6.8.0 | ||
tests: | ||
- No tests (auto formatted) |
Oops, something went wrong.