Controlling Internet Access using a Pac File

Proxy Access Control (PAC) files solve a problem that is becoming more and more common as organizations spread out geographically and workers become more mobile. If this sounds like your company, and you’re an administrator concerned with how your users access the Internet and company intranets, keep reading. This might be exactly what you’re looking for.

Introduction

Controlling how users across an organization connect to the Internet and intranets can be a tricky thing to administer. You could have people at different physical locations that all want to go out through different proxies, internal sites that you need them to access directly and roaming users who sometimes work from their own connections at home.

This is where a PAC (Proxy Access Control) file comes in. With this one file you can control how all of your clients access the web. Most of the modern browsers will use a PAC file without any problems. The only exception I know of was the old Macs prior to OSX. I’ve not used one over the last few years so I don’t know if they still have this problem.

Basics of the PAC file

The PAC file contains one function called FindProxyForURL. This function is passed the parameters URL and host. URL is the address of the site that the browser is requesting, while host is the IP address of the client machine making the request.

Once all the logic of the PAC file has been read, the last thing it will do is return the correct proxy server to use (in the format of "PROXY IPAddress:PortNo") or if it is an address that can be reached internally, it will return DIRECT.

Below is a very simple example of a PAC file. Use notepad or your preferred text editor to create the file, and save it as proxy.pac.

function FindProxyForURL(url, host)

{

  //set the ip address of the proxy into a variable named proxy

  var proxy = "PROXY 192.168.0.1:8080";

 

  return proxy;

}

This very simple example of a PAC file will just re-direct traffic out through a proxy server running at 192.168.0.1 on port 8080. This of course is a very simple example but it should work. To test the PAC file drop it onto one of your internal web servers, and point your browser’s automatic config file at the URL, e.g.

http://webserver1/proxy.pac

Once you have set this, as long as the IP and port in the PAC file match yours, you should start using the logic contained in this file. How to distribute the PAC file to clients is discussed later in the article.

If that is working, you can now build more into the file to control what happens to certain sites and subnets.

{mospagebreak title=Setting the Proxy server based on the Client subnet}

One of the most useful things you can do is send different subnets through different proxies. This is very good for multiple sites that each have their own Internet connection.

To do this we use a simple if statement to test for the client’s IP address, and then set the proxy server based on what is returned, like this:

// Test for entire subnets

// if (isInNet(myIpAddress(), "192.168.0.0", "255.255.255.0"))

// proxy = "PROXY 192.168.0.1:8080";

This statement uses the function IsInNet to determine whether or not the client’s IP address matches the one you specify. In the above code if you are any client on the 192.168.0.0 subnet, the function will return true and set the proxy variable to be 192.168.0.1:8080.

You can also use a similar block of code to send individual clients to a certain proxy instead of the entire subnet. This could be useful if certain people are heavy Internet users and have a dedicated proxy.

// Test for individual clients

if (isInNet(myIpAddress(), "192.168.0.25", "255.255.255.255"))

  proxy = "PROXY 192.168.0.242:8080";

Of course if you want to use this on certain clients, they will have to have a static IP address.

Directing Traffic based on URL

Next we will test for a certain site URL, and if we find the user wants to access that URL we will send them down yet another proxy.

if (url.substring(0, 24) == "http://www.microsoft.com")

  proxy = "PROXY 192.168.0.242:8080";

This function tests the URL string passed into the PAC file for the website address. The 0, 24 parameters tell the function to start at position 0 from the left hand side of the string, and count 24 characters. If the string matches the function returns true and the proxy variable is set.

As we are using the Substring function, we can not only test for a specific web site, but specific protocols as well. For instance you might want all of your FTP traffic to go out over a different link from your normal HTTP and HTTPS traffic.

if (url.substring(0, 4) == "ftp:")

  proxy = "PROXY 192.168.0.241:8080";

{mospagebreak title=Making sure Internal Sites don’t use the Proxy}

Now that we have got this far, we are almost ready to send the user to the correct proxy. The only thing we have not done yet is make sure that, if they are trying to access a site on your internal network, they go directly and do not use the proxy. This is done like so:

if (isInNet(host, "192.168.0.0", "255.255.0.0"))

  {

    return "DIRECT";

  }

else {

    return proxy;

  }

