August 20, 2008

Windows 2008 Server Musings

I am currently coordinating an effort building a reference architecture with Microsoft Sharepoint 2007, diving into a rich content model and service-enabling the front-end with the overall goal to use it as a “real” web content management. Our goal was to stick with the Microsoft toolstack (simply because we have free access to all products at work with Razorfish now being a Microsoft-owned company)

Our first idea was to start prototyping on the new Windows 2008 Server.

32 vs 64 Bit

Our initial gut instinct was to stick with what’s working, plus we where only installing on PCs with 4 Gigs of RAM. After the installation I tried to get Hyper-V (built-in virtualization) to work, however, Hyper-V is only supported on the 64 bit version of Windows. I then tried to install Virtual PC 2007 — it complained that the version of Windows I was using wasn’t compatible and that I at least needed to install IIS 6 compatibility modules.

We decided to re-install with the 64 bit version of Windows Server.

Hyper-V

As mentioned, Hyper-V requires 64 bit Windows. After installing all the optional patches and rebooting twice from Windows Update Hyper-V suddenly appeared. I proceeded to install a 64 bit Windows 2008 guest. Once it was installed and booted up I noticed it had no network access. Strange — Hyper-V was configured with a virtual adapter. As it turns out, the drivers for the virtual network adapter are part of the Windows Update patches for Server 2008 — so the guest couldn’t see the network without the updates and of course couldn’t download the updates without the network adapter — if Hyper-V wasn’t technically a CTP I’d be really pissed now. Luckily one can configure a “legacy network adapter” that a freshly installed guest can see at least long enough to fetch the updated drivers.

Hyper-V now runs along just fine with the small caveat that it randomly crashes with a nice message box “Worker process died”.

Verdict: This is not a stable development or even production environment yet.

MOSS 2007

At this point I decided to install MOSS 2007. After a download of the MOSS 2007 ISO the first installation attempt failed — Windows complained there were known compatibility issues with the software and the operating system. Now we downloaded the SP1 files for the WSS foundation and MOSS 2007. Unfortunately there is no consolidates ISO — you have copy the MOSS 2007 ISO to a temporary folder and manually extract the SP1 executables via the command line and copy the contents into the temporary folder with the ISO. The installation then fails with an error that the sample database cannot be set up.

Just trying to install WSS 3.0 SP1 by itself at least works fine.

Really?

At this point we decided to go back to Windows 2003 Server R2. Stay tuned for updates.

.NET 3.5 and LINQ

I had a little spare time last week and finally dug into .NET 3.5, mostly extension methods, lamda expressions, and LINQ. Pronounced LINK, it is the embedded query language in .NET 3.5. I am not going to outline what it does or how it works, there’s plenty of that on the internet, but I will continue my tradition to outline some funny annoyances :)

The nicest think IMHO about LINQ is the SQL bridge, which essentially implements a full OR-mapping framework like Hibernate in the .NET framework. Let’s dive into an example and map the following class to a database table:

[Table(Name = "c_schema")]
public
class LinqSchema
{
private long id;
private string uuid;
private string name;
[Column]
private long id_creator = 1;
[Column]
private long id_owner = 1;
[Column]
private int version = 1;
[Column]
private bool is_public = true;
[Column(Name = "id", IsPrimaryKey = true)]
public long Id
{
get { return id; }
set { id = value; }
}
[Column(Name = "uuid")]
public string Uuid
{
get { return uuid; }
set { uuid = value; }
}
[Column(Name = "name")]
public string Name
{
get { return name; }
set { name = value; }
}
}

So far so good, that looks almost like an annotated Hibernate mapping in Java. Now let’s read some existing data out of the database:

string
connectionString = @”…”;

DataContext db = new DataContext(connectionString);
Table
<LinqSchema> schemata = db.GetTable<LinqSchema>();

// nice little LINQ expression
var
query1 = from ss in schemata where ss.Name == “minimal” select ss;

// iterate of the query result
foreach
(var qq in query1)

Console.WriteLine(string.Format(“Schema {0}”, qq.Uuid));

That’s pretty self-explanatory. Now the funny caveats. Initially, one of the columns was an integer type, and in .NET I put in a long. One would think integers can get casted into longs, yet the error I got was this:

Error Snaphot

I was quite stunned that the SqlDependency extensions for query caching which were introduced in SQL Server 2005 and .NET 2.0 didn’t work with LINQ at all. There are a few hacks out on the internet, but I couldn’t get them to work reliably.

I found association mappings to be somewhat inelegant, especially compared to Hibernate which doesn’t require you to redundantly map the IDs and the referenced objects (1-1 mapping shown in this example):

// column maps the id
[Column(Name="id_schema")]
public
long id_schema;

// entity reference holds the reference object
private
EntityRef<LinqSchema> _Schema;

// the actual association. Storage refers to the entity reference,
// ThisKey to the variable containing the id.
[Association(Storage = "_Schema", ThisKey = "id_schema", OtherKey = "Id")]
public
LinqSchema Schema
{
get
{ return _Schema.Entity;}
set
{ _Schema.Entity = value; }
}

