Three Is It

Because two isn't enough and four is just too many

Evil men, obsessed with ambition and unburdened by conscience, must be taken very seriously--and we must stop them before their crimes can multiply.
George W. Bush, Nov. 2005
Home Blogs Genealogy Brad's Bookshelf Subscriptions Contact Sign in
 

About the author

Brad Butts is a .NET developer and architect. He is married with children and enjoys reading, working out, and genealogy is his five minutes of spare time.
E-mail me Send mail
National Debt Clock

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2009

Deploying to different environments

My company maintains different environments--Dev, IT, UAT, Production, and Disaster Recovery--in which to run and test software solutions: particularly web solutions.  My company also (I think rightly so) places restrictions on what environments developers can access and which ones they can't--the old Segregation of Duties principle.  So, the question is, how do developers build applications mallable enough to run under different environments that can be deployed as simply as possible so as to not confuse the server admins and deployment personnel?

Our deployment vehicle of choice is the old Microsoft Installer file (MSI).  With regard to environment-sensitive settings, we simply "extend" applicable sections of the web.config to a second environ.config file.  There are always edge cases, but this approach seems to work out pretty well on the whole.  The following is an example...

Step 1: Abstract whatever environment settings to a separate file.  In this example, I use the appSettings section to house my configuration values, but this pattern works for most other sections I've tried (eg. connectionStrings)

In my web.config


<appSettings file="environ.config">
<add key="key1" value="someOtherValue"/>
</appSettings>


In my environ.config
<appSettings>
<add key="MyEnviron" value="Development"/>
</appSettings>
 

Interestingly, the appSettings node seems to be the only node that supports the "file" attribute.  All other configuration sections I've extended use the attribute "configSource".  I noticed the other day, though, that appSettings also seems to support the "configSource" attribute.  It looks like you need to be careful about your use of that attribute, though (see the Note in the description of the "file" attribute).

Step 2: Create other environment configuration files for the different environments you need to address.  In my example, I've created a IT.environ.config, UAT.environ.config, and Prod.environ.config.  Each configuration should look the same--only the values should change.


Step 3: Test your app to make sure it works in your "Development" environment with your default environ.config file.

Step 4: Now, add a web setup project to your solution to package/deploy your solution.  For simplicity, I've used Scott Guthrie's Web Application Project so I can easily add the Primary Output and Content Files to my web setup project.  On the Content Files output, make sure to add the ExcludeFilter: *environ.config.  This will exclude our environment specific config files--we'll add them in later.

 

