EPIC START

So this turned out to be much longer than I thought - the basic goal is to utilise two of the coolest features of Window Server 2008 - Core and IIS7. The idea is to create an ultimately low footprint web server on Microsoft Windows Web Server 2008 Core and show how that can easily support the Apache Software Foundation's Open Source Java Server - Tomcat. Ideally I don't want to logon to the console or via Remote Desktop of the server at any point - and I'll use our remote management tools to configure and install in a true 'headless' environment. My Starting point is a clean install of Windows Web Server 2008 Core on Hyper-V, not on a domain and just having had the computer name set to "servercore". Let see how I go!

Set for Remote Management

So this the one thing I must do on the console (or I can script it as part of install). I need to configure our remote management tools WinRM to allow connections from my workstation to the server. (note that you should NOT leave WinRM with Basic auth set to true in a production environment - I need to do this because the machine is not in a domain)

WinRM quickconfig
WinRM set winrm/config/service/auth @{Basic="true"}
WinRM set winrm/config/client @{TrustedHosts="jorkeo-hp"}

WinRM set winrm/config/service/auth @{Basic="true"}
WinRM set winrm/config/client @{TrustedHosts="servercore"}

Now I connect to the server from the command prompt on my workstation ("jorkeo-hp") using WinRS, and I'm going to instantiate a remote command prompt:

WinRS -r:servercore -u:Administrator -p:****** cmd.exe

image

Now we have our remote shell - all commands from this point forward are run here.

Install Basic IIS requirements

Normally I would use ocsetup to install everything with dependencies, but since I'm attempting a low footprint web server I want pick the exact packages I need to install without installing everything - to do this I use package manager - pkgmgr - and select the roles/modules to install.

start /w pkgmgr /iu:IIS-WebServerRole;IIS-WebServer;IIS-CommonHttpFeatures;IIS-StaticContent;IIS-DefaultDocument;IIS-DirectoryBrowsing;IIS-HttpErrors;IIS-HttpRedirect;IIS-ApplicationDevelopment;IIS-ISAPIExtensions;IIS-ISAPIFilter;IIS-HealthAndDiagnostics;IIS-HttpLogging;IIS-LoggingLibraries;IIS-RequestMonitor;IIS-HttpTracing;IIS-Security;IIS-BasicAuthentication;IIS-URLAuthorization;IIS-RequestFiltering;IIS-IPSecurity;IIS-Performance;IIS-HttpCompressionStatic;IIS-HttpCompressionDynamic;IIS-WebServerManagementTools;IIS-ManagementScriptingTools;WAS-WindowsActivationService;WAS-ProcessModel;

Installation dependencies can be found here:http://learn.iis.net/page.aspx/130/understanding-setup-in-iis-7/

Then you need to create the site under IIS7 to host your servlets.

mkdir c:\inetpub\mytomcat

C:\windows\system32\inetsrv\appcmd.exe add site /name:"mytomcat.com" /bindings:http://mytomcat.com:80
/physicalPath:"c:\inetpub\mytomcat"
SITE object "mytomcat.com" added
APP object "mytomcat.com/" added
VDIR object "mytomcat.com/" added

c:\windows\system32\inetsrv\appcmd.exe add apppool /name:mytomcat

APPPOOL object "tomcat" added

c:\windows\system32\inetsrv\appcmd.exe set site "mytomcat.com"
/applicationDefaults.applicationPool:"mytomcat"

SITE object "mytomcat.com" changed

Install JRE + Tomcat

Now we need to install the Java Runtime Environment - I just download the latest one from http://java.sun.com and install with defaults, the version I ended up with was JRE6 update 7 - the offline installation - I then ran the installer like so:

jre-6u7-windows-i586-p.exe /passive

Verified that was in place, by looking for the java directories under \Program Files

Then just downloaded the latest version of Apache Tomcat from my buddies at AussieHQ. There are a couple of ways you can deploy this, by running the installer service/downloading the file - I like to run the installer on a reference machine then copy the contents of the tomcat directory to the server - it seems to clear out all the unnecessary stuff at installation time. I've copied my contents of a reference install to c:\tomcat on servercore.

Run Tomcat as a service

Now this took aaaggess as the installation syntax is overly sensitive, case sensitive etc. So to save you the time here is my installation script line to install with all options pointing to my new installation of JRE:

