Prisma Access Egress IP feed

Dynamically retrieve and add to allow list IPs Prisma Access uses to egress traffic to the internet and SaaS apps.

Data Enrichment & Threat Intelligence · Palo Alto Networks - Strata Cloud Manager · Feed

Details

IDPrisma Access Egress IP feed
ProviderPalo Alto Networks
CategoryData Enrichment & Threat Intelligence
From Version5.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud

README

Dynamically retrieve and allow IPs Prisma Access uses to egress traffic to the internet and SaaS apps.

Configure Prisma Access Egress IP feed in Cortex

Parameter Description Required
feed Fetch indicators False
URL URL True
api_key Prisma Access API Key from Panorama True
serviceType Service Type True
addrType Address Type True
location Location False
feedReputation Indicator Reputation False
feedReliability Source Reliability True
tlp_color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. More information about the protocol can be found at https://us-cert.cisa.gov/tlp False
feedExpirationPolicy   False
feedExpirationInterval   False
feedFetchInterval Feed Fetch Interval False
feedBypassExclusionList Bypass exclusion list False
insecure Trust any certificate (not secure) False
proxy Use system proxy settings False

Retrieve Egress API Key

To retrieve the Egress API key from Panorama - Click “Generate API Key” under Panorama->Cloud Services->Configuration->Service Setup. For more information, see Retrieve the IP Addresses for Prisma Access.

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

prisma-access-get-indicators


Gets indicators from the feed.

Base Command

prisma-access-get-indicators

Input
Argument Name Description Required
limit The maximum number of results to return. By default all IPs are returned. Optional
Context Output
Path Type Description
PrismaAccess.Egress.IP.Address string Prisma Access Egress IP address
PrismaAccess.Egress.IP.Zone string Prisma Access Egress IP zone
Command Example

!prisma-access-get-indicators limit=300

Configuration parameters

  • feed — Fetch indicators
  • URL — URL (required)
  • credentials
  • serviceType — Service Type
  • addrType — Address Type
  • location — Location
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability
  • tlp_color — Traffic Light Protocol Color
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedFetchInterval — Feed Fetch Interval
  • feedBypassExclusionList — Bypass exclusion list
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • feedTags — Tags
  • api_key — Prisma Access API Key (Deprecated)

Commands (1)

  • prisma-access-get-indicators

    Gets indicators from the feed.

from collections.abc import Callable
from typing import Any

import demistomock as demisto  # noqa: F401
import urllib3
from CommonServerPython import *  # noqa: F401

# disable insecure warnings
urllib3.disable_warnings()
INTEGRATION_NAME = "Prisma Access"


class Client(BaseClient):
    """
    Client to use in the Prisma Access Feed integration. Overrides BaseClient.
    Prisma Access V2 API: https://api.gpcloudservice.com/getPrismaAccessIP/v2
    https://docs.paloaltonetworks.com/prisma/prisma-access/prisma-access-panorama-admin/prisma-access-overview/retrieve-ip-addresses-for-prisma-access
    """

    def __init__(
        self,
        clientConfigs: list,
        api_key: str,
        insecure: bool = False,
        proxy: bool = False,
        tags: list | None = [],
        tlp_color: str | None = None,
    ):
        """
        Implements class for Prisma Access feed.
        :param clientConfigs: config data
        :param insecure: boolean, if *false* feed HTTPS server certificate is verified. Default: *false*
        :param proxy: boolean, if *false* feed HTTPS server certificate will not use proxies. Default: *false*
        :param tlp_color: Traffic Light Protocol color.
        """
        self._apiKey = api_key
        self.tags = [] if tags is None else tags
        self.tlp_color = tlp_color
        super().__init__(base_url=clientConfigs, verify=not insecure, proxy=proxy)

    def build_iterator(self) -> list:
        """Retrieves all entries from the feed.

        Returns:
            A list of objects, containing the indicators.
        """
        result = []
        for feed_obj in self._base_url:
            feed_url = feed_obj.get("FeedURL")
            postData = feed_obj.get("feedParams", {"serviceType": "all", "addrType": "all", "location": "all"})

            try:
                response = requests.post(
                    url=feed_url, verify=self._verify, headers={"header-api-key": self._apiKey}, data=json.dumps(postData)
                )
                response.raise_for_status()
                responseData = response.json()
                prismaStatus = responseData.get("status", "")
                if prismaStatus == "success":
                    zones = responseData.get("result", [])
                    for z in zones:
                        zoneName = z.get("zone", "")
                        addresses = z.get("addresses", [])
                        for addr in addresses:
                            indicator = {"zone": zoneName, "value": addr, "FeedURL": feed_url}
                            if postData["serviceType"] != "all":
                                indicator["serviceType"] = postData["serviceType"]
                            if postData["addrType"] != "all":
                                indicator["addrType"] = postData["addrType"]
                            result.append(indicator)
                else:
                    demisto.debug(str(prismaStatus))
                    raise Exception(
                        f"Non-success status returned from call to {INTEGRATION_NAME}.\n"
                        f"Raw response: " + json.dumps(responseData, indent=2)
                    )
            except requests.exceptions.SSLError as err:
                demisto.debug(str(err))
                raise Exception(f"SSL error in the API call to {INTEGRATION_NAME}.\nCheck your not secure parameter.\n\n{err}")
            except requests.ConnectionError as err:
                demisto.debug(str(err))
                raise Exception(
                    f"Connection error in the API call to {INTEGRATION_NAME}.\nCheck your Server URL parameter.\n\n{err}"
                )
            except requests.exceptions.HTTPError as err:
                demisto.debug(str(err))
                raise Exception(f"HTTP error in the API call to {INTEGRATION_NAME}:\n\n" + str(err))
            except ValueError as err:
                demisto.debug(str(err))
                raise ValueError(f"Could not parse returned data to Json. \n\nError message: {err}")
        return result


