I have a WCF service running locally, and a client running remotely. I have matched bindings on both sides to:
<binding name="TcpBindingConfiguration_2">
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
<message clientCredentialType="Windows"/>
</security>
</binding>
I have a dummy method setup that simply returns Thread.CurrentPrincipal.Identity.Name, ServiceSecurityContext.Current.WindowsIdentity.Name, and OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name, if available.
When running a client on the same machine as the server, the Transport mode works just fine and all three identity names are available. When connecting to my local machine from a remote host (tested on hosts on both same and different domains), however, I get the dreaded "The server has rejected the client credentials" message.
If I change the binding to:
<binding name="TcpBindingConfiguration_1">
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="None"/>
</binding>
the connection is made and the dummy method returns none of the names (as I'd imagine).
Can someone share their configuration of enabling Windows authentication across different hosts (and sometimes different trusted domains) using net.tcp binding? I've seen plenty of examples just saying set the security mode to None, which is all well and good, but I still want to identify who is making the call.
So what's next? Get mode="Transport" working? Use mode="None" and implement some other type of authorisation? Change to a different binding that uses windows authentication successfully?
Apologies if this has been answered elsewhere; I just can't find somewhere it's definitively answered.
Update: This is the code I'm now using to create my client:
var endPointAddress = "net.tcp://" + remoteHost + ":8092/Marc/WcfService";
EndpointAddress address = new EndpointAddress(new Uri(endPointAddress), EndpointIdentity.CreateSpnIdentity("AppName/" + remoteHost), new AddressHeaderCollection());
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
using (var remoteService = new RemoteService.GuestServiceClient(binding, address))
{
try
{
MessageBox.Show(remoteService.Hello("Marc"));
remoteService.Close();
}
catch (Exception ex)
{
remoteService.Abort();
MessageBox.Show("Error occurred:\n\n" + ex.Source + "\n\n" + ex.Message, "Could not connect to " + remoteHost, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
remoteHost is being set to the IP address of the remote machine.
Should I be using channels and proxies and stuff? I've seen other code elsewhere that does, but this plain instantiation of the GuestServiceClient() seems to work (at least locally).
I typically configure my WCF services in code, so I'm not sure how this would translate into the config file.
From my research on this years ago, you need to add a service principal name (SPN) as part of the client's endpoint. The server side didn't need to change.
In code it looks like this:
The second line is what adds the SPN to the connection information. In the EndpointIdentity.CreateSpnIdentity, the string is in the format of AppName/ServerAddress. The 'AppName' part can be anything, but I recommend something that is unique for your app. I genenerally use the FQDN (host.domain.com) format for the address.
Again, I'm not sure how this translates into the config file. I hope this helps get you started down the right path.