Search code examples
c#active-directorywcf-security

WCF configuration using kerberos fails on certain domains


I'm not exactly sure, this question is right here on stackoverflow... maybe it belongs to serverfault... anyhow, here it goes:

We have server/client setup implementing WCF Services. Both the client and the service are running under a domain-user. The domain user, which hosts the server side service is also a local administrator on the server. This setup works just fine for most customers. Certain customers complain, that they cannot use Single-Sign-On (SSO) using the hostname of the server. We followed up on that problem and found, that if we specify the IP address of the server, things work just fine. To me this indicates, that the configuration we have works fine, but certain domain configurations will interfere with our setup.

WCF Configuration:

<configuration>
  <system.serviceModel>
    <client>
      <!-- minimal needed config, address will be ignored -->
      <endpoint address="net.tcp://inihost:10100/ApplicationService" binding="netTcpBinding"
                bindingConfiguration="nosecNetTcpBinding" name="ApplicationServiceHost"
                    contract="Kaba.Exos.Transport.Infrastructure.ICommandExecutionService" />
      <endpoint address="net.tcp://inihost:10100/ApplicationServiceSSO"     binding="netTcpBinding"
                bindingConfiguration="ssoNetTcpBinding"     name="ApplicationServiceSsoHost"
                    contract="Kaba.Exos.Transport.Infrastructure.ICommandExecutionService" />
    </client>
    <bindings>
      <netTcpBinding>
        <binding name="nosecNetTcpBinding" receiveTimeout="00:30:00"     sendTimeout="00:30:00" maxBufferPoolSize="134217728" maxBufferSize="134217728"     maxReceivedMessageSize="134217728">
          <security mode="None"></security>
        </binding>
        <binding name="ssoNetTcpBinding" receiveTimeout="00:30:00"     sendTimeout="00:30:00" maxBufferPoolSize="134217728" maxBufferSize="134217728"     maxReceivedMessageSize="134217728">
          <security mode="Transport">
            <message clientCredentialType="Windows" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

C# Code to setup WCF connection:

        var exeConfigFileFullPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\" + exeConfigFilename;

        var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = exeConfigFileFullPath };

        var newConfig = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);

        try
        {
            this.channelFactory = new ConfigurationChannelFactory<ICommandExecutionServiceChannel>(endpointConfigurationName, newConfig, null);
        }
        catch (Exception e)
        {
            traceLogger.TraceException(e, "CommandExecutionServiceClient", "CommandExecutionServiceClient");
            throw;
        }

        if (!string.IsNullOrEmpty(serviceConnection.Item1))
        {
            var builder = new UriBuilder(this.channelFactory.Endpoint.Address.Uri) { Host = serviceConnection.Item1, Port = serviceConnection.Item2 };
            this.channelFactory.Endpoint.Address = new EndpointAddress(builder.Uri);
        }

        this.AddForwardExceptionToClientEndpointBehavior();
    }

After implementing two small changes ....

In the WCF File

  <!-- minimal needed config, address will be ignored -->
  <endpoint address="net.tcp://inihost:10100/ApplicationServiceSSO" binding="netTcpBinding"
            bindingConfiguration="ssoNetTcpBinding" name="ApplicationServiceSsoHost"
            contract="Kaba.Exos.Transport.Infrastructure.ICommandExecutionService" behaviorConfiguration="kerberosFallbackToNtlm">

    <behaviors>
      <endpointBehaviors>
        <behavior name="kerberosFallbackToNtlm">
          <clientCredentials>
            <!-- if Kerberos is required, set the following value to false and configure the proper identity for Exos9300ServiceSsoHost above. -->
            <windows allowNtlm="true" />
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>

and in the code

this.channelFactory.Endpoint.Address = new EndpointAddress(builder.Uri, EndpointIdentity.CreateSpnIdentity(String.Empty));

This setup works in a domain setup, where we previously had an "SSPI context could not be established" error.

Now my question:

Why would this setup work just fine on one domain, but fail on another domain. Are there any Active Directory settings, that could influence this behavior?


Solution

  • Well it turns out I was misinformed.... In the cases, where it was working, we had a local User running the service instead of a domain user. So the service authentication will fallback on NTLM if the service is hosted by a local admin user.

    I did manage to get this scenario to work by removing the SPN which was created by default and manually adding a new SPN.

    To remove the existing SPN which was created by default:

    setspn -D HOST/{HOSTNAME server} {HOSTNAME server}
    

    To add the new SPN for the domain user:

    setspn -S HOST/{HOSTNAME server} {Domainuser service}
    

    ATTENTION!!

    once I issued the command:

    setspn -D HOST/{FQDN server} {HOSTNAME server}
    

    and managed to disable my server from authenticating any domain users with the domain controller because it was no longer in the trust database.