![]() |
Show Changes |
![]() |
Edit |
![]() |
|
![]() |
Recent Changes |
![]() |
Subscriptions |
![]() |
Lost and Found |
![]() |
Find References |
![]() |
Rename |
| Search |
History
| 4/14/2010 5:31:20 PM |
![]() |
List all versions |
Version 1.x of the .NET Framework does not support user authentication, integrity protection, or encryption of the TCP remoting channel. While it is possible to layer this support onto the HTTP channel by hosting your server objects in IIS, that approach is a bit kludgy, as you’ll see. Version 2.0 of the .NET Framework just went into its first beta cycle as I am wrapping up final edits on this book, and the TCP channel now supports these important security features, which is great news. This new feature is based on the NegotiateStream class that I showed in Item 66.
For those who are relying on version 1.x of the .NET Framework, let’s first consider using the HTTP channel and hosting in IIS. To do this, you create a virtual directory and copy your remoting configuration file into a web.config file at its root. Figure 67.1 shows an example remoting configuration file, and figure 67.2 shows the web.config file you should end up with. Note that there are a few differences.
<configuration>
<system.runtime.remoting>
<application name='MyApp'>
<channels>
<channel ref='tcp' port='4242'/>
</channels>
<service>
<wellknown type='Server, server'
mode='Singleton'
objectUri='MyEndpoint'/>
</service>
</application>
</system.runtime.remoting>
</configuration>
Figure 67.1 A typical remoting configuration file
Here's what I changed in moving to the web.config file. From top to bottom, first I removed the application name attribute. This part of the client's URL will now be controlled by the name of the virtual directory in IIS. Next, I changed the channel from tcp to http, which is the only supported channel when hosting in IIS. Also note that I removed the port attribute, because that will be controlled by IIS as well. By default when you use the TCP channel, you're also using the binary formatter, but with HTTP the default is the less efficient SOAP formatter, so I've overridden the default and requested the binary formatter instead. When hosting in IIS, you can use any formatter you like. Finally, note the subtle change in the objectUri attribute. I changed MyEndpoint to MyEndpoint.rem, making it look like a file name. There won't actually be any file with this name, but using this format allows the script map in IIS to forward requests for this endpoint to the .NET Framework, where it will then be dispatched to the remoting plumbing (if you look in machine.config, you'll find a handler for .rem and .soap extensions that's supplied by the the Remoting team).
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref='http'>
<serverProviders>
<formatter ref='binary'/>
</serverProviders>
</channel>
</channels>
<service>
<wellknown type='Server, server'
mode='Singleton'
objectUri='MyEndpoint.rem'/>
</service>
</application>
</system.runtime.remoting>
</configuration>
Figure 67.2 Converting to a web.config file
Based on the original remoting configuration file, the client would have connected to tcp://hostname:4242/MyApp/MyEndpoint. Now that we've moved into IIS, the client connection string changes to http://hostname:80/vdir/MyEndpoint.rem@, where hostname@ is the name of the Web server and vdir is the name of the virtual directory. This really isn't all that hard to do. I've diagrammed the resulting architecture in Figure 67.3. What I want you to note is that any remote calls initiated by the client are sent through IIS and into the server object. These calls can be secured via SSL, Kerberos, and so forth, as I'll talk about shortly. However, if the server fires events or makes other callbacks to the client, IIS knows nothing about the connection from the worker process back to the client. It's a raw connection that has no authentication, integrity, or encryption on the channel. Hosting in IIS secures only forward calls, not callbacks or events!

