Saturday, June 6, 2009

Multiple host headers, ssl, and WCF failures

Came across an interesting scenario in deploying a wcf service that used transport security (ssl).

Symptoms:

Navigating to your WCF service in the browser shows you the wrong internal address.
You are trying to access your site as www.PublicAddress.com/MyService.svc and the page coming back tells you to run

svcutil.exe http://www.PrivateMachineName/MyService.svc

NOOooo you say. That's not what I want! Luckily the solution isn't that difficult but you do have several choices in increasing difficulty.


Solutions:
1. Set host header
This method is the quickest but you have to be aware of a couple situations.
a. You are not using ssl - Simply go into IIS (Im assuming 6 for this posting) and right click on your site, then click "advanced" to add the host headers. Typical scenarios have it configured as "All unassigned addresses" to port 80. Or a specific IP address assigned to port 80, with no host header listed. If either of these fit your case, just click "edit" and add your host name such as www.PublicSite.com
NOTE: If you already have a host header listed or need two host headers, which is very common for sites (such as www.publicsite.com and publicsite.com) WCF will fail when you try to run your service with:
This collection already contains an address with scheme http. There can be at most one address per scheme in this collection.

You CANNOT by default start a wcf service with multiple host names bound to an address. Doh! If you must add multiple hosts to your site, then you cannot use this method. In that case it may make sense to create another site in IIS that responds to a different host header. Such as services.publicsite.com and you can add your host header to iis for that new site, update your web.config if necessary to fit the new address (if you have specified an identity in your web.config then change it, if not then your service will figure out what the proper identity is). For example you only need to update your config if the following case fits or you need ssl on your site and need to modify the element

<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="www.publicsite.com"/>
</identity>



Now, if you are using ssl it becomes a tiny bit trickier. You see adding the host header to port 80 binding in iis like we did above does not apply to ssl. You must specifically add the host header for ssl using the adsutil.vbs script usually located at c:\inetpub\adminscripts
if it is not there and it was possibly removed and you don't feel like installing it sometimes it can be found on a server at %SYSTEMROOT%\ServicePackFiles\i386

You can add host headers for port 80 here as well, so lets go over both.

1. If you are running more than one site on the machine verify the site id to use in the scripts by running at a command prompt:
cscript.exe adsutil.vbs ENUM /P W3SVC

Shows:
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

[/W3SVC/1]
[/W3SVC/439224544]
[/W3SVC/AppPools]
[/W3SVC/Filters]
[/W3SVC/Info]

Take note in this case I have two sites setup. The first site is /1, the second is a random number.

Check the server bindings
a. Cscript.exe adsutil.vbs GET W3SVC/1/ServerBindings this will return something like the following, note no host header listed after port 80
ServerBindings : (LIST) (1 Items)
"23.16.44.11:80: "

b. If this is not the site you want, you need to look up the site id by running this command, and substitute the /1/ above with the actual site id returned from the ENUM command above.

c. Add the binding for non-ssl and ssl
Cscript.exe adsutil.vbs set W3SVC/1/ServerBindings ":80:www.mysite.com"
Cscript.exe adsutil.vbs set W3SVC/1/SecureBindings ":443:www.mysite.com"

Note if you want to have just mysite.com, you will run into a problem because of multiple host headers since you will have www.mysite.com and mysite.com

You can verify changes by running these commands, and you should now see the beta.drtms.net listed for each item.
d. Cscript.exe adsutil.vbs GET W3SVC/1/ServerBindings
e. Cscript.exe adsutil.vbs GET W3SVC/1/SecureBindings

For e. above, yes it true, just because you have a host header assigned in iis does not mean ssl inherits it. You must explicitly set this via the script here to work with WCF. Once done, reset iis (run iisreset as a command line) and that should do it for you.


2. Try binding filters (I'll edit this post shortly on this item)
3. Implement a custom factory to tell ServiceHost which uris to bind to.
4. Avoid the whole problem and use restful services. This involves changing a bit of your code so will consider it not as easy as the main solution here.
5. Implement with web services and let IIS fully handle ssl without worrying about multiple bindings. If you required authentication use basic authentication over ssl configured in IIS. If you don't want to use windows accounts for basic authentication you can implement a custom basic auth provider, such as shown here on codeplex: http://www.codeplex.com/CustomBasicAuth

2 comments:

  1. BlueHost is ultimately the best hosting company with plans for all of your hosting requirements.

    ReplyDelete