The first part of the IF statement is the IsInNet function again, this time on the IP address of the destination passed into the PAC file. If it is in the IP scope specified, then the user is sent directly to the site. At this point the logic of the PAC file concludes and the user is sent to the site.

If the IF statement returns false, then the user is sent to the proxy server that is currently held in the proxy variable.

Put it all Together

Now that we have all the building blocks, we can create our PAC file. If we wish, we can also set it up so that, instead of using the IP address of the proxy, it points to a DNS entry. This is useful if you have backup proxies. In that case, if the main one goes offline you can change the DNS entry to the backup and the clients will be re-directed, without you having to alter the PAC file. We use this in our final completed example.

function FindProxyForURL(url, host)

{

  //Set a default proxy if non are returned below

  var proxy = "PROXY 192.168.0.244:8080";

 

  // Test for Prestons subnets

  if (isInNet(myIpAddress(), "192.168.1.0", "255.255.255.0"))

    proxy = "PROXY proxy_preston:8080";

  if (isInNet(myIpAddress(), "192.168.2.0", "255.255.255.0"))

    proxy = "PROXY proxy_preston:8080";

 

  // Test for Londons subnets

  if (isInNet(myIpAddress(), "192.168.3.0", "255.255.255.0"))

    proxy = "PROXY proxy_blackpool:8080";

  if (isInNet(myIpAddress(), "192.168.4.0", "255.255.255.0"))

    proxy = "PROXY proxy_blackpool:8080";

 

  //Now direct the user out through the proxy if not internal site

  if (isInNet(host, "192.168.0.0", "255.255.0.0"))

  {

    return "DIRECT";

  }

  else if (url.substring(0, 24) == "http://www.microsoft.com")

  {

    return "PROXY 159.180.13.52:3128";

  }

  else if (url.substring(0, 5) == "http:")

  {

    return proxy;

  }

  else if (url.substring(0, 4) == "ftp:")

  {

    return proxy;

  }

}

{mospagebreak title=Directing Users to your PAC File}

Now that you have your PAC file, you need to get your users to start using it. There are a couple of ways you can accomplish this, apart from the obvious one of getting them to do it themselves.

Please note that all the options discussed below are for use with Internet Explorer; they won’t affect FireFox, Opera, Safari etc. If you have any way of setting up these browsers send them to me and I’ll attach them as a comment to this article.

Hacking the Registry

The IE home page is just a simple setting in the registry. It is specific for each user profile on the machine and lives in

HKCUSoftwareMicrosoftWindowsCurrentVersionInternet SettingsAutoConfigURL

So if you use VBS login scripts you could have a function in them that writes down your config file, something like this:

‘Create shell objext

Set objwsh = WScript.CreateObject("WScript.Shell")

 

‘Assign the PAC file

Const PROXY_LOC = "http://webserver1/proxy.pac"

 

objwsh.RegWrite "HKCUSoftwareMicrosoftWindowsCurrentVersionInternet SettingsAutoConfigURL", PROXY_LOC, "REG_SZ"

Using a Group Policy

If you are running in an active directory domain, this is probably the best way to control it. By using this method you can not only set the location of the PAC file, but disable the option in Internet Explorer so that the user can’t go in and change it.

Open up (or create) a Policy file that will hold the settings for your users. The options you need are all in the USER section under Windows Settings/Internet Explorer Maintenance/Connection. To use your PAC file, open up the Automatic Browser Configuration option.

In here tick the Enable Automatic Configuration box, and then in the bottom text area named Auto-proxy URL, put the address of your PAC file in. For example,  http://webserver1/proxy.pac.

You can also set a time in minutes that will reload the PAC file. If you leave this blank the PAC file is just re-read every time you reload the browser.

Going Forward

As the language of the PAC file is JavaScript-based, there are a lot more functions you could build into it, such as re-directing the user to one site if they try to visit a page they shouldn’t be visiting. There are also various other options for distributing the PAC file. One of the most interesting is using DNS and DHCP to implement the Web Proxy Autodiscovery (WPAD) protocol.

