dotnet/Kerberos.NET

How best to use this package for client access

RussKahler1970 opened this issue · 7 comments

I am working on building an integration between two environments and will be making a lot of HttpClient Posts to a legacy IIS site internal in our network that is running windows authentication. In testing below is a sample of my code I used to test getting a Kerberos ticket and attaching it to my HttpClient as an Auth header. this works. I am working from a linux container not running in our domian so I need to set authentication into my HttpClient to authenticate into the site.

            var apiHost = "http://[internal web server]";
            Guard.Against.NullOrEmpty(apiHost);
            var hostUri = new Uri(apiHost);
            using var client= ClientFactory.CreateClient("[Facotry client name]");
            client.BaseAddress = hostUri;

            DnsQuery.RegisterImplementation(new PlatformIndependentDnsClient());

            using var kClient = new KerberosClient();
            var kerbCred = new KerberosPasswordCredential("[Account]", "[Password]", "[]Domain");

            await kClient.Authenticate(kerbCred);

            var ticket = await kClient.GetServiceTicket($"http/{hostUri.Host}");

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Negotiate", Convert.ToBase64String(ticket.EncodeGssApi().ToArray()));

            var apiPath= "[Path to App]";

            string inputJson = Newtonsoft.Json.JsonConvert.SerializeObject(GetEvent());
            HttpContent inputContent = new StringContent(inputJson, System.Text.Encoding.UTF8, "application/json");
     
            var apiResponse = await client.PostAsync(apiPath, inputContent);
            apiResponse.EnsureSuccessStatusCode();

I am wonder if there are some ideas to make it perform with high volume of calls coming through? Is there a cache I should use? Do I make singleton to provide the token and only refresh it as needed? I guess any ideas you have to use this logic in a high volume (using the same account ) environment would be greatly appreciated.

There are a handful of knobs you can use to make it work well.

  • KerberosClient is per-user meaning that if you only ever have a single identity for the lifetime of the app, you can make the client static. Internal TCP stacks use pooled resources to make this cheap.
  • The expensive operation in Kerberos is getting the TGT. That occurs on the call to Authenticate(cred).
  • The TGT is cached in memory by default for the lifetime of the KerberosClient. This can be persisted to disk using the Krb5TicketCache class.
  • TGTs do not refresh automatically. You can set that on individual clients with client.RenewTickets = true.
  • Services tickets are also cached in the same cache as the TGT. GetServiceTicket is expensive for the first call for each SPN, but additional calls are cheap.
  • Always call GetServiceTicket for every request. This generates a new authenticator for each request, but doesn't reach out to the DC. The server app receiving the AP-REQ needs a new authenticator for each request otherwise it will drop it as a replay.

Otherwise, I think it's just a matter of profiling and monitoring where things are running poorly.

thanks for the info. So if I set the client to RenewTickets will it handle the renewal of the TGT when it needs to?

Since its caching for the lifetime of the client if I maintain a client for the lifetime of my app will each call to Authenticate(cred) just get from the cache as it finds tickets and renew when it needs to? Or do I just call authenticate once and then just call GetServiceTicket which will deal with authenication renewal if it needs to?

Thanks again for all your help with this.

Sounds good, so should I reauthenticate daily? would that be often enough? Or should I do it more frequent?

Should I do it on any exception or is there a typed exception I should react to?

Specifically the call to GetServiceticket will throw an exception InvalidOperationException("Cannot request a service ticket until a user is authenticated"). I would avoid doing it on all exceptions, as you could also potentially see networking or name resolution failures due to environmental issues here as well under different exception types.