tomcat6.exe //IS//Tomcat6 --Install="c:\tomcat\bin\tomcat6.exe" --StartClass=org.apache.catalina.startup.Bootstrap --StopClass=org.apache.catalina.startup.Bootstrap --StartParams=start --StopParams=stop --JvmOptions "-Dcatalina.home=c:\tomcat;-Dcatalina.base=c:\tomcat;-Djava.endorsed.dirs=c:\tomcat\common\endorsed;-Djava.io.tmpdir=c:\tomcat\temp;-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager;-Djava.util.logging.config.file=c:\tomcat\conf\logging.properties;" --Jvm="C:\Program Files\Java\jre1.6.0_07\bin\client\jvm.dll" --JavaHome="C:\Program Files\Java\jre1.6.0_07" --Classpath="c:\tomcat\bin\bootstrap.jar" --LogPath=c:\tomcat\logs --StdError=auto --StdOutput=auto --StartPath=c:\tomcat --StopPath=c:\tomcat --StartMode=jvm --StopMode=jvm

At this point I found that tomcat would fail to start

C:\tomcat\bin>net start tomcat6
The Tomcat6 service is starting.
The Tomcat6 service could not be started.

A service specific error occurred: 0.

More help is available by typing NET HELPMSG 3547.

C:\tomcat\bin>C:\tomcat\bin>tomcat6 //TS//Tomcat6
C:\tomcat\bin>

hmmm nothing... looking in tomcat\logs folder the "jakarta_service.log" file seemed to be the latest timestamp, opening that in notepad and its full of this:

2008-09-16 12:39:50] [info] Procrun (2.0.3.0) started
[2008-09-16 12:39:50] [info] Debugging Service...
[2008-09-16 12:39:50] [info] Starting service...
[2008-09-16 12:39:50] [174  javajni.c] [error] The specified module could not be found.
[2008-09-16 12:39:50] [986  prunsrv.c] [error] Failed creating java C:\Program Files\Java\jre1.6.0_07\bin\client\jvm.dll
[2008-09-16 12:39:50] [1260 prunsrv.c] [error] ServiceStart returned 1
[2008-09-16 12:39:50] [info] Debug service finished.
[2008-09-16 12:39:50] [info] Procrun finished.

which appears to be a problem finding an api to connect to, but the error is pretty vague as to what its actually trying to do..

GROAN - this is the FIRST time i have to logon to the server via RDP/Console.. remember everything else so far has been via a remote winrs console...

Plunger Cat Fail

Log On... Fire up sysinternals Process Monitor (thank you Mr Russinovich) to see what this tomcat process is trying to talk to (after some thought, I reckon I could probably spawn this remotely with a backing file to disk and load that on another machine... but too much stuffing around..). After attaching to the tomcat process, aha - there it is:

image

When the tomcat process fires up it pokes around the OS looking for MSVCR71.dll - which is the a bunch of C libraries generally shipped with the Microsoft C runtime library. So all I needed to do is find that file and put it in path - hang- on.. I remembered back to my Java development days in University - pretty sure that the JRE ships with the libraries in tow... ah... looking into the JRE\bin directory of my Java Runtime Environment install, there it is, I'll copy that to my tomcat\bin folder and try again.

Ok - lets log off that server real quick - and back to our remote shell.

So I kicked off starting tomcat from the command line again.. success!

image

You'll see its started with the default of http listening on port 8080 - so browsing to this (hoping no random proxy in the way) - Yee ha!

image

Awesome, now I just have to CTRL-BREAK out of that and start the service:

C:\tomcat\bin>net start tomcat6
The Tomcat6 service is starting.
The Tomcat6 service was started successfully.

And test again - also a Screen shot to prove it:

image

But this is only the first step - Now I need to serve Tomcat through IIS7.

Setting up the IIS ISAPI Redirector

Download the IIS Tomcat connector - http://tomcat.apache.org/download-connectors.cgi

The connector acts as a broker between IIS and Tomcat and as far as IIS is concerned just an ISAPI filter.

For this example I've grabbed isapi_redirect-1.2.26.dll - and renamed it to isapi_redirect.dll just to make it easy.

Now this is where your configuration choice can get interesting. Depending on how you're supporting tomcat you have the choice of using the registry for storing configuration information for the ISAPI filter OR using a file. This of course depends on how you intend to host it as well. If you intend host this in a multi-tenant environment with several java sites on the same server and possibly different customers as well I would recommend using a file based approach as you can set a configuration file per site. If you are just looking after one site on the server, registry configuration is fine. One thing to remember though, is when you need to replicate the configuration to another server then you will have to make sure the registry entries follow the site.

For the purposes of this example, I'm going to attempt a multi-tenant configuration to allow the most flexibility, plus prevents me from having to play in the registry. When you need to create another site on the same server, create a new folder for the site under tomcat\conf and tomcat\logs and simply follow these steps for each new site :) (or script it)

