Author: Willem de Klerk
Background
Recently I have been developing various (web-) services based on WCF. Most of which are only accessible from a highly secured environment. Services that target consumers outside of the high security domain are fully detached from any resources residing in the domain; all safe and sound.
Needless to say, the paradise of safety created by physical separation of critical resources had some major drawbacks and limitations. Most services targeting outside anonymous consumers, still needed at least some basic access to resources within the high secure environment. In order to make resources available to these services while respecting the separation of the high security environment and the outside world, several McGyver-style solutions were implemented, most of which involved ad-hoc designing and bending of architectural concepts.
In order to prevent the construction of a system patched together with duct-tape, an architectural decision was made: An extra layer of security (DMZ http://en.wikipedia.org/wiki/DMZ_%28computing%29) was added to the architecture. External targeted services run inside the high security zone, but are accessible from the outside through a proxy service residing in the DMZ.
In order to make such a scenario possible, a proxy service (or service router) needs to be implemented. In short, a service router needs to do the following stuff:
- Receive any incoming SOAP message
- Optionally do some basic operations (security checking / load balancing)
- Pass the message to the internal service being targeted
WCF, with its highly configurable endpoints, provides a solid base for the development of such a service. In the following text, I will explain how such a router could be constructed, based on an example.
Step 1 – Constructing the web service
Constructing the web service is straightforward, as the service router can target any service To quickly build an example, take the following steps:
1. Create a new WCF service application (under c# > web)
2. Alter the class and interface names of the Service contract and the service implementation
3. Remove all standard code and comments in both files
4. Change the reference to Service1 in the SVC file to target your renamed service
5. Add the following code to the service contract:
[OperationContract]
String GetValue(int id);
6. Add the following code to the Service implementation:
public String GetValue(int id)
{
return "returning the value for ID #" + id;
}
7. Set the project to debug on a static port number (project context menu > properties > web)
You should now have a basic WCF service running, implementing one method that returns a string. The service should run on a designated port.
Step 2 – Constructing the client
1. Add a console application project to your solution
2. Add a service reference to the project (References context menu > add service reference…) by using the discover button in the add service window
3. Add the following code to the main method (Assuming your service is named WCFServiceClient):
WCFServiceClient client = new WCFServiceClient();
Console.WriteLine(client.GetValue(321));
Console.ReadKey();
4. Run your solution, and check if the service responds as expected.
5. Add a custom Binding Configuration using the WCF configuration tool:
- Bindings context > Add Binding configuration
- On the security tab select none instead of message
- Set the Binding configuration of the client endpoint
Step 3 – Constructing the Router
1. Add a new WCF service application to your solution (under c# > web)
2. Rename all of the components as done in step 1, delete all the generated code in the Service contract and the service implementation
3. Update the interface with the following code (alter interface names as needed):
[ServiceContract]
public interface IWCFRouter
{
[OperationContract(Name = "RouteMessage", Action = "*", ReplyAction= "*")]
Message RouteMessage(Message m);
}
In the example above you can see that the action and reply action for the RouteMessage operation are marked with *. SOAP messages carry the ‘Action’ field in there headers. The Action field is used to indicate which method of the service is used to process the SOAP message. The action attribute indicates which SOAP messages are processed by the method the attribute belongs to (the action attribute defaults to the name of the method). Defining the * sign as a value for this attribute, indicates that this method should process all incoming messages regardless of the value of the Action header in the SOAP message (unless another method is explicitly configured to handle the Action defined in the SOAP message).
4. Add the following code to the Service implementation (Change service / configuration names respectively):
public class WCFRouter : IWCFRouter
{
public Message RouteMessage(System.ServiceModel.Channels.Message m)
{
ChannelFactory<IWCFRouter> factory = new ChannelFactory<IWCFRouter>("TargetServiceEndpointConfiguration");
IWCFRouter proxy = factory.CreateChannel();
Message res = proxy.RouteMessage(m);
return res;
}
}
This method will create an endpoint instance using the configuration created in the next few steps (named TargetServiceEndpointConfiguration in this example). This method sends the message received (m) through the instance of the endpoint (proxy) the result (res) is then send back to the client. This is all very basic, this would be the place where you implement custom logic.
5. Create a service endpoint pointing to the web service manually using the WCF configuration tool (Tools > WCF configuration tool). DO NOT USE THE ADD SERVICE REFERENCE! As this will generate a proxy to the web service. A proxy is not needed in the router, as messages will be forwarded based on the SOAP headers, rather than the content of the message. Configure the endpoint as follows:
- Address: The physical address of the web service
- Contract: The service contract of the web service router
- Binding: WsHttpBinding
- BehaviourConfiguration: Custom, see step 5
-BindingConfiguration: Custom, see step
6. Configure custom behavior for the endpoint:
- Add the endpoint configuration (Advanced > Endpoint behaviors context > new behavior)
- Add the ViaUri element to the Endpoint behavior
-Set the endpoint behavior of the service endpoint
7.. Add a custom Binding Configuration:
- Bindings context > Add Binding configuration
- On the security tab select none instead of message
- Set the Binding configuration of the service endpoint
Message level security cannot be used with the service router as this would encrypt the SOAP message itself, rendering the router incapable of accessing the necessary message components.
References
For more in depth information on WCF routing check out the following MSDN article:
http://msdn.microsoft.com/en-us/magazine/cc500646.aspx