OpenAI IP Allowlist Configuration Changes


Description

Detects changes to OpenAI IP allowlist configurations including creation, updates, deletion, activation, and deactivation. IP allowlists restrict API and console access to specific IP addresses or CIDR ranges, providing network-level access control. Changes to IP allowlists can indicate: - Security control removal (deletion/deactivation) - CRITICAL - Addition of dangerous IPs like 0.0.0.0 (updates) - HIGH - Configuration changes for visibility (creation/activation) - MEDIUM Unauthorized modifications can expose the organization to unauthorized access, bypass network security controls, or indicate preparation for malicious activity.

Query · python

IP_ALLOWLIST_EVENTS = [
    "ip_allowlist.created",
    "ip_allowlist.updated",
    "ip_allowlist.deleted",
    "ip_allowlist.config.activated",
    "ip_allowlist.config.deactivated",
]


def rule(event):
    return event.get("type") in IP_ALLOWLIST_EVENTS


def title(event):
    event_type = event.get("type", "")
    email = event.deep_get("actor", "session", "user", "email", default="<UNKNOWN_USER>")

    action_map = {
        "ip_allowlist.created": "Created",
        "ip_allowlist.updated": "Updated",
        "ip_allowlist.deleted": "Deleted",
        "ip_allowlist.config.activated": "Activated",
        "ip_allowlist.config.deactivated": "Deactivated",
    }

    action = action_map.get(event_type, "Modified")
    return f"OpenAI IP Allowlist {action} by [{email}]"


def severity(event):
    event_type = event.get("type")

    if event_type in ["ip_allowlist.deleted", "ip_allowlist.config.deactivated"]:
        return "CRITICAL"

    if event_type == "ip_allowlist.updated":
        return "HIGH"

    if event_type in ["ip_allowlist.created", "ip_allowlist.config.activated"]:
        return "LOW"

    return "DEFAULT"


def alert_context(event):
    event_type = event.get("type")

    context = {
        "event_type": event_type if event_type else "<UNKNOWN_EVENT_TYPE>",
        "event_id": event.get("id", "<UNKNOWN_EVENT_ID>"),
        "actor_email": event.deep_get(
            "actor", "session", "user", "email", default="<UNKNOWN_ACTOR_EMAIL>"
        ),
        "actor_id": event.deep_get("actor", "session", "user", "id", default="<UNKNOWN_ACTOR_ID>"),
        "source_ip": event.deep_get(
            "actor", "session", "ip_address", default="<UNKNOWN_SOURCE_IP>"
        ),
        "user_agent": event.deep_get(
            "actor", "session", "user_agent", default="<UNKNOWN_USER_AGENT>"
        ),
        "ip_details": event.deep_get("actor", "session", "ip_address_details", default={}),
    }

    # Add event-specific fields
    if event_type == "ip_allowlist.created":
        context["allowlist_id"] = event.deep_get(
            "ip_allowlist_created", "id", default="<UNKNOWN_ALLOWLIST_ID>"
        )
        context["allowlist_name"] = event.deep_get(
            "ip_allowlist_created", "name", default="<UNKNOWN_ALLOWLIST_NAME>"
        )
        context["allowed_ips"] = event.deep_get("ip_allowlist_created", "allowed_ips", default=[])
    elif event_type == "ip_allowlist.updated":
        context["allowlist_id"] = event.deep_get(
            "ip_allowlist_updated", "id", default="<UNKNOWN_ALLOWLIST_ID>"
        )
        context["allowed_ips"] = event.deep_get("ip_allowlist_updated", "allowed_ips", default=[])
    elif event_type == "ip_allowlist.deleted":
        context["allowlist_id"] = event.deep_get(
            "ip_allowlist_deleted", "id", default="<UNKNOWN_ALLOWLIST_ID>"
        )
        context["allowlist_name"] = event.deep_get(
            "ip_allowlist_deleted", "name", default="<UNKNOWN_ALLOWLIST_NAME>"
        )
        context["allowed_ips"] = event.deep_get("ip_allowlist_deleted", "allowed_ips", default=[])
    elif event_type == "ip_allowlist.config.activated":
        context["activated_configs"] = event.deep_get(
            "ip_allowlist_config_activated", "configs", default=[]
        )
    elif event_type == "ip_allowlist.config.deactivated":
        context["deactivated_configs"] = event.deep_get(
            "ip_allowlist_config_deactivated", "configs", default=[]
        )

    return context

