August, 2008

Posts
  • CarlosAg Blog

    Back again

    • 2 Comments

    For the past month its been pretty quiet in my blog since I have been a little bit busy with the best thing that has happened in my life, my first baby Sofia was borne and today we are celebrating her first month. Days have been incredibly full of love, fun and joy (but certainly not of sleep). Now nights are much better, sleeping for a few hours now and back at work with lots of enthusiasm for the upcoming work in IIS and the incredibly exiting times at the team. So now by blog should show a bit more activity moving forward.

    Here is a picture of my first baby, named Sofia celebrating her first month of life.

    Sofy

  • CarlosAg Blog

    Application Request Routing and the IIS 7.0 Web Management Service

    • 1 Comments

    Yesterday I was having a conversation with Anil Ruia who happens to be the ARR (Application Request Routing) developer and based on customer feedback we discussed the idea of using ARR in the context of Remote Management in IIS which solves a question that several people asked me before and thought it would be fun to try it out.

    Basically the question that I got asked was "Can I have a single entry-point exposed for Remote Management?", or in other words "Can I provide users with remote administration giving them a single server name like management.myhostingcompany.com, instead of having to give them the specific machine name where their site lives?". So far the answer to these questions was "not easily", however with the use of ARR and URL Rewriter we will see how easy it is to achieve this.

    The only thing you need for this to work is install the new URL Rewrite and ARR Module both available here http://blogs.iis.net/bills/archive/2008/07/09/new-iis7-releases-url-rewrite-application-routing-and-load-balancing-and-powershell-cmd-lets.aspx.

    Background

    The Web Management Service (WMSvc) is the service that enables remote administration for IIS 7.0 Manager, providing an HTTPS end-point that exposes functionality similar to Web Services to manage the Web Server (IIS) remotely. This service uses HTTPS for its communication and exposes several configuration options that support giving access to Non-Windows Users (What we call IIS Manager Users), provide a list of IP Restrictions, support only local connections and many more that can be managed using the Management Service feature inside IIS Manager.

    To enable remote administration typically you need to: 1) Configure a valid Certificate for SSL, 2) Allow Remote Connections and 3) Start the WMSvc Service, all of which can be performed in IIS Manager. Once you have successfully enabled the remote service you should be able to go to a different machine and be able to connect remotely.

    Note: If you are using Windows Vista, Windows XP, or Windows 2003 to connect to a Windows Server 2008 you need to download and install the client to do this: http://www.iis.net/downloads/default.aspx?tabid=34&g=6&i=1626

    However, one of the drawbacks is that in order to be able to connect to a Web Site, the end-user needs to know the machine name, as well as the name of the Web Site they will be connecting to, which sometimes it would be better to be dynamic. The following image shows the information required to enter when connecting to a Web Site. Note that if connecting to an application you will also need to enter the name of the application.

    ConnectingToSite

    However, this can potentially reduce the flexibility for deployment options, since now your customers have specific knowledge of the physical machine and will limit the flexibility of moving the site to different machines or even changing the name of the site where it is being hosted.

    ARR and URL Rewrite to the rescue.

    ARR has several very interesting capabilities that are really useful for this scenario. First, we can configure it to act as a proxy and basically forward the requests to another server where they actually get processed. This is the simplest configuration option and what it allows you is to have something similar to the next image:

    WMSvcRouting

    To set up this configuration where a front-end management server forwards the IIS Remote Management requests to another server running WMSVC you have to:

    1. Install ARR and URL Rewrite in the Server that is intended to be used as the front-end for management requests. Lets call this ServerA.
    2. Create a new Web Site.
      1. Navigate to IIS Manager->Site
      2. Click Add Web Site.
      3. In the dialog set: Site name:ManagementSite, Binding: https, port: 8172 and choose a valid SSL certificate, specify a phisical path. Click OK
    3. Configure URL Rewrite to Route requests to the IIS Management Service running in the other computer.
      1. Navigate to IIS Manager->Sites->Management Site->URL Rewrite Module
      2. Click Add Rule
      3. Set: Name: RouteWMSvc, Pattern:.*, Rewrite URL:https://<RemoteServer>:8172/{R:0}, Stop Processing rules: Checked.
      4. This should generate a web.config with similar content (note that my backend, ie the RemoteServer in my case is carlosag2-iis below):

        <configuration>
           
        <system.webServer>
               
        <rewrite>
                   
        <rules>
                       
        <rule name="RouteWMSvc" stopProcessing="true">
                           
        <match url=".*" />
                            <
        action type="Rewrite" url="https://carlosag2-iis:8172/{R:0}" />
                        </
        rule>
                   
        </rules>
               
        </rewrite>
           
        </system.webServer>
        </configuration>

    4. Now you can run IIS Manager in any client machine, specify the ServerA as the machine name and specify any web site in the remote RemoteServer, the result will be that all requests will be forwarded to the WMSvc running in the remote server.

    Now, that is interesting and the scenario it allows you to do is potentially have WMSvc IP Request Filtering in the RemoteServer and only allow calls from the Management Server where you can do further configuration. Note that this also means that you can have a single public SSL Certificate in the management server and use privately issued certificates (or potentially even self-signed certificates in the remoteserver since you can control installing the certificate into the management server). It also means that the customers no longer use the physical name of the RemoteServer machine but instead connect to the Management Server allowing you to completely move them to another machine and not have to update your clients.

    Troubleshooting: If you are having troubles testing this, the best thing to do is enable Failed Request Tracing in the ManagementSite, which will tell you exactly what is going on. For example you will get entries like:

    Warning: ModuleName="ApplicationRequestRouting", Notification="EXECUTE_REQUEST_HANDLER", HttpStatus="502", HttpReason="Bad Gateway", HttpSubStatus="3", ErrorCode="2147954575", ConfigExceptionInfo=""

    If you lookup the ErrorCode, it is actually: ERROR_WINHTTP_SECURE_FAILURE, this means that you have most likely issues with the certificate. In my case, just to test this what I did is generate a self-signed certificate in the RemoteServer with the name of the machine (carlosag2-iis) and then I installed that certificate using the MMC certificates snap-in in the management server into the Trusted Root Certification Authority. Disclaimer Warning!! this is something you should only do for testing purposes or if you know what you are doing.

    More Advanced Stuff... Dynamically choosing the machine

    Now, trying to push the capabilities of this I decided to solve another requests that we've heard which is closely related "Can I have a single management server and dynamically route the requests to the machine where a particular site lives?"

    The following picture represents this, where the Management Server dynamically resolves the server that it should talk to using the URL Rewrite Maps functionality.

    WMSvcRoutingMultiple

    Turns out this is really simple using URL Rewrite, basically you can write a Rewrite Rule that matches the Site name that is included as part of the Query String and use the Rewrite Maps support for figuring out the machine where this site lives. The following shows such a rule:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
     
    <system.webServer>
       
    <rewrite>
         
    <rules>
           
    <rule name="RouteWMSvc" stopProcessing="true">
             
    <match url=".*" />
              <
    conditions>
               
    <add input="{QUERY_STRING}" pattern="Site=([^&amp;]+)" />
              </
    conditions>
             
    <action type="Rewrite" url="https://{ServersTable:{C:1}}:8172/{R:0}" appendQueryString="true" />
            </
    rule>
         
    </rules>
         
    <rewriteMaps>
           
    <rewriteMap name="ServersTable">
             
    <add key="CarlosAgWebSite" value="carlosag2-iis" />
              <
    add key="SomeOtherUserSite" value="carlosag1-iis" />
              <
    add key="SomeOtherUserSite2" value="carlosag3-iis" />
            </
    rewriteMap>
         
    </rewriteMaps>
       
    </rewrite>
     
    </system.webServer>
    </configuration>

    Basically, URL Rewrite matches every request and uses the condition entry to parse the Query String and find the Site name within it. With it, it and using the Map ServersTable to resolve the machine name based on the Site name it rewrites the request to the machine where its currently located. This makes it basically route "https://localhost:8172/Service.axd?...&Site=CarlosAgWebSite" into https://carlosag2-iis:8172/Service.axd?...&Site=CarlosAgWebSite. The end result is that can dynamically at any time just update this table and make ARR route the requests to the right machine giving you complete flexibility on the deployment of sites.

    One thing to note is that URL Rewrite is one of the ways you can make the ARR scenario work, however, you could also write your own module that uses any dynamic behavior such as going to a database or a provisioning system or anything else and rewrite the URL programmatically in a way that ARR will understand it and do the routing automatically.

    Also, worth to mention that ARR has way more features than just this, making it possible to load-balance requests and many more interesting stuff that I will try to get back in a future post.

    With all this you can imagine several benefits, such as single public end-point for remote management of multiple servers, only one valid certificate is needed facing public machines, you can relocate sites at your own will since customers will never really know the real machine name where their site lives, you can use a similar technique to rewrite even the Site Name and give them some friendly name such as their user name or whatever.

    Acknowledgements: I want to thank Anil Ruia and Daniel Vasquez Lopez who helped figuring out a few issues during this blog and Ruslan Yakushev and Won Yoo for reviewing its technical accuracy.

  • CarlosAg Blog

    Configuring SSL using Javascript in IIS 7.0

    • 1 Comments

    In IIS 7.0 the configuration system has a nice feature that lets you extend it using what we sometimes refer as dynamic properties. This properties rather than being hard-coded in XML in some .config file they are implemented by COM interface that whenever a tool queries its value, the configuration system will create an instance of such a COM object and will query its value (through the IAppHostPropertyExtension interface). In our configuration system we used this capability to surface some runtime features such as the state of an Application Pool. We also used this feature to expose the SSL Certificate configured in a site binding. Furthermore, this extensibility also allows us to expose methods that provide functionality in a very similar way (through the IAppHostMethodExtension interface). We used this capability to provide functionality such as recycling an Application Pool, restarting a Site, or adding a certificate to a binding.

    So in this post I just want to show how using this capability from Javascript you can easily provision an HTTPS web site by leveraging our configuration system and the extensions we provide for HTTP.SYS (HttpQueryServiceConfiguration/HttpSetServiceConfiguration) configuration below. Note that functionality we expose as properties include reading the certificate hash, reading the certificate store, reading the DS Mapper flag. And also as Methods we expose the ability to configure a certificate, enable and disable the DS Mapper functionality.

    try {
       
    // Setup SSL in default web site, port 443, certificate hash
        SetupSsl("Default Web Site", "*:443:", "3efb3448636941cde7dae47c377f14188cbeb740");
    }
    catch(e) {
       
    WScript.Echo(e.description);
    }

    function SetupSsl(siteName, bindingInformation, certificateHash) {
       
    var adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager');

       
    var sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST"); 

       
    //-------------------------------------------------------------
        // Find the Site
        var siteElementPos = FindElement(sitesSection.Collection, "site", ["name", siteName]);
       
    if (siteElementPos == -1) throw new Error( "Site not found!");
       
    var siteElement = sitesSection.Collection.Item(siteElementPos);

       
    //-------------------------------------------------------------
        // Verify if the binding already exists...
        var bindingsCollection = siteElement.ChildElements.Item("bindings").Collection;
       
    var bindingElementPos = FindElement(bindingsCollection, "binding", ["protocol", "https", "bindingInformation", bindingInformation]);
       
    if (bindingElementPos != -1) throw new Error( "Binding Already Exists!!!");
        
       
    //-------------------------------------------------------------
        // Create the binding in the IIS configuration system
        var bindingElement = bindingsCollection.CreateNewElement("binding");
       
    bindingElement.Properties.Item("protocol").Value = "https";
       
    bindingElement.Properties.Item("bindingInformation").Value = bindingInformation;
       
    bindingsCollection.AddElement(bindingElement, -1);

       
    //-------------------------------------------------------------
        // Configure HTTP.Sys SSL certificate using the method extension
        var methodInstance = bindingElement.Methods.Item("AddSslCertificate").CreateInstance();
       
    methodInstance.Input.Properties.Item("certificateHash").Value = certificateHash;
       
    methodInstance.Input.Properties.Item("certificateStoreName").Value = "MY";
       
    methodInstance.Execute();
        
       
    //-------------------------------------------------------------
        // You can also use the certificateHash extension property to read the certificate Hash
        WScript.Echo(bindingElement.Properties.Item("certificateHash").Value);

       
    //-------------------------------------------------------------
        // You could also Configure HTTP.Sys DS Mapper functionality if needed:
        WScript.Echo(bindingElement.Properties.Item("isDsMapperEnabled").Value);
       
    //bindingElement.Methods.Item("EnableDsMapper").CreateInstance().Execute();
        //bindingElement.Methods.Item("DisableDsMapper").CreateInstance().Execute();

       
    adminManager.CommitChanges();
    }

        //-------------------------------------------------------------
        // Helper method to find an element in a collection based on valuesToMatch
    function FindElement(collection, elementTagName, valuesToMatch) {
       
    for (var i = 0; i < collection.Count; i++) {
           
    var element = collection.Item(i);
            
           
    if (element.Name == elementTagName) {
               
    var matches = true;
               
    for (var iVal = 0; iVal < valuesToMatch.length; iVal += 2) {
                   
    var property = element.GetPropertyByName(valuesToMatch[iVal]);
                   
    var value = property.Value;
                   
    if (value != null) {
                       
    value = value.toString();
                   
    }
                   
    if (value != valuesToMatch[iVal + 1]) {
                       
    matches = false;
                       
    break;
                   
    }
               
    }
               
    if (matches) {
                   
    return i;
               
    }
           
    }
       
    }
        
       
    return -1;
    }

    Some of the nice side effects of exposing this as extensions in the IIS Configuration system is that it is now possible to call this method from a remote machine as well through the DCOM support of AHADMIN.

Page 1 of 1 (3 items)