def test_module(client: Client, *_) -> tuple[str, dict[Any, Any], dict[Any, Any]]:
    """Builds the iterator to check that the feed is accessible.
    Args:
        client: Client object.

    Returns:
        Outputs.
    """
    client.build_iterator()
    return "ok", {}, {}


def fetch_indicators(client: Client, limit: int = -1) -> list[dict]:
    """Retrieves indicators from the feed

    Args:
        client: Client object with request
        limit: limit the results

    Returns:
        Indicators.
    """
    iterator = client.build_iterator()
    indicators = []
    if limit > 0:
        iterator = iterator[:limit]

    for item in iterator:
        value = item.get("value")
        raw_data = {
            "value": value,
            "serviceType": item.get("serviceType", ""),
            "addrType": item.get("addrType", ""),
            "zone": item.get("zone", ""),
        }
        indicator_mapping_fields = {}
        indicator_mapping_fields["geocountry"] = item.get("zone", "")
        indicator_mapping_fields["description"] = "IP from Prisma Access Egress API"
        indicator_mapping_fields["tags"] = client.tags
        if client.tlp_color:
            indicator_mapping_fields["trafficlightprotocol"] = client.tlp_color

        indicators.append(
            {
                "value": value,
                "type": FeedIndicatorType.IP,
                "rawJSON": raw_data,
                "fields": indicator_mapping_fields,
                "zone": item.get("zone", ""),
            }
        )

    return indicators


def get_indicators_command(client: Client, args: dict[str, str]) -> tuple[str, dict[Any, Any], dict[Any, Any]]:
    """Wrapper for retrieving indicators from the feed to the war-room.

    Args:
        client: Client object with request
        args: demisto.args()

    Returns:
        Outputs.
    """
    limit = int(demisto.args().get("limit")) if "limit" in demisto.args() else 0
    indicators = fetch_indicators(client, limit)
    human_readable = tableToMarkdown("Prisma Access Egress IPs:", indicators, headers=["zone", "value"], removeNull=True)

    outputs = {"PrismaAccess.Egress.IP": [{"Address": ip.get("value", ""), "Zone": ip.get("zone", "")} for ip in indicators]}

    retIndicators = {"raw_response": indicators}

    return human_readable, outputs, retIndicators


def fetch_indicators_command(client: Client) -> list[dict]:
    """Wrapper for fetching indicators from the feed to the Indicators tab.

    Args:
        client: Client object with request

    Returns:
        Indicators.
    """
    indicators = fetch_indicators(client)
    return indicators


def main():
    PRISMA_ACCESS_EGRESS_V2_URI = "getPrismaAccessIP/v2"
    """
    PARSE AND VALIDATE INTEGRATION PARAMS
    """
    params = demisto.params()
    param_api_key = (params.get("credentials") or {}).get("password") or params.get("api_key")
    if not param_api_key:
        raise Exception("API Key must be provided.")
    insecure = params.get("insecure", False)
    proxy = params.get("proxy")
    tags = argToList(params.get("feedTags"))
    tlp_color = params.get("tlp_color")
    baseURL = params.get("URL")
    if baseURL[-1] != "/":
        baseURL += "/"
    feedURL = baseURL + PRISMA_ACCESS_EGRESS_V2_URI

    feedParams = {
        "serviceType": demisto.params().get("serviceType", "all"),
        "addrType": demisto.params().get("addrType", "all"),
        "location": demisto.params().get("location", "all"),
    }
    clientConfigs = [{"FeedURL": feedURL, "feedParams": feedParams}]
    command = demisto.command()
    demisto.info(f"Command being called is {command}")

    try:
        client = Client(clientConfigs, param_api_key, insecure, proxy, tags, tlp_color)
        commands: dict[str, Callable[[Client, dict[str, str]], tuple[str, dict[Any, Any], dict[Any, Any]]]] = {
            "test-module": test_module,
            "prisma-access-get-indicators": get_indicators_command,
        }
        if command in commands:
            return_outputs(*commands[command](client, demisto.args()))

        elif command == "fetch-indicators":
            indicators = fetch_indicators_command(client)
            for iter_ in batch(indicators, batch_size=2000):
                demisto.createIndicators(iter_)

        else:
            raise NotImplementedError(f"Command {command} is not implemented.")

    except Exception as err:
        err_msg = f"Error in {INTEGRATION_NAME} Integration. [{err}]"
        return_error(err_msg)


if __name__ in ["__main__", "builtin", "builtins"]:
    main()