npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
When to Use
- When replacing traditional VPN concentrators with application-level zero trust access
- When providing remote users secure access to internal applications without network-level connectivity
- When implementing least-privilege access where users only see authorized applications
- When needing to make internal applications invisible to unauthorized users and the internet
- When integrating ZTNA with existing SASE architecture using Zscaler Internet Access (ZIA)
Do not use for applications requiring raw UDP access (ZPA primarily supports TCP), for providing full network-level access equivalent to site-to-site VPN (use ZPA AppProtection or branch connector instead), or when the organization requires on-premises-only access control without cloud dependency.
Prerequisites
- Zscaler Private Access subscription (Business or Transformation edition)
- Identity provider configured: Okta, Microsoft Entra ID, Ping Identity, or SAML 2.0 IdP
- App Connector VM requirements: Linux VM (CentOS 7/8, RHEL 7/8, Ubuntu 18.04+, Amazon Linux 2) with 2 vCPU, 4GB RAM minimum
- Outbound connectivity from App Connector to ZPA cloud on port 443 (no inbound ports required)
- DNS resolution from App Connector to internal application FQDNs
- Zscaler Client Connector deployed on user endpoints
Workflow
Step 1: Deploy App Connectors in Application Network
App Connectors establish outbound-only tunnels to the ZPA cloud, providing access to internal applications.
# Download and install App Connector on Linux VM
# Obtain provisioning key from ZPA Admin Portal > Administration > App Connectors
# For RHEL/CentOS
sudo yum install -y https://yum.private.zscaler.com/yum/el7/zpa-connector-latest.rpm
# For Ubuntu/Debian
curl -sS https://dist.private.zscaler.com/apt/pubkey.gpg | sudo apt-key add -
echo "deb https://dist.private.zscaler.com/apt stable main" | sudo tee /etc/apt/sources.list.d/zpa.list
sudo apt update && sudo apt install -y zpa-connector
# Configure the connector with provisioning key
sudo /opt/zscaler/bin/zpa-connector configure \
--provision-key "PROVISIONING_KEY_FROM_PORTAL"
# Start the connector service
sudo systemctl enable zpa-connector
sudo systemctl start zpa-connector
# Verify connector status
sudo systemctl status zpa-connector
sudo /opt/zscaler/bin/zpa-connector status
# Deploy second connector for HA (minimum 2 per site)
# Repeat on second VM with same App Connector Group provisioning keyStep 2: Define Server Groups and Application Segments
Map internal applications to server groups and create application segments.
ZPA Admin Portal Configuration:
1. Server Groups:
Navigate to: Administration > App Connectors > Server Groups
- Name: "DC-East-Servers"
- App Connector Group: "DC-East-Connectors"
- Servers:
- hr-portal.internal.corp (10.1.1.50, TCP 443)
- finance-app.internal.corp (10.1.1.51, TCP 443)
- git.internal.corp (10.1.2.10, TCP 22, 443)
2. Application Segments:
Navigate to: Resources > Application Segments > Add Application Segment
- Name: "HR Applications"
- Domain/URL: hr-portal.internal.corp
- TCP Ports: 443
- Server Group: DC-East-Servers
- Health Reporting: Continuous
- Bypass Type: Never (force all traffic through ZPA)
- Name: "Engineering Tools"
- Domain/URL: git.internal.corp, ci.internal.corp, wiki.internal.corp
- TCP Ports: 22, 80, 443
- Server Group: DC-East-Servers
- Segment Group: "Engineering Segment Group"Step 3: Configure Access Policies
Define who can access which application segments based on identity and device posture.
ZPA Admin Portal > Policies > Access Policy:
Rule 1: HR Team Access
- Name: "HR Portal Access"
- Action: ALLOW
- Criteria:
- User Groups: "HR-Department" (from IdP)
- Application Segment: "HR Applications"
- Device Posture Profile: "Corporate Managed Device"
- Client Type: Zscaler Client Connector
- Conditions:
- SAML Attribute: department = "Human Resources"
- Device Trust Level: "HIGH" (CrowdStrike ZTA score > 70)
Rule 2: Engineering Access
- Name: "Engineering Tools Access"
- Action: ALLOW
- Criteria:
- User Groups: "Engineering-Team", "DevOps-Team"
- Application Segment: "Engineering Tools"
- Device Posture Profile: "Developer Workstation"
- Conditions:
- Machine Group: "Engineering Laptops"
Rule 3: Contractor Limited Access
- Name: "Contractor Wiki Access"
- Action: ALLOW
- Criteria:
- User Groups: "External-Contractors"
- Application Segment: "Wiki Only"
- Client Type: Zscaler Client Connector OR Browser Access
- Conditions:
- Time Window: Mon-Fri 08:00-18:00 EST
Rule 4: Default Deny
- Name: "Block All Other Access"
- Action: DENY
- Criteria: All Users, All Applications
- Log: EnabledStep 4: Configure Device Posture Profiles
Integrate device posture signals from endpoint security tools.
ZPA Admin Portal > Administration > Device Posture:
Profile 1: Corporate Managed Device
- CrowdStrike Falcon: Running, ZTA Score >= 60
- OS: Windows 10 21H2+, macOS 13+, Ubuntu 22.04+
- Disk Encryption: Enabled (BitLocker/FileVault)
- Firewall: Enabled
- Screen Lock: Enabled
Profile 2: Developer Workstation
- Inherits: Corporate Managed Device
- CrowdStrike Falcon: ZTA Score >= 70
- Patch Level: Within 30 days of latest
- Certificate: Valid corporate certificate present
Profile 3: BYOD Device
- OS: Latest minus 1 version
- Browser: Chrome 120+ or Edge 120+
- Antivirus: Any recognized AV runningStep 5: Enable Browser Access for Clientless ZTNA
Configure Browser Access for users without Zscaler Client Connector installed.
ZPA Admin Portal > Resources > Application Segments:
For "HR Applications" segment:
- Enable Browser Access: Yes
- Browser Access Type: HTTPS
- Custom Domain: hr.access.company.com
- Certificate: Upload TLS certificate for custom domain
- Authentication: SAML via corporate IdP
- Session Timeout: 4 hours
- Clipboard Control: Disabled for sensitive apps
- File Upload/Download: Restricted
For Browser Access Portal:
- Portal URL: access.company.com
- IdP: Microsoft Entra ID (SAML 2.0)
- MFA: Required
- Applications shown: Only authorized per user groupStep 6: Configure Logging and Monitoring
Set up log streaming for SIEM integration and continuous monitoring.
ZPA Admin Portal > Administration > Log Streaming Service:
Log Receiver Configuration:
- Name: "Splunk-SIEM"
- Type: Splunk (HEC)
- Destination: https://splunk-hec.company.com:8088
- HEC Token: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
- Log Types:
- User Activity: Enabled
- App Connector Status: Enabled
- Audit Logs: Enabled
- Browser Access: Enabled
# Splunk search for ZPA access anomalies
index=zscaler_zpa sourcetype=zpa:useractivity
| where action="denied"
| stats count by user, application, policy_name
| where count > 10
| sort -countKey Concepts
| Term | Definition |
|---|---|
| App Connector | Lightweight Linux service that creates outbound-only encrypted tunnels from internal networks to ZPA cloud, providing access to applications without inbound ports |
| Application Segment | Logical grouping of internal applications defined by FQDN/IP and ports, mapped to server groups for access policy enforcement |
| Server Group | Collection of application servers associated with App Connector groups that can serve requests for application segments |
| Access Policy | Rules defining which users/groups can access which application segments under what conditions (device posture, time, location) |
| Zscaler Client Connector | Endpoint agent installed on user devices that routes traffic to ZPA cloud for policy enforcement and application access |
| Browser Access | Clientless ZTNA option allowing application access through a web browser without requiring Zscaler Client Connector installation |
Tools & Systems
- Zscaler Private Access (ZPA): Cloud-native ZTNA platform replacing VPN with identity-based application access
- Zscaler Client Connector: Cross-platform endpoint agent routing traffic through ZPA for policy enforcement
- ZPA App Connector: Outbound-only tunnel endpoint deployed in application networks
- ZPA Admin Portal: Web-based management console for policy, segment, and connector configuration
- ZPA Log Streaming Service (LSS): Real-time log export to SIEM platforms (Splunk, Sentinel, QRadar)
- CrowdStrike ZTA Integration: Device posture scoring for conditional access policy enforcement
Common Scenarios
Scenario: Migrating 500-User Organization from Cisco AnyConnect VPN to ZPA
Context: A financial services firm with 500 employees uses Cisco AnyConnect for remote access. VPN split-tunnel configuration creates security gaps, and full-tunnel mode causes performance issues. The firm needs application-level access control for SOX compliance.
Approach:
- Deploy 4 App Connectors (2 per data center) with HA configuration
- Define application segments for 20 internal applications grouped by business function
- Configure access policies mapping AD groups to application segments with device posture requirements
- Integrate CrowdStrike ZTA scores as device posture input (minimum score 60 for standard, 80 for financial apps)
- Enable Browser Access for contractors accessing the vendor portal
- Configure LSS to stream access logs to Splunk for SOX audit trail
- Run parallel operation for 3 weeks: VPN and ZPA side by side
- Phase out VPN connections after validating all application access through ZPA
Pitfalls: App Connector DNS must resolve all internal FQDNs used in application segments. Wildcard domain segments can cause performance issues if too broad. Browser Access does not support all web application frameworks (WebSocket-heavy apps may require Client Connector). CrowdStrike ZTA integration requires Falcon sensor deployment on all endpoints before enforcing posture policies.
Output Format
ZPA ZTNA Deployment Report
==================================================
Organization: FinanceCorp
Deployment Date: 2026-02-23
INFRASTRUCTURE:
App Connectors: 4 (2x DC-East, 2x DC-West)
Connector Status: All healthy
Connector Version: 24.1.2
APPLICATION COVERAGE:
Application Segments: 20
Total Applications: 45
Server Groups: 4
Segment Groups: 6
ACCESS POLICIES:
Total Rules: 12
Allow Rules: 11
Deny Rules: 1 (default deny)
Device Posture Profiles: 3
USER ACCESS (last 30 days):
Active Users: 487 / 500
Total Sessions: 124,567
Allowed Sessions: 123,890 (99.5%)
Denied Sessions: 677 (0.5%)
Browser Access Sessions: 2,341
VPN MIGRATION:
Users migrated to ZPA: 487 / 500
VPN decommission date: 2026-03-15References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.4 KB
Zscaler Private Access ZTNA — API Reference
Libraries
| Library | Install | Purpose |
|---|---|---|
| requests | pip install requests |
ZPA REST API client |
| zscaler-sdk-python | pip install zscaler-sdk-python |
Official Zscaler Python SDK |
ZPA API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /signin |
Authenticate and get bearer token |
| GET | /mgmtconfig/v1/admin/customers/{id}/application |
List app segments |
| GET | /mgmtconfig/v1/admin/customers/{id}/connector |
List app connectors |
| GET | /mgmtconfig/v1/admin/customers/{id}/serverGroup |
List server groups |
| GET | /mgmtconfig/v1/admin/customers/{id}/policySet/rules |
List access policies |
| GET | /mgmtconfig/v1/admin/customers/{id}/segmentGroup |
List segment groups |
Key ZPA Concepts
| Component | Description |
|---|---|
| App Segment | Application definition with domain/IP and port ranges |
| App Connector | On-premise agent that brokers connections to apps |
| Server Group | Group of application servers behind connectors |
| Access Policy | Rules defining who can access which app segments |
| Segment Group | Logical grouping of app segments |
External References
standards.md1.9 KB
Zscaler Private Access ZTNA - Standards & References
NIST SP 800-207: Zero Trust Architecture
- Section 2.1: Zero Trust Tenets - ZPA implements "never trust, always verify" with per-session application access
- Section 3.1: Policy Engine / Policy Administrator - maps to ZPA access policy engine
- Section 3.3: Agent/Gateway Model - maps to Zscaler Client Connector + App Connector architecture
- URL: https://csrc.nist.gov/publications/detail/sp/800-207/final
CISA Zero Trust Maturity Model v2.0
- Network Pillar: Application micro-segmentation - ZPA application segments eliminate network-level access
- Identity Pillar: Continuous validation - ZPA evaluates identity and posture per session
- URL: https://www.cisa.gov/zero-trust-maturity-model
Zscaler Documentation
- ZPA Admin Guide: https://help.zscaler.com/zpa
- Step-by-Step Configuration: https://help.zscaler.com/zpa/step-step-configuration-guide-zpa
- App Connector Deployment: https://help.zscaler.com/zpa/about-app-connectors
- Access Policy Configuration: https://help.zscaler.com/zpa/configuring-access-policies
- Browser Access: https://help.zscaler.com/zpa/about-browser-access
- Log Streaming Service: https://help.zscaler.com/zpa/about-log-streaming-service
- ZPA Reference Architecture (AWS): https://help.zscaler.com/downloads/zpa/reference-architecture
NIST SP 800-63-3: Digital Identity Guidelines
- Section 4-6: Identity assurance levels relevant to ZPA IdP integration
- URL: https://pages.nist.gov/800-63-3/
SOX Compliance (Sarbanes-Oxley)
- Section 302/404: Access controls for financial systems - ZPA provides auditable per-application access
- Application-level access logging satisfies audit trail requirements
Gartner ZTNA Market Guide
- ZPA classified as agent-based ZTNA with service-initiated architecture
- Reference: Gartner Market Guide for Zero Trust Network Access (2024)
workflows.md3.3 KB
ZPA ZTNA Configuration Workflow
Phase 1: Planning and Prerequisites (Week 1)
1.1 Architecture Design
- Map current VPN access patterns: which users access which applications
- Identify all internal applications by FQDN, IP, and port
- Group applications into logical segments by business function and sensitivity
- Design App Connector placement: minimum 2 connectors per data center for HA
- Plan DNS resolution: App Connectors must resolve all application FQDNs
1.2 Identity Provider Integration
- Configure SAML 2.0 or OIDC integration with corporate IdP (Okta, Entra ID, Ping)
- Map IdP user groups to ZPA access policy groups
- Configure SCIM provisioning for automated user/group sync
- Test SSO authentication flow end-to-end
Phase 2: Infrastructure Deployment (Week 2-3)
2.1 App Connector Deployment
- Provision Linux VMs (CentOS/RHEL/Ubuntu) meeting minimum specs (2 vCPU, 4GB RAM)
- Ensure outbound HTTPS (443) to ZPA cloud domains (*.private.zscaler.com, *.zpath.net)
- Generate provisioning keys in ZPA Admin Portal (one per App Connector Group)
- Install and configure App Connector packages
- Verify connector enrollment and health status in portal
- Deploy second connector per group for high availability
2.2 Server Group and Application Segment Configuration
- Create server groups mapping to App Connector groups
- Add application servers with FQDNs and ports to server groups
- Create application segments grouping related applications
- Configure health monitoring for each segment
- Test application reachability from App Connector
Phase 3: Policy Configuration (Week 3-4)
3.1 Access Policy Design
- Create allow rules ordered from most specific to least specific
- Map IdP groups to application segments in each rule
- Add device posture profile requirements to rules
- Configure time-based restrictions for contractor access
- Create default deny rule as the last policy rule
- Test each policy rule with representative users
3.2 Device Posture Configuration
- Define posture profiles for each device category (managed, developer, BYOD)
- Integrate CrowdStrike ZTA scores for real-time posture
- Configure OS version, encryption, and firewall requirements
- Test posture evaluation with compliant and non-compliant devices
Phase 4: User Migration (Week 4-6)
4.1 Client Connector Deployment
- Deploy Zscaler Client Connector via MDM (Intune, Jamf, SCCM)
- Configure auto-enrollment with IdP authentication
- Test client connectivity to ZPA cloud
- Validate application access through Client Connector
4.2 Phased Rollout
- Phase 1: IT/Security team (50 users) - validate all segments
- Phase 2: Engineering (200 users) - validate developer tools
- Phase 3: Business users (remaining) - validate general applications
- Monitor access logs for denials and policy gaps at each phase
Phase 5: Browser Access Configuration (Week 5)
- Enable Browser Access for applications requiring clientless access
- Configure custom domains and TLS certificates
- Set session timeout and clipboard/file transfer policies
- Test with contractor accounts
Phase 6: VPN Decommission (Week 7-8)
- Run VPN and ZPA in parallel for 2-3 weeks
- Monitor VPN usage to identify remaining dependencies
- Migrate remaining applications and users
- Disable VPN and redirect users to ZPA
- Decommission VPN infrastructure
Scripts 2
agent.py4.3 KB
#!/usr/bin/env python3
"""Zscaler Private Access (ZPA) ZTNA audit agent using ZPA API."""
import json
import sys
import argparse
from datetime import datetime
try:
import requests
except ImportError:
print("Install: pip install requests")
sys.exit(1)
class ZPAClient:
"""Zscaler Private Access API client."""
def __init__(self, client_id, client_secret, customer_id):
self.base_url = "https://config.private.zscaler.com"
self.customer_id = customer_id
self.token = self._authenticate(client_id, client_secret)
def _authenticate(self, client_id, client_secret):
resp = requests.post(f"{self.base_url}/signin", json={
"client_id": client_id, "client_secret": client_secret,
}, timeout=30)
resp.raise_for_status()
return resp.json()["token"]
def _get(self, endpoint):
resp = requests.get(f"{self.base_url}/mgmtconfig/v1/admin/customers/{self.customer_id}/{endpoint}",
headers={"Authorization": f"Bearer {self.token}"}, timeout=30)
resp.raise_for_status()
return resp.json()
def list_app_segments(self):
return self._get("application")
def list_server_groups(self):
return self._get("serverGroup")
def list_app_connectors(self):
return self._get("connector")
def list_access_policies(self):
return self._get("policySet/rules")
def list_segment_groups(self):
return self._get("segmentGroup")
def audit_zpa_config(client):
"""Audit ZPA configuration for security posture."""
findings = []
apps = client.list_app_segments()
for app in apps.get("list", []):
if not app.get("enabled"):
findings.append({
"type": "disabled_app_segment",
"name": app.get("name", ""),
"severity": "LOW",
})
if app.get("bypassType") == "ALWAYS":
findings.append({
"type": "bypass_enabled",
"name": app.get("name", ""),
"severity": "HIGH",
"recommendation": "Remove bypass to enforce ZPA inspection",
})
connectors = client.list_app_connectors()
for conn in connectors.get("list", []):
if conn.get("runtimeStatus") != "running":
findings.append({
"type": "connector_down",
"name": conn.get("name", ""),
"status": conn.get("runtimeStatus", ""),
"severity": "CRITICAL",
})
return findings
def run_audit(client_id, client_secret, customer_id):
"""Execute ZPA ZTNA audit."""
client = ZPAClient(client_id, client_secret, customer_id)
print(f"\n{'='*60}")
print(f" ZSCALER PRIVATE ACCESS ZTNA AUDIT")
print(f" Generated: {datetime.utcnow().isoformat()} UTC")
print(f"{'='*60}\n")
apps = client.list_app_segments()
app_list = apps.get("list", [])
print(f"--- APPLICATION SEGMENTS ({len(app_list)}) ---")
for a in app_list[:10]:
print(f" {a.get('name', '')}: enabled={a.get('enabled', '')} bypass={a.get('bypassType', '')}")
connectors = client.list_app_connectors()
conn_list = connectors.get("list", [])
print(f"\n--- APP CONNECTORS ({len(conn_list)}) ---")
for c in conn_list[:10]:
print(f" {c.get('name', '')}: {c.get('runtimeStatus', '')}")
findings = audit_zpa_config(client)
print(f"\n--- AUDIT FINDINGS ({len(findings)}) ---")
for f in findings:
print(f" [{f['severity']}] {f['type']}: {f.get('name', '')}")
return {"apps": len(app_list), "connectors": len(conn_list), "findings": findings}
def main():
parser = argparse.ArgumentParser(description="ZPA ZTNA Audit Agent")
parser.add_argument("--client-id", required=True, help="ZPA API client ID")
parser.add_argument("--client-secret", required=True, help="ZPA API client secret")
parser.add_argument("--customer-id", required=True, help="ZPA customer ID")
parser.add_argument("--output", help="Save report to JSON file")
args = parser.parse_args()
report = run_audit(args.client_id, args.client_secret, args.customer_id)
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"\n[+] Report saved to {args.output}")
if __name__ == "__main__":
main()
process.py11.5 KB
#!/usr/bin/env python3
"""
Zscaler Private Access (ZPA) - Deployment Audit and Compliance Checker
Queries ZPA Admin API to audit App Connector health, application segment
coverage, access policy configuration, and user activity for ZTNA compliance.
Requirements:
pip install requests
"""
import json
import sys
import time
from datetime import datetime, timezone
from typing import Any
import requests
ZPA_BASE_URL = "https://config.private.zscaler.com"
class ZPAAuditor:
"""Audit Zscaler Private Access deployment configuration."""
def __init__(self, client_id: str, client_secret: str, customer_id: str):
self.client_id = client_id
self.client_secret = client_secret
self.customer_id = customer_id
self.token = None
self._authenticate()
def _authenticate(self):
"""Authenticate with ZPA API."""
resp = requests.post(
f"{ZPA_BASE_URL}/signin",
json={
"apiKey": self.client_id,
"username": self.client_secret,
"password": self.customer_id
},
timeout=30
)
resp.raise_for_status()
self.token = resp.json().get("token")
print("[AUTH] Successfully authenticated with ZPA API")
def _get(self, endpoint: str, params: dict = None) -> dict:
"""Make authenticated GET request to ZPA API."""
headers = {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
resp = requests.get(
f"{ZPA_BASE_URL}/mgmtconfig/v1/admin/customers/{self.customer_id}/{endpoint}",
headers=headers,
params=params or {},
timeout=30
)
resp.raise_for_status()
return resp.json()
def audit_app_connectors(self) -> dict[str, Any]:
"""Audit App Connector deployment and health."""
print("\n[1/5] Auditing App Connectors...")
data = self._get("connector")
connectors = data.get("list", [])
stats = {
"total": len(connectors),
"healthy": 0,
"unhealthy": 0,
"groups": {},
"version_distribution": {},
"unhealthy_list": []
}
for conn in connectors:
name = conn.get("name", "unknown")
enabled = conn.get("enabled", False)
runtime_status = conn.get("runtimeStatus", "UNKNOWN")
version = conn.get("currentVersion", "unknown")
group = conn.get("appConnectorGroupName", "ungrouped")
stats["groups"][group] = stats["groups"].get(group, 0) + 1
stats["version_distribution"][version] = stats["version_distribution"].get(version, 0) + 1
if enabled and runtime_status == "ACTIVE":
stats["healthy"] += 1
else:
stats["unhealthy"] += 1
stats["unhealthy_list"].append({
"name": name, "status": runtime_status, "group": group
})
print(f" [WARN] Unhealthy connector: {name} (status: {runtime_status})")
print(f" Total: {stats['total']}, Healthy: {stats['healthy']}, Unhealthy: {stats['unhealthy']}")
print(f" Groups: {stats['groups']}")
# Check HA: each group should have >= 2 connectors
for group, count in stats["groups"].items():
if count < 2:
print(f" [CRITICAL] Group '{group}' has only {count} connector(s) - no HA!")
return stats
def audit_application_segments(self) -> dict[str, Any]:
"""Audit application segment configuration."""
print("\n[2/5] Auditing Application Segments...")
data = self._get("application")
segments = data.get("list", [])
stats = {
"total": len(segments),
"enabled": 0,
"disabled": 0,
"bypass_enabled": 0,
"health_reporting": {"continuous": 0, "on_access": 0, "none": 0},
"segments": []
}
for seg in segments:
name = seg.get("name", "unknown")
enabled = seg.get("enabled", False)
bypass_type = seg.get("bypassType", "NEVER")
health = seg.get("healthReporting", "NONE")
domains = seg.get("domainNames", [])
tcp_ports = seg.get("tcpPortRanges", [])
if enabled:
stats["enabled"] += 1
else:
stats["disabled"] += 1
print(f" [WARN] Disabled segment: {name}")
if bypass_type != "NEVER":
stats["bypass_enabled"] += 1
print(f" [WARN] Bypass enabled on segment: {name} (type: {bypass_type})")
health_key = health.lower() if health.lower() in stats["health_reporting"] else "none"
stats["health_reporting"][health_key] += 1
stats["segments"].append({
"name": name,
"enabled": enabled,
"domains": domains[:5],
"ports": tcp_ports[:5],
"bypass": bypass_type
})
print(f" Total: {stats['total']}, Enabled: {stats['enabled']}, Disabled: {stats['disabled']}")
print(f" Bypass enabled: {stats['bypass_enabled']}")
print(f" Health reporting: {stats['health_reporting']}")
return stats
def audit_access_policies(self) -> dict[str, Any]:
"""Audit access policy configuration."""
print("\n[3/5] Auditing Access Policies...")
data = self._get("policySet/rules?policyType=ACCESS_POLICY")
rules = data.get("list", [])
stats = {
"total": len(rules),
"allow_rules": 0,
"deny_rules": 0,
"has_default_deny": False,
"rules_without_conditions": 0,
"rules_with_posture": 0,
"rules": []
}
for rule in rules:
name = rule.get("name", "unknown")
action = rule.get("action", "UNKNOWN")
conditions = rule.get("conditions", [])
order = rule.get("order", 0)
if action == "ALLOW":
stats["allow_rules"] += 1
elif action == "DENY":
stats["deny_rules"] += 1
if not conditions:
stats["rules_without_conditions"] += 1
print(f" [WARN] Rule '{name}' has no conditions - overly permissive")
# Check for device posture conditions
has_posture = any(
c.get("operands", [{}])[0].get("objectType") == "POSTURE_PROFILE"
for c in conditions if c.get("operands")
)
if has_posture:
stats["rules_with_posture"] += 1
stats["rules"].append({
"name": name, "action": action, "order": order,
"conditions_count": len(conditions), "has_posture": has_posture
})
# Check for default deny rule (should be last rule with DENY action)
if rules and rules[-1].get("action") == "DENY":
stats["has_default_deny"] = True
else:
print(" [CRITICAL] No default deny rule found! All unlisted access may be permitted.")
print(f" Total rules: {stats['total']}")
print(f" Allow: {stats['allow_rules']}, Deny: {stats['deny_rules']}")
print(f" Rules with device posture: {stats['rules_with_posture']}")
print(f" Default deny: {'Yes' if stats['has_default_deny'] else 'NO - MISSING!'}")
return stats
def audit_server_groups(self) -> dict[str, Any]:
"""Audit server group configuration."""
print("\n[4/5] Auditing Server Groups...")
data = self._get("serverGroup")
groups = data.get("list", [])
stats = {
"total": len(groups),
"enabled": 0,
"servers_total": 0,
"groups": []
}
for group in groups:
name = group.get("name", "unknown")
enabled = group.get("enabled", False)
servers = group.get("servers", [])
if enabled:
stats["enabled"] += 1
stats["servers_total"] += len(servers)
stats["groups"].append({
"name": name, "enabled": enabled, "server_count": len(servers)
})
print(f" Total groups: {stats['total']}, Enabled: {stats['enabled']}")
print(f" Total servers: {stats['servers_total']}")
return stats
def generate_report(self, connectors, segments, policies, server_groups) -> str:
"""Generate comprehensive ZPA audit report."""
print("\n[5/5] Generating audit report...")
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
report = f"""
ZPA ZTNA Deployment Audit Report
{'=' * 55}
Customer ID: {self.customer_id}
Generated: {now}
1. APP CONNECTORS
Total deployed: {connectors['total']}
Healthy: {connectors['healthy']}
Unhealthy: {connectors['unhealthy']}
Connector groups: {len(connectors['groups'])}
HA compliance (2+ per group): {'PASS' if all(v >= 2 for v in connectors['groups'].values()) else 'FAIL'}
2. APPLICATION SEGMENTS
Total segments: {segments['total']}
Enabled: {segments['enabled']}
Disabled: {segments['disabled']}
With bypass enabled: {segments['bypass_enabled']}
Health reporting: {segments['health_reporting']}
3. ACCESS POLICIES
Total rules: {policies['total']}
Allow rules: {policies['allow_rules']}
Deny rules: {policies['deny_rules']}
Default deny rule: {'YES' if policies['has_default_deny'] else 'MISSING - CRITICAL'}
Rules with device posture: {policies['rules_with_posture']} / {policies['total']}
4. SERVER GROUPS
Total groups: {server_groups['total']}
Total servers: {server_groups['servers_total']}
5. RECOMMENDATIONS
"""
recommendations = []
if connectors['unhealthy'] > 0:
recommendations.append(f" - Fix {connectors['unhealthy']} unhealthy App Connector(s)")
if not policies['has_default_deny']:
recommendations.append(" - ADD DEFAULT DENY RULE immediately")
if policies['rules_with_posture'] < policies['allow_rules']:
gap = policies['allow_rules'] - policies['rules_with_posture']
recommendations.append(f" - Add device posture to {gap} allow rule(s)")
if segments['bypass_enabled'] > 0:
recommendations.append(f" - Review {segments['bypass_enabled']} segment(s) with bypass enabled")
if not recommendations:
recommendations.append(" - No critical issues found")
report += "\n".join(recommendations)
return report
def main():
if len(sys.argv) < 4:
print("Usage: python process.py <client_id> <client_secret> <customer_id>")
print("\nAudits ZPA deployment for connector health, segment coverage, and policy compliance.")
sys.exit(1)
auditor = ZPAAuditor(sys.argv[1], sys.argv[2], sys.argv[3])
connectors = auditor.audit_app_connectors()
segments = auditor.audit_application_segments()
policies = auditor.audit_access_policies()
server_groups = auditor.audit_server_groups()
report = auditor.generate_report(connectors, segments, policies, server_groups)
print(report)
filename = f"zpa_audit_{datetime.now().strftime('%Y%m%d')}.txt"
with open(filename, "w") as f:
f.write(report)
print(f"\nReport saved to: {filename}")
if __name__ == "__main__":
main()