theimpatientcoder
theimpatientcoder

Reputation: 1098

How to connect to LDAP using gMSA in C#

I want to bind to to a Directory Object on Active Directory using gMSA in a C# service.

What I am aware (... and able to do ):

If the C# service is running in the context of the gMSA, then following code works fine (this is password less bind):

DirectoryEntry entry = new DirectoryEntry("LDAP://172.5.25.5:389/CN=MyUser,OU=MyOU,DC=Example,DC=lab");
_log.info("Authentication Type: " + entry.AuthenticationType.GetTypeCode());
_log.info("Name is: " + entry.Name);

Question 1

(Before actually asking the question, I will try to give the background and provide the code for my attempts)

But what I actually want to do is to bind to the Directory Object like a normal User/ Service account!

Reasons:

  1. I don't want to run the service within the context of a particular gMSA
  2. This is because, the C# service intends to connect with multiple DCs (Cross Domain and Cross Forests)
  3. If the C# service runs under the context of particular gMSA, then that particular gMSA needs to have permissions over all the DCs (cross domain as well as cross forest) which I want to avoid
  4. Hence, I want to use dedicated gMSA accounts for each DC.

Why gMSA:

  1. We want to utilise the gMSA feature wherein its password will be managed by the Active Directory.
  2. This way, the C# service doesn't have to be updated with the passwords every now and then. This would be the case if we intend to use normal User accounts or Service Accounts (by the way, this is what we have to do now)
  3. We want to eliminate the password management in our C# service.
  4. Hence, use the gMSAs

Assumptions and Prerequisites

  1. C# service is running under the context of the Administrator of Domain A
  2. This Administrator will will be added to the principalsAllowedToRetrivePassword for all the local/cross domain/ cross forest gMSAs of the domains to which the service needs to bind for LDAP connection
  3. Now the Administrator can retrieve the password for each gMSA. I want to use this password to bind to the respective domains using corresponding gMSA accounts!

For a normal User account, this is how we usually bind to a Directory Object using its username and password:

DirectoryEntry entry = new DirectoryEntry("LDAP://172.5.25.5:389/CN=MyUser,OU=MyOU,DC=Example,DC=lab",
           "[email protected]", "P@ssw0rd!", AuthenticationTypes.Secure);

But when I do the same with gMSA it fails with error: Username or password is incorrect

Here are more details in to the code:

Retrieve the gMSA Password:

using (PowerShell ps = PowerShell.Create())
{
    // This will succeed since the C# service is running under the context of 
    //Administrator and is been added to principalsAllowedToRetrievePassword for the concerned gMSA
    ps.AddCommand("Get-ADServiceAccount")
      .AddParameter("Identity", gMSA)
      .AddParameter("Properties", "msds-ManagedPassword"); 

    Collection<PSObject> results = ps.Invoke();

    foreach (PSObject result in results)
    {
         Object managedPasswordBlob = result.Properties["msds-ManagedPassword"].Value;
         ps.Commands.Clear();

         // This is a DS-Internals hack to get the password for gMSA in clear text
         // I have installed their package on my machine...
         // This code is pretty straight forward can can directly incorporated in code
         // without the need for the package installation...
         ps.AddCommand("ConvertFrom-ADManagedPasswordBlob")
           .AddParameter("Blob", managedPasswordBlob);

         Collection<PSObject> passwordResults = ps.Invoke();
         
         if (passwordResults.Count > 0)
         {
             string currentPassword = passwordResults[0].Properties["CurrentPassword"].Value.ToString();
         }
    }        
}

This code perfectly works fine and I get the cleartext password for the gMSA. But when I use the same in following piece of code, I get an error saying: username or password is incorrect

The code that fails:

// note: currentPassword is what I got through the above snippet!
DirectoryEntry entry = new DirectoryEntry("LDAP://172.5.25.5:389/CN=MyUser,OU=MyOU,DC=Example,DC=lab", 
           "[email protected]", currentPassword, AuthenticationTypes.Secure);

