WSH, Windows Server 2003
I am in the process of migrating several web sites from windows 2000 IIS5 to windows server 2003 R2 IIS6. I am running into several issues with this, so it will probably generate several entries to this blog. I thought I'd document one of the first issues I encountered: using the windows scripting shell to execute commands on a Windows 2003 web server.
First, let me explain what this particular web page does. We have a benefits provider that ftp's us data files that need to get imported into our payroll system, and we send them data files that are exported from our payroll system. Our payroll administrator goes to our intranet and uploads the files using ASPSmartUpload. On the backend, the web page places the files on the server, PGP encrypts them using GnuPG, and them copies them to the ftp server using the scripting.fileSystemObject. I'll try to cover the topic of uploading and encrypting with GnuPG in another blog entry.
This all worked fine and dandy until we moved over to Windows Server 2003, which has additional security features that make this much more difficult to accomplish. The biggest problem I faced is that the cmd.exe in Win2003R2 has new security checks that do not allow it to run unless it is running as LocalSystem. Because the process is spawned by IIS, it is not running as LocalSystem, and can't run. To get around this, you need to create an additional application pool that runs as Local System and then tie this application pool to the directory or site that is running the script. Of course, tying it down to the directory is more secure than to the entire site. You don't want anything running as Local System that doesn't have to be.
To accomplish this, first open up IIS Manager. Right-click application pools -> new -> Application Pool... screenshot. Give the pool a name, and use the default settings. I called mine ShellInteraction. Right click the newly created AppPool and go to properties. Click the identity tab. Click the Predefined radio button and select Local System from the list, then click apply. screenshot. You will get a warning about running the AppPool as the Local System, and rightly so. Like I said, you are circumventing several security features in IIS6 by doing this, so make sure you tie it down to only the exact scripts you want to run as Local System (I'll get to this later). Click yes.
Now you need to tell the script to use the new Application Pool. In IIS manager, Navigate through Web Sites to the directory containing the script. I'd have a special directory just for containing such scripts. Right click the directory, select properties. On the Directory tab (Home Directory tab if you're doing this for an entire site - NOT recommended), locate Application Pool: and select the name of your new AppPool. Give it whatever Application Name you want. Execute permissions should be scripts and executables. screenshot
Time to test! That's pretty much it, except some other basics. Make sure whatever script you're running has rights to do whatever it is doing (ntfs rights on folders for the System account, etc.) You may also wonder what the ASP looks like to run the script. Mine looks something like this:
set wshShell=server.createObject("wscript.shell")
strEncrypt="c:\gnupg\cmd.exe /c " & chr(34) & "c:\gnupg\gpg --encrypt-files -r operator --yes " & zscTempPath & " " & slrTempPath & chr(34)
returnCode=wshShell.run(strEncrypt,1,true) '1=activate and display the window, true=wait to execute before continuing
There's a lot of stuff in strEncrypt that's not relevant to this article, it's just the command issued (the same as if you were in a dos box) to encrypt files using gpg. Note that in my case I opted to copy cmd.exe out of the windows\system32 folder and place it in the same folder as the executable (gpg.exe) I'm using cmd.exe to run just to keep it clean.
Also, if you are running commands on the server that don't require cmd.exe, consider creating a separate local account on the web server and using that as the identity for your Application Pool instead of Local System. You can then lock down exactly what that account can do.
Next blog I'll discuss using a web server to upload and encrypt files using GnuPG.
First, let me explain what this particular web page does. We have a benefits provider that ftp's us data files that need to get imported into our payroll system, and we send them data files that are exported from our payroll system. Our payroll administrator goes to our intranet and uploads the files using ASPSmartUpload. On the backend, the web page places the files on the server, PGP encrypts them using GnuPG, and them copies them to the ftp server using the scripting.fileSystemObject. I'll try to cover the topic of uploading and encrypting with GnuPG in another blog entry.
This all worked fine and dandy until we moved over to Windows Server 2003, which has additional security features that make this much more difficult to accomplish. The biggest problem I faced is that the cmd.exe in Win2003R2 has new security checks that do not allow it to run unless it is running as LocalSystem. Because the process is spawned by IIS, it is not running as LocalSystem, and can't run. To get around this, you need to create an additional application pool that runs as Local System and then tie this application pool to the directory or site that is running the script. Of course, tying it down to the directory is more secure than to the entire site. You don't want anything running as Local System that doesn't have to be.
To accomplish this, first open up IIS Manager. Right-click application pools -> new -> Application Pool... screenshot. Give the pool a name, and use the default settings. I called mine ShellInteraction. Right click the newly created AppPool and go to properties. Click the identity tab. Click the Predefined radio button and select Local System from the list, then click apply. screenshot. You will get a warning about running the AppPool as the Local System, and rightly so. Like I said, you are circumventing several security features in IIS6 by doing this, so make sure you tie it down to only the exact scripts you want to run as Local System (I'll get to this later). Click yes.
Now you need to tell the script to use the new Application Pool. In IIS manager, Navigate through Web Sites to the directory containing the script. I'd have a special directory just for containing such scripts. Right click the directory, select properties. On the Directory tab (Home Directory tab if you're doing this for an entire site - NOT recommended), locate Application Pool: and select the name of your new AppPool. Give it whatever Application Name you want. Execute permissions should be scripts and executables. screenshot
Time to test! That's pretty much it, except some other basics. Make sure whatever script you're running has rights to do whatever it is doing (ntfs rights on folders for the System account, etc.) You may also wonder what the ASP looks like to run the script. Mine looks something like this:
set wshShell=server.createObject("wscript.shell")
strEncrypt="c:\gnupg\cmd.exe /c " & chr(34) & "c:\gnupg\gpg --encrypt-files -r operator --yes " & zscTempPath & " " & slrTempPath & chr(34)
returnCode=wshShell.run(strEncrypt,1,true) '1=activate and display the window, true=wait to execute before continuing
There's a lot of stuff in strEncrypt that's not relevant to this article, it's just the command issued (the same as if you were in a dos box) to encrypt files using gpg. Note that in my case I opted to copy cmd.exe out of the windows\system32 folder and place it in the same folder as the executable (gpg.exe) I'm using cmd.exe to run just to keep it clean.
Also, if you are running commands on the server that don't require cmd.exe, consider creating a separate local account on the web server and using that as the identity for your Application Pool instead of Local System. You can then lock down exactly what that account can do.
Next blog I'll discuss using a web server to upload and encrypt files using GnuPG.


