1 comments

MaxTokenSize Implications for HTTP.SYS

Published on Thursday, November 13, 2014 in , , , , , , ,

One of my customers had problems with certain users being member of a lot of Active Directory groups. This resulted in several client side issues. There’s an easy and well-known “fix” for that: raise the MaxTokenSize registry key on all Windows operating systems in your domain. On Windows 8(.1) / 2012 (R2) the MaxTokenSize is already at its maximum (advised) value out of the box. That value is 48.000 bytes. In order to mitigate these users their access problems we raised the MaxTokenSize to 48.000 bytes on all clients and servers that are running Windows 7/ Windows 2008 R2. After this change the typical issues were gone. However new ones came up:

From time to time, when HTTP is involved, issues were encountered:

  • Opening the Direct Access management console (depends on WinRM)
  • Open the FIM Portal
  • Streaming App-V packages over HTTP

Typically the user would receive several authentication prompts and even after specifying valid credentials another prompt would reappear. Example browser based issue:

image

As you can see the browser gives an HTTP 400 Bad Request error. Using a network trace we can easily see why it’s considered bad:

trace01

And the packet details:

trace02

The details clearly state that The size of the request headers is too long.

The problem here is that the token is allowed to be up to 48.000 bytes where it used to be 12.000 bytes. The http subsystem of a windows server has several parameters that are supposed to protect the server from oversized requests. However, as the token can now be a lot larger, the maximum request size has to be tuned as well:

From: KB820129

Below: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters there are two interesting values:

InfoIIS

And from: KB2020943 we can find a formula to calculate the MaxFieldLength to set based on the MaxTokenSIze.

If MaxToken is 48.000 bytes (default in Windows 2012 and configure by GPO for 2008 R2/ Win7):

  • (4/3 * 48000) + 200 = 64200

We’ll use the maximum allowed value of MaxFieldLength 65534 (=~ 64200) to allow tokens up to 48000 bytes. We’ll also use this value for MaxRequestBytes.

col

  • MaxFieldLength: we can take the maximum allowed value: 65534
  • MaxRequestBytes:  65534

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters

reg

Other useful information:

I specifically wanted to post this information as in many other only articles/posts I always see people just using the maximum allowed value for MaxRequestBytes and I don’t feel 100% comfortable with that. Second,  in my opinion it’s advised to have these values pushed out to all your server systems. Especially now that Windows 2012 and up have a MaxTokenSize of 48.000 by default. If you don’t push these HTTP.sys parameters, you’ll end up troubleshooting the same phenomena multiple times from different angles. Why waste time?

6 comments

3PAR: Connect to WebAPI using PowerShell

Published on Wednesday, November 5, 2014 in ,

I’m currently involved in a CloudCruiser implementation. CloudCruiser is not one of my usual technologies, but as it’s something new to me it’s refreshing to do. CloudCruiser allows you to collect information from your infrastructure and then generate billing information. You could generate bills for virtual machine instances or storage usage. My customer has 3PAR storage and I had to wrote a script which runs frequently and collects volume information.

As far as I can tell there are two approaches:

  • Use the 3PAR CLI utilities
  • Use the 3PAR Web API

I wanted to avoid the CLI utilities. They need to be installed (or copied) to the server where you want to run the script and integrating these tools and their data with PowerShell is less intuitive. I loved the idea of a Web API. This goes hand in hand with the Invoke-WebRequest cmdlet in PowerShell. This cmdlet does many of the heavy lifting and makes it real easy to talk with a given Web API. Here’s how I connected to the 3PAR device and how I got the volume information.

Calling a method of the 3PAR Web API is a two part job: first you have to call the /credentials method using a HTTP POST and provide a valid username and password. The result of that call will be a session key that you can use in subsequent calls to the Web API.

Getting the session key:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
#Credentials
$username = "3PAR user" 
$password = "3PAR user password" 

#IP of the 3PAR device
$IP = "10.0.0.1" 
#API URL
$APIurl = "https://$($IP):8080/api/v1" 

$postParams = @{user=$username;password=$password} | ConvertTo-Json 
$headers = @{} 
$headers["Accept"] = "application/json" 
$credentialdata = Invoke-WebRequest -Uri "$APIurl/credentials" -Body $postParams -ContentType "application/json" -Headers $headers -Method POST -UseBasicParsing 
$key = ($credentialdata.Content | ConvertFrom-Json).key

And that’s it! After this you should get a string in the the $key variable which can be used in calls further down the script. But I have to take a step back. To be honest the above code didn’t work. The problem in my case was that I was accessing the API over HTTPS but the certificate couldn’t be validated. I was using the IP to access the device and it was a self signed certificate. So reasons enough why the Invoke-WebRequest cmdlet was sad… I found the following workaround which you can place somewhere before your first Invoke-WebRequest cmdlet:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
#avoid issues with an invalid (self-signed) certificate, try avoid tabs/spaces as this might mess up the string block
#http://stackoverflow.com/questions/11696944/powershell-v3-invoke-webrequest-https-error
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
 
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

Calling the method:

And now on to the actual magic. Here we’ll do a GET to the /volumes method.

001
002
003
004
005
006
007
$headers = @{}
$headers["Accept"] = "application/json"
$headers["X-HP3PAR-WSAPI-SessionKey"] = $key
$volumedata = Invoke-WebRequest -Uri "$APIurl/volumes" -ContentType "application/json" -Headers $headers -Method GET -UseBasicParsing 
$volumedataPS = ($volumedata.content | ConvertFrom-Json).members
#also works:
#$volumedata = Invoke-RestMethod -Uri "$APIurl/volumes" -ContentType "application/json" -Headers $headers -Method GET

And that’s all there is to it! $volumedataPS now contains an array with objects you can iterate through. No need to work with intermediate CSV files or other tricks.

Some additional information:

The UseBasicParsing parameter. When running the PowerShell script as the user I was logged in I didn’t had any troubles. Once I started running it as SYSTEM (for a scheduled task), it gave the following error: Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again. The UseBasicParsing seems to avoid using the IE engine altogether and thus the script runs fine under SYSTEM.

Invoke-WebRequest versus Invoke-RestMethod: It’s to my understanding  that they both work for calling the Web API, but Invoke-WebRequest seems to return more information regarding the actual call whereas Invoke-RestMethod simply returns the requested data. I figured this might help when adding additional logging.

The Web API might not be enabled by default. You could provide the following instructions to your 3PAR admin: Veeam: Enabling the HP 3PAR Web Services API Server They are from Veeam but I found them to be accurate.