Note: I tried this with following LDAP URLS but with no luck:

a. LDAP://example.lab:389/CN=MyUser,OU=MyOU,DC=Example,DC=lab
b. LDAP://hostname.example.lab:389/CN=MyUser,OU=MyOU,DC=Example,DC=lab

Now, getting back to the actual question: What am I doing wrong here? Is this even possible?

Question 2

What worked for me?

I was able to form the LdapConnection using the gMSA and its retrieved password:

LdapConnection connection = new LdapConnection(new LdapDirectoryIdentifier("hostname.example.lab", 389, false, false));
            NetworkCredential cred = new NetworkCredential("gMSA", currentPassword, "example.lab");

 connection.SessionOptions.Sealing = true;
 connection.SessionOptions.SaslMethod = "GSSAPI";
 connection.Credential = cred;

 try 
 {
    connection.Bind();
    SearchRequest searchRequest = new SearchRequest();
    searchRequest.DistinguishedName = "CN=My,OU=MyOU,DC=Example,DC=lab";
    searchRequest.Scope = System.DirectoryServices.Protocols.SearchScope.Subtree;
    searchRequest.Attributes.Add("SamAccountName");
            
    DirectoryResponse response = connection.SendRequest(searchRequest);
 } 
 catch(Exception e) 
 {
     //....
 }

This code works perfectly fine!

So:

  1. If I am able to correctly for the LdapConnection using the gMSA's password, then why can't I for the DirectoryEntry using the same?
  2. Will I be able to form the DirectoryEntry object, either from the LdapConnection object/ DirectoryResponse object?
  3. Will this solution be scalable, meaning will I be able to achieve the same for all the gMSA's in question ? (cross domain and cross forest per se? )

Question 3: Security Considerations

  1. Is my approach secure? That is is it okay if the Administrator to be added to principalsAllowedToRetrievePassword for the respective gMSA's. Will it be accepted, along with the intended use by wider audience/ customers?
  2. What security aspects would be of major concern over here?
  3. Since the problem I am solving is of password management, I will have to use some or the other workaround!
  4. Are there any concerns of adding the Administrator to the principalsAllowedToRetrievePassword list of the gMSAs?
  5. What do you feel about my approach of retrieving the password internally and then using it (I know gMSAs are not meant for this, but is there any alternative to what I am solving? )

Thanks in Advance!


Update 1: Trying to reduce the confusion of the complicated ask.

We have a solution where:

  1. There is a UI where the customers can configure which Domain Controllers they want to manage with our solution. So, in that they provide information like, domain name and userName and password with which they want us to use for connection
  2. Now we have two components, Java and a C# service that utilises these details to connect. In java we support both, simple bind (using username and password and we also support kerberos authentication)
  3. The same is with C# service.
  4. The purpose and responsibilities of each Java and C# service is different. C# service is running on a DC or a DC attached machine while Java service is running on a separate server (probably unix)
  5. And both want to connect to Active Directory LDAP using the same connection information

Now the ask is to avoid taking password as input. We cannot use keytab files and also don't want to use certificates as at some point they may get expired. So, we want to use something that can go for ever!

Naturally gMSA came to our mind and we are now exploring of using it!

I hope this clears the doubts further and makes sense to my questions and ask

  1. There is no support for gMSA on unix machines. Also, we cannot generate keytab files for it. But, given a username and password for the gmsa, we can use kerberos authentication for LdapContext!
  2. That is what we are doing. We will be securely passing the gMSA password to the Java service that can use for LdapConnection (This problem is solved)
  3. The C# service can do this for us
  4. But the C# service itself cannot form the DirectoryEntry (we want this because our huge code base is currently modelled around it for updates, modifications, creation, deletion etc...)
  5. But we are able to form the LdapConnection using the gMSA username and password then why not DirectoryEntry

Upvotes: 2

Views: 515

Answers (0)

Related Questions