Figure 67.3 Hosting in IIS
If you decide to host in IIS, you'll first want to think about authentication. If the client and server are in a Windows domain, and you want to use the client's domain credentials to authenticate with the server, turn on Windows Integrated Authentication for your virtual directory and disable anonymous access. But note that this won't work out of the box! The .NET Remoting architecture normally prevents clients from disclosing their identities to servers, thus protecting the client's anonymity. Clearly this is not what you want if your system needs to authenticate clients. To fix this, use a client configuration file as well, as shown in Figure 67.4. You’ll probably already have a client configuration file anyway in order to use the the http channel with the binary formatter. Note in my client configuration file how I've specified the useDefaultCredentials attribute. This isn't well documented, but it tells the remoting plumbing that the client wishes to identify herself to servers using her Windows logon.
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref='http' useDefaultCredentials='true'>
<clientProviders>
<formatter ref='binary'/>
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Figure 67.4 A client-side remoting configuration file
Besides the insecure callback problem I described earlier, the other thing that bothers me about this solution is that even if you're using Kerberos to authenticate the client, IIS is simply not designed to use Kerberos to protect the channel. In other words, if all you do is turn on Integrated Windows Authentication and disable anonymous access, you won't get any sort of integrity protection or encryption. So there's the potential for connection hijacking, tampering, and eavesdropping attacks. The way to mitigate these attacks is to either require SSL or turn on IPSEC. This seems like overkill to me, which is one reason that I called this solution kludgy at the beginning of this item.
What I really want is Kerberos support directly in the .NET Remoting channel, and fortunately that’s exactly what version 2.0 provides. To get started, all you need to do is change your configuration files a bit. Now please note that we are working with a very early implementation, and I would not be surprised if some of these details change before Visual Studio 2005 actually ships. In fact, I plan on making some change requests myself now that I’ve seen the implementation.
Here’s how the TCP channel needs to be configured on the server to require authentication (obviously the port is up to you):
<channel ref='tcp' port='4242' authenticationMode='IdentifyCallers'/>
And of course if the server requires authentication, the client must comply, or she will be denied access. Here’s what the client-side reference should look like (refer to Item 51 to help you pick an appropriate impersonationLevel):
<channel ref='tcp' useDefaultCredentials='true' impersonationLevel='Impersonate'/>
In order for authentication to work with Kerberos, the client will need to specify a service principal name. If the client will only be talking to a single server, she can simply put the SPN in her configuration file (in this example, I’m assuming she wants to connect to a service of type “WeatherService” (if this makes no sense, revisit Item 60 to learn about SPNs, then follow up with Item 66, which provides a concrete example of the concept).
<channel ref='tcp'
useDefaultCredentials='true'
impersonationLevel='Impersonate'
spn='WeatherService/host.acme.com'/>
If the client needs to use different SPNs for different proxies, she should specify the SPN programmatically, which will override any value in the configuration file (or the default value, which is an empty string). For example:
IFoo proxy = (IFoo)RemotingServices.Connect(typeof(IFoo),
"tcp://...");
string spn = string.Format("WeatherService/{0}",
serverHostName);
ChannelServices.GetChannelSinkProperties(proxy)["spn"] = spn;
So far what I’ve shown you is how to perform authentication, but we still need to add integrity protection and encryption to the channel. Once you’ve got authentication working, it’s quite easy to add these two features. Just add another property to both the client and server-side channel references (I’m going to lobby the .NET remoting team to change the name of this attribute from encryption to protection):
encryption='EncryptAndSign'
Be sure to do this on both the client and server side.
Now that we’ve got the channel secured, we can talk about how the server discovers (and perhaps impersonates) the client’s identity. In the code I’m looking at here in Beta 1, this isn’t as clean as it should be. Remember how I specified authenticationMode='IdentifyCallers'? Well, you’d expect that the caller’s identity would be communicated to the server via Thread.CurrentPrincipal, a pattern I described in Item 33. Sadly, in this beta, it’s not. In fact, I see no way to retrieve the client’s identity through public methods or properties at all right now! It appears as though the only way to discover the client’s identity is to go one step further and specify authenticationMode='ImpersonateCallers', which not only sets Thread.CurrentPrincipal, but also impersonates the client before calling the method on the object. Hopefully by the time the next Beta rolls around, you’ll see this fixed so that Thread.CurrentPrincipal is set even if you only specify IdentifyCallers. I’ll be pushing for that change, for certain. You shouldn’t have to impersonate the client in order to know who they are!
Keith's first book-in-a-wiki. If you would like to read the book online or order a physical copy to throw at annoying coworkers, surf to the HomePage. Please note that due to overwhelming wikispam, this particular wiki is no longer editable.
About FlexWiki.
Recent Topics