1. Create a folder under the website root called jakarta
2. Copy the isapi_redirect.dll into the jakarta folder.
3. Create a file in the jakarta folder called isapi_redirect.properties (note that the name before the extension MUST match the dll filename)
4. Edit the isapi_redirect.properties file in notepad (server core IDE) - paste in the following:

# Configuration file for the Jakarta ISAPI Redirector

# The path to the ISAPI Redirector Extension, relative to the website
# This must be in a virtual directory with execute privileges
extension_uri=/jakarta/isapi_redirect.dll

# Full path to the log file for the ISAPI Redirector
log_file=c:\tomcat\logs\mytomcat\isapi_redirect.log

# Log level (debug, info, warn, error or trace)
log_level=INFO

# Full path to the workers.properties file
worker_file=c:\tomcat\conf\mytomcat\workers.properties

# Full path to the uriworkermap.properties file
worker_mount_file=c:\tomcat\conf\mytomcat\uriworkermap.properties

5. You can see that the log_file directive is going to a folder that doesn't exist so we need to create the folder tomcat\logs\mytomcat\ - and also the configuration folder tomcat\conf\mytomcat\ - by creating a separate folder for each site you allow the separation of site logging and configuration.
6. Now we need to create the workers.properties and uriworkermap.properties files under the tomcat\conf\tomcatsite1\ folder. Create the worker.properties file first - This controls the configuration of tomcat worker processes allows control of resources to the site - something best left to the server administrators (more info on tomcat workers) - lets create the file notepad.exe tomcat\conf\mytomcat\workers.properties - and populate with the following:

# list of workers

worker.list=mytomcat,ajp13
worker.mytomcat.type=ajp13
worker.ajp13.type=ajp13

# worker mytomcat
worker.mytomcat.host=localhost
worker.mytomcat.port=8009
worker.mytomcat.lbfactor=1

# worker ajp13
worker.ajp13.host=localhost
worker.ajp13.port=8009
worker.ajp13.lbfactor=1

# time out
worker.mytomcat.connection_pool_timeout=600
worker.mytomcat.socket_timeout=60

You can see here we are trying to make sure we keep the naming specific to the site. Save the worker.properties file, then create the uriworkermap.properties file - notepad.exe tomcat\conf\mytomcat\uriworkermap.properties - this is where we map the request to the tomcat worker process. There is a whole level of URI re-mapping you can do here, but for the moment we are just going to take everything that comes to the site is fed to tomcat - just make sure we match up with the worker specified in the workers.properties file.

# Use www.foo.org as virtual host
# /www.foo.org/myapp/*=myworker
# Normal mapping

