Scope:
Step-by-step walkthrough to establish a secure connection between a WCF Client and Service by using a Kerberos token. This example will focus on message security, though WCF also offers transport security. We will establish mutual authentication between service and client, using the wsHttpBinding.
Kerberos delegation will only have to be granted to the Service, not to the Client. We can choose to run the Service under a NetworkService or Localsystem ‘service’ account, or under a ‘domain’ account (domain\user). The Service will be self-hosted (serviceHost).
Requirements:
.NET 3.5
.Windows Domain with Active Directory
.Domain Administrator access
Tools:
SetSPN.exe, Klist.exe, Kerbtray.exe
SetSPN.exe makes it easy to define service principal names for computers or user accounts in the active directory. The other 2 tools can be useful to inspect the kerberos tickets on the machine where you will run the WCF Service. They can be downloaded from the microsoft site, in one of their Resource toolkits for windows servers
Running the Service under a Service Account
1. The Service
Code:
using System; using System.Collections.Generic; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceProcess; using System.ServiceModel.Description; namespace WCFWithKerberos { class Program { static void Main(string[] args) { // create the service host ServiceHost myServiceHost = new ServiceHost(typeof(Echo)); // create the binding WSHttpBinding binding = new WSHttpBinding(); binding.Security.Mode = SecurityMode.Message; binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows; // disable credential negotiation and establishment of the security context binding.Security.Message.NegotiateServiceCredential = false; binding.Security.Message.EstablishSecurityContext = false; // Creata a URI for the endpoint address Uri httpUri = new Uri("http://yamata:8000/Echo"); // Create the Endpoint Address with the SPN for the Identity EndpointAddress ea = new EndpointAddress(httpUri, EndpointIdentity.CreateSpnIdentity("HOST/yamata.marbie.net:8000")); /* works with * setspn -a HOST/yamata.marbie.net:8000 yamata */ // Get the contract from the IEcho interface ContractDescription contract = ContractDescription.GetContract(typeof(IEcho)); // Create a new Service Endpoint ServiceEndpoint se = new ServiceEndpoint(contract, binding, ea); // Add the Service Endpoint to the service myServiceHost.Description.Endpoints.Add(se); // Open the service myServiceHost.Open(); Console.WriteLine("Listening..."); Console.ReadLine(); // Close the service myServiceHost.Close(); } } public class Echo : IEcho { public string Reply(string message) { Console.WriteLine(message); return message; } } [ServiceContract] public interface IEcho { [OperationContract] string Reply(string message); } }
Configuration
‘Yamata’ is the name of the machine I will be running the Service on.
Now we need to grant Kerberos delegation to this machine. For this, we will connect to the Domain Server and ‘Manage users and computers in Active Directory’. Browse to the computer name (yanaga in my case), in the ‘Computers’ tree, rightclick to select ‘properties’, select the ‘Delegation’ tab. For simplicity, we will Thrust this computer for delegation to any service, but you can tighten it to the service of type ‘HOST’, and limit it to specific ports (8000 in our example) if you want.
Next, we need to add the service principal name (SPN) we used in CreateSPNIdentity (HOST/yamata.marbie.net:8000) to the computer. On the domain controller, we can exete in a dosbox the following command:
setspn -a HOST/yamata.marbie.net:8000 yamata
Verify that there are no double entries, with the command: setspn -L yamata
Double entries will break the kerberos authentication.
The next important thing we need to do is make sure this kerberos ticket is propagated to the machine where we want to run the service. Most often, this can be done by -on the machine where we will run the service- lock the screen, and unlock it by typing in the password. This should cause a refresh of the kerberos tickets. Klist.exe or Kerbtray.exe can offer you insight on the list of tickets on your machine. The entry SPN we added should be in this list. If you use Kerbtray, you can check if the ‘delegate’ checkbox is checked dor this entry. It should be. If it is not, the ‘Kerberos Delegation’ right has not been granted to the machine for this Service.
Now, we can launch the Servide under a service account, we will use the LocalSystem account on yamata in this example. To do this, we execute the following command in start| Run:
at HH:mm /i cmd.exe
where we replace HH:mm with the current computer time + 1 minute. Wait a minute and a dos box pops up. This box will not run under your local computer user account, but under LocalSystem. In this dos box, we startup the WCFWithKerberos.exe
2. The Client
Code
using System; using System.Collections.Generic; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceProcess; using System.ServiceModel.Description; namespace Client { class Client { private ChannelFactory factory; private WCFWithKerberos.IEcho echoClient; private WSHttpBinding myBinding; private EndpointAddress ea; public Client() { // Create the biding myBinding = new WSHttpBinding(); myBinding.Security.Mode = SecurityMode.Message; myBinding.Security.Message.ClientCredentialType = MessageCredentialType.Windows; // disable credential negotiation and the establishment of a security context myBinding.Security.Message.NegotiateServiceCredential = false; myBinding.Security.Message.EstablishSecurityContext = false; // Create the endpoint address and set the SPN identity. // The SPN must match the identity of the service's SPN ea = new EndpointAddress(new Uri("http://yamata:8000/Echo"), EndpointIdentity.CreateSpnIdentity("HOST/yamata.marbie.net:8000")); /* works with * setspn -a HOST/yamata.marbie.net:8000 yamata */ } static void Main(string[] args) { Client c = new Client(); c.Open(); c.echoClient.Reply("test"); c.Close(); } public void Open() { // Create the proxy during runtime factory = new ChannelFactory(myBinding,ea); echoClient = factory.CreateChannel(); } public void Close() { ((IChannel)echoClient).Close(); } } }
Configuration
It doesn’t matter under which account or where you run the client, as long as we run it under an account or on a workstation that is properly logged in into the domain (received valid kerberos ticket). Open a dos box and launch the Client.exe
The Service should echo ‘test’ in the console and the client should exit without output or errors. Even when we disabled credential negotiation and security context establishment, we have connected our Client to the Service on a secure way by letting WCF handle all the authentication hassle through the delegation of Kerberos.
Running the Service under a Domain Account
Configuration
There is nothing that needs to be done on the code side, if we re-use the same SPN.
We need to remove the SPN entry from the computer on the active directory, with the SetSPN tool:
setspn -d HOST/yamata.marbie.net:8000 yamata
Now we add the SPN to the domain account.
setspn -a HOST/yamata.marbie.net:8000 marbie\dreezst
where ‘dreezst’ is the domain user and ‘marbie’ is the domain name, under which I will be running the Service.
Additionally, we need to go to the Active Directory again, browse to the user ‘dreezst’, and grant kerberos delegation.
Next, we need to update our kerberos tickets on our Service machine by locking and unlocking the Computer again. Use Klist or Ktray to inspect your kerberos tickets and see that the SPN entry is there and updated. With Ktray, verify the delegation checkbox.
Now, we need to run the service as our domain account. In a dos box, enter: runas /user:marbie\dreezst WCFWithKerberos.exe, and enter the password for the domain user.
Once the Service is up and listening, launch the client from any machine under any (logged in) account.
The difference here is that we are running the Service under a domain account, which will probably be more likely in a production environment where you typically have domain accounts that are configured as Service accounts to run these services.
March 3, 2009 at 1:06 pm |
I followed your instructions but I keep getting the following exception:
“The athentication modes using Kerberos do not support the impersonation level ‘Delegation’. Specify identification or impersonation.
Parameter name: tokenImpersonationLevel”.
Do you have any idea?
March 3, 2009 at 10:26 pm |
Yes, this is typically the error you get when you have not or incorrectly granted delegation of impersonation rights to the machine(+process) that hosts the WCF service in the Active directory.
October 18, 2009 at 10:29 pm |
Hi,
Is it possible to make kerberos delegation to any user?
Example: Domain A has an IIS with a Web Application published: MyApp.com.
Access to this app should be: Defined User’s from a trusted secure network (not from the same Domain and no trusts between) should start the MyApp.com (example with UPN as http header) and be able to make a SSO to MyApp.com!
My Idea: The WCF Service on IIS where the MyApp.com is published takes the UPN Argument and makes a kerberos delegation to this User (provisioned as Shadow User to Domain A) :)!
Is this possible?
Thanks a lot..
Tosha
October 27, 2009 at 7:09 am |
If there’s no trust between the domains it will not work.
In this particular case i would prefer to use certificates for client and service to establish mutual authentication.
December 29, 2009 at 6:31 pm |
I’ve always configured my delegating service connections using an SPN that starts with “HTTP”, I noticed you use “HOST”. In the WCF world, when would you use one versus the other? My guess would be based on the identity of the running service host: HOST = Local Machine, HTTP = Network Service?
January 4, 2010 at 7:17 am |
The way i understood it, it depends on where you host your WCF services. If it is self-hosted, it will be running as a svchost.exe process and HOST should be used. If you host it in IIS i think HTTP should be used.
October 29, 2010 at 12:09 pm |
[…] I started from the WCF kerberos sample on Marbie’s blog. I modified the programmatic binding in the webpart to match this […]
February 23, 2012 at 11:33 am |
Ohhhh!!!
Thank you very much!!!
I’m seeking this tutorial very long time) Because have some problems with passing user account to wcf service throw tcp.net.
Now it works!
July 31, 2013 at 6:05 pm |
I did delegation for my service and configured SPN. but the klist does not display my service SPN there. In what format it shoud appear over there?
August 1, 2013 at 5:15 pm |
matching my example:
HOST/yamata.marbie.net:8000
so pending on self-hosted or iis-hosted service it can be HTTP/yourmachine:port or HOST/yourmachine:port
May 7, 2015 at 1:27 pm |
Just a side note.
I accidentally deleted my server SPN for example setspn -d HOST/yamata.marbie.net yamata and not HOST/yamata.marbie.net:8000 yamata
This resulted that i had to rebuild my VM. if you delete the server SPN while on a network you will get disconnected from your domain controller and you will loose trust with the DC.
It is best to use another type like your application name instead of host when doing self hosted.
setspn -a MyApp/yamata.marbie.net marbie\dreezst
August 7, 2015 at 8:21 am |
Blogged about Transport Delegation and WCF. Your site helped. thanks.
http://www.waynecliffordbarker.co.za/wcf-tcp-transport-security-with-delegation/