Skip to content

Instantly share code, notes, and snippets.

@rajanadar
Created September 10, 2024 12:33

Revisions

  1. rajanadar created this gist Sep 10, 2024.
    115 changes: 115 additions & 0 deletions KerberosAuthMethodHelper.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,115 @@
    using System;
    using System.Threading.Tasks;
    using Kerberos.NET.Client;
    using Kerberos.NET.Configuration;
    using Kerberos.NET.Credentials;
    using Kerberos.NET.Crypto;

    // Written by Raja Nadar for users of VaultSharp dealing with Kerberos based AuthMethod
    namespace VaultSharp.V1.AuthMethods.Kerberos
    {
    /// <summary>
    /// Helper class for Kerberos based Vault Auth Method
    /// Include the Nuget package Kerberos.NET before using this helper class.
    /// </summary>
    public static class KerberosAuthMethodHelper
    {
    /// <summary>
    /// Creates the Kerberos Service Principal Negotiation Token header from the keytab and conf files.
    /// </summary>
    /// <param name="serviceAccountUsername">
    /// The username for the entry within the keytab to use for logging into Kerberos.
    /// This username must match a service account in LDAP.
    /// </param>
    /// <param name="servicePrincipalName">
    /// The service principal name to use in obtaining a service ticket for gaining a SPNEGO token.
    /// This service must exist in LDAP.
    /// </param>
    /// <param name="realmName">
    /// The name of the Kerberos realm.
    /// This realm must match the UPNDomain configured on the LDAP connection.
    /// This check is case-sensitive.
    /// </param>
    /// <param name="keyTabFilePath">
    /// The path to the keytab in which the entry lives for the entity authenticating to Vault.
    /// Keytab files should be protected from other users on a shared server using appropriate file permissions.
    /// </param>
    /// <param name="krb5ConfigurationFilePath">
    /// The path to a valid krb5.conf file describing how to communicate with the Kerberos environment.
    /// </param>
    /// <param name="disableFastNegotiation">
    /// For disabling the Kerberos auth method's default of using FAST negotiation.
    /// FAST is a pre-authentication framework for Kerberos.
    /// It includes a mechanism for tunneling pre-authentication exchanges using armoured KDC messages.
    /// FAST provides increased resistance to passive password guessing attacks.
    /// Some common Kerberos implementations do not support FAST negotiation.
    /// </param>
    /// <returns>The "Negotiate " + ServicePrincipalNegotiationToken value.</returns>
    public static async Task<string> GetServicePrincipalNegotiationTokenAsync(
    string serviceAccountUsername,
    string servicePrincipalName,
    string realmName,
    string keyTabFilePath,
    string krb5ConfigurationFilePath,
    bool disableFastNegotiation)
    {
    if (string.IsNullOrEmpty(serviceAccountUsername))
    {
    throw new ArgumentNullException(nameof(serviceAccountUsername));
    }

    if (string.IsNullOrEmpty(servicePrincipalName))
    {
    throw new ArgumentNullException(nameof(servicePrincipalName));
    }

    if (string.IsNullOrEmpty(realmName))
    {
    throw new ArgumentNullException(nameof(realmName));
    }

    if (!System.IO.File.Exists(krb5ConfigurationFilePath))
    {
    throw new System.IO.FileNotFoundException("Krb5Configuration file could not be found: " + krb5ConfigurationFilePath);
    }

    if (!System.IO.File.Exists(keyTabFilePath))
    {
    throw new System.IO.FileNotFoundException("KeyTabFile file could not be found: " + keyTabFilePath);
    }

    try
    {
    var krb5Config = Krb5Config.Parse(krb5ConfigurationFilePath);
    var kerberosClient = new KerberosClient(krb5Config);

    // Assume pre-auth
    kerberosClient.AuthenticationOptions &= AuthenticationOptions.PreAuthenticate;

    if (disableFastNegotiation)
    {
    kerberosClient.AuthenticationOptions &= ~AuthenticationOptions.PreAuthenticate;
    }

    var keyTable = new KeyTable(System.IO.File.ReadAllBytes(keyTabFilePath));

    KerberosCredential keytabCredential =
    new KeytabCredential(serviceAccountUsername,
    keyTable, realmName);

    await kerberosClient.Authenticate(keytabCredential);

    var ticket = await kerberosClient.GetServiceTicket(servicePrincipalName);

    var servicePrincipalNegotiationToken = Convert.ToBase64String(ticket.EncodeGssApi().ToArray());

    var kerberosAuthHeader = "Negotiate " + servicePrincipalNegotiationToken;
    return kerberosAuthHeader;
    }
    catch (Exception ex)
    {
    throw new Exception("Failed to generate Kerberos service principal negotiation token.", ex);
    }
    }
    }
    }