PAC (Proxy-Auto Config) File
Wie weleens met proxyservers werkt weet dat het niet altijd even gemakkelijk is om een cliënt de juiste route naar een resource te laten kiezen. Als cliënt naar A gaat dan moet deze door de Proxy1, als een gebruiker naar B wilt moet deze door Proxy2 en als een cliënt naar C gaat dan moet deze de proxy omzeilen en mag hij direct naar het internet (of door de firewall). Binnen een uitgebreide proxy kan veel geregeld worden. Omdat soms bepaalde configuraties erg ingewikkeld kunnen worden is het ideaal om een cliënt te kunnen voorzien van een proxy configuratiescript die bepaalde taken voor zijn rekening neemt. Het voordeel hiervan is dat niet elke cliënt van eenzelfde configuratiescript voorzien hoeft te worden. Dit zou je b.v. op basis van een group policy kunnen deployen. Deze proxy configuratiescripts noemen we PAC Files ofwel Proxy-Auto Config files. In deze post kijken we naar het PAC File formaat zodat je meer inzicht krijgt in de opbouw en makkelijker zelf aan de slag kunt als je PAC Files moet maken.
Webbrowsers en ander user agents kiezen hun route naar een specifieke resource op basis van een aantal gegevens. Denk hierbij aan de routetabel, DNS, host files en uiteraard de proxy configuratie. In dit voorbeeld gaan we uit van een zogenaamde “Web Proxy”. De Web Proxy helpt Cliënts om vanuit het lokale netwerk een externe resource te benaderen. Het gebruik van een proxyserver heeft diverse voordelen. Denk bijvoorbeeld aan een extra beveiligingslaag, ontlasting voor de processorkracht van de firewall, snelheid door caching, en reductie van het aantal benodigde IPv4 adressen.
Een proxyserver kan transparant zijn. Dit betekend dat de proxyserver zo is ingesteld dat deze willekeurige IP adressen uitgeeft aan de cliënts. Op deze manier blijven cliënts “anoniem” en zorgt het ervoor dat resources bereikbaar worden die voorheen (b.v. door landcensuur) niet bereikbaar waren.
Een derde soort proxy welke vaak voorkomt is de zogenaamde “Reverse Proxy”. Zoals de naam al impliceert is dit een server die van buiten naar binnen werkt en dus het verkeer “de andere kant” op routeert. Een reverse proxy wordt ingezet om diverse redenen zoals load balancing, failover en SSL offloading.
Als we een normale web proxy gebruiken dan is deze normaliter niet transparant. Met andere woorden, het is voor de gebruiker zichtbaar dat er een proxyserver gebruikt wordt:

