Captive Portals and Not So Captive Portals

A few years ago as I was first cutting my teeth in the Palo Alto firewall world, I had a customer request something “unique” and I created a one-off solution. The request was to have a stand-alone portal that the end-users could go to and log in to gain additional access to resources. The reason for this was BYOD and other non-managed devices that wouldn’t show up in User-ID through the normal User-ID agent polling Windows. The traditional captive portal wasn’t a good fit either as the customer didn’t want to purchase a 3rd party signed TLS certificate. So I pulled out some PHP and created a very simple portal that did an LDAP authentication and then used Palo Alto’s XML-API to send a username and IP pair to the firewall. This worked well until recently when that server started to have issues after upgrading to PHP 8.1.


I took the issues with the PHP application as a sign to do something better. By this point, I knew a lot more about the Palo Alto firewalls and decided it was time to try going through the captive portal documentation again and see what I could do. I set up a standard captive portal using the authentication policy. For the certificate, I used a certificate signed by the customer’s internal CA. I had an ulterior motive in getting the firewall set up as a SubCA so that we could implement SSL Decrypt. This all worked well, but it still didn’t give me the stand-alone portal that the customer’s users could just go to on their own… or did it?

On a whim, I copied the URL that happened on the redirect and started playing with it. The default URL looks like this:


So let’s take a look at this URL. The port isn’t “standard”, but everything else just looks like a PHP website that is passed a few values. Vsys is obviously there in case the Palo Alto firewall has multiple vsys operating. So by default, it will be 1 if there is only one vsys. The next parameter is rule. I’m fairly sure that this corresponds to the authentication rule and apparently is indexed starting at 0. The last two parameters are very much related to the redirection, so I thought why not just leave them off and see what happens? As it turns out it worked! Just passing the vsys and rule parameters created an entry in user-id.

The next challenge was to get this to work without the BYOD devices needing to trust the internal CA. A Reddit post gave me the secret sauce.

  • 6080 – Captive portal with NTLM automatic login
  • 6081 – Captive portal with HTTP
  • 6082 – Captive portal with HTTPS

Ok, I know using HTTP isn’t secure and that it means the password is going clear text, but sometimes we have to make concessions. The number of these devices is fairly limited and the traffic is all inside so the risk was accepted. I am still trying to get them to consider a better strategy that will include a 3rd party cert. (Can all of the firewall vendors please just support ACME on their gear so we can use Let’s Encrypt?) So the final URL that I’m using with the customer is https://FQDN:6081/php/uid.php?vsys=1&rule=0. Not the most elegant URL, but it works and meets their needs.

Final Thoughts

So I learned a lot of things during this process. The bottom line is that we’ve come to rely on getting users to IP mappings out of Active Directory and we need to rapidly come up with new methodologies as cloud-first becomes the mantra. At the time of this writing, there is no methodology from Palo Alto to get these mappings from either Azure AD (or Intune) or the Google Suite consoles. Identity is King, but we need to get it with as little friction with the end-user as possible.