npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Prerequisites
- Understanding of zero trust principles (NIST SP 800-207)
- Familiarity with identity providers (Okta, Azure AD, Ping Identity)
- Knowledge of network security fundamentals
- Access to Zscaler Private Access (ZPA) tenant
Overview
Zero Trust Network Access (ZTNA) replaces traditional VPN architectures by enforcing identity-based, context-aware access to private applications without placing users on the corporate network. Zscaler Private Access (ZPA) is a leading ZTNA solution that brokers secure connections between authenticated users and internal applications through the Zscaler Zero Trust Exchange cloud platform.
This skill covers end-to-end deployment of ZPA including connector setup, application segmentation, policy configuration, and integration with identity providers for continuous verification.
When to Use
- When deploying or configuring implementing zero trust network access with zscaler capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Familiarity with zero trust architecture concepts and tools
- Access to a test or lab environment for safe execution
- Python 3.8+ with required dependencies installed
- Appropriate authorization for any testing activities
Architecture
Zscaler Private Access Components
- Client Connector: Lightweight agent on user endpoints that establishes outbound TLS tunnels to the nearest ZPA Service Edge
- ZPA Service Edge: Cloud-hosted broker (or Private Service Edge on-premises) that stitches user-to-app connections after policy evaluation
- App Connector: Lightweight VM deployed in the application environment that creates outbound tunnels to the Service Edge
- ZPA Admin Portal: Centralized management console for defining applications, segments, and access policies
Connection Flow
User Device (Client Connector)
|
v [Outbound TLS tunnel]
ZPA Service Edge (Policy Evaluation + IdP Auth)
|
v [Outbound TLS tunnel]
App Connector --> Internal ApplicationKey principle: No inbound connections are required. Both the Client Connector and App Connector initiate outbound-only connections, eliminating the attack surface of traditional VPNs.
Key Concepts
Application Segments
Define specific applications or groups of applications by IP address, FQDN, port, and protocol. Segments enable granular microsegmentation rather than broad network access.
Access Policies
Policies combine user identity, group membership, device posture, and contextual signals (location, time) to grant or deny access to application segments.
Server Groups
Logical groupings of App Connectors that serve specific application segments, enabling high availability and geographic distribution.
Browser Access
ZPA supports clientless browser-based access for web applications, enabling ZTNA for unmanaged devices and third-party users without requiring the Client Connector.
Workflow
Phase 1: Foundation Setup
-
Configure Identity Provider Integration
- Navigate to Administration > IdP Configuration in ZPA Admin Portal
- Add SAML 2.0 or OIDC integration with your IdP (Azure AD, Okta, Ping)
- Configure SCIM provisioning for automatic user/group synchronization
- Test SSO authentication flow
-
Deploy App Connectors
- Provision App Connector VMs in each application environment (data center, AWS VPC, Azure VNet)
- Download the provisioning key from ZPA Admin Portal
- Install and enroll the App Connector using the provisioning key
- Verify connector status shows "Healthy" in the admin portal
- Deploy at least two connectors per environment for high availability
-
Create Server Groups
- Group App Connectors by geographic location or application tier
- Configure health check intervals and failover behavior
Phase 2: Application Segmentation
-
Define Application Segments
- Create segments for each application or logical group
- Specify domains/IPs, ports, and protocols
- Associate segments with appropriate server groups
- Enable or disable browser access as needed
-
Create Segment Groups
- Organize application segments into logical groups (e.g., HR apps, Finance apps)
- Use segment groups to simplify policy management
Phase 3: Policy Configuration
-
Configure Access Policies
- Define rules matching user groups to application segments
- Apply conditions: device posture, client type, SAML attributes
- Order rules by priority (most restrictive first)
- Create deny rules for blocked access scenarios
-
Enable Device Posture Checks
- Configure posture profiles requiring OS patch level, disk encryption, antivirus status
- Integrate with endpoint management (CrowdStrike, Microsoft Intune, Carbon Black)
- Associate posture profiles with access policies
Phase 4: Client Deployment
- Deploy Client Connector
- Package the Zscaler Client Connector with enrollment token
- Deploy via MDM (Intune, Jamf, SCCM) or manual installation
- Configure forwarding profile to route private app traffic through ZPA
- Test user authentication and application access
Phase 5: Monitoring and Optimization
-
Enable Logging and Monitoring
- Configure log streaming to SIEM (Splunk, Sentinel, QRadar)
- Set up alerts for policy violations, connector health, and authentication failures
- Review ZPA Insights dashboard for usage analytics
-
Iterative Refinement
- Analyze access logs to identify shadow IT and unauthorized access attempts
- Refine application segments based on actual traffic patterns
- Expand coverage from pilot applications to full enterprise deployment
Validation Checklist
- Identity provider integration tested with SSO and SCIM sync
- App Connectors deployed and showing healthy status in all environments
- Application segments defined with correct IPs/FQDNs, ports, protocols
- Access policies enforce least-privilege per user group
- Device posture checks block non-compliant endpoints
- Client Connector deployed to all managed endpoints
- Log streaming to SIEM confirmed with test events
- Failover tested by disabling one App Connector per server group
- Browser Access configured for web apps requiring third-party access
- VPN decommission plan documented with rollback procedures
References
- NIST SP 800-207: Zero Trust Architecture
- CISA Zero Trust Maturity Model v2.0 - Network Pillar
- Zscaler Private Access Architecture Guide
- CSA Software-Defined Perimeter and Zero Trust Specification v2.0
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.6 KB
API Reference: Zscaler Private Access (ZPA)
ZPA Management API
Authentication
POST https://config.private.zscaler.com/signin
Body: client_id=X&client_secret=Y
Returns: {"access_token": "...", "token_type": "Bearer"}Application Segments
| Method | Endpoint | Description |
|---|---|---|
| GET | /mgmtconfig/v1/admin/customers/{id}/application |
List app segments |
| POST | /mgmtconfig/v1/admin/customers/{id}/application |
Create app segment |
Server Groups
| Method | Endpoint | Description |
|---|---|---|
| GET | /mgmtconfig/v1/admin/customers/{id}/serverGroup |
List server groups |
Access Policies
| Method | Endpoint | Description |
|---|---|---|
| GET | /mgmtconfig/v1/admin/customers/{id}/policySet/rules |
List policy rules |
Connectors
| Method | Endpoint | Description |
|---|---|---|
| GET | /mgmtconfig/v1/admin/customers/{id}/connector |
List connectors |
App Segment Fields
| Field | Description |
|---|---|
name |
Application segment name |
enabled |
Whether segment is active |
bypassType |
NEVER, ALWAYS, or ON_NET |
domainNames |
FQDN list for the segment |
tcpPortRanges |
Allowed TCP port ranges |
Bypass Types
| Value | Security Implication |
|---|---|
NEVER |
Always enforce ZPA (recommended) |
ALWAYS |
Bypass ZPA entirely (high risk) |
ON_NET |
Bypass when on corporate network |
References
- ZPA API: https://help.zscaler.com/zpa/about-zpa-api
- ZPA Admin Guide: https://help.zscaler.com/zpa
standards.md4.9 KB
Standards and Frameworks Reference
NIST SP 800-207: Zero Trust Architecture
Core Tenets Applicable to ZTNA
- All data sources and computing services are considered resources - Every application behind ZPA is treated as a discrete resource requiring explicit access grants
- All communication is secured regardless of network location - ZPA encrypts all tunnels end-to-end regardless of whether users are on-premises or remote
- Access to individual enterprise resources is granted on a per-session basis - ZPA evaluates policy for each connection request rather than granting persistent network access
- Access to resources is determined by dynamic policy - ZPA policies incorporate identity, device posture, location, and behavioral signals
- The enterprise monitors and measures the integrity and security posture of all owned and associated assets - Device posture checks validate endpoint compliance before granting access
- All resource authentication and authorization are dynamic and strictly enforced before access is allowed - ZPA requires authentication through IdP and authorization through policy engine for every session
NIST ZTA Deployment Models
- Enhanced Identity Governance: ZPA implements this model by using identity as the primary decision factor combined with device trust signals
- Micro-Segmentation: ZPA application segments function as software-defined microsegments at the application layer
- Software Defined Perimeters: ZPA directly implements the SDP model with its broker-based architecture
NIST SP 800-207A: Zero Trust Architecture Model for Cloud-Native Applications
- Extends zero trust principles to multi-cloud environments
- ZPA App Connectors can be deployed across AWS, Azure, GCP, and on-premises
- Supports workload-to-workload zero trust with ZPA for workloads
CISA Zero Trust Maturity Model v2.0
Network Pillar
| Maturity Level | Capability | ZPA Implementation |
|---|---|---|
| Traditional | Macro-segmentation with static rules | Legacy VPN replaced by ZPA |
| Initial | Define network architecture with isolation | App Connectors isolate segments |
| Advanced | Micro-perimeters with identity-based access | Per-app segments with IdP integration |
| Optimal | Dynamic microsegmentation with continuous verification | Real-time posture + behavioral analytics |
Identity Pillar
| Maturity Level | Capability | ZPA Implementation |
|---|---|---|
| Traditional | Password-based, agency-managed | Basic IdP integration |
| Initial | MFA for privileged users, federated identity | SAML/OIDC with IdP, SCIM provisioning |
| Advanced | MFA for all users, phishing-resistant | Conditional access with posture checks |
| Optimal | Continuous validation, risk-based authentication | ZPA + CrowdStrike/UEBA integration |
Devices Pillar
| Maturity Level | Capability | ZPA Implementation |
|---|---|---|
| Traditional | Limited device visibility | Manual device inventory |
| Initial | Compliance enforcement for some devices | Basic posture profiles |
| Advanced | Real-time device analytics | CrowdStrike ZTA score integration |
| Optimal | Continuous diagnostics and mitigation | EDR-driven dynamic access decisions |
CSA Software-Defined Perimeter Specification v2.0
SDP Architecture Mapping to ZPA
| SDP Component | ZPA Equivalent |
|---|---|
| SDP Controller | ZPA Service Edge + Policy Engine |
| Initiating Host (IH) | Client Connector |
| Accepting Host (AH) | App Connector |
| SDP Gateway | ZPA Service Edge |
SDP Deployment Models
- Client-to-Gateway: Standard ZPA deployment (user to application via Service Edge)
- Client-to-Server: ZPA Browser Access (direct browser connection through Service Edge)
- Server-to-Server: ZPA Workload-to-Workload (App Connector to App Connector)
- Client-to-Server-to-Client: Not directly supported in ZPA
DoD Zero Trust Reference Architecture v2.0
Pillar Alignment
- ZPA maps to the Network & Environment pillar through application-layer microsegmentation
- ZPA maps to the User pillar through IdP integration and continuous authentication
- ZPA maps to the Device pillar through endpoint posture assessment
- ZPA maps to the Application & Workload pillar through per-application access control
- Visibility & Analytics pillar addressed through ZPA log streaming and analytics dashboards
Compliance Mapping
| Regulation | Requirement | ZPA Capability |
|---|---|---|
| NIST 800-53 AC-4 | Information flow enforcement | Application segment policies |
| NIST 800-53 AC-17 | Remote access | ZTNA replaces VPN |
| PCI DSS 4.0 Req 1 | Network security controls | Microsegmentation per cardholder segment |
| HIPAA 164.312(e) | Transmission security | End-to-end encrypted tunnels |
| SOX Section 404 | Access controls over financial systems | Auditable per-session access logs |
| FedRAMP | Continuous monitoring | ZPA FedRAMP Moderate authorized |
workflows.md7.6 KB
ZTNA Implementation Workflows
Workflow 1: Initial ZPA Deployment
┌─────────────────┐
│ Pre-Assessment │
│ - Inventory apps │
│ - Map user groups│
│ - Classify data │
└───────┬─────────┘
v
┌─────────────────────┐
│ IdP Integration │
│ - SAML/OIDC config │
│ - SCIM provisioning │
│ - MFA enrollment │
│ - Test SSO flow │
└───────┬─────────────┘
v
┌─────────────────────┐
│ App Connector Deploy │
│ - Provision VMs │
│ - Generate enroll key│
│ - Install + enroll │
│ - Health validation │
└───────┬─────────────┘
v
┌─────────────────────┐
│ Application Segments │
│ - Define apps by │
│ FQDN/IP + ports │
│ - Create seg groups │
│ - Map to server grps │
└───────┬─────────────┘
v
┌─────────────────────┐
│ Access Policies │
│ - User->App mapping │
│ - Posture conditions │
│ - Deny rules │
│ - Priority ordering │
└───────┬─────────────┘
v
┌─────────────────────┐
│ Client Deployment │
│ - Package connector │
│ - MDM distribution │
│ - Forwarding profile │
│ - User acceptance │
└───────┬─────────────┘
v
┌─────────────────────┐
│ Validation & Monitor │
│ - Access testing │
│ - SIEM integration │
│ - Dashboard setup │
│ - Incident playbooks │
└─────────────────────┘Workflow 2: Access Request Evaluation (Runtime)
User Request
│
v
┌──────────────────┐ ┌──────────────────┐
│ Client Connector │───>│ ZPA Service Edge │
│ - Capture request │ │ - Receive tunnel │
│ - Forward to edge │ │ - Identify user │
└──────────────────┘ └────────┬─────────┘
│
┌────────────v────────────┐
│ Authentication │
│ - Redirect to IdP │
│ - Validate SAML/OIDC │
│ - Check MFA completion │
└────────────┬────────────┘
│
┌────────────v────────────┐
│ Authorization │
│ - Match access policies │
│ - Evaluate posture │
│ - Check context signals │
│ - Apply least privilege │
└────────────┬────────────┘
│
┌────YES─────┴─────NO────┐
v v
┌──────────────┐ ┌──────────────┐
│ Grant Access │ │ Deny Access │
│ - Select App │ │ - Log denial │
│ Connector │ │ - Alert SIEM │
│ - Stitch tunnel│ │ - User notify │
│ - Monitor │ └──────────────┘
└──────────────┘Workflow 3: VPN-to-ZTNA Migration
Phase 1: Assessment (Weeks 1-2)
├── Catalog all VPN-accessed applications
├── Map user groups to applications
├── Identify application dependencies
├── Baseline VPN performance metrics
└── Document compliance requirements
Phase 2: Parallel Deployment (Weeks 3-6)
├── Deploy ZPA alongside existing VPN
├── Configure App Connectors for pilot apps
├── Create policies mirroring VPN ACLs
├── Deploy Client Connector to pilot users
└── Validate access and performance
Phase 3: Migration Waves (Weeks 7-16)
├── Wave 1: Low-risk web applications
├── Wave 2: Business-critical web apps
├── Wave 3: Non-web TCP/UDP applications
├── Wave 4: Legacy applications
└── Each wave: test → validate → migrate → monitor
Phase 4: VPN Decommission (Weeks 17-20)
├── Verify all applications accessible via ZPA
├── Disable VPN for migrated user groups
├── Monitor for access issues (2-week soak)
├── Decommission VPN concentrators
└── Update disaster recovery documentationWorkflow 4: Device Posture Enforcement
┌───────────────────┐
│ Device Connects │
└───────┬───────────┘
v
┌───────────────────┐
│ Posture Assessment │
│ - OS version │
│ - Patch level │
│ - Disk encryption │
│ - AV/EDR status │
│ - Firewall enabled │
│ - Domain joined │
└───────┬───────────┘
v
┌───────────────────┐
│ Posture Evaluation │
│ Compare against │
│ posture profiles │
└───┬──────────┬────┘
│ │
PASS FAIL
│ │
v v
┌────────┐ ┌──────────────────┐
│ Full │ │ Restricted Access │
│ Access │ │ - Browser only │
└────────┘ │ - Limited apps │
│ - Remediation msg │
└──────────────────┘Workflow 5: Incident Response with ZPA
Alert Triggered (SIEM/SOAR)
│
v
┌──────────────────┐
│ 1. Triage │
│ - Review ZPA logs │
│ - Identify user │
│ - Identify app │
│ - Classify event │
└───────┬──────────┘
v
┌──────────────────┐
│ 2. Containment │
│ - Revoke user │
│ access in ZPA │
│ - Isolate app │
│ segment │
│ - Block device │
│ via posture │
└───────┬──────────┘
v
┌──────────────────┐
│ 3. Investigation │
│ - Pull session │
│ logs from ZPA │
│ - Correlate with │
│ IdP/EDR/SIEM │
│ - Map lateral │
│ movement │
└───────┬──────────┘
v
┌──────────────────┐
│ 4. Recovery │
│ - Update policies │
│ - Re-enable access│
│ - Post-incident │
│ review │
└──────────────────┘Scripts 2
agent.py5.5 KB
#!/usr/bin/env python3
"""Agent for auditing Zscaler Private Access (ZPA) zero trust configuration via API."""
import argparse
import json
import os
import requests
from datetime import datetime, timezone
ZPA_BASE = os.environ.get("ZPA_BASE_URL", "https://config.private.zscaler.com")
def authenticate(client_id, client_secret, customer_id):
"""Authenticate to Zscaler ZPA API."""
url = f"{ZPA_BASE}/signin"
payload = {"client_id": client_id, "client_secret": client_secret}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
resp = requests.post(url, data=payload, headers=headers, timeout=30)
resp.raise_for_status()
token = resp.json()["access_token"]
print("[*] Authenticated to ZPA API")
return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
def list_app_segments(headers, customer_id):
"""List ZPA application segments."""
url = f"{ZPA_BASE}/mgmtconfig/v1/admin/customers/{customer_id}/application"
resp = requests.get(url, headers=headers, timeout=30)
resp.raise_for_status()
apps = resp.json().get("list", [])
print(f"\n[*] Application Segments: {len(apps)}")
findings = []
for app in apps:
bypass = app.get("bypassType", "NEVER")
if bypass != "NEVER":
findings.append({"name": app["name"], "bypass": bypass, "severity": "HIGH"})
print(f" {app['name']} - bypass={bypass}, enabled={app.get('enabled', False)}")
return apps, findings
def list_server_groups(headers, customer_id):
"""List server groups and their connectors."""
url = f"{ZPA_BASE}/mgmtconfig/v1/admin/customers/{customer_id}/serverGroup"
resp = requests.get(url, headers=headers, timeout=30)
resp.raise_for_status()
groups = resp.json().get("list", [])
print(f"\n[*] Server Groups: {len(groups)}")
for g in groups:
connectors = g.get("connectors", [])
print(f" {g['name']} - {len(connectors)} connectors, enabled={g.get('enabled')}")
return groups
def list_access_policies(headers, customer_id):
"""List ZPA access policies to verify least-privilege enforcement."""
url = f"{ZPA_BASE}/mgmtconfig/v1/admin/customers/{customer_id}/policySet/rules"
resp = requests.get(url, headers=headers, timeout=30)
resp.raise_for_status()
rules = resp.json().get("list", [])
findings = []
print(f"\n[*] Access Policy Rules: {len(rules)}")
for r in rules:
action = r.get("action", "")
conditions = r.get("conditions", [])
if action == "ALLOW" and not conditions:
findings.append({"rule": r.get("name"), "issue": "ALLOW with no conditions",
"severity": "CRITICAL"})
print(f" [!] {r.get('name')}: ALLOW without conditions")
else:
print(f" {r.get('name')}: action={action}, conditions={len(conditions)}")
return rules, findings
def check_connector_health(headers, customer_id):
"""Check connector enrollment and health status."""
url = f"{ZPA_BASE}/mgmtconfig/v1/admin/customers/{customer_id}/connector"
resp = requests.get(url, headers=headers, timeout=30)
resp.raise_for_status()
connectors = resp.json().get("list", [])
issues = []
for c in connectors:
status = c.get("currentVersion", "unknown")
enabled = c.get("enabled", False)
if not enabled:
issues.append({"connector": c.get("name"), "issue": "disabled", "severity": "MEDIUM"})
print(f" {c.get('name')}: enabled={enabled}, version={status}")
print(f"[*] Connectors: {len(connectors)} total, {len(issues)} disabled")
return connectors, issues
def generate_report(apps, app_findings, policy_findings, connector_issues, output_path):
"""Generate ZPA audit report."""
report = {
"audit_date": datetime.now(timezone.utc).isoformat(),
"summary": {"app_segments": len(apps), "bypass_findings": len(app_findings),
"policy_findings": len(policy_findings),
"connector_issues": len(connector_issues)},
"bypass_findings": app_findings, "policy_findings": policy_findings,
"connector_issues": connector_issues,
}
with open(output_path, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"\n[*] Report saved to {output_path}")
def main():
parser = argparse.ArgumentParser(description="Zscaler ZPA Zero Trust Audit Agent")
parser.add_argument("action", choices=["apps", "servers", "policies", "connectors", "full-audit"])
parser.add_argument("--client-id", required=True)
parser.add_argument("--client-secret", required=True)
parser.add_argument("--customer-id", required=True)
parser.add_argument("-o", "--output", default="zpa_audit.json")
args = parser.parse_args()
headers = authenticate(args.client_id, args.client_secret, args.customer_id)
if args.action == "apps":
list_app_segments(headers, args.customer_id)
elif args.action == "servers":
list_server_groups(headers, args.customer_id)
elif args.action == "policies":
list_access_policies(headers, args.customer_id)
elif args.action == "connectors":
check_connector_health(headers, args.customer_id)
elif args.action == "full-audit":
apps, af = list_app_segments(headers, args.customer_id)
list_server_groups(headers, args.customer_id)
_, pf = list_access_policies(headers, args.customer_id)
_, ci = check_connector_health(headers, args.customer_id)
generate_report(apps, af, pf, ci, args.output)
if __name__ == "__main__":
main()
process.py15.7 KB
#!/usr/bin/env python3
"""
ZTNA Deployment Readiness Assessment and ZPA Configuration Validator
This script performs pre-deployment checks for Zscaler Private Access (ZPA)
implementation, validates existing configurations, and generates deployment
readiness reports.
"""
import json
import csv
import socket
import ssl
import subprocess
import sys
import ipaddress
from datetime import datetime
from pathlib import Path
from typing import Optional
def check_dns_resolution(fqdn: str) -> dict:
"""Validate DNS resolution for application FQDNs."""
result = {"fqdn": fqdn, "resolved": False, "addresses": [], "error": None}
try:
addresses = socket.getaddrinfo(fqdn, None)
result["resolved"] = True
result["addresses"] = list(set(addr[4][0] for addr in addresses))
except socket.gaierror as e:
result["error"] = str(e)
return result
def check_port_connectivity(host: str, port: int, timeout: int = 5) -> dict:
"""Test TCP connectivity to application endpoints."""
result = {
"host": host,
"port": port,
"reachable": False,
"latency_ms": None,
"error": None,
}
start = datetime.now()
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
sock.connect((host, port))
elapsed = (datetime.now() - start).total_seconds() * 1000
result["reachable"] = True
result["latency_ms"] = round(elapsed, 2)
sock.close()
except (socket.timeout, ConnectionRefusedError, OSError) as e:
result["error"] = str(e)
return result
def check_tls_certificate(host: str, port: int = 443) -> dict:
"""Validate TLS certificate for HTTPS applications."""
result = {
"host": host,
"port": port,
"valid": False,
"issuer": None,
"subject": None,
"expiry": None,
"days_remaining": None,
"error": None,
}
try:
context = ssl.create_default_context()
with socket.create_connection((host, port), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=host) as ssock:
cert = ssock.getpeercert()
result["valid"] = True
result["issuer"] = dict(x[0] for x in cert.get("issuer", []))
result["subject"] = dict(x[0] for x in cert.get("subject", []))
expiry_str = cert.get("notAfter", "")
if expiry_str:
expiry = datetime.strptime(expiry_str, "%b %d %H:%M:%S %Y %Z")
result["expiry"] = expiry.isoformat()
result["days_remaining"] = (expiry - datetime.utcnow()).days
except Exception as e:
result["error"] = str(e)
return result
def validate_app_segment_config(config: dict) -> list:
"""Validate application segment configuration against best practices."""
issues = []
if not config.get("name"):
issues.append({"severity": "critical", "message": "Application segment name is missing"})
domains = config.get("domains", [])
ips = config.get("ip_addresses", [])
if not domains and not ips:
issues.append({
"severity": "critical",
"message": "No domains or IP addresses defined for segment"
})
ports = config.get("tcp_ports", []) + config.get("udp_ports", [])
if not ports:
issues.append({"severity": "critical", "message": "No ports defined for segment"})
for port_range in config.get("tcp_ports", []):
if "-" in str(port_range):
start, end = port_range.split("-")
if int(end) - int(start) > 100:
issues.append({
"severity": "warning",
"message": f"Wide port range {port_range} violates least-privilege. Consider narrowing."
})
if not config.get("server_group"):
issues.append({
"severity": "warning",
"message": "No server group assigned. High availability not configured."
})
for ip in ips:
try:
network = ipaddress.ip_network(ip, strict=False)
if network.prefixlen < 24:
issues.append({
"severity": "warning",
"message": f"Broad IP range {ip} (/{network.prefixlen}). Consider narrowing to specific hosts."
})
except ValueError:
pass
if config.get("bypass_type") == "always":
issues.append({
"severity": "critical",
"message": "Bypass is set to 'always'. Traffic will not be inspected."
})
return issues
def validate_access_policy(policy: dict) -> list:
"""Validate access policy configuration."""
issues = []
if not policy.get("name"):
issues.append({"severity": "critical", "message": "Policy name is missing"})
if not policy.get("conditions"):
issues.append({
"severity": "critical",
"message": "No conditions defined. Policy grants unrestricted access."
})
conditions = policy.get("conditions", {})
if not conditions.get("user_groups") and not conditions.get("users"):
issues.append({
"severity": "warning",
"message": "No user or group conditions. Consider restricting by group."
})
if not conditions.get("posture_profiles"):
issues.append({
"severity": "warning",
"message": "No device posture profile attached. Unmanaged devices may access."
})
if policy.get("action") == "allow" and not conditions.get("saml_attributes"):
issues.append({
"severity": "info",
"message": "Consider adding SAML attribute conditions for finer-grained access."
})
app_segments = policy.get("app_segments", [])
if len(app_segments) > 20:
issues.append({
"severity": "warning",
"message": f"Policy covers {len(app_segments)} segments. Consider splitting for manageability."
})
return issues
def generate_app_inventory_csv(apps: list, output_path: str) -> str:
"""Generate a CSV inventory of applications for ZPA migration planning."""
fieldnames = [
"app_name", "fqdn", "ip_address", "port", "protocol",
"user_groups", "criticality", "current_access_method",
"migration_wave", "zpa_segment_name", "status"
]
with open(output_path, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for app in apps:
writer.writerow({field: app.get(field, "") for field in fieldnames})
return output_path
def assess_deployment_readiness(config: dict) -> dict:
"""Perform comprehensive deployment readiness assessment."""
report = {
"timestamp": datetime.now().isoformat(),
"overall_status": "ready",
"checks": [],
"summary": {"critical": 0, "warning": 0, "info": 0, "passed": 0},
}
# Check IdP configuration
idp = config.get("identity_provider", {})
if idp.get("type") in ["saml", "oidc"]:
report["checks"].append({
"category": "Identity Provider",
"check": "IdP type configured",
"status": "passed",
"details": f"Type: {idp['type']}"
})
report["summary"]["passed"] += 1
else:
report["checks"].append({
"category": "Identity Provider",
"check": "IdP type configured",
"status": "critical",
"details": "No IdP integration configured"
})
report["summary"]["critical"] += 1
if idp.get("scim_enabled"):
report["checks"].append({
"category": "Identity Provider",
"check": "SCIM provisioning enabled",
"status": "passed",
"details": "Automated user/group sync active"
})
report["summary"]["passed"] += 1
else:
report["checks"].append({
"category": "Identity Provider",
"check": "SCIM provisioning enabled",
"status": "warning",
"details": "Manual user management required without SCIM"
})
report["summary"]["warning"] += 1
# Check App Connectors
connectors = config.get("app_connectors", [])
if len(connectors) >= 2:
report["checks"].append({
"category": "App Connectors",
"check": "High availability",
"status": "passed",
"details": f"{len(connectors)} connectors deployed"
})
report["summary"]["passed"] += 1
elif len(connectors) == 1:
report["checks"].append({
"category": "App Connectors",
"check": "High availability",
"status": "warning",
"details": "Single connector. Deploy at least 2 for HA."
})
report["summary"]["warning"] += 1
else:
report["checks"].append({
"category": "App Connectors",
"check": "High availability",
"status": "critical",
"details": "No App Connectors configured"
})
report["summary"]["critical"] += 1
# Check application segments
segments = config.get("app_segments", [])
for seg in segments:
seg_issues = validate_app_segment_config(seg)
for issue in seg_issues:
report["checks"].append({
"category": f"App Segment: {seg.get('name', 'unknown')}",
"check": issue["message"],
"status": issue["severity"],
"details": ""
})
report["summary"][issue["severity"]] += 1
if not seg_issues:
report["summary"]["passed"] += 1
# Check access policies
policies = config.get("access_policies", [])
if not policies:
report["checks"].append({
"category": "Access Policies",
"check": "Policies defined",
"status": "critical",
"details": "No access policies configured"
})
report["summary"]["critical"] += 1
for pol in policies:
pol_issues = validate_access_policy(pol)
for issue in pol_issues:
report["checks"].append({
"category": f"Policy: {pol.get('name', 'unknown')}",
"check": issue["message"],
"status": issue["severity"],
"details": ""
})
report["summary"][issue["severity"]] += 1
if not pol_issues:
report["summary"]["passed"] += 1
# Check SIEM integration
siem = config.get("siem_integration", {})
if siem.get("enabled"):
report["checks"].append({
"category": "Monitoring",
"check": "SIEM integration",
"status": "passed",
"details": f"Streaming to {siem.get('type', 'unknown')}"
})
report["summary"]["passed"] += 1
else:
report["checks"].append({
"category": "Monitoring",
"check": "SIEM integration",
"status": "warning",
"details": "No SIEM integration. Access events not centrally monitored."
})
report["summary"]["warning"] += 1
if report["summary"]["critical"] > 0:
report["overall_status"] = "not_ready"
elif report["summary"]["warning"] > 3:
report["overall_status"] = "ready_with_warnings"
return report
def connectivity_scan(targets: list) -> list:
"""Scan application endpoints for connectivity from App Connector perspective."""
results = []
for target in targets:
host = target.get("host", "")
ports = target.get("ports", [])
dns_result = check_dns_resolution(host) if not host.replace(".", "").isdigit() else None
for port in ports:
conn_result = check_port_connectivity(host, port)
tls_result = None
if port == 443:
tls_result = check_tls_certificate(host, port)
results.append({
"host": host,
"port": port,
"dns": dns_result,
"connectivity": conn_result,
"tls": tls_result,
})
return results
def generate_migration_plan(apps: list) -> dict:
"""Generate a phased VPN-to-ZTNA migration plan."""
waves = {
"wave_1": {"name": "Low-risk Web Apps", "apps": [], "duration_weeks": 2},
"wave_2": {"name": "Business-critical Web Apps", "apps": [], "duration_weeks": 3},
"wave_3": {"name": "Non-web TCP/UDP Apps", "apps": [], "duration_weeks": 3},
"wave_4": {"name": "Legacy Applications", "apps": [], "duration_weeks": 4},
}
for app in apps:
criticality = app.get("criticality", "medium").lower()
protocol = app.get("protocol", "https").lower()
legacy = app.get("is_legacy", False)
if legacy:
waves["wave_4"]["apps"].append(app)
elif protocol not in ["http", "https"]:
waves["wave_3"]["apps"].append(app)
elif criticality in ["high", "critical"]:
waves["wave_2"]["apps"].append(app)
else:
waves["wave_1"]["apps"].append(app)
total_weeks = sum(w["duration_weeks"] for w in waves.values())
return {
"total_duration_weeks": total_weeks,
"waves": waves,
"generated": datetime.now().isoformat(),
}
def main():
"""Run the ZTNA deployment readiness assessment."""
import argparse
parser = argparse.ArgumentParser(
description="ZPA Deployment Readiness Assessment Tool"
)
parser.add_argument(
"--config", type=str, help="Path to ZPA configuration JSON file"
)
parser.add_argument(
"--scan", type=str, help="Path to targets JSON for connectivity scan"
)
parser.add_argument(
"--inventory", type=str, help="Path to app inventory JSON for CSV generation"
)
parser.add_argument(
"--migrate", type=str, help="Path to app list JSON for migration planning"
)
parser.add_argument(
"--output", type=str, default="report.json", help="Output file path"
)
args = parser.parse_args()
if args.config:
with open(args.config) as f:
config = json.load(f)
report = assess_deployment_readiness(config)
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"Readiness report: {report['overall_status']}")
print(f" Critical: {report['summary']['critical']}")
print(f" Warnings: {report['summary']['warning']}")
print(f" Passed: {report['summary']['passed']}")
print(f"Report saved to {args.output}")
elif args.scan:
with open(args.scan) as f:
targets = json.load(f)
results = connectivity_scan(targets)
with open(args.output, "w") as f:
json.dump(results, f, indent=2)
reachable = sum(1 for r in results if r["connectivity"]["reachable"])
print(f"Scan complete: {reachable}/{len(results)} endpoints reachable")
print(f"Results saved to {args.output}")
elif args.inventory:
with open(args.inventory) as f:
apps = json.load(f)
csv_path = args.output.replace(".json", ".csv")
generate_app_inventory_csv(apps, csv_path)
print(f"Inventory CSV saved to {csv_path}")
elif args.migrate:
with open(args.migrate) as f:
apps = json.load(f)
plan = generate_migration_plan(apps)
with open(args.output, "w") as f:
json.dump(plan, f, indent=2)
for wave_id, wave in plan["waves"].items():
print(f" {wave_id}: {wave['name']} - {len(wave['apps'])} apps ({wave['duration_weeks']} weeks)")
print(f"Migration plan saved to {args.output}")
else:
parser.print_help()
if __name__ == "__main__":
main()