/mytomcat.com/*=mytomcat
/mytomcat.com/=mytomcat

Save that file.

7. The last bit of hacking around in tomcat is that we need to tell it that we've setup a site for it to to be aware of. To kick this I opened the file tomcat\conf\server.xml in notepad.

Between the <Service> xml tags another <Engine> tag that describes our site needs to put into place - this is the syntax I entered:

<Engine name="Catalina" defaultHost="mytomcat.com">
      <Host name="mytomcat.com" appBase="c:\inetpub\mytomcat" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
          <Valve className="org.apache.catalina.valves.RequestDumperValve"/>
          <Valve className="org.apache.catalina.valves.AccessLogValve" directory="c:\tomcat\logs\mytomcat" prefix="mytomcat_access_log." suffix=".log" pattern="common" resolveHosts="false"/>
      </Host>
    </Engine>

( Note the Valves in place to dump engine logs - http access etc )

You will need to restart the tomcat service to pick up these changes - if any weirdness happens just run the service from the console to pick up any issues.

8. Now we need to tell IIS that we're using an ISAPI filter to serve the content - we just need to add the ISAPI filter via APPCMD. First allow the ISAPI filter into the CGI/ISAPI restriction policy - then we unlock the handlers config in applicationhost.config then actually add the ISAPI filter.

appcmd.exe set config  -section:system.webServer/security/isapiCgiRestriction
/+"[path='c:\inetpub\mytomcat\jakarta\isapi_redirect.dll',allowed='True',description='tomcat']" /commit:apphost

c:\windows\system32\inetsrv\appcmd.exe unlock config /section:system.webserver/handlers

Unlocked section "system.webServer/handlers" at configuration path "MACHINE/WEBROOT/APPHOST".

c:\windows\system32\inetsrv\appcmd.exe set config "mytomcat.com"
-section:system.webServer/handlers
/+"[name='tomcat',path='*',verb='*',
scriptProcessor='c:\inetpub\mytomcat\jakarta\isapi_redirect.dll']"

Applied configuration changes to section "system.webServer/handlers" for "MACHIN
E/WEBROOT/APPHOST/mytomcat.com" at configuration commit path "MACHINE/WEBROOT/APPHOST/mytomcat.com"

9. One thing I almost forgot is that we need make sure that the context our site is running on has the ability to read the tomcat configuration and files. Because our application pool runs as the built in account - "Network Service" I have to ensure it can read and write in the appropriate locations around tomcat. Now I'm being a little lazy here - if I really wanted to lock it down I would create a user account, remove it from all groups and then sit with Process Monitor and find the exact settings required. But this is enough to get me over the hump. Allowing read to all of tomcat, and allowing write to tomcat\logs

cacls c:\tomcat /T /E /C /G "NETWORK SERVICE":R

cacls c:\tomcat\logs /T /E /C /G "NETWORK SERVICE":C

10. Finally we need to drop some content to serve, I'm just going to drop in the examples that ship with the default tomcat build, normally found in the tomcat\webapps\examples folder. I've just copied all the files into the root of my site and now lets browse to my site http://mytomcat.com/servlets/servlet/HelloWorldExample (local demo site) ... and:

image

Its working - HOORAY!

Just to verify, looking at my IIS logs you can see the requests coming through to the server:

008-09-17 00:50:32 192.168.0.2 GET /servlets/servlet/HelloWorldExample - 80 - 127.0.0.1 HTTP/1.1 Mozilla/5.0+(Windows;+U;+Windows+NT+6.0;+en-GB;+rv:1.9)+Gecko/2008052906+Firefox/3.0+(.NET+CLR+3.5.30729) JSESSIONID=A86ABA6C645C8FC922551765899D09A0 - mytomcat.com 200 0 0 511 513 194
2008-09-17 00:50:32 192.168.0.2 GET /servlets/images/code.gif - 80 - 127.0.0.1 HTTP/1.1 Mozilla/5.0+(Windows;+U;+Windows+NT+6.0;+en-GB;+rv:1.9)+Gecko/2008052906+Firefox/3.0+(.NET+CLR+3.5.30729) JSESSIONID=A86ABA6C645C8FC922551765899D09A0
http://mytomcat.com/servlets/servlet/HelloWorldExample mytomcat.com 200 0 0 519 538 17
2008-09-17 00:50:32 192.168.0.2 GET /servlets/images/return.gif - 80 - 127.0.0.1 HTTP/1.1 Mozilla/5.0+(Windows;+U;+Windows+NT+6.0;+en-GB;+rv:1.9)+Gecko/2008052906+Firefox/3.0+(.NET+CLR+3.5.30729) JSESSIONID=A86ABA6C645C8FC922551765899D09A0
http://mytomcat.com/servlets/servlet/HelloWorldExample mytomcat.com 200 0 0 1460 540 21

The request loads through the ISAPI filters, so looking at the tomcat access logs:

127.0.0.1 - - [17/Sep/2008:10:44:33 +1000] "GET /servlets/servlet/HelloWorldExample HTTP/1.1" 200 359
127.0.0.1 - - [17/Sep/2008:10:44:38 +1000] "GET /servlets/servlet/HelloWorldExample HTTP/1.1" 200 359
127.0.0.1 - - [17/Sep/2008:10:44:55 +1000] "GET /servlets/servlet/HelloWorldExample HTTP/1.1" 200 359
127.0.0.1 - - [17/Sep/2008:10:45:12 +1000] "GET /servlets/servlet/HelloWorldExample HTTP/1.1" 200 359
127.0.0.1 - - [17/Sep/2008:10:45:57 +1000] "GET /servlets/servlet/HelloWorldExample HTTP/1.1" 200 359

Further Security Lock Downs

One very important you should to for your site is to ensure that the jakarta folder is blocked from http reading, i.e. just being able to browse to it - this can be controlled by the request filtering feature that is built into IIS7. To protect the folder, very simply appcmd directive:

appcmd.exe set config "mytomcat.com/jakarta" -section:system.webServer/security/requestFiltering /+"hiddenSegments.[segment='jakarta']"

Then remember to rollback the WinRM configuration when going into production:

WinRM set winrm/config/service/auth @{Basic="false"}

EPIC DONE!

And we are done - so this is my no means an exhaustive configuration guide - merely a glimpse of how you can configure some competitive technologies with Windows Server 2008 Core and IIS7.

- jorke

p.s. (Fineprint) this whole post is totally without warranty, if you try this and it works or doesn't work its not my fault. If you girlfriend/wife/cat leaves you because of this - believe me it wasn't this - you have something else to worry about..