Step 5: At some point, someone has to know/decide what environment your web app is being deployed into.  I contend that that decision should be made at the point of deployment.  That decision can be fairly easily automated (and I'll probably talk about that in a future post), but for now, we'll place that decision in the hands of the human installing your MSI on a given server.  

  1. In Solution Explorer, right-click on your web setup project and select View > User Interface from the context menu.
  2. In the User Interface tab, right-click on the Start node and select Add Dialog from the context menu.
  3. In the Add Dialog dialog, select the RadioButtons (4 Buttons) choice and click OK
  4. Move your new dialog to the appropriate order (usually after the Welcome dialog)
  5. Set the properties of the dialog more or less as follows:


 

Step 6: Go back to the File System tab.  Under the Web Application Folder, one by one, add each of your environment config files.  For each file you will need to set two properties:

  1. On the Condition property, set and equation like this: ENVIRON="Dev"
  2. On the TargetName property, set the value to "environ.config"

Obviously, your Condition equation will change for each file.  For the Prod.environ.config file, the Condition property will be ENVIRON="Prod".

 

Step 7: Finally, build your MSI and test it out.  When you choose, say, the UAT environment, make sure that your UAT config file gets deployed and renamed appropriately.  Attached is a sample app for your perusal.
 

 


MSIEnviron.zip (568.92 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Brad on Friday, September 26, 2008 1:51 PM
Permalink | Comments (0) | Post RSSRSS comment feed

WCF, Interop, and the elusive BinarySecurityToken

If you plan to write WCF services that will be consumed by clients of technologies other than .NET--and would like to include some level of security, like message signing (via X.509 certificates)--chances are you'll have to find some way to emit a BinarySecurityToken value in your responses.

When I first configured the security of my service, I went with a custom binding and a configuration like this:

<binding name="CustomBindingForX509">
<textMessageEncoding messageVersion="Soap11" />
<security allowSerializedSigningTokenOnReply="true" authenticationMode="MutualCertificate"
requireDerivedKeys="false" securityHeaderLayout="Lax" messageProtectionOrder="EncryptBeforeSign"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
<secureConversationBootstrap />
</security>
<httpTransport />
</binding>


This seemed to make sense (as far as WCF configuration can make sense, anyway): I was sharing certificates with my client (a Java Axis2 client), hence the MutualCertificate setting seemed appropriate.  I was also going for the lowest common denominator SOAP settings (as most folks in the interop space seem to recommend), so I went with those recommended SOAP settings.  This configuration had the effect of rendering my response to look as such (I only show the SOAP header section since it is what's relevant here):

<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-5965cceb-61d9-4d3f-a503-c3f4dc7fe08a-3">
<u:Created>2008-07-17T20:33:42.153Z</u:Created>
<u:Expires>2008-07-17T20:38:42.153Z</u:Expires>
</u:Timestamp>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>3dcC17frvtyzp8G+kR5otzreQf0=</DigestValue>
</Reference>
<Reference URI="#uuid-5965cceb-61d9-4d3f-a503-c3f4dc7fe08a-3">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>h/DtKybbi4Q3RRtYKm26SGM2mcM=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>IRlfHoVu/JispcD5CdMCKnbHNZcSVVNNLBtXbnP3fcid+nPi1F4WNGVsHjkF6PnaIzKM/5j2Vhnxbkm1tTwFjeKelQipCHErrwXsxOKMaVKlP/2gjeiJ0K2kkEO7LIUIcmqQ9MNx/AfGr9zE4c6EPGrkbPJVYLvra5jUhypMAcM=</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Sample Service, OU=Rampart, O=Apache, L=Colombo, S=Western, C=LK</X509IssuerName>
<X509SerialNumber>1187603713</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
</o:Security>
</s:Header>


The problem is, my client didn't like these settings and kept throwing the error "The signature verification failed".  I suspect the client didn't like this response because the MutualCertificate setting seems to want the client to look up the certificate via distinguished name or serial number to verify that the message was signed appropriately.  Java clients, and apparently a few other technologies, don't seem to work that way.

So, what to do?  Well, eventually I found this post describing a similar problem between a WCF service and an SAP NetWeaver client.  Part III of the post describes what they did to resolve the signature verification problem: namely, get the WCF service to inject a BinarySecurityToken instead of the SecurityTokenReference (with issuer name and serial number).

I'm not totally fond of their implementation--they did the old school write-a-console-app-to-host-my-WCF-service approach--but this gave me an important clue to how to inject the BinarySecurityToken: use the AsymmetricSecurityBindingElement class!  Wow, why didn't I think of that?  That's fairly...uh...obvious.

Ok, so how do I use this AsymmetricSecurityBindingElement class?  I know I don't want to write a console app just to plug in this functionality--that's what IIS is for.  Guess I'll have to go to a custom BindingElement extension (see the attachment for the code and thanks to Scott, the Microsoft tech who wrote it--and helped me with this problem):

(excerpt from the client and service configurations)
<extensions>
<bindingElementExtensions>
<add name="MySecurityBindingElement" type="MySecurityBE.AsymetricSecurityBEExtentionElement, MySecurityBE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
<bindings>
<customBinding>
<binding name="MyCoolBinding">
<MySecurityBindingElement/>
<textMessageEncoding messageVersion="Soap11"/>
<httpTransport/>
</binding>
</customBinding>
</bindings>



But wait, there's more!  It turns out that you don't even need to write your own custom BindingElement.  It turns out that the authentication mode MutualCertificateDuplex makes use of the AsymmetricSecurityBindingElement class.  What?  Didn't you know that?  So, all you really have to do is expose an endpoint with this kind of binding and you're set.  So, this service configuration:

<binding name="CustomBindingForX509">
<security authenticationMode="MutualCertificateDuplex"
requireDerivedKeys="false"  messageProtectionOrder="EncryptBeforeSign"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/>
<textMessageEncoding messageVersion="Soap11" />
<httpTransport  />
</binding>


Will render this response (again, I'm just including the header for brevity):

<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-39531f92-c0d4-4129-b491-6222c06d3bf5-1">
<u:Created>2008-07-25T16:03:59.959Z</u:Created>
<u:Expires>2008-07-25T16:08:59.959Z</u:Expires>
</u:Timestamp>
<o:BinarySecurityToken>
<!-- Removed-->
</o:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>2x4uPP0r/Zo7auuboFg+8h0k3Yo=</DigestValue>
</Reference>
<Reference URI="#uuid-39531f92-c0d4-4129-b491-6222c06d3bf5-1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>Z161SvyizqMC3alennK2c6FLiZg=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>EDpLDry/QuwxVuXe+/0LZrmrLkTcoZ1Ls45qvu+RebTRFfkq8HksKMN3Ip4T2begyDCLfGOTpEHfX/ohyMS7HIsxluyIwJ971kyLVt6nUZPfjqQ3iD3hCI2cCSRtNbC1p+aZDr3Tn/KbLxjWQ4aFfm7lRKbLGeVDEY5BJYyVCqY=</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#uuid-f32fc9b9-695e-4542-98bb-acf40424321b-2"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
</o:Security>
</s:Header>


Note that the response now includes the BinarySecurityToken and, ironically, has removed the x509 name and serial number data.  Don't worry about that "removed" comment, the binary token was sent to my client, it's just that WCF automatically removes privacy information from its logs--and I'm pulling this data from the log files.  For more info on privacy removal, see here.

Now, my non-.NET clients are happy, but I've just hosed my .NET clients.  From what I'm told, duplex mode is no walk in the park for .NET clients.  So, what to do?  Well, I'll tell you what I plan to do: just expose another endpoint.  If you're a .NET client, you'll get a traditional MutualCertificate binding endpoint.  If you're not a .NET client, you'll get the duplex endpoint.  The custom extension is nice, but not absolutely necessary at this point.

AsymExample.zip (899.63 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Technology Blog
Posted by Brad on Thursday, September 18, 2008 4:33 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Where o where did my power go?

"Be Prepared." - the Boy Scout Motto

Ah, beautiful Cincinnati--all the benefits of a Category 1 hurricane with none of the ocean front property.  Where gas, ice, food, and Duke Energy repair trucks were rare and valuable commodities.

Fortunately, my power is back on after about 2.5 days of darkness (my office is still running on generators, though).  I can't believe how dependent I am/we are on electricity--that's an interesting, but scary dependency to consider.  Some things I need to think about when planning for the next power outage/apocalypse:

1. D batteries are essential
2. I need to get that battery backup for my sump pump (fortunately, we had no rain during the outage, but you can never be too careful)
3. Wouldn't it be great to have a generator?  I wonder how long it would take my Homeowner's Association to write me an angry letter/take legal action?
4. Does no one make cheap transistor radios anymore--particularly those capable of running off of batteries?  Forget those clock radios with the "battery backup."  That feature won't power a radio when the power's out--it only saves you the time of resetting the time when the power comes back on again.  The standard radios I have take an average of 43.7 D batteries.  What sort of conspiracy is this from Portable Power cartel?
5. Generate my own power?  Solar paneled ridge vents (a roofer I spoke to mentioned the existence of such things but I can't seem to find them)?  Opportunity or hype?  Discuss amongst yourselves.
6. Ample battery-powered lighting is good as well as alternative ways to cook food--like the grill I've never used with the propane tank I've never replaced.
7. Real men take cold showers.  Good thing there was still some luke-warm water left over in my water heater tank.
8. Are my bills getting paid?  Am I receiving important emails I need to answer?  I pay most of my bills online and I maintain a variety of accounts to do so.  Many of these account credentials I manage from a centralized, encrypted file...on my hard drive...which I can't get to because of the power outage.  I back this stuff up to a local independent storage device and do offsite backup in the form of DVDs kept in my safe deposit box, but I wonder if I need to rethink this strategy and include something like Amazon S3 (perhaps via Jungle Disk; Steve Gibson's take on Jungle Disk).  Some time ago, Scott Hanselman shared his interesting backup strategy.  Maybe I should borrow some/all of that.
9. While I sat in the dark smelling my food rot, apparently, my life savings were rotting away, as well.  Note to self: convert as much of my monopoly money as is reasonable to gold.  Oh, and make sure I read Murray Rothbard's What Has Government Done to our Money?
10. Fortunately, the people in my neighborhood remained civil, even helpful.  We were lucky that this situation didn't merit an invasion by FEMA or any other government henchmen.  However, I'm always thinking about the safety and protection of my family, friends, and property (in that order).  One interesting book I've read recently (well, partially read) that addresses this topic is Boston T. Party's Gun Bible.  And, no, I'm not some nutjob alone in a cabin writing my manifesto.  I'm just trying to be prepared--for all situations.

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Brad on Thursday, September 18, 2008 4:14 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Who needs the CodeBehind attribute?

Maybe this is obvious to everyone but me because I've found no discussions on the topic...or maybe my googling skills just stink...or maybe both...but after looking at the markup in some of SharePoint's ASPX pages and seeing neither a CodeBehind nor a CodeFile attribute in the Page Directive, it occurred to me that at least precompiled ASPX pages don't need either of these attributes.  The required attribute is Inherits.  Presumably, the .NET runtime uses the fully qualified assembly name in the Inherits attribute to discover the code behind for the requested page.  

Here's what I mean...

When I start a new Web Application Project in Visual Studio 2008 (or add a new page to such a project) and look at the markup in the ASPX page, I see a page directive declaration like this:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Examples.CodeBehindAttrTest.Default" %>

I can jump to the code-behind file (Default.aspx.cs) and add this code to the Page_Load event:

protected void Page_Load(object sender, EventArgs e)
{
Response.Write(String.Format("The time is: {0}", System.DateTime.Now.ToString()));
}


I can compile and run my project, browse to the default.aspx page, and see the timestamp print out with no problem.  

So, what happens when I remove the CodeBehind attribute?  Well, the very same behavior happens.  This makes sense, too, because I don't deploy CS files to my production Web servers yet I make great use of code-behind files.  Of course, Microsoft basically says as much:

"This attribute [CodeBehind] is not used at run time." (from the Page Directive reference)

For your Web Site projects, I assume you could equally remove the CodeFile attribute, but that appears much stickier, what with having to precompile the site, setting the Inherits attribute correctly, and all.

The drawback of removing the CodeBehind attribute from your pages seems to be one of development-time convenience.  Without knowing what code file represents a given ASPX page, should I, say, try to add a click event handler to my server side button by double-clicking the button in the Design tab, Visual Studio will add server-side code inline to my ASPX page instead of the default behavior of taking me to my code-behind page.  If I cut and paste the inline code, drop it into my code behind page, recompile, and run the app, the event will fire as coded in my compiled DLL.  Visual Studio is even smart enough to declare my button control in the designer partial class.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: Technology Blog
Posted by Brad on Friday, September 12, 2008 11:30 AM
Permalink | Comments (0) | Post RSSRSS comment feed

ASP.NET Routing Security Backdoor

Some time ago, Phil Haack posted a really cool entry on the System.Web.Routing feature, new in .NET 3.5 SP1.

In his post, he noted a "subtle potential security issue" where you might think you've secured a particular directory via a nested web.config and appropriate <authorization> element, but, in reality, you've totally circumvented that security.  Phil then re-included his security check by calling to the UrlAuthorizationModule.CheckUrlAccessForPrincipal method in his IRouteHandler implementation to determine if the user has permissions to the web resource being requested.  Here are two alternative approaches without having to add the UrlAuthorizationModule code:

Option 1: Change the "BackDoor" Route
Since the rule is to deny all requests to resources in the Admin sub-directory, if we change the route slightly to this:
routes.MapWebFormRoute("Secret", "admin/BackDoor", "~/Admin/SecretPage.aspx", false);


we can get the desired result:

 

Option 2: Add a Location Path to the Authorization Configuration
Leaving the Backdoor route as it is in the original demo code, another approach to take would be to add a <location> element around the authorization rule.  Thus, in the parent web.config file, we could add this configuration:

  <location path="backdoor">
<system.web>
<authorization>
<deny users="*"/>
</authorization>
</system.web>
</location>


and achieve the desired result:

 

 

All in all, though, I'm glad Phil chose the route he did because I had the chance to learn about the UrlAuthorizationModule.

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Technology Blog
Posted by Brad on Friday, September 12, 2008 11:14 AM
Permalink | Comments (0) | Post RSSRSS comment feed

MSBuildEmitSolution

I always forget this little nugget, so I'm going to dump it out here...


For those of you arriving late, MSBuild is the build engine for .NET 2.0 and above.  You'll find the executable in %windir%\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe.

To use MSBuild to build your assemblies, you'll need to write MSBuild script in the form of XML.  What's nice about Visual Studio, though, is that Visual Studio pretty much writes your MSBuild script for you as you code your project.  If you drop your project files (CSPROJ for C# projects) into Notepad, you'll see what I mean.

Now, we all know that projects are housed under a solution described by a SLN file and we typically build all our projects at once by building the solution.  So, it stands to reason that the SLN file is persisted as MSBuild script, too, right?  Wrong.  Just drop that sucker into Notepad--that's not MSBuild syntax...I don't know what that is!  So how can MSBuild build an entire solution from an SLN file that's not even MSBuild script?

The secret is that apparently MSBuild, on-the-fly, converts the SLN file to MSBuild script then runs that script to generate the necessary output.  By default, we never see this converted file; however there is a way to see it.  Follow these steps:

  1. From the command line, type: set msbuildemitsolution=1
  2. Now run MSBuild: msbuild Solution1.sln
  3. Look in the directory of your SLN file and you'll find Solution1.sln.proj (a .cache file is also created which appears to be a copy of the .proj file)

Thanks to Sayed Ibrahim Hashimi for blogging about this.

The next question is, of course, should I generate one of these PROJ files first, then run MSBuild against that file to build the artifacts of my solution--particularly if I need to do some operations at the solution level before, during, or after the build?  The answer is: I'm not sure.  I recall seeing some posts from folks who did just this and got some unexpected behavior (try googling msbuildemitsolution).  So, I would think that the safest course of action would be to customize you MSBuild script (CSPROJ files) as much as you can first.  If you can't quite address all your needs at the project level--say, moving different reference files around or whatever--I might consider writing a batch file around the whole process to see if that can better address your requirements.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: Technology Blog
Posted by Brad on Thursday, September 11, 2008 5:51 AM
Permalink | Comments (0) | Post RSSRSS comment feed