WCF: Exposing and consuming WCF service behind SSL Off loader
Problem Statement: Service external address looks correct over HTTPS, however metadata location points to Http end point.
- Host headers getting corrected with the use of <useRequestHeadersForMetadataAddress/>, no machine name. but the metadata location on service page is pointing to Http.
- Because of this, clients are not able to get the metadata.
Service in question: https://xyz.com/TestServices/Service.svc
WSDL Address on service main page:
Internal end point address in WSDL (point to Http):
Solution part 1:
To correct the internal service address and get it over https:
- We add a “logicalAddress” attribute to service end point using “HTTPS”.
- Add “ListenURI” attribute to service end point using “HTTP” Address.
- Update the useRequestHeaderForMetaDataAddress and added the specific port (54888) – if not using default port.
With this we can get the service up and running with Https END point. Able to use the SingleWSDL link to get the relevant meta data location.
However still on main service page, we see svcutil.exe http:// and not svcutil.exe https:// But internally address is correct.
Nested problem 1:
One more error, which may see with the setting is – 404 error – no service end point listening. To resolve the 404 error, we need to use “AddressFilterMode” attribute at service class.
Request coming via LB will have two addresses:
- Via (Physical Address)
- To (Logical Address)
To get the service end point over HTTPS in WSDL document we added “via” attribute to HTTP and “To” attribute to HTTPS.
When doing load balancing ideally only “via” attribute is changed from HTTPS to HTTP. Some load balancer may even set the “To” attribute to HTTP, and as a result service won’t be able to handle the request.
Below setting will help us handle these request from LB.
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class Service1 : IService1
Nested Problem 2:
We got the service WSDL over HTTPS, but we may observe that the internal meta data (XSD) location points to Http. Because of this if we try to use the default WSDL end point, we still fail to add reference.
However, we can use the SingleWSDL link added in framework 4.5 to address the same issue.
- Is there a way we can force service main page to show Https endpoint?
- Only one way – ExternalMetaDataLocation
- Is there a way to disable default wsdl link and only use singlewsdl?
- Not possible.
Solution part 2:
- To disable the normal WSDL link, we need to set ExternalMetadataLocation
- To use externalMetaDataLocation, we can copy the WSDL file which gets created when we add a service reference to single WSDL file in any client project.
- Next we copy this WSDL file to server at same location where we have Service.svc file.
- Once done, we can set the metadata location to point to this WSDL file and client will only see this location.
I placed service1.wsdl file at same level as SVC file.
This file is copied from client project – when I add reference to Single WSDL.
Once done, all we have to do is set ExternalMetaDataLocation WSDL file:
With this when we browse the service, we only see the location set to get MetaData and is the only way to override the MetaData location manually.
Only problem with this approach is, when you have new schema or WSDL created, you need to disable external Metadata location in build environment and use SingleWSDL to get correct WSDL with schema generated.
Once you get new WSDL in sample client project, simple put the WSDL on server and rest all will continue to work fine.
It’s like you have to push WSDL file (if any changes done in structure) along with binaries… that’s it.
To create Flat WSDL in framework 4.0 even for “?WSDL” link…
We can use below blog, but to change the WSDL Address from Http to Https, we need to use External Meta Data Location tag.
On top of that, it still exposes the “?SingleWSDL” link..
Solution part 3:
Using IIS Re-write module to redirect the request for “?wsd” to “singlewsdl”.
Steps to get and use URL re-write module:
- URL Re-write X64 : http://go.microsoft.com/fwlink/?LinkID=615137
- URL Re-write X86 : http://go.microsoft.com/fwlink/?LinkID=615138
- One you install it on IIS server, follow below steps to add a new Rewrite rule.
Click on – View Rewrite maps:
Add new Re-write Map
Give a name:
Select Add Mapping Entry:
Add Mapping, like this
Now go back to Rule window and add new re-write rule:
Select rule, we created:
Once added, it should rewrite all request to WSDL to SINGLEWSDL and user should be able to get the Metadata in one shot.
One catch with this is, I cannot override the External WSDL URl. Client will still see Http address infront of svcutil.exe
However, if they just put WSDL in Https Address, they will get single WSDL.
Visual Studio Add service reference will break, if just refer the service address without WSDL or SingleWSDL added at the end.
If we enter the service url https://xyz.com/Test/Service.svc we get a 400 error
This what happens when we try to add a service reference:
- Initial SSL Handshake.
- Second request try to get the Service PAGE and it eventually read the same address what gets printed infront of exe http://
- In below screen we can see that packet 6 and 7 are actually sent over HTTP, since Packet 5 returned Http end point in main service page (see Screen 2).
- And eventually third request is sent over HTTP and in your case it fails.
What can be done?
Since Add Service reference is designed to read the service page to get WSDL DOC:
- Either we need to get WSDL Doc available over Http (Not possible for SSL offloading).
- Hard code the ExternalMetaDataLocation in service behavior and keep the WSDL file on server.
- This will make sure that when we need metadata (browser or VS), it eventually get’s it from this fixed location and no chances of error.
I hope this help in understanding WCF life behind SSL off-loader LB.