The Only PAC File you will need v1.0

Hi All,

I prepared a PAC file with the help of Zscaler that will include most of the scenarios we face.
The comments and extra code added are to help you create your own PAC file.

Make sure to remove any comments from the PAC file to minimize the file size.

Do let me know if you want to update the PAC file or have any suggestions.

function FindProxyForURL(url, host) {

    //        ****************************************************************************
    //        This is an example PAC file that should be edited before being put to use.
    //        ****************************************************************************

    //        Consider the following:
    //         - Keep production PAC files small. Delete all comments if possible
    //         - Delete any examples or sections that do not fit your needs
    //         - Consolidate bypass criteria into fewer if() statements if possible
    //         - Be sure you are bypassing only traffic that *must* be bypassed
    //         - Be sure not to perform any DNS resolution in the PAC
    //         - Zscaler recommends sending bypassed internet traffic via on-premise proxy compared
    //           to the internet directly
    //         - Recommended PAC file size 25KB for best user experience

    //==============================Section I ==== Internal/Specific Destinations ============================== 

    //        Most special use IPv4 addresses (RFC 5735) are defined within this regex.
    var privateIP = /^(0|10|127|192\.168|172\.1[6789]|172\.2[0-9]|172\.3[01]|169\.254|192\.88\.99)\.[0-9.]+$/;
    var resolved_ip = dnsResolve(host);




    // If the url is https and the URL is an IP return direct, this can be useful if you want to bypass remote device that use a Public IP
    var get_url = url.substring(8, 21);
    var ip_checker = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
    if (ip_checker.test(get_url) && url.substring(0, 6) == "https:") {
        return "DIRECT";
    }

    //Bypass proxy for internal hosts
    //
    if (
        isInNet(host, "0.0.0.0", "255.0.0.0") ||
        isInNet(host, "10.0.0.0", "255.0.0.0") ||
        isInNet(host, "127.0.0.0", "255.0.0.0") ||
        isInNet(host, "169.254.0.0", "255.255.0.0") ||
        isInNet(host, "172.16.0.0", "255.240.0.0") ||
        isInNet(host, "192.168.0.0", "255.255.0.0")
    ) {
        return "DIRECT";
    }
    //Specific destinations can be bypassed here. Example lines for the host, and
    //domain provided. Replace with your specific information. 
    //Add internal domains that cannot be publicly resolved here.

    if (isPlainHostName(host) || (host == "host.example.com") ||
        shExpMatch(host, "*.example.com"))
        return "DIRECT";


    //============================== Section II ==== Website/URL Bypass or Block ============================== 
//######IMPORTANT NOTE: bypassed Traffic should be assessed for risk. There should be no reason to bypass traffic from ZTE=====

    if (shExpMatch(host, "*okta.com") || shExpMatch(host, "*oktacdn.com"))
        return "DIRECT";

    // you can use the PAC file to send the traffic to a black hole, in some cases, can be useful if you want to utilise it for blocking
    if (shExpMatch(host, "*example.url.com*")
    )
        return "PROXY proxy.testDomain.local:8080";// you can keep "proxy.testDomain.com" or replace it with any dead end destination

    // ============================== Section III ==== Bypasses Using DNS resolution ============================
//######IMPORTANT NOTE: bypassed Traffic should be assessed for risk. There should be no reason to bypass traffic from ZTE=====

    // If you have a website that is hosted both internally and externally,
    //If you want to bypass the proxy for the internal version only, use the following

    //Be sure to optimize DNS resolution. Only perform DNS resolution on the client if necessary. 
    //DNS resolution in the PAC file can cause performance issues while waiting for the DNS response and is then perceived as Browser/PAC/Proxy issue.

    /* Don't send non-FQDN*/
    if (shExpMatch(host("*.internal.domain"))) {
        if (privateIP.test(resolved_ip))
            return "DIRECT";
    }

    // you can use dnsDomainIs to match the domain of the hostname
    //not recommended to use it a lot as it could affect the performance
    if (
        (dnsDomainIs(host, "example.com"))
    ) {
        return "DIRECT";
    }

    // ============================== Section IV ==== Bypasses for Zscaler ===================================

    //        Go direct for queries about Zscaler infrastructure status 

    var trust = /^(trust|ips).(zscaler|zscalerone|zscalertwo|zscalerthree|zsdemo|zscalergov|zscloud|zsfalcon|zdxcloud|zdxpreview|zdxbeta|zspreview|zsdevel|zsbetagov|zscalerten|zdxten|zdxdevel|zdxgov|zsqa).(com|net)$/;
    if (trust.test(host) || /^trust.zscaler.us$/.test(host))
        return "DIRECT";

    // ============================== Section V ==== Bypasses for ZPA ===================================
    /* test with ZPA*/
    if (isInNet(resolved_ip, "100.64.0.0", "255.255.0.0"))
        return "DIRECT";

    // ============================== Section VI ==== DEFAULT FORWARDING ================================ 

    // Forward Traffic for sepcific websites that are Geolocation locked
    // List of datacenter can be retrieved from "config.zscaler.com"
    // Make sure to pick the currect ZEN based on your cloud e.g zscloud.net,zscalertwo.net...
    if (shExpMatch(host, "*example.com"))
        return "PROXY ams2-2.sme.zscaler.net:80";

    // list of supported country variables is in "https://help.zscaler.com/downloads/zia/documentation-knowledgebase/forwarding-your-traffic/pac-files/using-pac-files/writing-pac-file/zia-writing-pac-file-country-variable-names-codes.txt"
    //example: if the user is in the US, always send traffic to ZEN NYC3
    var country = "${COUNTRY}";
    if (shExpMatch(country, "United States")) {
        return "PROXY nyc3.sme.zscaler.net:80";
    }




    //        If your company has purchased a dedicated port, kindly use that in this file.
    //        Port 9400 is the default port, followed by 80. If that does not resolve, we send directly:
    //Default
    return "PROXY ${GATEWAY}:9400; PROXY ${SECONDARY_GATEWAY}:9400; PROXY ${GATEWAY}:80; PROXY ${SECONDARY_GATEWAY}:80; DIRECT";
    
    //For Dynamic ZEN
    // return "PROXY ${FX_GATEWAY}:9400; PROXY ${FX_SECONDARY_GATEWAY}:9400; PROXY ${FX_GATEWAY}:80; PROXY ${FX_SECONDARY_GATEWAY}:80; DIRECT";

    //if you have a subcloud
    //return "PROXY ${GATEWAY.organization_name.zscaler.net} 9400; ${SECONDARY_GATEWAY.organization_name.zscaler.net}:9400; ${GATEWAY.organization_name.zscaler.net}:80; PROXY ${SECONDARY_GATEWAY.organization_name.zscaler.net}:80; DIRECT";
}
3 Likes

I’d be really curious to know if your blackhole section works as expected and under what conditions. I tried this about a year ago (mind you I am ZCC-only, no tunnels or PAC-only users), and it didn’t work for me trying to blackhole client.wns.windows.com. I was trying to achieve a similar effect to using a Pi-hole DNS blocklist or hosts file.

I also really like the cleaned way of checking for Zscaler entries with RegEx.