16 thoughts on “Controlling Internet Access using a Pac File

  1. Hi !
    I found the article very informative and easy to understand 🙂
    One question: How can I be sure that web sites on locahost not are directed to proxy (for developers) can I simply check on 127.0.0.1 ?
    Regards
    Paal H. Johnsen

  2. Hi Paal,

    Glad you like the article.

    Yes, to go to the web service running on the local machine you should just check on 127.0.0.1, something like this should work (but i’ve not checked it)

    if (isInNet(host, “127.0.0.1”, “255.255.255.255”))
    {
    return “DIRECT”;
    }

    Cheers
    Luke

  3. Thank you for your valuable information. I am very happy to catach this valuable information.

    I have one doubt, i am having this type of requirement. there are some sites which works on port numbers.

    i.e. *.:8001, *.:8002, *.:8003….

    please let me know how to include these urls on pac file.

    I am waitiing for your prompt responce.

    dlnmurthy@gmail.com

  4. Hi,

    I’m a bit confused as to what you are trying to archive. I don’t think you can use a pac file to automatically re-direct to a certain port on a website, thats kind of up to the web server, or the user to specify the port number on the url.

    You might be able to do something on your proxy server using NAT.

  5. The best way that direct users to the pac file that you dont mention here is through DHCP option 252

  6. We have implemented the pac file solution with the timeout set to 5 mins to check for changes to the pac file, but have now got a problem whereby if the IIS server hosting the pac file is unavailable, users are unable to access the Internet. As a copy of the pac file is stored under c:\Documents and Settings\UserID\Local Settings\Temporary Internet Files, shouldn’t this be referenced if the IIS server is unavailable ?

    Thanks

  7. Hi,

    If you are making it check every 5 mins and it cant find the iis server then no it probably wont be able to access the file any more.

    I think i would be more concerned as to why you keep losing access to your iis servers

  8. Hi:

    I am trying to edit our PAC file, I would like to use an “AND” function can you help me with the correct syntax please. I tried editing as follows but I do not think it is correct? I get a syntax error when I use Google PAC tester.

    if (isInNet(myIpAddress(), “10.68.248.0”, “255.255.252.0”))&&
    dnsDomainIs (host, “.webex.com”)
    return “PROXY 1.1.1.1:8080”;

  9. Thank you for the great info. You mention it is possible to even redirect urls. I’m not having any luck finding how that is done.

  10. Great article. We have a pretty complicated PAC file, but it just got even more complicated based on a request I just received. For one particular external website URL, we want to bypass proxy entirely and send it straight out to the internet.

    In my Squid Proxy server, I have the pointer to my ISP DNS.

    So in theory, what I am trying to accomplish is in the PAC file, route this particular URL straight to the internet. But I do not do external DNS resolution internally and I don’t want to.

    What do I put in my function for FindProxyForURL as the RETURN ?

    Here is a snippet of what I think I need to do:

    function FindProxyForURL(url, host)
    {
    if (isBposEmail(url, host)) return “What do I put in here to bypass the proxy?”;

    Later on in PAC I have:

    function isBposEmail(url, host)
    {
    host = fixhost(host);
    return (
    shExpMatch(host, “*.local”) ||
    shExpMatch(host, “mail.emea.microsoftonline.com“) ||
    shExpMatch(host, “*.mail.emea.microsoftonline.com”));
    }

  11. How can I configure my proxy.pac to automatically pick up proxy settings if the laptop is on the local LAN, but point it to a different remote proxy (cloud computing service) if the laptop is not on the local LAN.
    For instance, moving a laptop from a WK network with a proxy server to a remote site w/ no proxy but I want it to automatically point the browser to a paid web filtering service.

  12. Is there a way you can force the browser to reload the PAC script every X minutes? We would like to make changes to the pac script and have open browsers pick those changes up without depending on users to open new browser or press reload on firefox.

  13. Yes, I have the same problem with users using a private 192.168.. address in the office, but also at home, Starbucks etc.

    To address this I added all my users into DHCP on my work LAN with static reservations, so I know their address in our network. Then when they’re not on our LAN (and I assume they get a different address to this) they get another one, which makes the pac file behave differently. I use this line to test if on my LAN, and it seems to work, mostly (still have to troubleshoot some small issues but think its IE). (BTW, ‘proxy_no’ is DIRECT)

    if (isInNet(myIpAddress(), “192.168.51.124”, “255.255.255.255”)) { return proxy_no; }

  14. I currently have a question regarding redirecting traffic that is for a specific external IP subnet (not URL) to the proxy server.

    The catch here is that I’m looking at subnets like 200.15.130.0/24 & 200.15.131.0/25… is there any way to specify this in the proxy?

[gp-comments width="770" linklove="off" ]