zero trust architecture

Implementing Zero Trust Network Access with Zscaler

Implement Zero Trust Network Access using Zscaler Private Access (ZPA) to replace traditional VPN with identity-based, context-aware access to private applications through the Zscaler Zero Trust Exchange.

network-accessvpn-replacementzero-trustzscalerztna
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

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

  1. Client Connector: Lightweight agent on user endpoints that establishes outbound TLS tunnels to the nearest ZPA Service Edge
  2. ZPA Service Edge: Cloud-hosted broker (or Private Service Edge on-premises) that stitches user-to-app connections after policy evaluation
  3. App Connector: Lightweight VM deployed in the application environment that creates outbound tunnels to the Service Edge
  4. 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 Application

Key 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

  1. 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
  2. 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
  3. Create Server Groups

    • Group App Connectors by geographic location or application tier
    • Configure health check intervals and failover behavior

Phase 2: Application Segmentation

  1. 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
  2. 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

  1. 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
  2. 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

  1. 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

  1. 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
  2. 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
Source materials

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

standards.md4.9 KB

Standards and Frameworks Reference

NIST SP 800-207: Zero Trust Architecture

Core Tenets Applicable to ZTNA

  1. All data sources and computing services are considered resources - Every application behind ZPA is treated as a discrete resource requiring explicit access grants
  2. All communication is secured regardless of network location - ZPA encrypts all tunnels end-to-end regardless of whether users are on-premises or remote
  3. 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
  4. Access to resources is determined by dynamic policy - ZPA policies incorporate identity, device posture, location, and behavioral signals
  5. 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
  6. 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 documentation

Workflow 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
Display-only source. This catalog never executes bundled scripts.
#!/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
Display-only source. This catalog never executes bundled scripts.
#!/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()

Assets 1

template.mdtext/markdown · 3.5 KB
Keep exploring