I’ve been experimenting with Tomasz Janczuk’s awesome iisnode project lately, in hopes of hosting some of my Node.js sites on IIS. Those sites are running well enough on an Ubuntu VPS currently, but the features that iisnode offers are compelling (and I have unused capacity on one of my Windows servers).
Along the way, I ran into one issue that isn’t addressed in the current iisnode documentation or examples very well. If you try to set up a standalone Node.js website in IIS you’re likely to be greeted with this error when you load it up for the first time:
Remotely, you’ll see the dreaded “The service is unavailable.” error:
Those nondescript errors don’t exactly make resolving the problem very straightforward. So, I want to recap what I found to be the underlying problem in my case and a few solutions, in hopes of helping anyone else that ends up with the same issue.
The problem boils down to the default application pool identity account not having sufficient permission to carry out iisnode’s work. Part of the IIS error message does somewhat cryptically hint at what’s wrong:
An invalid identity in the application pool could cause this error.
That’s not very actionable though.
In the specific case of a barebones Node.js site running under iisnode’s default configuration, this occurs due to iisnode attempting to create its log file when your application is accessed for the first time.
If you’ve run iisnode locally, you’ve probably seen the
script-name.js.logs folder that appears after first run. Unfortunately for iisnode, creating this folder (and the log file(s) inside it) is beyond the default application pool identity’s abilities and that results in our 503 error.
The nuclear solution (don’t do this)
The most straightforward way to eliminate the 503 error and get back to work is to change your site’s application pool identity from the default ApplicationPoolIdentity to LocalSystem:
That works well enough if you’re just playing around on a development machine and security isn’t a concern.
However, elevating the application pool’s privileges that high is not something you should do in production. If an attacker finds a hole in your Node.js code, running the site under LocalSystem means they will gain unfettered access to your entire web server. Not good.
Avoiding the problem altogether
Before changing any permissions whatsoever, ask yourself whether or not you care about iisnode’s logging to begin with. It’s enabled by default, but easy enough to disable by tweaking your site’s web.config:
<iisnode loggingEnabled="false" />
With that small change, iisnode won’t attempt to create a log directory at all, and the file system permissions issue is moot.
If your Node.js code doesn’t attempt to write to disk otherwise and you don’t care about iisnode’s logging feature, this is probably the best solution since it requires no privilege elevation at all.
A more limited escalation
Instead of giving your application pool the keys to the kingdom, a better solution is to grant ApplicationPoolIdentity elevated privileges only on the specific location where your Node.js application resides.
You can grant those permissions via either the Windows GUI interface or the command line, depending on your preference.
Assuming you have access to the server’s desktop, you can give IIS write access through the usual Properties > Security > Edit… dialogs. In a default installation, the
IIS_IUSRS group is what you need to grant write access to:
You might notice that I’m only granting elevated privileges to
app.js.logs above, not the entire site structure.
It’s often the case that a single file, like my site’s
app.js, will be the exclusive entry point to an entire site. When that’s the case, there will only be a single iisnode logging folder and it’s a good idea to even further isolate write access to the corresponding
Command line approach
Using the Windows GUI to set file permissions is okay, but it can be a bit cumbersome to dig through the file system and then navigate through the dialog windows. More importantly, you may need to script this as part of an unattended deployment process.
Windows also includes a command line utility to handle this:
icacls. Here’s an example of how you could grant the IIS user (and iisnode) write access to an entire site structure from the command line:
icacls c:\path\to\your\site /grant IIS_IUSRS:(OI)(CI)W
Running that on my specific example of MySite.com looked like this:
Limiting access specifically to the logging folder, as shown in the GUI approach above, is simple enough too:
icacls c:\path\to\your\site\app.js.logs /grant IIS_IUSRS:(OI)(CI)W
If you’re curious about the syntax of the command, it’s less complicated than it looks. The path and
/grant are self-explanatory, but the rest is a bit terse.
IIS_IUSRS:(OI)(CI) means that the change should apply to any objects and containers within the IIS_IUSRS group.
W at the end determines what type of permission should be granted. We could also have used
M to give both write and modify permissions to iisnode, or
F to grant full control.
This applies to more than logging
Of course, hitting a permissions error trying to write logs is just one possible reason you might encounter this error. Since this particular cause is one that everyone will probably run into when setting up a standalone site under iisnode, I decided that it’s worth singling out individually though.
If your Node.js code itself needs access to write to the file system, you’ll need to grant the correct permissions in those areas of your site’s structure too (following the same approach as above, but altering the path appropriately).