All in all, however, a big step in the right direction. On the downside, .NET now has multiple competing options for remoting and persistence, and interoperability between all these options is not necessarily given.

 

Encrypting web.config

It’s usually a good practice to encrypt database connection strings and other sensitive configuration entries for a web application. .NET 2.0+ gives you a built-in mechanism to do this.

The instructions out there on MSDN are nice, but there doesn’t seem to be a page that outlines all the things you actually have to do to use a custom key container. I’m also faced with a deployment scenario on ancient Windows 2000 servers, and the commands are slightly different here. It’s pretty straight forward, so for posterity I’ll outline the steps here.

The core tool is delivered with the .NET framework and called aspnet_regiis.exe.

Key Generation

First of all, let’s create a new custom container to hold the application keys. Another option would be to use the system default container NetFrameworkConfigurationKey, but let’s create a container specific our application:

C:\app> aspnet_regiis -pc MyCustomContainer
Creating RSA Key container...
Succeeded!

This now created the container and a new key inside it. The next step is to export the key into an XML file:

C:\app> aspnet_regiis -px MyCustomContainer mykeys.xml
Exporting RSA Keys to file...
Succeeded!

The file mykeys.xml now contains the keys. Keep this file safe, we will need to distribute it on the web farm later. The contents of the file looks like this:

<RSAKeyValue><Modulus>5C31Pc/ppGZjWuoUdQ9HjiAIxp8UaVYvgYXz3K4i3qzpW9al6s+2kG8ktHpSfZ/y6H4qDpaqI2TU5ltYdxr4Iv6sPhnTEKMv1N7AWq/FNwVTCM8XiWXqs6K+UW7mDfCI
ArF1Fqo+kFV1LJtuPUahy2TOmnji+ZUzzuwFqzqFop0=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>

Encrypt your application configuration

First of all, a new encryption provider needs to be registered in the web.config which specified the custom key store to use. You can skip this when using the default key store. Pay special attention to the keyContainerName attribute, it must match the name of the container created.

<!-- sets up the encrypted configuration to use a special container -->
<configProtectedData>
<providers>
<add name="MyCustomProvider"
type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
keyContainerName="
MyCustomContainer"
useMachineContainer="true" />
</providers>
</configProtectedData>

To now encrypt the configuration file from the command line (you can easily embedd this into an msbuild/nant script), call the following command. Make sure the provider name passed in matches the provider name from the above configuration snippet. The following script will encrypt the connection string section:

aspnet_regiis -pef "connectionStrings" MyWebFilesDirectory -prov "MyCustomProvider"

The connection string section in the web.config file should now look like this:

<connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>qDaaLdZtxtv1pbrXplfiHvUCKLq0ZpwJQorowmlm74QIH3ogcZyhNpYTgLCSNxaB4KaTLtcryRaivqPr+v/Lr6iv18d/VaIV1Ht0sCagA3sZCUQu/GuSTcTgfpQGdveU4Qsx5g4ERC/B8mQEwXtHs7a6VNVsYD4f6GFlO9g4iUI=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>lw9Y03jHQ6foqIAdN7UN0nOxc4y2zJ4ajb5dUjtxKyFa87vCmmMbBnTkatcoZ/6ZzibdEBg7Xmhf4SBqC4iCfQIgV5XIEb0j+P3gmDLSt+kC+enTLZGVlHt3ngND6dXJTF28C2jsVB2DIxI/u0p6cJ2UHRGfqvvetG24HnDPpZonlHuMGEyvKy/PhLf3OUv2Vc/v0aVi8srRwydjA1Ueqs9sGo+wGGqT1Leui3R/TCNiEwZrp1eO3Rt6W1NMEaRNkG3nz9tctHwOsNo9xiC+lM0IfGI/DFR04uwSzjuQX61Ph18nB+Q0zgcpauOsqRrNtorBSPqu+mfW8EL0bztU90T5F5i0TJ50BoXXo6F8kdTnJ1i90REFxsFBv/8jOXh++bUZMWRxnu3GUf+tJ5f7Kzr52GlApfkoMunTCLbd5zkPG8yga2iMJ+MGdqu9K2b+Qakpu8VNu6Ioo80l30Dbxw==</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>

Register Keys on Web Farm

Register the key on all web servers running the application. First of all, create the container with the keys from the XML file created earlier:

C:\app> aspnet_regiis -pi MyCustomContainer mykeys.xml
Creating RSA Key container...
Succeeded!

Now the IIS application requires access to this container.

On Windows 2000, do this (assuming you’re not running IIS as a different user):

C:\> aspnet_regiis.exe -pa "MyCustomContainer" "ASPNET"
Adding ACL for access to the RSA Key container...
Succeeded!

On Windows 2003 and up, the following command shall work:

C:\> aspnet_regiis.exe -pa "MyCustomContainer" "NT AUTHORITY\NETWORK SERVICE"
Adding ACL for access to the RSA Key container...
Succeeded!


And that’s really it. Once the application with the encrypted web.config sections is deployed, IIS will automatically decrypt it.