Analyst notes

  1. Verify the IP allowlist change was authorized and aligns with approved network security policies. Review the specific IPs added, removed, or modified, and confirm they match expected organizational infrastructure.
  2. For deletions or deactivations, immediately assess if the organization is now exposed to unauthorized access. For updates, check if dangerous IPs like 0.0.0.0 (allow all), unknown ranges, or suspicious geolocations were added.
  3. If the change is unauthorized or suspicious, immediately revert the configuration, review all access from non-allowlisted IPs since the change, audit role assignments and API key usage, and escalate for security review.
Raw source OpenAI IP Allowlist Configuration Changes · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: rule
Description: |
  Detects changes to OpenAI IP allowlist configurations including creation, updates,
  deletion, activation, and deactivation.
  IP allowlists restrict API and console access to specific IP addresses or CIDR ranges,
  providing network-level access control. Changes to IP allowlists can indicate:
  - Security control removal (deletion/deactivation) - CRITICAL
  - Addition of dangerous IPs like 0.0.0.0 (updates) - HIGH
  - Configuration changes for visibility (creation/activation) - MEDIUM
  Unauthorized modifications can expose the organization to unauthorized access,
  bypass network security controls, or indicate preparation for malicious activity.
DisplayName: "OpenAI IP Allowlist Configuration Changes"
Enabled: true
Filename: openai_ip_allowlist_changes.py
Reference: https://platform.openai.com/docs/api-reference/audit-logs
Runbook: |
  1. Verify the IP allowlist change was authorized and aligns with approved network security policies. Review the specific IPs added, removed, or modified, and confirm they match expected organizational infrastructure.
  2. For deletions or deactivations, immediately assess if the organization is now exposed to unauthorized access. For updates, check if dangerous IPs like 0.0.0.0 (allow all), unknown ranges, or suspicious geolocations were added.
  3. If the change is unauthorized or suspicious, immediately revert the configuration, review all access from non-allowlisted IPs since the change, audit role assignments and API key usage, and escalate for security review.
Reports:
  MITRE ATT&CK:
    - TA0005:T1562.001  # Impair Defenses: Disable or Modify Tools
    - TA0005:T1562.007  # Impair Defenses: Disable or Modify Cloud Firewall
Severity: Medium
DedupPeriodMinutes: 60
Threshold: 1
LogTypes:
  - OpenAI.Audit
RuleID: "OpenAI.IP.Allowlist.Changes"
Tests:
  - Name: "IP allowlist deleted - Alert CRITICAL"
    ExpectedResult: true
    Log:
      id: "audit_log-ip001"
      type: "ip_allowlist.deleted"
      effective_at: 1702857600
      object: "organization.audit_log"
      actor:
        type: "session"
        session:
          user:
            id: "user-admin123"
            email: "admin@company.com"
          ip_address: "203.0.113.100"
          user_agent: "Mozilla/5.0"
      ip_allowlist_deleted:
        id: "ipallow_org-abc123"
        name: "Corporate VPN"
        allowed_ips:
          - "203.0.113.0/24"
          - "198.51.100.0/24"
  - Name: "IP allowlist deactivated - Alert CRITICAL"
    ExpectedResult: true
    Log:
      id: "audit_log-ip002"
      type: "ip_allowlist.config.deactivated"
      effective_at: 1702857600
      object: "organization.audit_log"
      actor:
        type: "session"
        session:
          user:
            id: "user-suspicious001"
            email: "contractor@external.com"
          ip_address: "192.0.2.100"
          user_agent: "curl/7.68.0"
      ip_allowlist_config_deactivated:
        configs:
          - id: "ipallow_org-abc123"
            name: "Corporate VPN"
  - Name: "IP allowlist updated with suspicious IP - Alert HIGH"
    ExpectedResult: true
    Log:
      id: "audit_log-ip003"
      type: "ip_allowlist.updated"
      effective_at: 1702857600
      object: "organization.audit_log"
      actor:
        type: "session"
        session:
          user:
            id: "user-admin123"
            email: "admin@company.com"
          ip_address: "203.0.113.100"
          user_agent: "Mozilla/5.0"
      ip_allowlist_updated:
        id: "ipallow_org-abc123"
        allowed_ips:
          - "0.0.0.0"
  - Name: "IP allowlist created - Alert MEDIUM"
    ExpectedResult: true
    Log:
      id: "audit_log-ip004"
      type: "ip_allowlist.created"
      effective_at: 1702857600
      object: "organization.audit_log"
      actor:
        type: "session"
        session:
          user:
            id: "user-admin123"
            email: "admin@company.com"
          ip_address: "203.0.113.100"
          user_agent: "Mozilla/5.0"
      ip_allowlist_created:
        id: "ipallow_org-new123"
        name: "New Office Network"
        allowed_ips:
          - "198.51.100.0/24"
  - Name: "IP allowlist activated - Alert MEDIUM"
    ExpectedResult: true
    Log:
      id: "audit_log-ip005"
      type: "ip_allowlist.config.activated"
      effective_at: 1702857600
      object: "organization.audit_log"
      actor:
        type: "session"
        session:
          user:
            id: "user-admin123"
            email: "admin@company.com"
          ip_address: "203.0.113.100"
          user_agent: "Mozilla/5.0"
      ip_allowlist_config_activated:
        configs:
          - id: "ipallow_org-abc123"
            name: "Corporate VPN"
  - Name: "Unrelated event - No Alert"
    ExpectedResult: false
    Log:
      id: "audit_log-other001"
      type: "login.succeeded"
      effective_at: 1702857600
      object: "organization.audit_log"
      actor:
        type: "session"
        session:
          user:
            id: "user-normal001"
            email: "user@company.com"
          ip_address: "203.0.113.200"
          user_agent: "Mozilla/5.0"


