I am running a C# .NET 6 App in a Linux Ubuntu 22.04 container. I need the app to connect to an Active Directory Domain Controller in order to authenticate users of the app. I can get non-secure LDAP connections to the DC to work, but I get exceptions for any attempts at SSL or TLS LDAP connections. The exception messages are fairly vague so I am stuck on how to investigate further. I need to use secure connections to LDAP so that the app does not send users' credentials in the clear.
My Linux container is not connected to the Active Directory Domain.
I am using nuget packages System.DirectoryServices Version 7.0.1 and System.DirectoryServices.Protocols Version 7.0.1. According to Pull Request #52904, support for TLS has been added but I'm not clear on what the prerequisites are or on which scenarios are supported.
Should I be using a different AuthType or am I missing a prerequiste package? Any help would be much appreciated.
Testing
I have been testing using the code below. (Where username
is a sAMAccountName and userDN
is a Distinguished Name). My thinking had been that I was using the wrong connection options so this code cycles through various options.
internal class LdapConnectionTest
{
enum EncryptionOption
{
None = 0,
SSL = 1,
TLS = 2
}
public static void StartTest(
string ldapServer,
string userName,
string userDN,
string password)
{
foreach (AuthType authType in new AuthType[] {
AuthType.Anonymous,
AuthType.Basic,
AuthType.Negotiate,
AuthType.Ntlm })
{
foreach (EncryptionOption encryptionOption in new EncryptionOption[] {
EncryptionOption.None,
EncryptionOption.SSL,
EncryptionOption.TLS})
{
foreach (int protocolVersion in new int[] {2,3})
{
foreach (ReferralChasingOptions referralChasingOption in new ReferralChasingOptions[] {
ReferralChasingOptions.None,
ReferralChasingOptions.All})
{
foreach (bool skipVerifyCertificate in new bool[] { false, true })
{
int port = 389; //TLS is also on port 389
if (encryptionOption == EncryptionOption.SSL)
{
port = 636;
}
if (encryptionOption == EncryptionOption.TLS && protocolVersion == 2) continue; //TLS not supported in LDAP v2
Console.WriteLine();
Console.WriteLine($"##### Attempt with AuthType: {authType} Encryption: {encryptionOption} Port: {port} Protocol Version: {protocolVersion} ReferalChasing: {referralChasingOption} Skip Verify Cert: {skipVerifyCertificate}");
try
{
LdapDirectoryIdentifier id = new(ldapServer, port);
LdapConnection connection = new(id)
{
AuthType = authType
};
connection.SessionOptions.ReferralChasing = referralChasingOption;
if (encryptionOption == EncryptionOption.SSL)
{
connection.SessionOptions.SecureSocketLayer = true;
if (skipVerifyCertificate)
{
connection.SessionOptions.VerifyServerCertificate = (con, cer) => true; //this is insecure
}
}
connection.SessionOptions.ProtocolVersion = protocolVersion;
NetworkCredential credential = new(authType == AuthType.Basic ? userDN : userName, password);
if (encryptionOption == EncryptionOption.TLS)
{
if (skipVerifyCertificate)
{
connection.SessionOptions.VerifyServerCertificate = (con, cer) => true; //this is insecure
}
connection.SessionOptions.StartTransportLayerSecurity(null);
}
if (authType == AuthType.Anonymous)
{
connection.Bind();
}
else
{
connection.Bind(credential);
}
Console.WriteLine($"Successfull connection Protocol Version: {connection.SessionOptions.ProtocolVersion} Secure: {connection.SessionOptions.SecureSocketLayer}");
connection.Dispose();
}
catch (Exception? ex)
{
while (ex != null)
{
Console.WriteLine($"Exception {ex.GetType().ToString()} {ex.Message}");
Console.WriteLine(ex.StackTrace);
//exit on wrong credential in order to not lock out account
if (ex.Message.Contains("credential", StringComparison.InvariantCultureIgnoreCase)) return;
ex = ex.InnerException;
if (ex != null)
{
Console.WriteLine("Inner Exception:");
}
}
}
}
}
}
}
}
}
}
All the test scenarios work when I run the test code from a Windows workstation. When I try it on Ubuntu 22.04 the insecure connections work but I get the following Exceptions for secure connections (summary of output from the code above). I was expecting at least one of the secure connection options to work.
##### Attempt with AuthType: Basic Encryption: None Port: 389 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Successfull connection Protocol Version: 3 Secure: False
##### Attempt with AuthType: Basic Encryption: SSL Port: 636 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Exception System.DirectoryServices.Protocols.LdapException The LDAP server is unavailable.
at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential)
at System.DirectoryServices.Protocols.LdapConnection.Bind(NetworkCredential newCredential)
at MyTestApp.Ldap.LdapConnectionTest.StartTest(String ldapServer, String userName, String userDN, String password) in /src/MyTestApp/MyTestApp/Ldap/LdapConnectionTest.cs:line 110
##### Attempt with AuthType: Basic Encryption: TLS Port: 389 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Exception System.DirectoryServices.Protocols.LdapException The connection cannot be established.
at System.DirectoryServices.Protocols.LdapSessionOptions.StartTransportLayerSecurity(DirectoryControlCollection controls)
at MyTestApp.Ldap.LdapConnectionTest.StartTest(String ldapServer, String userName, String userDN, String password) in /src/MyTestApp/MyTestApp/Ldap/LdapConnectionTest.cs:line 101
##### Attempt with AuthType: Negotiate Encryption: None Port: 389 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Exception System.DirectoryServices.Protocols.LdapException The feature is not supported.
at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential)
at System.DirectoryServices.Protocols.LdapConnection.Bind(NetworkCredential newCredential)
at MyTestApp.Ldap.LdapConnectionTest.StartTest(String ldapServer, String userName, String userDN, String password) in /src/MyTestApp/MyTestApp/Ldap/LdapConnectionTest.cs:line 110
##### Attempt with AuthType: Negotiate Encryption: SSL Port: 636 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Exception System.DirectoryServices.Protocols.LdapException The feature is not supported.
at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential)
at System.DirectoryServices.Protocols.LdapConnection.Bind(NetworkCredential newCredential)
at MyTestApp.Ldap.LdapConnectionTest.StartTest(String ldapServer, String userName, String userDN, String password) in /src/MyTestApp/MyTestApp/Ldap/LdapConnectionTest.cs:line 110
##### Attempt with AuthType: Negotiate Encryption: TLS Port: 389 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Exception System.DirectoryServices.Protocols.LdapException The connection cannot be established.
at System.DirectoryServices.Protocols.LdapSessionOptions.StartTransportLayerSecurity(DirectoryControlCollection controls)
at MyTestApp.Ldap.LdapConnectionTest.StartTest(String ldapServer, String userName, String userDN, String password) in /src/MyTestApp/MyTestApp/Ldap/LdapConnectionTest.cs:line 101
##### Attempt with AuthType: Ntlm Encryption: None Port: 389 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Exception System.DirectoryServices.Protocols.LdapException An unknown authentication error occurred.
at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential)
at System.DirectoryServices.Protocols.LdapConnection.Bind(NetworkCredential newCredential)
at MyTestApp.Ldap.LdapConnectionTest.StartTest(String ldapServer, String userName, String userDN, String password) in /src/MyTestApp/MyTestApp/Ldap/LdapConnectionTest.cs:line 110
##### Attempt with AuthType: Ntlm Encryption: SSL Port: 636 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Exception System.DirectoryServices.Protocols.LdapException An unknown authentication error occurred.
at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential)
at System.DirectoryServices.Protocols.LdapConnection.Bind(NetworkCredential newCredential)
at MyTestApp.Ldap.LdapConnectionTest.StartTest(String ldapServer, String userName, String userDN, String password) in /src/MyTestApp/MyTestApp/Ldap/LdapConnectionTest.cs:line 110
##### Attempt with AuthType: Ntlm Encryption: TLS Port: 389 Protocol Version: 3 ReferalChasing: None Skip Verify Cert: False
Exception System.DirectoryServices.Protocols.LdapException The connection cannot be established.
at System.DirectoryServices.Protocols.LdapSessionOptions.StartTransportLayerSecurity(DirectoryControlCollection controls)
at MyTestApp.Ldap.LdapConnectionTest.StartTest(String ldapServer, String userName, String userDN, String password) in /src/MyTestApp/MyTestApp/Ldap/LdapConnectionTest.cs:line 101
Environment
I have installed the AD root certificate using update-ca-certificates
. My ldap.conf file points to the crt file where all my CAs are installed.
# cat ldap.conf
#
# LDAP Defaults
#
# See ldap.conf(5) for details
# This file should be world readable but not world writable.
#BASE dc=example,dc=com
#URI ldap://ldap.example.com ldap://ldap-provider.example.com:666
#SIZELIMIT 12
#TIMELIMIT 15
#DEREF never
# TLS certificates (needed for GnuTLS)
TLS_CACERT /etc/ssl/certs/ca-certificates.crt
Other details of my environment:
# dotnet --info
.NET SDK (reflecting any global.json):
Version: 6.0.122
Commit: dc5a76ad5c
Runtime Environment:
OS Name: ubuntu
OS Version: 22.04
OS Platform: Linux
RID: ubuntu.22.04-x64
Base Path: /usr/lib/dotnet/sdk/6.0.122/
global.json file:
Not found
Host:
Version: 6.0.22
Architecture: x64
Commit: 4bb6dc195c
.NET SDKs installed:
6.0.122 [/usr/lib/dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.22 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.22 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]
Download .NET:
https://aka.ms/dotnet-download
Learn about .NET Runtimes and SDKs:
https://aka.ms/dotnet/runtimes-sdk-info
#
I have installed the following packages in my container:
FROM ubuntu:22.04
#update software package lists
RUN apt-get update && \
apt update && \
#install .NET ASP Core runtime
apt-get install -y aspnetcore-runtime-6.0 && \
#install LDAP library + symlink from old version that .NET wants https://github.com/dotnet/runtime/issues/69456
apt-get install -y libldap-2.5-0 && \
ln -s /usr/lib/x86_64-linux-gnu/libldap-2.5.so.0 /usr/lib/x86_64-linux-gnu/libldap-2.4.so.2 && \
#install ca certificates (required for ssl)
apt-get install -y ca-certificates && \
#tidyup
apt clean && \
apt-get clean
I am using the symlink work-around described in #69456 for the hard-coded reference to libldap-2.4.so.2 which is not available in Ubuntu 22.04.
I have successfully run ldapsearch
to make an ldaps:// connection to the domain controller from my Linux container.
klist
) before making the connection? Do you even need TLS if you're using Kerberos? (which, in the case of LDAP, adds its own encryption – and I believe AD DCs actually refuse to use both at the same time.) Are you testing ldapsearch with-Y GSSAPI
auth or are you testing a simple password-based bind? – Jawbreaker