原文:
https://github.com/dotnet/aspnetcore/issues/19397
Hi,
I want to do windows authentication against active directory server in docker container.
In documentation you say it is possible, but in reality it does not work.
I have created this stack overflow issue with my investigation in this matter:
https://stackoverflow.com/questions/60296237/windows-authentication-in-linux-docker-container
I believe that the only issue is that in the documentation it is not described clearly what needs to be configured in order it to work. Some piece of puzzle is missing there for obvious reason that when somebody does that what is required according to docs, no gssapi mechanism is installed, thus it must not work.
Is your feature request related to a problem? Please describe.
Example: I am trying to do windows authentication in kestrel according to documentation but it does not work.
Describe the solution you'd like
According to @davidsh he managed to make it work, however only in .net5 . (as described in the link below)
So please update the documentation with all required libraries or please make changes in .net core 3 so that it works according to the documentation.
Additional context
Related issues:
#4662
#4737
dotnet/runtime#887
dotnet/AspNetCore.Docs#17054
In my experience, you should not need domain joining as long as you can ensure that a valid Kerberos ticket is always available. You can use the kinit
tool and keytab files to do this, but the tickets expire so there needs to be some job running kinit
periodically. Running kinit
should save a Kerberos ticket in a cache (you can see this ticket if you execute klist
). ASP.NET/Kestrel should be able to automatically use this ticket.
I do this refreshing in Kubernetes by injecting a sidecar next to the main container. The sidecar runs kinit
periodically and the ticket cache is shared via memory with the main container. This post describes a very similar approach: https://blog.openshift.com/kerberos-sidecar-container/. Maybe some of the resources there will help you.
Remember that if you want to authenticate requests with Kerberos, you need to meet all the other requirements that you normally would need to on Windows as well, like setting up an SPN for your DNS name etc. Also, your container needs to have the respective domain controller configured as a DNS server (should be inherited from the machine it runs on hopefully)
@blowdart Yes, i was using the realm to join the domain with centos, and the result was that mechanism was not there which means that some gssapi library was missing. with my attempts with gssntlmssp library the results was "No credentials were supplied, or the credentials were unavailable or inaccessible" which meant that .net core did not sent properly information to gssapi and it has stopped at GSS_C_MA_NOT_DFLT_MECH attribute. Please see what they had said at fedora: https://pagure.io/gssntlmssp/issue/16
However i was able to pass this step by removing this flag from gssntlmssp library by compiling my own.
The bigger problem with realmd is that it works through dbus, so it is not usable in docker container as far as i know.
I was able to join the domain with kereberos and kinit .. I am able to validate the session with klist. I was able to connect for example to mssql with integrated security, so i know it works.
@valorl Thanks for tip for the expiring sessions, but i am not able to validate any users yet. I have SPN set up by my infrastructure department, and i believe it is correct.
So the question still resides.. What libraries in linux have you used?
When i do sudo apt-get install realmd krb5-user software-properties-common python-software-properties packagekit there is no mechanism set up in /etc/gss/mech.d/ so i do not understand how it should work according to the documentation when it relies on gssapi and there is no mechanism..
@davidsh the gssntlmssp issue sounds like something you've addressed?
Yes, I've addressed related issues. I still don't understand the exact repro here. The only time that gss-ntlmssp is needed is when raw NTM is being used (i.e. 'Www-Authenticate: NTLM') or Negotiate is being used but can't use Kerberos and wants to fall back to NTLM.
Is this a client-side problem or a server-side problem? I can't tell from the discussion thread above. I will say that NTLM on the server-side with ASP.NET is not really supported, either raw NTLM or Negotiate using fallback from Kerberos to NTLM. The latter, though, I fixed in .NET 5.
When i do sudo apt-get install realmd krb5-user software-properties-common python-software-properties packagekit there is no mechanism set up in /etc/gss/mech.d/ so i do not understand how it should work according to the documentation when it relies on gssapi and there is no mechanism..
When you first install the krb5-user package, it installs the GSS-API libraries and the built-in Kerberos mechanism (as well as Kerberos specific libraries). There will be no mechanisms listed in /etc/gss/mech.d. That is because Kerberos is considered "built-in". But if you install other mechanisms such as NTLM (via gss-ntlmssp) then it will show up in /etc/gss/mech.d folder.
I suspect what is happening in your situation is that Kerberos is not working for your ASP.NET Core server. So, it is trying to use NTLM as the fallback for Negotiate. But that scenario does not work at all except in .NET 5.
Since you probably want pure Kerberos anyways, you should continue to troubleshoot why your ASP.NET Core server is not working using Kerberos.
I recommend you take GSS-API traces via setting an environment variable prior to running the ASP.NET Core application in your container. Then you will see what is happening when a request comes into ASP.NET Core and it tries to use Kerberos inbound. You should also take the same traces from your client application which submits the request to ASP.NET Core server.
export KRB5_TRACE=/dev/stdout
@davidsh Hi, i have attemted to do the .net 5 ntlm challange with gssntlmssp library, but I got stucked at the same point as with .net core 3.1. (i have updated the stackoverflow page) What did you configure for gssntlmssp library to make it work?
Thanks for KRB5_TRACE env variable.. i have used that almost from the beginning of my research.. However it does not put any output when app tries to do the kerberos authentication, but it gets the output at the start of the application when it tries to connect to mssql which was successful. (with kinit run prior the dotnet app run)
@blowdart Do you suggest that the best practice is to use single sign on outside of kubernetes and make oauth authentication above windows authentication? In my opinion the best would be if any service knows the windows user who uses it and any other auth service is just complication to the whole architecture.
@davidsh Hi, i have attemted to do the .net 5 ntlm challange with gssntlmssp library, but I got stucked at the same point as with .net core 3.1. (i have updated the stackoverflow page) What did you configure for gssntlmssp library to make it work?
Do you really want NTLM and not Kerberos? Is this only a server-side scenario or a client-side scenario?
The 'gss-ntlmssp' plugin only really solves client-side NTLM issues. Server-side NTLM is not supported by ASP.NET Core. While I did a PR for .NET 5 to fix Negotiate authentication fallback from Kerberos to NTLM, it doesn't really use NTLM with Windows security database. It will not, for example, receive the NTLM challenge and route to the Windows active directory for password validation. The 'gss-ntlmssp' plugin does support Linux WinBind but only on the client side. It doesn't support WinBind on the server side. So, that means that NTLM on the server-side only uses explicit clear-text password files as documented in 'gss-ntlmssp'. This is probably not what you want to actually use at all. See the implementation here: https://pagure.io/gssntlmssp/blob/master/f/src/gss_creds.c
Hi @davidsh .. So NTLM vs Negotiate as far as I understand it, is that both works the way that web browser when it sees Authenticate: NTLM or Authenticate: Negotiate sends some hashed password with domain with some secret thing from ad to the server. The server sends also his secret from ad and prepares the challenge for the browser. Browser validates the server challenge so that it knows it can trust the server, and validates the challange. The challange is then verified by the server.
The difference between NTLM and Negotiate is that NTLM is old protocol and is supported in all browsers. Negotiate is new protocol and can be turned on for specific explicit domain in settings. Thats why I prefer NTLM because I would like to have all services implemented windows authentication and do not rely on central "single sign on" service.
note that
gss_creds.c
retmin = get_user_file_creds(name, cred);
if (retmin) {
retmin = external_get_creds(name, cred);
}
external.c
uint32_t external_get_creds(struct gssntlm_name *name,
struct gssntlm_cred *cred)
{
#if HAVE_WBCLIENT
return winbind_get_creds(name, cred);
#else
return ERR_NOTAVAIL;
#endif
}
winbind.c
uint32_t winbind_get_creds(struct gssntlm_name *name,
struct gssntlm_cred *cred)
{
..
if (name && name->data.user.domain) {
params.domain_name = name->data.user.domain;
} else {
wbc_status = wbcInterfaceDetails(&details);
if (!WBC_ERROR_IS_OK(wbc_status)) goto done;
params.domain_name = details->netbios_domain;
}
if (name && name->data.user.name) {
params.account_name = name->data.user.name;
} else {
params.account_name = getenv("NTLMUSER");
if (!params.account_name) {
params.account_name = getenv("USER");
}
if (!params.account_name) goto done;
}
params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
params.num_blobs = 0;
params.blobs = NULL;
wbc_status = wbcCredentialCache(¶ms, &result, NULL);
if(!WBC_ERROR_IS_OK(wbc_status)) goto done;
..
}
I know that gssntlmssp can verify users by the file, but it also supports somehow external credential verification. It must be compiled with winbind library which is supplied with samba. Sambas main purpose is file sharing and can share file with verification of users against active directory. So this library in my opinion should also support verifying users against active directory.
This is actually the point where it stops working for me. In .net core 3.1 as well as .net 5:
wbc_status = wbcCredentialCache(¶ms, &result, NULL);
So to summarize it.. You have made NTLM challange work in .net 5 with gssntlmssp with plain text user password file?
The 'gss-ntlmssp' plugin does support Linux WinBind but only on the client side. It doesn't support WinBind on the server side. So, that means that NTLM on the server-side only uses explicit clear-text password files as documented in 'gss-ntlmssp'.
If this was true it would not try to verify credentials in external winbind library after it fails to verify credentials in the plain text file.
So I am now trying two different approaches:
- NTLM - in my opinion this is preferable way as i have seen ntlm authenticate users in iis express without the dialog box, and does not require any special configuration in firefox or through group policy (please fix me if I am wrong)
- Negotiate
With regards for the negotiate i have managed to make some progres..
With this docker container i was able to get around the unsupported mechanism:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster as final
USER root
RUN whoami
RUN apt update && apt dist-upgrade -y
RUN DEBIAN_FRONTEND=noninteractive apt install -y krb5-config krb5-user
RUN mkdir /app
RUN apt install -y mc sudo syslog-ng python3-software-properties software-properties-common packagekit git gssproxy vim apt-utils
RUN apt install -y autoconf automake libxslt-dev doxygen findutils libgettextpo-dev libtool m4 make libunistring-dev libssl-dev zlib1g-dev gettext xsltproc libxml2-utils libxml2-dev xml-core docbook-xml docbook-xsl bison libkrb5-dev
RUN systemctl enable syslog-ng
RUN mkdir /src
RUN groupadd --gid 1000 app && useradd --uid 1000 --gid app --shell /bin/bash -d /app app
RUN echo BQIAAAA8..vI | base64 -d > /etc/krb5.keytab
COPY krb5.conf /etc/krb5.conf
COPY krb5.conf /usr/local/etc/krb5.conf
ADD ca/is.k01.mydomain.com.p12 /etc/ssl/certs/is.k01.mydomain.com.pfx
RUN cd /app
WORKDIR /app
However now I have other issue:
Request ticket server HTTP/is.k01.mydomain.com@MYDOMAIN.com kvno 3 found in keytab but not with enctype rc4-hmac
This seems to me that the keytab is not with rc4-hmac which is true, because the keytab was generated with
ktpass -princ HTTP/is.k01.mydomain.com@MYDOMAIN.COM -pass ***** -mapuser MYDOMAINis.k01.kerb -pType KRB5_NT_PRINCIPAL -out c: empis.k01.HTTP.keytab -crypto AES256-SHA1
as the .net documentation says.
I was not able to disallow use of rc4-hmac and allow only newer encoding, so i asked my infra department to generate new keytab with old rc4-hmac encoding.
This step has moved me further and I get this error instead: Request ticket server HTTP/is.k01.mydomain.com@MYDOMAIN.COM kvno 4 not found in keytab; keytab is likely out of date*
Which is very wierd because keytabs cannot get out of date, password has not been changed and was 100% valid one hour ago when the keytab was generated, and there is no information on web - "kvno 4 not found in keytab" fetch only 4 results in google.
So the question is:
Any idea what I am doing wrong?
I have solved the above error by fixing krb5.conf file.
For the record I am solving this issue now: #12938
I assume i will have to write for every service a LDAP connector which will fetch the groups for authenticated used. Any suggestions for best practicies?
System.Exception: An authentication exception occured (0xD0000/0x4E540016). ---> Interop+NetSecurityNative+GssApiException: GSSAPI operation failed with error - Unspecified GSS failure. Minor code may provide more information (Feature not available).
With this docker container i was able to get around the unsupported mechanism:
How exactly did you overcome these issues? I'm struggling with the same problem on Ubuntu 16 configured according to the documentation you've mentioned. I don't see any particular difference in Dockerfiles you've listed here and earlier versions on stackoverflow.