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

GCP-EXPANDR-3608: Determine potential offending firewall rules #32678

Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 find potential offending rules and not necessarily the rule that is matching traffic. (Available from Cortex XSOAR 6.8.0).
johnnywilkes marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import demistomock as demisto # noqa: F401
from CommonServerPython import * # noqa: F401
from typing import Dict, Any
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please execute the command demisto-sdk pre-commit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes in pre-commit changes

import traceback


''' STANDALONE FUNCTION '''


def test_range(port_range: str, port: str) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please change the name of the function to make it clearer? Usually, functions that perform checks and return boolean values start with is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""
Breaks a string port range (i.e. '20-25') into integers for comapirson
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.
"""
split_range = port_range.split('-')
if int(port) >= int(split_range[0]) and int(port) <= int(split_range[1]):
return True
return False


def test_match(port: str, protocol: str, rule: Dict, network_tags: list) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""
Breaks a string port range (i.e. '20-25') into integers for comapirson
Args:
port (str): port supplied in script args.
protocol (str): protocol supplied in script args.
rule (Dict): GCP firewall rule pulled from integration command.
network_tags (list): list of network tags (can be empty).

Returns:
bool: whether there was a match between the supplied port, protocol
and possible target tag combination and the GCP firewall rule.
"""
# 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.keys():
# 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.get('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.keys():
johnnywilkes marked this conversation as resolved.
Show resolved Hide resolved
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.keys():
johnnywilkes marked this conversation as resolved.
Show resolved Hide resolved
for port_entry in entry.get('ports'):
johnnywilkes marked this conversation as resolved.
Show resolved Hide resolved
if "-" in port_entry:
res = test_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:
list[CommandResults]: outputs, readable outputs and raw response for XSOAR.

"""

project_id = args.get("project_id")
johnnywilkes marked this conversation as resolved.
Show resolved Hide resolved
network_url = args.get("network_url")
port = args.get("port", "NO PORT")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the argument is required, the default value "NO PORT" is never used. You can add the defaultValue key to the YAML file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is mandatory as well so i just changed to look for dictionary key

protocol = args.get("protocol", "NO 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.
shmuel44 marked this conversation as resolved.
Show resolved Hide resolved
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"))
johnnywilkes marked this conversation as resolved.
Show resolved Hide resolved
]
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'):
johnnywilkes marked this conversation as resolved.
Show resolved Hide resolved
match = test_match(port, protocol, rule, network_tags)
if match:
final_match_list.append(rule['name'])
johnnywilkes marked this conversation as resolved.
Show resolved Hide resolved
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.86272
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)