Bij een transparante proxy is het niet zichtbaar dat er een proxy gebruikt wordt. Al het verkeer wordt simpelweg op de route door de proxy gestuurd waar het verkeer gefilterd wordt. In kleinere netwerken zal de firewall (gateway) vaak de proxy taak op zich nemen. Het voordeel is dat er geen proxy ingesteld hoeft te worden op de computer en dat altijd al het verkeer gefilterd wordt.
Een proxyserver zorgt o.a. voor het filteren van informatie, een beperking in de benodigde publieke IP adressen, betere prestaties en beveiliging van het netwerk. Ook kunnen proxyserver bandbreedte beperkingen toepassen en verkeer scannen op virussen en malware.
Door gebruik te maken van een PAC File kun je gespecificeerd verkeer door de proxy sturen en ander verkeer een andere route opsturen zoals b.v. direct naar de firewall of het internet.
PAC File
PAC staat voor “Proxy Auto Config” en is een bestand welke bedoeld is om proxy regels en condities toe te passen. De PAC File stamt uit 1996 en is uitgevonden door Netscape voor de Netscape Navigator 2.0 browser. Een PAC-bestand is:
- Ondersteund in de meeste browsers
- Eenvoudig te beheren
- Flexibel
- Uitbreidbaar
- Inzetbaar op mobiele apparaten die dit ondersteunen
Het grote voordeel van een PAC File is dat het kan worden opgeslagen op een (of meerdere) server binnen het netwerk. Kleinere netwerken kunnen het bestand op de proxy zelf opslaan. Om load-balancing toe te passen kan de PAC File op meerdere servers worden opgeslagen waardoor meteen fail-over mogelijk gemaakt wordt. Deze load balancing / failover code kan direct in de PAC File opgenomen worden.
In tegenstelling tot een normale proxy biedt de PAC File kritieke beveiliging en zorgt het ervoor dat verkeer altijd proxied maar dat hier uitzonderingen op gemaakt kunnen worden. In grote netwerken wordt al het internet verkeer (HTTP(S) / (S)FTP(S)) verkeer via een proxy verzonden en het intranet verkeer wordt direct naar de bestemming gerouteerd. Met een PAC File zorg je er dus voor dat er uitzonderingen mogelijk zijn. Een PAC File biedt een flexibele, eenvoudig te onderhouden, scriptgestuurde methode voor het beheren van de routering van webaanvragen.
Let wel, een device kan slechts 1 PAC File hebben. De locatie van het PAC bestand kan op verschillende manieren naar een device gepushed worden. Denk hierbij aan scripting, register changes en uiteraard via Group Policy. Het PAC bestand kan uiteraard ook handmatig ingegeven worden. Als een PAC bestand wordt gebruikt dan is deze zichtbaar binnen de “LAN Instellingen” waar ook de proxy server ingegeven kan worden. Bij gebruik van een PAC bestand kan de proxy setting uitgeschakeld worden omdat het PAC bestand bepaald wat er over de proxy gaat. Als beide opgegeven staat zal het PAC bestand al het verkeer afvangen.