# ------ paired body: openai_ip_allowlist_changes.py ------

IP_ALLOWLIST_EVENTS = [
    "ip_allowlist.created",
    "ip_allowlist.updated",
    "ip_allowlist.deleted",
    "ip_allowlist.config.activated",
    "ip_allowlist.config.deactivated",
]


def rule(event):
    return event.get("type") in IP_ALLOWLIST_EVENTS


def title(event):
    event_type = event.get("type", "")
    email = event.deep_get("actor", "session", "user", "email", default="<UNKNOWN_USER>")

    action_map = {
        "ip_allowlist.created": "Created",
        "ip_allowlist.updated": "Updated",
        "ip_allowlist.deleted": "Deleted",
        "ip_allowlist.config.activated": "Activated",
        "ip_allowlist.config.deactivated": "Deactivated",
    }

    action = action_map.get(event_type, "Modified")
    return f"OpenAI IP Allowlist {action} by [{email}]"


def severity(event):
    event_type = event.get("type")

    if event_type in ["ip_allowlist.deleted", "ip_allowlist.config.deactivated"]:
        return "CRITICAL"

    if event_type == "ip_allowlist.updated":
        return "HIGH"

    if event_type in ["ip_allowlist.created", "ip_allowlist.config.activated"]:
        return "LOW"

    return "DEFAULT"


def alert_context(event):
    event_type = event.get("type")

    context = {
        "event_type": event_type if event_type else "<UNKNOWN_EVENT_TYPE>",
        "event_id": event.get("id", "<UNKNOWN_EVENT_ID>"),
        "actor_email": event.deep_get(
            "actor", "session", "user", "email", default="<UNKNOWN_ACTOR_EMAIL>"
        ),
        "actor_id": event.deep_get("actor", "session", "user", "id", default="<UNKNOWN_ACTOR_ID>"),
        "source_ip": event.deep_get(
            "actor", "session", "ip_address", default="<UNKNOWN_SOURCE_IP>"
        ),
        "user_agent": event.deep_get(
            "actor", "session", "user_agent", default="<UNKNOWN_USER_AGENT>"
        ),
        "ip_details": event.deep_get("actor", "session", "ip_address_details", default={}),
    }

    # Add event-specific fields
    if event_type == "ip_allowlist.created":
        context["allowlist_id"] = event.deep_get(
            "ip_allowlist_created", "id", default="<UNKNOWN_ALLOWLIST_ID>"
        )
        context["allowlist_name"] = event.deep_get(
            "ip_allowlist_created", "name", default="<UNKNOWN_ALLOWLIST_NAME>"
        )
        context["allowed_ips"] = event.deep_get("ip_allowlist_created", "allowed_ips", default=[])
    elif event_type == "ip_allowlist.updated":
        context["allowlist_id"] = event.deep_get(
            "ip_allowlist_updated", "id", default="<UNKNOWN_ALLOWLIST_ID>"
        )
        context["allowed_ips"] = event.deep_get("ip_allowlist_updated", "allowed_ips", default=[])
    elif event_type == "ip_allowlist.deleted":
        context["allowlist_id"] = event.deep_get(
            "ip_allowlist_deleted", "id", default="<UNKNOWN_ALLOWLIST_ID>"
        )
        context["allowlist_name"] = event.deep_get(
            "ip_allowlist_deleted", "name", default="<UNKNOWN_ALLOWLIST_NAME>"
        )
        context["allowed_ips"] = event.deep_get("ip_allowlist_deleted", "allowed_ips", default=[])
    elif event_type == "ip_allowlist.config.activated":
        context["activated_configs"] = event.deep_get(
            "ip_allowlist_config_activated", "configs", default=[]
        )
    elif event_type == "ip_allowlist.config.deactivated":
        context["deactivated_configs"] = event.deep_get(
            "ip_allowlist_config_deactivated", "configs", default=[]
        )

    return context

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.