Skip to content

Commit

Permalink
GCP-EXPANDR-3608: Determine potential offending firewall rules (#32795)
Browse files Browse the repository at this point in the history
* 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
5 people authored and maimorag committed Feb 22, 2024
1 parent 83aad17 commit 2a4946a
Show file tree
Hide file tree
Showing 11 changed files with 715 additions and 144 deletions.
421 changes: 287 additions & 134 deletions Packs/GCP-Enrichment-Remediation/Playbooks/GCP_-_Enrichment.yml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ This playbook does not use any sub-playbooks.

### Integrations

* Google Cloud Compute
* GCP-IAM
* Google Cloud Compute

### Scripts

GCPProjectHierarchy
* GCPOffendingFirewallRule
* Set
* GCPProjectHierarchy

### Commands

* gcp-compute-aggregated-list-instances-by-ip
* gcp-compute-list-firewall
* gcp-iam-project-iam-policy-get
* gcp-iam-tagbindings-list
* gcp-iam-project-iam-policy-get
* gcp-compute-aggregated-list-instances-by-ip
* gcp-compute-get-instance

## Playbook Inputs

Expand All @@ -31,6 +33,8 @@ GCPProjectHierarchy
| **Name** | **Description** | **Default Value** | **Required** |
| --- | --- | --- | --- |
| GcpIP | GCP IP in alert | alert.remoteip | Required |
| port | Port to match traffic on for firewall rules. | ${alert.remoteport} | Optional |
| protocol | Protocol to match traffic on for firewall rules. | ${alert.protocol} | Optional |

## Playbook Outputs

Expand All @@ -39,13 +43,13 @@ GCPProjectHierarchy
| **Path** | **Description** | **Type** |
| --- | --- | --- |
| GoogleCloudCompute.Instances | GCP VM Instances information. | unknown |
| GoogleCloudCompute.Firewalls | GCP Firewall information | unknown |
| GCPIAM.Policy | GCP IAM information | unknown |
| GCPIAM.TagBindings | Project/Folder/Organization level tags. | unknown |
| GCPHierarchy | GCP project hierarchy information. | unknown |
| GCPHierarchy | GCP project hierarchy information. | unknown |
| GCPOffendingFirewallRule | One or more potential offending firewall rules in GCP based on port, protocol and possibly target tags \(network tags\). | unknown |

## Playbook Image

---

![GCP - Enrichment](../doc_files/GCP_-_Enrichment.png)
![GCP - Enrichment](../doc_files/GCP_-_Enrichment.png)
8 changes: 7 additions & 1 deletion Packs/GCP-Enrichment-Remediation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ This content pack includes the following playbooks:

Automation to determine GCP project hierarchy by looking up parent objects until the organization level is reached.

![GCPProjectHierarchy](https://raw.githubusercontent.com/demisto/content/7065e08ec9738db1ea3e2bc5d78ac643931f46d1/Packs/GCP-Enrichment-Remediation/doc_files/GCPProjecHierarchy.png)
![GCPProjectHierarchy](https://raw.githubusercontent.com/demisto/content/master/Packs/GCP-Enrichment-Remediation/doc_files/GCPProjecHierarchy.png)

#### GCPOffendingFirewallRule

Automation to determine potential offending firewall rules in GCP based on port, protocol and possibly target tags (network tags).

![GCPOffendingFirewallRule](https://raw.githubusercontent.com/demisto/content/2eabf20887e5664294168fb1fbd9d1bbdb916a35/Packs/GCP-Enrichment-Remediation/doc_files/GCP_-_Enrichment.png)
15 changes: 15 additions & 0 deletions Packs/GCP-Enrichment-Remediation/ReleaseNotes/1_1_16.md
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).
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()
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)

0 comments on commit 2a4946a

Please sign in to comment.