7 Comments:
Josh, I am running into a similar problem running command-line scripts from within ASP on Windows Server 2003 IIS. We have a home-grown data warehouse system, maintainted via three independent client systems (DEV, TEST, and PRD). When I make a change to a group of source files on the DEV machine, I use an FSO script to identify the files and their asolute paths and create a batch script file. All three machines have identical file/folder tructures. To make copying the files easier, I mapped the root directories of the TEST and PRD machines on the DEV machine as X: and Y: respectively.
For example, if I want to move a file named "login.asp" from DEV to TEST, the batch file is written as follows:
XCOPY C:\Inetpub\DW-DEV\login.asp X:\Inetpub\DW-DEV\ /R /Y
This works just fine if I am logged in to the machine via Remote Desktop and open a command-line window. But I really need to be able to invoke this from an ASP shell script. I read your blog entry about creating a separate application pool that utilizes a Local System account, but when I set the properties of the folder containing my script file to allow "Scripts & Executables", I do not have the option to change the application pool - it is greyed out and retains the same settings from the parent web site.
I have spent many hours researching this topic and have found all sorts of interesting articles about the limitations of shell scripting in Windows Server 2003 and IIS 6.0. I have given the IUSR_, IWAM_ and IIS_WPG accounts full access to virtually everything, probably more than I feel comfortable with, but still to no avail.
The ASP script I am trying to use is as follows:
Dim objShell
Set objShell=Server.CreateObject("WScript.Shell")
objShell.Run(XCOPY C:\Inetpub\DW-DEV\DEVtest1.txt C:\Inetpub\DW-QAS\ /R /Y)
Set objShell = nothing
In a nutshell, I need to be able to copy a file from one directory to another directory (either on the same machine or to another machine via a mapped network drive letter) from an ASP page. If there is an easier way to do this, any suggestions would be tremendously appreciated!
Thanks, Jeff
You are dealing with some different issues because multiple servers are involved, so you have issues with security credentials on different machines.
Here are some of my thoughts. First, I think you are making this harder than it needs to be. There are a multitude of programs out there to accomplish synchronization across different servers. From a web development standpoint, I use Macromedia Dreamweaver to set up a "site" as Dreamweaver calls it that manages my development server (which is usually my local machine), my testing server, and my production server. Dreamweaver manages all the synchronization - I make changes on the dev, and can then synchronize the files (Dreamweaver detects what is newer) and publishes to the prod or test or both. But you're dealing with a data warehouse system, so your requirements may be a bit more complex than simple synchronization or deployment of web sites. A couple of other synchronization programs I have used are FolderShare (free, recently bought my Microsoft, but still free, and web-based), and GoodSync, which is very customizable, schedulable, and quite powerful. Perhaps these can help your situation.
If you still need to do manual synchronization with scripts, the first thing I would recommend is to access network shares with UNC paths, not mapped drives. A mapped drive can be problematic - the account running the script may not know what x: or y: are - they are only applicable to the account that mapped them - i.e. the account you were logged in as when you mapped them. The second thing to consider is that you are crossing machines, so whatever credentials you are using to run the script must be valid and have sufficient rights on all servers. For this reason, the LocalSystem account is never going to work for what you are trying to do.
I am also wondering if creating the batch file and then running it from a web page is adding an unnecessary layer of complexity. If you are running FSO scripts to determine which files need to be copied, why not use FSO to actually copy the files rather than creating batch files? You then eliminate the whole problem of application pool identity and a whole set of potential permission problems. All you need to worry about is the account the script is running under and whether or not that account has sufficient ntfs rights on all shares.
If I am not understanding your situation fully, or if you have other questions, feel free to ask.
Josh, I had a heck of a time figuring out how to call an exe from an asp page (code that worked fine on our old 2000 systems) on 2003, until that is I found your post. Thanks a lot!
A related issue I'm having is that now that I can call and run the exe using a seperate App Pool that is using Local System as it's identity I'm trying to set it up to use my own 'ShellInteractionUser'. I've got the pages to come up using the second Pool/User but as far as calling the exe goes it's back to the same behavour... nothing happens other than the System event error. Just for fun I added the ShellInteractionUser to the Admin group, no help. one thing to note, to get the pages to work w/ the second pool/user I had to add that user to the "Act as part of the operating system" Setting under Local Policies > User Rights Assignment. Is there some other setting I need to add to get this to happen? Did you set up your implementation to use a second user? I appreciate your help!
Rob, unfortunately my implementation did not use a 'ShellInteractionUser - I stuck with the particular appPool running as the LocalSystem account (as far as I remember now - I'll have to double check) - so unfortunately I can't be much help there. I'm pretty late responding to your post, so I'm guessing you've figured it out by now - if you happen to check back here, let me know!
Josh,
I spent 2 hours tinkering with folder permissions for an ASP script that uses WSH. Most Google searches kept leading me in the same direction ("it's file/folder permissions").
Your solution did the trick.
Josh.., Thanks a ton... I found your post after a 4 hr search and trials of different options..
Your solution just worked like a magic...:) Thanks again..
Just a clarification - my original post made it seem as though you had to run your application pool as the local system account to make it work. That is not the case - you can run it as any account you want provided it has the necessary permissions. For purposes of this particular article, it would need read and execute permissions to cmd.exe.
Post a Comment
<< Home