The ABCs of ASP.NET Deployment
ASP.NET application deployment is a two-step process. The first step is straightforward and involves copying all the necessary files to the target machine. This process, known as XCOPY deployment, is performed by simply running an XCOPY command from a command prompt or by using any other form of file system copy, such as by performing a copy operation from within Microsoft® Windows® Explorer. The important point is that when you execute an XCOPY operation, the structure of directories is maintained between the development machine and the production server.
When you perform this copy operation, you also copy the web.config file, which inherits settings from its parent .config file, machine.config. The machine.config file is machine specific and should not be overwritten during an individual application deployment. This isn’t an issue if the development team controls the production server. But if you’re working in a shared or hosted scenario, or if there is more than one site or application hosted on the same server, touching machine.config can be risky since the contents of machine.config influence the behavior of all the applications on that server.
This leads us to the second step. In addition to performing an XCOPY, you may need to adjust configuration settings between web.config and machine.config to ensure that your application runs on the production server in the same way as it does on your development machine, without affecting other applications. So let’s take a look at what’s in an ASP.NET .config file so you can optimize your app and improve security.
The ABCs of ASP.NET Configuration
ASP.NET application settings are distributed across multiple configuration files, forming a hierarchical tree. The machine.config and web.config files are both XML files and share the same schema. The machine.config file contains settings that are valid for all ASP.NET applications running on a given server. Its contents represent the settings that all Web applications hosted on the machine initially share. By creating web.config files, you can override the inherited settings for a particular application or, in some cases, for a particular folder within the app. The ASP.NET runtime processes configuration information in a top-down manner, beginning with a root that is common to all the applications on the machine—machine.config—and then proceeding down to all the web.config files found in the various folders of a particular app.
The example shown in Figure 1 illustrates how ASP.NET processes system and application settings for each page in a Web site. When the first instance of web.config (which is located in the root folder) is reached, it merges with machine.config and overrides, restricts, or extends the initial settings. The resulting settings are applied to all pages in the root folder and its subfolders. A web.config file located in a subfolder can further affect settings. In this example, the second web.config file (which is located in the Protected subfolder) merges with the current settings (as defined by its parent web.config and machine.config files) and can override, restrict, or extend them. All these settings are applied to all the pages located in the Protected folder and its subfolders (providing a certain configuration for admin and power users).
Figure 1 ASP.NET Configuration Hierarchy
The machine.config file lives in the installation path of the .NET Framework on the Web server. For ASP.NET 1.1, the path is:
A Web application’s web.config file lives in the root folder of the application. Additional web.config files can be located in the various subdirectories of the app. However, in the case of apps which exist in the file system as subdirectories of other apps (in other words, Site B exists in a subdirectory of Site A), the web.config file located in the parent directory application (Site A) is ignored by the subdirectory application (Site B).
In general, administrators are the ones who tweak the contents of the machine.config file, as this file is needed for all hosted applications. web.config files, on the other hand, typically fall under the jurisdiction of the individual application developers.
The contents of machine.config determine not just default values but also which settings can be overridden by an application’s web.config files. This is especially handy in an application-hosting scenario, where the administrator can choose to lock certain machine settings to prevent installed applications from modifying these settings. But it is reasonable that different applications will require different sets of initial values. Thus the machine.config file can be adapted to serve the needs of individual applications while remaining the main entry point in the configuration hierarchy.
Two top-level machine.config sections are essential for administrators to pay attention to: <system.web> and <location>. The <system.web> section is the container in which all default values for feature-specific settings are stored. You must edit this section to lock down settings or to modify the standard configuration of the ASP.NET runtime. The <location> element lets you create embedded configuration sections associated with a particular directory or page. Let’s tackle the <location> section first.
The <location> Section
The <location> element supports two attributes: Path and allowOverride. Path represents the virtual path to which the embedded settings apply. An example is shown here:
<!– Settings for all apps here –>
<!– Settings for the AppName app –>
With the addition of a <location> element, the machine.config file ceases to be the unique container of shared machine-wide ASP.NET settings and actually becomes the configuration root of individual applications. This can allow for easier centralized maintenance, as the settings are all in one file. A multi-application machine.config file can meet the requirements of particular applications without compromising the overall security and stability of the Web server. To name an application in machine.config, you use the convention Web Site/Application Name, where Web site is the IIS name of the Web site as it appears in the IIS Manager.
As mentioned, when a new application is installed on a production server, changes might be required on the target machine to reflect the native environment of the application. Updating the machine.config file on the production machine is not an issue as long as yours is the only application running or if you can directly configure all the applications on that machine. However, in an application-hosting scenario, the system administrator may find it necessary to lock some machine settings to prevent installed applications from modifying them. This brings us to the second <location> attribute. The allowOverride attribute lets you lock some settings at both the machine and application levels:
These settings cannot be overridden.
Settings grouped in a <location>element with the allowOverride attribute set to false, as you’ve just seen, instruct ASP.NET to raise an exception if any of the settings are overridden in a lower-level configuration file. This means that the wrapped machine settings cannot be modified in lower-level configuration files—not even in the application’s own web.config file. By setting the allowOverride attribute to false, the administrator can help preserve the integrity of the hosting environment and guarantee that all applications share a given set of settings.
Nearly all predefined sections can appear within a <location> section, with just a few exceptions: the <runtime>, <mscorlib>, <system.runtime.remoting>, and <startup> elements are not allowed.
All configuration files contain an area named <section>, which outlines the schema of the various sections. Configuration files, including machine.config, use the <section> element to list all the subsequent feasible elements and define their characteristics. Default values, on the other hand, are found in the <system.web> section.
The <section> element requires two attributes: name and type. The name attribute indicates the name of the section (such as <authentication>), whereas the type attribute provides class and assembly information for the class the ASP.NET runtime uses in order to read and process those values. You do not have to change any of this; you should just rely on the values in the original machine.config you get from the default ASP.NET installation.
The <section> element also has two optional attributes—allowDefinition and allowLocation. The allowDefinition attribute specifies the types of configuration files in which the section can be used. The allowLocation attribute dictates whether or not the section can be customized on a per-application basis through the <location> section.
The <system.web> Section
The second top-level machine.config section (along with <location>) that administrators must pay attention to is <system.web>. The <system.web> section contains all the elements that set up the ASP.NET runtime environment, and controls how ASP.NET applications behave. Each of the elements listed in the table features its own schema and provides attributes and enumerations. Let’s take a tour of some key sections that are of particular importance to administrators and developers who want to improve performance, scalability, and security.
The ASP.NET process model handles Web requests using the ASP.NET HTTP pipeline. The way the process model works is subject to a number of parameters that can be specified through the <processModel> section. Note that the <processModel> section can exist only within a machine.config file.
In IIS 6.0, you can choose between native mode (also known as the worker process isolation mode) and IIS 5.0 isolation mode. The IIS 5.0 isolation mode is less efficient and consists of using the ASP.NET specific worker process and reading runtime settings from machine.config. If you opt for the native IIS 6.0 process model (the default setting in IIS 6.0 and Windows Server™ 2003) then settings are read from the IIS metabase.
The parameters you can fine-tune affect the worker process of the application rather than the ASP.NET Web application itself. One of the most important parameters you can set is the identity of the worker process handling the ASP.NET application. By default, the attribute is set to machine, which runs the process under the low-privileged ASPNET or NETWORK SERVICE account. By changing this attribute, you can have the worker process impersonate a specified Windows user. If you opt for a custom account, make sure you give the account at least the same set of privileges as the default accounts; otherwise your applications may not function properly.
Other parameters you can control concern runtime conditions, such as specifying the critical memory level at which recycling begins (the default is 60 percent of the virtual memory), the size of the request queue, the timeout of the worker process, and whether Web garden mode is enabled.
Web Farms and Web Gardens:
What if you want to deploy an ASP.NET application on a Web farm? Assuming that you already have a network environment properly configured with load-balancing software, all you have to do is ensure that all the machine.config files in the Web farm store the same values for the sections.
In a Web farm, each incoming request can be served by a different machine. This means that each networked machine must be able to decode the ViewState of a request, read the user’s credentials (if any), and correctly deserialize the session state. By default, the machine.config file contains machine-specific cryptographic keys to protect ViewState and authentication tickets. These values, used for encryption and decryption of sensitive data, are randomly generated when ASP.NET is installed and stored in the Local Security Authority (LSA) of the local server. As you can see, each machine is likely to have a different set of keys which would make it quite difficult to perform ViewState decoding and process credentials. When all the servers are assigned the same set of cryptographic settings, each machine can successfully respond to requests.
In addition, if the ASP.NET application requires user authentication or supports session state, you must ensure that all the machines in the Web farm are configured to use the same authentication model (Windows, Passport, or Forms). Each machine must also use the same out-of-process storage medium (a SQL Server™ instance or a remote state server process) for reading and writing session state.
The simplest way to ensure that each machine is properly configured is to pack all the required settings in the application’s web.config file. This approach greatly simplifies deployment because once you set up the app on each machine, no other changes are needed. However, this approach may be impractical if you are deploying the app in an environment where the machine.config file doesn’t allow you to override the necessary settings. In this case, you can only replicate the changes in an application-specific <location> section in the machine.config file on each machine in the Web farm.
A Web garden is a multiprocessor system on which multiple processors run the same worker process. Deploying an ASP.NET application in a Web garden requires two steps. First, you need to tell the ASP.NET runtime that gardening is enabled. This is done by setting the webGarden attribute of <processModel> to true (though <processModel> is ignored in favor of the IIS 6.0 process model when running in IIS 6.0 native mode). You then define the CPU mask through the cpuMask attribute. The cpuMask attribute is a bitmask that indicates which CPUs can run the ASP.NET worker process —by default, all CPUs are enabled.
The ASP.NET ISAPI module—the system component that governs the execution of ASP.NET requests—schedules one process per each CPU set in the mask. You don’t need to sync up values for authentication and ViewState decoding (after all, this is taking place on the same machine), but you do need to opt for an out-of-process scheme for session state management. Note that regardless of the settings for session state, each ASP.NET worker process has its own copy of the application’s cache.
Security Policies and Levels
If you want to restrict permissions granted to Web applications, you should change the default trust level and ensure that overrides are not allowed. The <securityPolicy> section defines trust levels and specifies the policy file that contains the list of permissions for that trust level. The <securityPolicy> section can also be configured at the application level, but not in web.config files that are located in an application’s subdirectories.
You can add trust levels by adding new entries to the configuration section. These entries specify the trust level name and the (custom) policy file to be used. A few policy files are located in the same folder as machine.config and list the permissions granted to applications. Note that in order to preserve the default settings, ASP.NET installs two copies of each policy file. One is the original file, which is consumed by the system, and the other a copy (with a .default.config extension) that is preserved as a backup.
By default, ASP.NET applications are fully trusted by the common language runtime (CLR). This means that the behavior of an ASP.NET application is not restricted in any way by the CLR code access security (CAS) rules, only by standard Windows security mechanisms. In other words, an ASP.NET application can do whatever its Windows security token allows it to do. For example, an ASP.NET application can’t create a disk file if the worker process account lacks required writing permissions.
The <trust> section in the configuration file indicates the level of CAS trust ASP.NET allows the application, determining the CAS restrictions applied to the application. This is completely orthogonal to Windows security mechanisms. For example, the account running an ASP.NET application might have administrator privileges with free reign of the system, but if the ASP.NET application is configured at a medium-trust level, the CLR automatically prevents the application from accessing a database using a blank password.
You may have noticed that the <location> attribute is used without a path attribute. Using a <location> directive without a path attribute applies the configuration to the entire machine. A more subtle yet important thing to address, however, is the use of the allowOverride attribute in this example. To block applications from choosing their own trust level, you should specify an allowOverride=false attribute. If you want to force all applications on a given server machine to adhere to the same security rules, you can simply set the <trust> element to the value you want and block any overrides.
By default, an ASP.NET application lacks permissions to write to or create a file (including simple text files and Microsoft Access databases). It is up to the administrator to grant this permission. The simplest, although less secure, way of doing this is to update the worker process account to LocalSystem. This approach significantly weakens security by allowing ASP.NET code to access all server resources, as was the case in classic ASP.
A much better technique involves changing the security settings on individual files. However, this approach relies on a couple things: the files must exist (or at least be created) at the time of setup, and the files can never be deleted.
When the application requires a bit of flexibility, the best (and most common) approach is typically to apply permissions at the directory level. It’s a best practice for ASP.NET applications to use helper server files in a tree of directories with a unique root. In this particular scenario, it is easy for an administrator to check and grant write permissions on the specified tree of directories.
Requests sent to an ASP.NET application are first intercepted and addressed by IIS. After authenticating the request at the gate, an IIS pooled thread serves the request by identifying a registered module and then looking in the metabase to locate a match for the requested resource extension. A local application that handles internal files of a given extension might want to prevent end users from accessing the contents of these files through the browser.
In this case, the best approach is to let the application handle the file extension (say, *.my) and attach an HttpForbiddenHandler so the ASP.NET runtime will block the request and display a standard unauthorized access page. To do this, you add the following code to the <httpHandlers> section for the given application:
<add verb=”*” path=”*.my” type=”System.Web.HttpForbiddenHandler”/>
You can distinguish various HTTP verbs and repeat the setting for as many extensions as needed. This setting can also be specified in all web.config files.
Summing It Up
ASP.NET applications offer a variety of configurable settings and parameters that can be controlled at different levels. Configuration is hierarchical by nature, letting you apply different configuration schemes at various levels of granularity—the machine, the Web site, or the folder. Settings can be overridden, extended, and restricted as appropriate.
Configuration files are probably the most critical aspect that you should consider when preparing the deployment of ASP.NET applications. Arranging a setup program is quite easy, but deciding how to replicate the settings of the native environment can take a lot of thought.