De opbouw van een PAC File
Een PAC File is een JavaScript bestand welke regels en condities beschrijft. Een PAC bestand bestaat minimaal uit:
- Een functie
- Proxy Server variabele
- Proxy regels
- Fallback instructies
- Comments
Voordat we naar de functies kunnen kijken is het zaak om een aantal best-practises in ogenschouw te nemen.
- Comment, comment, comment. Dit is een generieke best practise voor ELK script. Zorg ervoor dat je duidelijke comments plaatst zodat iedereen (ook de mensen die na jou het PAC bestand gaan beheren) weet wat er in het bestand gebeurt.
- Bouw het PAC bestand op in “system default encoding” en niet in “Unicode encoding” zoals UTF8. Gebruik dus een editor zonder opmaak zoals “Notepad”.
- Controleer of JavaScript functies ondersteund worden alvorens je ze gaat toepassen.
- Gebruik reguliere expressies om het bestand multifunctioneel en zo klein mogelijk te houden.
- Hou het bestand zo klein mogelijk.
- Dubbel-check de gebruikte IP adressen en URL’s alvorens je deze gaat inzetten.
- Plaats de regels die het vaakst / eerst gebruikt worden bovenaan in het bestand ( het bestand wordt van boven naar beneden gelezen).
- Localhosts en loopback adressen moeten niet door de proxy gestuurd worden. Zet deze als uitzondering bovenaan in je PAC File.
- Probeer zo min mogelijk “if / else” loops te genereren.
- ”If” statements die slechts bestaan uit 1 regel hebben geen begin- en eind bracket nodig ({ }).
- Vermeid zoveel mogelijk her gebruik van DNS loopups omdat dit vertraging op kan leveren. Dus gebruik de volgende functies alleen waar nodig: isResolvable(), dnsResolve() en isInNet()
- Beperk het gebruik van externe variabele en functies.
- Noteer nooit geen gevoelige data in de PAC Files. Iedereen kan het PAC bestand inzien.
- Bescherm het PAC bestand met de juiste rechten zodat deze niet overschreven kan worden.
- Pas sortering toe (alfabetisch) om het bestand zo duidelijk mogelijk te houden en om later snel terug te kunnen vinden of informatie aanwezig is.
- Groepeer “common return values” samen in een enkele conditionele “if” controle.
- Als er slechts 1 proxyserver gebruikt wordt zorg er dan voor dat het statische IP adres van de proxyserver geretourneerd wordt. Hiermee voorkom je overbodige DNS lookups.
- Als je het PAC bestand gaat publiceren voer hier dan een dubbel-check op uit. Controleer dat alle waardes correct zijn en dat de gebruikte JavaScript code foutloos is.
Ok… goede leerlessen! Maar laten we eens een voorbeeld PAC File bekijken (een heel kleintje uiteraard):
//Bepaal welk verkeer door de proxy moet gaan // functionFindProxyForURL(url, host) { //Sluit FTP uit // if (url.substring(0, 4) == "ftp:") { return "DIRECT"; } //Sluit het interne netwerk uit // if (isInNet(host, "192.168.1.0", "255.255.255.0")|| isInNet(host, "192.168.2.254", "255.255.255.255")) { return "DIRECT"; } //Sluit het volgende domein uit // if (dnsDomainIs(host, "mail.mijndomein.nl") { return "DIRECT"; } return "PROXY mijnproxy.local:9400; mijntweedeproxy.local:9400; DIRECT"; } |
Zoals je ziet is dit PAC bestand voorzien van duidelijke comments welke aangegeven worden met een dubbele slash “//”. De functie (bovenaan) die gebruikt wordt is “FindProxyForURL”. Vervolgens gaan we FTP verkeer en interne subnetten uitsluiten van de proxy middels het “return “DIRECT”;” commando. Al het verkeer dat niet aan de eerder genoemde voorwaarden voldoet wordt naar de proxy (primary of secondary) gestuurd.
De PAC File bestaat dus uit functies, argumenten en bijbehorende parameters. De functies bepalen het doel van de regel en de argumenten bepalen hoe dat doel behaald kan worden. De variabele waardes van het argument noemen we dan de parameters.
Wat we ook zien in het voorbeeld is een “if” loop en dat waardes binnen de “if” loop gescheiden zijn met een dubbele streep “||”. Dit is de OR waarde. Als het adres voldoet aan een host binnen het 192.168.1.0 subnet OF als de host “192.168.2.254” verkeer routeert, routeer deze dan om de proxy server heen.
Je ziet… we zijn dus echt aan het scripten 🙂
PAC File Functies
De functies in de PAC File zorgen er dus voor dat je het verkeer kunt controleren en op basis van de uitkomst een beslissing kunt nemen. Daarom is het goed om eerst eens naar de verschillende functies te kijken. Dit zijn de meest voorkomende:
- dateRange()
Deze functie geeft een datum bereik aan. Bijvoorbeeld, gebruik proxy A v.a. januari tot juni en gebruik proxy B v.a. juli tot december. - dnsDomainLevels()
Deze functie telt de punten (.) in een domeinnaam. Hierdoor weet de functie hoeveel DNS domain niveaus er gebruikt worden. Dit kan handig zijn om korte domeinnamen zonder punt, welke meestal intern zijn direct door te sturen zonder deze door de proxy te halen. - dnsResolve()
De naam van de functie zegt het al. Deze functie zet een DNS naam om naar een IP adres. Door deze functie te gebruiken kun je het aantal DNS lookups beperken. - dnsDomainIs()
Deze functie evalueert de gegeven domeinnaam. Bestaat de domeinnaam zal de functie “true” retourneren en anders “false”. - localHostOrDomainIs()
Deze functie valideert de opgegeven hostname. Als de opgegeven host bestaat zal de functie “true” genereren. - isInNet()
Deze functie resolved een IP adres of een domeinnaam en controleert deze tegen een IP range. Als het IP adres of de hostnaam voorkomt in de opgegeven IP range dan zal de functie “true” retourneren. Als een hostname door deze functie gaat zal deze eerst worden omgezet naar een IP adres. - isPlainHostName()
Deze functie retourneert alleen “true” als de opgegeven hostname geen punten bevat en dus logischerwijs een locale hostname is (https://intranet). - isResolvable()
Deze functie retourneert “true” als de opgegeven domeinnaam resolvable is (en dus omgezet kan worden naar een IP adres). Let wel op, als de opgegeven domeinnaam niet beschikbaar is kan de browser tijdelijk bevriezen. - myIpAddress()
Deze functie retourneert het IP adres van de host machine. - shExpMatch()
Deze functie in een interessante. Deze functie matched een hostname of domeinnaam tegen een “shell regular expression” en als deze matchen zal de functie als “true” retourneren. Shell regular expressions lijken erg op “normale” regular expressions maar er zijn kleine verschillen. Shell regular expressions zijn wat beperkter. Bekijk hier meer informatie over shell regular expressions. Zo kunnen we bijvoorbeeld vrij simpel voor HTTPS verkeer een andere proxy selecteren:if (shExpMatch(url, "https://*")) return "PROXY sslproxy.mijnbedrijf.nl:8443";
- timeRange()
De “timeRange” functie is een beetje vergelijkbaar met de “dateRange” functie. Hier kunnen we echter geen datumrange specificeren maar een tijdsspanne. - weekdayRange()
De “weekdayRange” functie is ook een functie waarmee we een afweging kunnen maken op basis van tijd. Maar dan niet middels voorgedefinieerde datums of tijden maar op basis van weekdagen. Bijvoorbeeld, gebruik van maandag tot vrijdag proxy A en in het weekeind proxy B.
PAC File Voorbeelden
Nu we weten welke functie we hebben is het zaak deze te combineren in een PAC File. Laten we eerst een paar kleine voorbeelden bekijken en vervolgens een situatie schetsen waar we dan een complete PAC File voor maken.
Voorbeeld 1:
In dit eerste voorbeeld willen we dat lokale hosts niet door de proxy gaan maar alle andere hosts wel.
function FindProxyForURL(url, host) { if (isPlainHostName(host)) return "DIRECT"; else return "PROXY mijnproxy:80"; } |
We beginnen de PAC File met de basisfunctie “FindProxyForURL”. Deze functie zorgt ervoor dat elke URL die door de browser verstuurd wordt 2 waardes retourneert. De URL en de Hostnaam. De hostnaam wordt alleen voor het gemak uit de URL gefilterd. Dit is het gedeelte tussen de “:” en de eerste “.” of “/”.
Vervolgens controleren we met “isPlainHostName” of de hostnaam een lokale hostnaam is of niet. Zo ja, dan wordt het verkeer niet door de proxy gestuurd en anders wel.
Voorbeeld 2:
Bovenstaande voorbeeld kunnen we uitbreiden. Nu gaan we al het verkeer door de proxy sturen behalve als de opgevraagde host een lokale hostnaam is. Maar als het verkeer naar een “mijnbedrijf.nl” pagina gaat mag het ook langst de proxy behalve als het www- of home.mijnbedrijf.nl is want dan moet het weer wel langst de proxy.
function FindProxyForURL(url, host) { if ((isPlainHostName(host) || dnsDomainIs(host, ".mijnbedrijf.nl")) && !localHostOrDomainIs(host, "www.mijnbedrijf.nl") && !localHostOrDoaminIs(host, "home.mijnbedrijf.nl")) return "DIRECT"; else return "PROXY mijnproxy:80"; } |
Je ziet dat bovenstaande functie is uitgebreid. We controleren nu op een lokale hostnaam OF (||) op een host van .mijnbedrijf.nl welke (&&) NIET (!) voldoet aan www.mijnbedrijf.nl of home.mijnbedrijf.nl. Als de host hieraan voldoet (en de functie “true” retourneert dan verstuur het verkeer niet door de proxy (DIRECT) en anders verstuur het verkeer door de proxy.
Voorbeeld 3:
In dit voorbeeld mogen hosts die binnen een specifiek intern subnet vallen een directe verbinding maken. Alle overige hosts moeten via de proxy.
function FindProxyForURL(url, host) { if (isInNet(host, "10.10.9.16", "255.255.255.248")) return "DIRECT"; else return "PROXY mijnproxy:80"; } |
We zien hier de functie “isInNet” aan het werk waarbij we een 29-bits subnet opgeven (10.10.9.16 – 10.10.9.23). Als de host welke aangeroepen wordt zich in dit subnet bevind dan mag deze direct verbinden zonder tussenkomst van de proxy.
Voorbeeld 4:
In dit vierde voorbeeld willen we bovenstaande voorbeeld opnieuw gebruiken maar dan met variabelen.
var COMP_PROXY1 = mijnproxy:80 var CLIENT_NETWORKS = [ ["10.10.9.16", "255.255.255.248"] ]; function FindProxyForURL(url, host) { if (isInNet(host, CLIENT_NETWORKS)) return "DIRECT"; else return "PROXY COMP_PROXY1"; } |
Zoals je ziet doen we in bovenstaande voorbeeld exact hetzelfde als in voorbeeld 3 maar nu met variabelen. Het voordeel van variabelen is dat deze herbruikbaar zijn en dus voor waardes die vaker voorkomen handig zijn omdat dan niet keer op keer de waardes opnieuw gespecificeerd hoeven te worden. Ook kunnen variabelen de leesbaarheid een stuk verduidelijken.
Complete PAC file
Nu is het tijd om een complete PAC file te maken. Om dit bestand te kunnen maken moet heel duidelijk zijn wat we exact willen bewerkstellen. In dit scenario gaan we uit van het volgende.
Al het verkeer moet via de proxy, tenzij:
- het een localhost adres is
- de cliënt afkomstig is uit een intern client subnet: (192.168.10.0/24) & 192.168.20.0/24
- het een adres is van een interne host, in de netwerken: (172.16.0.0/12) & 192.168.0.0/16
- TeamViewer of WebExURL is
- Office365 URL is
- FTP verkeer is
In dit voorbeeld hebben we 2 proxy servers. We willen deze gebalanceerd gebruiken op basis van IP adres. Als het IP adres van de aanvrager “even” is dan gebruik proxy A en als het oneven is dan gebruik proxy B.
Om te achterhalen welke URL’s we voor Office365 nodig hebben gebruik ik de “Office 365 Proxy PAC Generator”. Dit PowerShell script kun je hier vinden.
Uiteindelijk is de informatie die we nodig hebben vrij simpel:

Deze gaan we dus gebruiken in ons volledige PAC bestand. Deze ziet er als volgt uit:
//Dit is de default PAC File voor mijnbedrijf.nl // //##### VARIABELEN: ##### // //CLIENT NETWERKEN //Hier specificeren we de netwerken waarin een cliënt moet zitten om via de proxy te kunnen gaan. var ClientNetworks = [ ["192.168.10.0","255.255.255.0"], ["192.168.20.0","255.255.255.0"] ]; // //LOKALE NETWERKEN //Hier specificeren we de lokale netwerken welke buiten de proxy om gaan: var LocalNetworks = [ ["172.16.0.0", "255.240.0.0"], ["192.168.0.0", "255.255.0.0"] ]; // //DIRECTE URLS //Hier specificeren we de URL’s welke we niet door de proxy willen laten gaan: var DirectURLs = new Array( "*.teamviewer.nl", "teamviewer.nl", "*.webex.com" "webex.com", "*.office365.com" "office365.com", ); // //PROXY SERVERS //Hier specificeren we alle proxy servers: var MIJNPROXY1 = "PROXY mijnproxy1.nl:80"; var MIJNPROXY2 = "PROXY mijnproxy2.nl:8080"; var NOPROXY = "DIRECT"; // //##### SCRIPT ##### // functionFindProxyForURL(url, host) { // // Even of Oneven: var mijnIP = myIpAddress(); var mijnOctet = myIP.split("."); var mijnHost = parseInt(myOctet[3]); var Even = (mijnHost===Math.floor(mijnHost/2)*2); // // Directe hostnamen hebben geen proxy nodig: if(isPlainHostName(host)) { return NOPROXY; } // // Locale Netwerken – proxy bypass if (shExpMatch(host, "/^\d+.\d+.\d+.\d+$/g")) { for (var loop=0; loop<LocalNetWorks.length; loop++) { var Network = LocalNetworks[loop][0]; var SubnetMask = LocalNetworks[loop][1]; if (isInNet(host, Network, SubnetMask)) { return NOPROXY; } } } // // Hostname – proxy bypass for (var loop=0; loop<DirectURLs.length; loop++) { var checksite = DirectURLs[loop]; if (shExpMatch(host, checksite)) { return NOPROXY; } } // // FTP – proxy bypass if (url.substring(0, 4) == "ftp:") { return NOPROXY; } // // Verstuur het verkeer over de juiste proxy op basis van IP var MijnIP = myIpAddress(); for (var loop=0; loop<ClientNetworks.length; loop++) { var Network = ClientsNetworks[loop][0]; var SubnetMask = ClientNetworks[loop][1]; if (isInNet(MijnIP), Network, SubnetMask)) { if (Even) { return MIJNPROXY1; } else { return MIJNPROXY2; } } } } |
In bovenstaande script vallen een aantal best-practices op, waaronder:
- Comments gebruikt om het e.e.a. te verduidelijken.
- Gestart met variabelen welke de leesbaarheid en aanpasbaarheid vergroten.
- Meest gebruikte regels zijn bovenaan geplaatst voor optimale snelheid.
- Code is netjes opgemaakt middels TAB indents. Ook dit verhoogt de leesbaarheid.
- De uitzonderingen worden eerst toegepast (NOPROXY) en vervolgens laten we al het overige verkeer, mits intern via de proxy verlopen.
Wat verder opvallend is aan dit script is de volgende regel:
if (shExpMatch(host, "/^\d+.\d+.\d+.\d+$/g")) {} |
Deze regel kijkt of de opgegeven host een IP adres is. Als dat zo is dan zal de regel verder gaan met het controleren of het IP adres op de lijst met lokale netwerken staan om de proxy te bypassen. De reden dat we deze controle doen is om te voorkomen dat de functie iedere keer een DNS lookup gaat doen. Dit resulteert in erg veel DNS lookups op het netwerk welke mogelijk vertragingen of storingen tot gevolg kunnen hebben.
Als het zo is dat je wel DNS lookup wilt dan moet je het codeblok als volgt aanpassen:
// Locale Netwerken – proxy bypass var HostIP = dnsResolve(host); for (var loop=0; loop<LocalNetWorks.length; loop++) { var Network = LocalNetworks[loop][0]; var SubnetMask = LocalNetworks[loop][1]; if (isInNet(HostIP, Network, SubnetMask)) { return NOPROXY; } } |
Zo zie je maar dat het schrijven van een goed doordachte PAC File aardig wat werk is. Het kost veel voorbereiding en testen. Maar als het vervolgens functioneert dan is een PAC File zelfs vandaag de dag nog een handige tool om proxy verkeer te managen. PAC Files zijn gemakkelijk aan te passen en overzichtelijk in gebruik. Het enige nadeel is dat je er wel een beetje programmeer / scripting kennis voor nodig hebt.
Hopelijk heeft deze post je een beetje geholpen als je een PAC File wilt maken of modificeren. Heel veel succes en please… geef me een like of deel mijn post met bronvermelding! Hier help je me ontzettend mee en dat houd me weer actief om vervolg posts te maken. Dankjewel!