title: Windows authentication in linux docker container
body: <p>i am trying to use windows authentication in linux docker container under kubernetes.</p>
  
  <p>I am following this settings: <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&amp;tabs=visual-studio#kestrel" rel="noreferrer">https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&amp;tabs=visual-studio#kestrel</a></p>
  
  <p>App is in .net core3, with nuget Microsoft.AspNetCore.Authentication.Negotiate and running in kestrel</p>
  
  <p>I have added the </p>
  
  <pre class="lang-cs prettyprint-override"><code>services.AddAuthentication(Microsoft.AspNetCore.Authentication.Negotiate.NegotiateDefaults.AuthenticationScheme).AddNegotiate();
  </code></pre>
  
  <p>as well as </p>
  
  <pre class="lang-cs prettyprint-override"><code>app.UseAuthentication();
  </code></pre>
  
  <p>and setup my devbase image as </p>
  
  <pre><code>FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster as final
  USER root
  RUN whoami
  RUN apt update &amp;&amp; apt dist-upgrade -y
  
  ADD ca/ca.crt /usr/local/share/ca-certificates/ca.crt
  RUN chmod 644 /usr/local/share/ca-certificates/*
  RUN update-ca-certificates
  
  
  RUN DEBIAN_FRONTEND=noninteractive apt install -y krb5-config krb5-user
  
  COPY krb5.conf /etc/krb5.conf
  RUN mkdir /app
  
  RUN echo BQIAAA..== | base64 -d &gt; /app/is.k01.HTTP.keytab
  WORKDIR /app
  
  #RUN docker version
  
  RUN groupadd --gid 1000 app &amp;&amp; useradd --uid 1000 --gid app --shell /bin/bash -d /app app
  
  RUN apt install -y mc sudo syslog-ng realmd gss-ntlmssp
  </code></pre>
  
  <p>the build in tfs pipeline creates app docker image derived from above and adds following env variables, also copies build to /app</p>
  
  <pre><code>RUN chmod 0700 run.sh
  ENV KRB5_KTNAME=/app/is.k01.HTTP.keytab
  ENV KRB5_TRACE=/dev/stdout
  ENV ASPNETCORE_URLS=http://*:80;https://+:443
  RUN chown app:app /app -R
  USER app
  </code></pre>
  
  <p>the app is being run by run.sh</p>
  
  <pre class="lang-sh prettyprint-override"><code>service syslog-ng start
  kinit HTTP/is.k01.mydomain.com@MYDOMAIN.COM -k -t /app/is.k01.HTTP.keytab
  klist
  dotnet dev-certs https
  dotnet /app/SampleApi.dll
  </code></pre>
  
  <p>klist lists the principal which has assigned the SPN to the machine</p>
  
  <p>in ie and firefox i have added the network.negotiate-auth.trusted-uris to my app</p>
  
  <p>however i am getting the login dialog with no success to log in</p>
  
  <p>so the question is:</p>
  
  <p><strong>How can I enable debug log with Microsoft.AspNetCore.Authentication.Negotiate package?</strong></p>
  
  <p>My assumption is that this package does not communicate with kerberos properly, perhaps some package is missing, not running or something.</p>
  
  <p>Also note that the container and .net app is connected successfully to the domain because I use integrated security for connection to the database which works.</p>
  
  <p>**** Edit > Answer to first part</p>
  
  <p>To enable logs, one should enable logs in kestrel:
  in appsettings.json:</p>
  
  <pre><code>  "Logging": {
      "LogLevel": {
        "Default": "Debug",
      }
    },
  </code></pre>
  
  <p>In program.cs:</p>
  
  <pre class="lang-cs prettyprint-override"><code>Host.CreateDefaultBuilder(args)
  .ConfigureLogging(logging =&gt;
  {
      logging.AddFilter("Microsoft", LogLevel.Debug);
      logging.AddFilter("System", LogLevel.Debug);
      logging.ClearProviders();
      logging.AddConsole();
  })
  .ConfigureWebHostDefaults(webBuilder =&gt;
  {
  </code></pre>
  
  <p>In Startup.cs one can track the negotiate events:</p>
  
  <pre class="lang-cs prettyprint-override"><code>services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate(
  
      options =&gt;
      {
          options.PersistKerberosCredentials = true;
          options.Events = new NegotiateEvents()
          {
              OnAuthenticated = challange =&gt;
              {
                  ..
              },
              OnChallenge = challange =&gt;
              {
                  ..
              },
              OnAuthenticationFailed = context =&gt;
              {
                  // context.SkipHandler();
                  Console.WriteLine($"{DateTimeOffset.Now.ToString(czechCulture)} OnAuthenticationFailed/Scheme: {context.Scheme.Str()}, Request: {context.Request.Str()}");
                  Console.WriteLine("context?.HttpContext?.Features?.Select(f=&gt;f.Key.Name.ToString())");
                  var items = context?.HttpContext?.Features?.Select(f =&gt; "- " + f.Key?.Name?.ToString());
                  if (items != null)
                  {
                      Console.WriteLine(string.Join("\n", items));
                  }
                  Console.WriteLine("context.HttpContext.Features.Get&lt;IConnectionItemsFeature&gt;()?.Items " + context.HttpContext.Features.Get&lt;IConnectionItemsFeature&gt;()?.Items?.Count);
                  var items2 = context.HttpContext?.Features.Get&lt;IConnectionItemsFeature&gt;()?.Items?.Select(f =&gt; "- " + f.Key?.ToString() + "=" + f.Value?.ToString());
                  if (items2 != null) {
                      Console.WriteLine(string.Join("\n", items2));
                  }
                  return Task.CompletedTask;
              }
          };
      }
  );
  </code></pre>
  
  <p>**** Edit</p>
  
  <p>Meanwhile according my goal to allow windows authentication in .net core docker web app i was going through the source code of .net core, and corefx and trucated the auth code to this sample console app:</p>
  
  <pre class="lang-cs prettyprint-override"><code>try
  {
      var token = "MyToken==";
      var secAssembly = typeof(AuthenticationException).Assembly;
      Console.WriteLine("var ntAuthType = secAssembly.GetType(System.Net.NTAuthentication, throwOnError: true);");
      var ntAuthType = secAssembly.GetType("System.Net.NTAuthentication", throwOnError: true);
      Console.WriteLine("var _constructor = ntAuthType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).First();");
      var _constructor = ntAuthType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).First();
      Console.WriteLine("var credential = CredentialCache.DefaultCredentials;");
      var credential = CredentialCache.DefaultCredentials;
      Console.WriteLine("var _instance = _constructor.Invoke(new object[] { true, Negotiate, credential, null, 0, null });");
      var _instance = _constructor.Invoke(new object[] { true, "Negotiate", credential, null, 0, null });
  
      var negoStreamPalType = secAssembly.GetType("System.Net.Security.NegotiateStreamPal", throwOnError: true);
      var _getException = negoStreamPalType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(info =&gt; info.Name.Equals("CreateExceptionFromError")).Single();
  
  
      Console.WriteLine("var _getOutgoingBlob = ntAuthType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(info =&gt; info.Name.Equals(GetOutgoingBlob) &amp;&amp; info.GetParameters().Count() == 3).Single();");
      var _getOutgoingBlob = ntAuthType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(info =&gt; info.Name.Equals("GetOutgoingBlob") &amp;&amp; info.GetParameters().Count() == 3).Single();
      Console.WriteLine("var decodedIncomingBlob = Convert.FromBase64String(token);;");
      var decodedIncomingBlob = Convert.FromBase64String(token);
      Console.WriteLine("var parameters = new object[] { decodedIncomingBlob, false, null };");
      var parameters = new object[] { decodedIncomingBlob, false, null };
      Console.WriteLine("var blob = (byte[])_getOutgoingBlob.Invoke(_instance, parameters);");
      var blob = (byte[])_getOutgoingBlob.Invoke(_instance, parameters);
      if (blob != null)
      {
          Console.WriteLine("var out1 = Convert.ToBase64String(blob);");
          var out1 = Convert.ToBase64String(blob);
          Console.WriteLine(out1);
      }
      else
      {
          Console.WriteLine("null blob value returned");
  
  
          var securityStatusType = secAssembly.GetType("System.Net.SecurityStatusPal", throwOnError: true);
          var _statusException = securityStatusType.GetField("Exception");
          var securityStatus = parameters[2];
          var error = (Exception)(_statusException.GetValue(securityStatus) ?? _getException.Invoke(null, new[] { securityStatus }));
          Console.WriteLine("Error:");
          Console.WriteLine(error);
          Console.WriteLine("securityStatus:");
          Console.WriteLine(securityStatus.ToString());
      }
  }
  catch(Exception exc)
  {
      Console.WriteLine(exc.Message);
  }
  </code></pre>
  
  <p>So i found out that the library communicates with 
  <strong>System.Net.NTAuthentication</strong>
  which communicates with 
  <strong>System.Net.Security.NegotiateStreamPal</strong>
  which communicates with unix version of
  <strong>Interop.NetSecurityNative.InitSecContext</strong></p>
  
  <p>which should somehow trigger the GSSAPI in os</p>
  
  <p>In dotnet runtime git they tell us that gss-ntlmssp is required for this to work even that it is not mentioned anyhow in the aspnet core documentation. </p>
  
  <p><a href="https://github.com/dotnet/runtime/issues?utf8=%E2%9C%93&amp;q=gss-ntlmssp" rel="noreferrer">https://github.com/dotnet/runtime/issues?utf8=%E2%9C%93&amp;q=gss-ntlmssp</a></p>
  
  <p>Nevertheless I have compiled the gss-ntlmssp and found out that without this library it throws error "<strong>An unsupported mechanism was requested.</strong>". With my library it throws error "<strong>No credentials were supplied, or the credentials were unavailable or inaccessible.</strong>", but never access to any gss_* methods.</p>
  
  <p>I have tested usage of gss methods by adding the log entry to file which never occured.. fe:</p>
  
  <pre class="lang-c prettyprint-override"><code>OM_uint32 gss_init_sec_context(OM_uint32 *minor_status,
                                 gss_cred_id_t claimant_cred_handle,
                                 gss_ctx_id_t *context_handle,
                                 gss_name_t target_name,
                                 gss_OID mech_type,
                                 OM_uint32 req_flags,
                                 OM_uint32 time_req,
                                 gss_channel_bindings_t input_chan_bindings,
                                 gss_buffer_t input_token,
                                 gss_OID *actual_mech_type,
                                 gss_buffer_t output_token,
                                 OM_uint32 *ret_flags,
                                 OM_uint32 *time_rec)
  {
     FILE *fp;
     fp = fopen("/tmp/gss-debug.log", "w+");
     fprintf(fp, "gss_init_sec_context\n");
     fclose(fp);
      return gssntlm_init_sec_context(minor_status,
                                      claimant_cred_handle,
                                      context_handle,
                                      target_name,
                                      mech_type,
                                      req_flags,
                                      time_req,
                                      input_chan_bindings,
                                      input_token,
                                      actual_mech_type,
                                      output_token,
                                      ret_flags,
                                      time_rec);
  }
  </code></pre>
  
  <p>So .net calls gssapi, and gssapi does not call mechanism. </p>
  
  <p>I have observed the same behavior in centos7 vm, ubuntu windows subsystem, and debian docker image (customized mcr.microsoft.com/dotnet/core/sdk:3.1-buster)</p>
  
  <p><strong>So the question now is, how can I debug gssapi ?</strong></p>
  
  <p>I assume my current gssapi is managed by this library:</p>
  
  <pre><code>readelf -d /usr/lib64/libgssapi_krb5.so
  </code></pre>
  
  <pre><code>Dynamic section at offset 0x4aa48 contains 34 entries:
    Tag        Type                         Name/Value
   0x0000000000000001 (NEEDED)             Shared library: [libkrb5.so.3]
   0x0000000000000001 (NEEDED)             Shared library: [libk5crypto.so.3]
   0x0000000000000001 (NEEDED)             Shared library: [libcom_err.so.2]
   0x0000000000000001 (NEEDED)             Shared library: [libkrb5support.so.0]
   0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
   0x0000000000000001 (NEEDED)             Shared library: [libkeyutils.so.1]
   0x0000000000000001 (NEEDED)             Shared library: [libresolv.so.2]
   0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
   0x000000000000000e (SONAME)             Library soname: [libgssapi_krb5.so.2]
   0x000000000000000c (INIT)               0xb1d8
   0x000000000000000d (FINI)               0x3ebcc
   0x0000000000000019 (INIT_ARRAY)         0x24a120
   0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
   0x000000000000001a (FINI_ARRAY)         0x24a128
   0x000000000000001c (FINI_ARRAYSZ)       16 (bytes)
   0x000000006ffffef5 (GNU_HASH)           0x1f0
   0x0000000000000005 (STRTAB)             0x3048
   0x0000000000000006 (SYMTAB)             0x720
   0x000000000000000a (STRSZ)              9167 (bytes)
   0x000000000000000b (SYMENT)             24 (bytes)
   0x0000000000000003 (PLTGOT)             0x24b000
   0x0000000000000002 (PLTRELSZ)           8088 (bytes)
   0x0000000000000014 (PLTREL)             RELA
   0x0000000000000017 (JMPREL)             0x9240
   0x0000000000000007 (RELA)               0x58b0
   0x0000000000000008 (RELASZ)             14736 (bytes)
   0x0000000000000009 (RELAENT)            24 (bytes)
   0x000000006ffffffc (VERDEF)             0x5788
   0x000000006ffffffd (VERDEFNUM)          3
   0x000000006ffffffe (VERNEED)            0x57e0
   0x000000006fffffff (VERNEEDNUM)         4
   0x000000006ffffff0 (VERSYM)             0x5418
   0x000000006ffffff9 (RELACOUNT)          504
   0x0000000000000000 (NULL)               0x0
  </code></pre>
  
  <p>so far i have compiled new latest gssapi from mit source, and found out that it is throwing me error "An unsupported mechanism was requested." because gssapi requires gss interpreter which is not provided. In centos7 i had another issue that the openssl library was using shared kerberos library which was incompatible, thus yum stopped working.</p>
  
  <p>*** edit</p>
  
  <p>I have found out that the gss-ntlmssp has flag GSS_C_MA_NOT_DFLT_MECH thus it was failing with the message "No credentials were supplied, or the credentials were unavailable or inaccessible.". The solution is to build custom gss-ntlmssp without this attribute because i desire to use it as default auth mechanism.</p>
  
  <p>My sample console app to check credentials works now, I will try to put it work in docker container now.</p>
  
  <p>*** edit</p>
  
  <p>I was able to run my ConsoleApp successfully in kubernetes:</p>
  
  <pre><code>FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster as final
  USER root
  RUN whoami
  RUN apt update &amp;&amp; apt dist-upgrade -y
  
  ADD ca/ca.crt /usr/local/share/ca-certificates/ca.crt
  RUN chmod 644 /usr/local/share/ca-certificates/*
  RUN update-ca-certificates
  
  
  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
  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 cd /src &amp;&amp; wget https://web.mit.edu/kerberos/dist/krb5/1.18/krb5-1.18.tar.gz
  RUN cd /src &amp;&amp; tar -xf krb5-1.18.tar.gz
  RUN cd /src/krb5-1.18/src &amp;&amp; ./configure &amp;&amp; make &amp;&amp; make install
  
  RUN cd /src &amp;&amp; git clone https://github.com/scholtz/gss-ntlmssp.git 
  RUN cd /src/gss-ntlmssp/ &amp;&amp; autoreconf -f -i &amp;&amp; ./configure &amp;&amp; make &amp;&amp; make install
  RUN cp /src/gss-ntlmssp/examples/mech.ntlmssp.conf /etc/gss/mech.d/mech.ntlmssp.conf
  
  COPY testgss /testgss
  RUN cd /testgss &amp;&amp; dotnet ConsoleApp3.dll
  
  RUN groupadd --gid 1000 app &amp;&amp; useradd --uid 1000 --gid app --shell /bin/bash -d /app app
  
  RUN echo BQIA..AAAB | base64 -d &gt; /app/user.keytab
  RUN echo BQIA..oQ== | base64 -d &gt; /etc/krb5.keytab
  RUN echo BQIA..oQ== | base64 -d &gt; /app/is.k01.HTTP.keytab
  RUN echo BQIA..AAA= | base64 -d &gt; /app/is.k01.kerb.keytab
  
  COPY krb5.conf /etc/krb5.conf
  COPY krb5.conf /usr/local/etc/krb5.conf
  RUN ln -s /etc/gss /usr/local/etc/gss
  
  RUN cd /app
  WORKDIR /app
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/HJqI7.png" rel="noreferrer"><img src="https://i.stack.imgur.com/HJqI7.png" alt="enter image description here"></a></p>
  
  <p>However, i am getting this error now:</p>
  
  <pre><code>System.Exception: An authentication exception occured (0xD0000/0x4E540016).
   ---&gt; Interop+NetSecurityNative+GssApiException: GSSAPI operation failed with error - Unspecified GSS failure.  Minor code may provide more information (Feature not available).
     at System.Net.Security.NegotiateStreamPal.GssAcceptSecurityContext(SafeGssContextHandle&amp; context, Byte[] buffer, Byte[]&amp; outputBuffer, UInt32&amp; outFlags)
     at System.Net.Security.NegotiateStreamPal.AcceptSecurityContext(SafeFreeCredentials credentialsHandle, SafeDeleteContext&amp; securityContext, ContextFlagsPal requestedContextFlags, Byte[] incomingBlob, ChannelBinding channelBinding, Byte[]&amp; resultBlob, ContextFlagsPal&amp; contextFlags)
  </code></pre>
  
  <p>*** edit
  Now it fails in here: 
  gssntlm_init_sec_context..
  gssntlm_acquire_cred..
  gssntlm_acquire_cred_from..</p>
  
  <pre class="lang-c prettyprint-override"><code>    if (cred_store != GSS_C_NO_CRED_STORE) {
          retmin = get_creds_from_store(name, cred, cred_store);
      } else {
          retmin = get_user_file_creds(name, cred);
          if (retmin) {
              retmin = external_get_creds(name, cred);
          }
      }
  </code></pre>
  
  <p>get_user_file_creds() returns error as i do not have specific file setup as i want to verify users from ad</p>
  
  <p>external_get_creds() fails here:</p>
  
  <pre class="lang-c prettyprint-override"><code>    wbc_status = wbcCredentialCache(&amp;params, &amp;result, NULL);
      if(!WBC_ERROR_IS_OK(wbc_status)) goto done;
  </code></pre>
  
  <p>external_get_creds tries to authenticate with winbind library and obviously in the credential cache there is no user present</p>
  
  <p>i managed to compile it with the winbind library that samba has provided</p>
  
  <p>so the question now is:
  <strong>How to setup winbind library to communicate with AD?</strong></p>
  
  <p>*** Edit</p>
  
  <p>I have tried to use .net 5 as at github i was told that NTLM works in .net 5. However i get the same result as with .net 3.1.</p>
  
  <p>Docker image with which i have tried that:</p>
  
  <pre><code>FROM mcr.microsoft.com/dotnet/core-nightly/sdk:5.0-buster as final
  USER root
  RUN whoami
  RUN apt update &amp;&amp; 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 cd /src &amp;&amp; git clone https://github.com/scholtz/gss-ntlmssp.git 
  RUN DEBIAN_FRONTEND=noninteractive apt install -y libwbclient-dev samba samba-dev
  #RUN cat /usr/include/samba-4.0/wbclient.h
  
  COPY gss-ntlmssp /usr/local/src/gss-ntlmssp
  RUN cd /usr/local/src/gss-ntlmssp/ &amp;&amp; autoreconf -f -i &amp;&amp; ./configure &amp;&amp; make &amp;&amp; make install
  RUN cp /usr/local/src/gss-ntlmssp/examples/mech.ntlmssp.conf /etc/gss/mech.d/mech.ntlmssp.conf
  RUN groupadd --gid 1000 app &amp;&amp; useradd --uid 1000 --gid app --shell /bin/bash -d /app app
  
  RUN echo BQIAAABMA..ArHdoQ== | base64 -d &gt; /etc/krb5.keytab
  
  COPY krb5.conf /etc/krb5.conf
  COPY smb.conf /etc/samba/smb.conf
  COPY krb5.conf /usr/local/etc/krb5.conf
  
  RUN DEBIAN_FRONTEND=noninteractive apt install -y winbind
  
  ENV KRB5_TRACE=/dev/stdout
  
  RUN mkdir /src2
  WORKDIR /src2
  RUN dotnet --list-runtimes
  RUN dotnet new webapi --auth Windows 
  RUN dotnet add package Microsoft.AspNetCore.Authentication.Negotiate
  
  
  RUN sed -i '/services.AddControllers/i services.AddAuthentication(Microsoft.AspNetCore.Authentication.Negotiate.NegotiateDefaults.AuthenticationScheme).AddNegotiate();' Startup.cs 
  
  RUN sed -i  '/app.UseAuthorization/i app.UseAuthentication();' Startup.cs
  run echo a
  RUN cat Startup.cs
  
  RUN dotnet restore
  RUN dotnet build
  
  
  ENV ASPNETCORE_URLS="http://*:5002;https://*:5003"
  EXPOSE 5002
  EXPOSE 5003
  
  RUN cd /app
  WORKDIR /app
  </code></pre>
  
  <pre><code>docker run -it -p 5003:5003 -it registry.k01.mydomain.com/k01-devbase:latest
  </code></pre>
  
  <p>In docker container:</p>
  
  <pre><code>kinit HTTP/myuser@MYDOMAIN.COM -k -t /etc/krb5.keytab
  klist
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/ICDMZ.png" rel="noreferrer"><img src="https://i.stack.imgur.com/ICDMZ.png" alt="enter image description here"></a></p>
  
  <pre><code>dotnet run src2.dll
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/jlYqc.png" rel="noreferrer"><img src="https://i.stack.imgur.com/jlYqc.png" alt="enter image description here"></a></p>
  
  <p>I have put my own debug info in gssntlmssp library and i put it to file</p>
  
  <pre><code>cat /tmp/gss-debug.log
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/Aae8c.png" rel="noreferrer"><img src="https://i.stack.imgur.com/Aae8c.png" alt="enter image description here"></a></p>
  
  <p>This is exactly the same end where i finished with .net core 3.1 .</p>
  
  <p>wbcCredentialCache (samba lib) fails at the point where it cannot find cached credentials</p>
  
  <p>This is my krb5.conf:</p>
  
  <pre><code>[appdefaults]
      default_lifetime      = 25hrs
      krb4_convert          = false
      krb4_convert_524      = false
  
      ksu = {
          forwardable       = false
      }
  
      pam = {
          minimum_uid       = 100
          forwardable       = true
      }
  
      pam-afs-session = {
          minimum_uid       = 100
      }
  
  [libdefaults]
      default_realm         = MYDOMAIN.COM
  
  [realms]
       MYDOMAIN.COM = {
          kdc            = DC01.MYDOMAIN.COM
          default_domain = MYDOMAIN.COM
      }
  
  [domain_realm]
      mydomain.com.    = MYDOMAIN.COM
      .mydomain.com.    = MYDOMAIN.COM
  
  [logging]
  default      = CONSOLE
  default      = SYSLOG:INFO
  default = FILE:/var/log/krb5-default.log
  kdc = CONSOLE
  kdc = SYSLOG:INFO:DAEMON
  kdc = FILE:/var/log/krb5-kdc.log
  admin_server = SYSLOG:INFO
  admin_server = DEVICE=/dev/tty04
  admin_server = FILE:/var/log/krb5-kadmin.log
  </code></pre>
  
  <p>and part of samba file:</p>
  
  <pre><code>[global]
    security = domain
    workgroup = mydomain.com
    password server = *
    idmap config * : range = 16777216-33554431
    template shell = /bin/bash
    winbind use default domain = yes
    winbind offline logon = false
    wins server = 10.0.0.2
  </code></pre>
  
  <p>In my opinion i would like more to have NTLM then Negotiate because Negotiate is not supported among browsers as far as I know. For example in firefox the person must setup the about:config for negotiate server. Wildcards are not supported, ... </p>
  
  <p>nevertheless it seems that i will not be able to run .net core 5 web app with ntlm, so i will attempt to setup it without the gssntlmssp library now with some default kerberos mechanism. <strong>Any idea what is wrong with my krb5.conf settings?</strong></p>
  
  <p>**** Edit
  So I am now trying two different approaches: </p>
  
  <ol>
  <li>NTLM - in my opinion this is preferable way as i have seen ntlm authenticate users in iis express for example without the dialog box, and does not require any special configuration in firefox or through group policy (please fix me if I am wrong)</li>
  <li>Negotiate</li>
  </ol>
  
  <p>With regards for the negotiate i have managed to make some progres.. </p>
  
  <p>With this docker container i was able to get around the unsupported mechanism:</p>
  
  <pre><code>FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster as final
  USER root
  RUN whoami
  RUN apt update &amp;&amp; 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 &amp;&amp; useradd --uid 1000 --gid app --shell /bin/bash -d /app app
  
  RUN echo BQIAAAA8..vI | base64 -d &gt; /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
  </code></pre>
  
  <p>However now I have other issue: 
  <strong>Request ticket server HTTP/is.k01.mydomain.com@MYDOMAIN.com kvno 3 found in keytab but not with enctype rc4-hmac</strong></p>
  
  <p>This seems to me that the keytab is not with rc4-hmac which is true, because the keytab was generated with </p>
  
  <pre><code>ktpass -princ HTTP/is.k01.mydomain.com@MYDOMAIN.COM -pass *****  -mapuser MYDOMAIN\is.k01.kerb -pType KRB5_NT_PRINCIPAL -out c:\temp\is.k01.HTTP.keytab -crypto AES256-SHA1
  </code></pre>
  
  <p>as the .net documentation says.</p>
  
  <p>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.</p>
  
  <p>This step has moved me further and I get this error instead: <strong>Request ticket server HTTP/is.k01.mydomain.com@MYDOMAIN.COM kvno 4 not found in keytab; keytab is likely out of date*</strong></p>
  
  <p>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.</p>
  
  <p>**** EDIT</p>
  
  <p>So finally I have managed to make it work :)</p>
  
  <p>The issue with "kvno 4 not found in keytab" was in krb5.conf file, where I in favor of forcing aes encryption i have added lines </p>
  
  <pre><code>#   default_tkt_enctypes  = aes256-cts-hmac-sha1-96 aes256-cts-hmac-sha1-9
  #   default_tgs_enctypes  = aes256-cts-hmac-sha1-96 aes256-cts-hmac-sha1-9
  #   permitted_enctypes    = aes256-cts-hmac-sha1-96 aes256-cts-hmac-sha1-9
  </code></pre>
  
  <p>After I have commented them out, the authentication using Negotiate has started to work. I have tested the NTLM with .net 5 and it still does not work.</p>
  
  <p>The krb5.conf file with which negotiate in docker container as build above works :</p>
  
  <pre><code>[appdefaults]
      default_lifetime      = 25hrs
      krb4_convert          = false
      krb4_convert_524      = false
  
      ksu = {
          forwardable       = false
      }
  
      pam = {
          minimum_uid       = 100
          forwardable       = true
      }
  
      pam-afs-session = {
          minimum_uid       = 100
      }
  
  [libdefaults]
      default_realm         = MYDOMAIN.COM
  
  [realms]
       MYDOMAIN.COM = {
          kdc            = DC02.MYDOMAIN.COM
          default_domain = MYDOMAIN.COM
      }
  
  [domain_realm]
      mydomain.com.    = MYDOMAIN.COM
      .mydomain.com.    = MYDOMAIN.COM
  
  [logging]
  default      = CONSOLE
  default      = SYSLOG:INFO
  default = FILE:/var/log/krb5-default.log
  kdc = CONSOLE
  kdc = SYSLOG:INFO:DAEMON
  kdc = FILE:/var/log/krb5-kdc.log
  admin_server = SYSLOG:INFO
  admin_server = DEVICE=/dev/tty04
  admin_server = FILE:/var/log/krb5-kadmin.log
  </code></pre>
  
  <p>So the question now: <strong>Is there any way how to allow many services run negotiate protocol without adding each to spn by one, and manualy setting the browsers?</strong></p>
  
  <p>So at the moment every new web service must have:</p>
  
  <pre><code>setspn -S HTTP/mywebservice.mydomain.com mymachine
  setspn -S HTTP/mywebservice@MYDOMAIN.COM mymachine
  </code></pre>
  
  <p>and must be allowed in internet explorer > settings > security > webs > Details > domain should be listed there
  in firefox about:config > network.negotiate-auth.trusted-uris
  chrome as far as i know takes internet explorer settings</p>
  
  <p>i assume that internet explorer settings should be possible somehow update by the domain group policy.. <strong>anybody any idea how?</strong></p>
  
  <p>**** EDIT
  I have tested wildcard in domain for negotiate settings in browsers and these are the results:</p>
  
  <ul>
  <li><strong>chrome</strong>: SUPPORTS *.k01.mydomain.com </li>
  <li><strong>ie</strong>: SUPPORTS *.k01.mydomain.com </li>
  <li><strong>firefox</strong> (73.0.1 (64-bit)): DOES NOT SUPPORT *.k01.mydomain.com - only full domain eg is.k01.mydomain.com</li>
  <li><strong>edge</strong> 44.18362.449.0 - dont know why but none of ie settings were propagated.. not working with *.k01.mydomain.com nor is.k01.mydomain.com</li>
  </ul>
  
  <p>**** EDIT 
  I have started to use the win auth with negotiate, however I get some issues now in .net core </p>
  
  <p>This code under IIS express shows user in form of MYDOMAIN\myuser:</p>
  
  <pre class="lang-cs prettyprint-override"><code>var userId = string.Join(',', User?.Identities?.Select(c =&gt; c.Name)) ?? "?";
  
  </code></pre>
  
  <p>In linux it shows as myuser@mydomain.com</p>
  
  <p>User.Indentities.First() under IIS express is WindowsIdentity and I can list all groups of the user</p>
  
  <p>User.Indentities.First() under Linux is ClaimsIdentity with no group information</p>
  
  <p>When I try to restrict it with group in IIS Express i get:</p>
  
  <pre class="lang-cs prettyprint-override"><code>//Access granted
  [Authorize(Roles = "MYDOMAIN\\GROUP1")]
  </code></pre>
  
  <pre class="lang-cs prettyprint-override"><code>//403
  [Authorize(Roles = "MYDOMAIN\\GROUP_NOT_EXISTS")]
  </code></pre>
  
  <p>Linux kestrel with negotiate:</p>
  
  <pre class="lang-cs prettyprint-override"><code>//403
  [Authorize(Roles = "MYDOMAIN\\GROUP1")]
  </code></pre>
  
  <p>So it seems that negotiate in kestrel does not list groups properly. So i am going to investigate now, <strong>how to get WindowsIdentity in kestrel</strong>.</p>