Problem: A user connects to a service on a machine, such as an IIS web site or a SQL Server database. The site or the database need to gain access to network resources such as file shares (the most common) or a database on a different server. Permission is denied.
This is because the user the service is running under doesn't have network permissions in the first place, or if it does, it doesn't have rights to access the remote resource.
I keep running into this problem over and over again and am tired of not having a really solid way of handling it.
Here are some workarounds I'm aware of:
Run IIS as a custom-created domain user who is granted high permissions
If permissions are granted one file share at a time, then every time I want to read from a new share, I would have to ask a network admin to add it for me. Eventually, with many web sites reading from many shares, it is going to get really complicated. If permissions are just opened up wide for the user to access any file shares in our domain, then this seems like an unnecessary security surface area to present.
This also applies to all the sites running on IIS, rather than just the selected site or virtual directory that needs the access, a further surface area problem.
Do the same thing, but use an application pool or individually set credentials
One doesn't have to run the entire IIS instance as a particular user: each site, application, and folder can be set to run under a particular application pool or manually-chosen set of credentials. This solves some small part of the surface area problem, but doesn't really solve the other issues about managing permission granularity and doing so across many resources.
Still use the IUSR account but give it network permissions and set up the same user name on the remote resource (not a domain user, a local user)
This also has its problems. For example, there's a file share I am using that I have full rights to for sharing, but I can't log in to the machine. So I have to find the right admin and ask him to do it for me. Any time something has to change, it's another request to an admin.
Allow IIS users to connect as anonymous, but set the account used for anonymous access to a high-privilege one
This is even worse than giving the IIS IUSR full privileges, because it means my web site can't use any kind of security in the first place.
Connect using Kerberos, then delegate
This sounds good in principle but has all sorts of problems. First of all, if you're using virtual web sites where the domain name you connect to the site with is not the base machine name (as we do frequently), then you have to set up a Service Principal Name on the webserver using Microsoft's SetSPN utility. It's complicated and apparently prone to errors. Also, you have to ask your network/domain admin to change security policy for both the web server and the domain account so they are "trusted for delegation." If you don't get everything perfectly right, suddenly your intended Kerberos authentication is NTLM instead, and you can only impersonate rather than delegate, and thus no reaching out over the network as the user. Also, this method can be problematic because sometimes you need the web site or database to have permissions that the connecting user doesn't have.
Create a service or COM+ application that fetches the resource for the web site
Services and COM+ packages are run with their own set of credentials. Running as a high-privilege user is okay since they can do their own security and deny requests that are not legitimate, putting control in the hands of the application developer instead of the network admin. Problems: I am using a COM+ package that does exactly this on Windows Server 2000 to deliver highly sensitive images to a secured web application. I tried moving the web site to Windows Server 2003 and was suddenly denied permission to instantiate the COM+ object, very likely registry permissions. I trolled around quite a bit and did not solve the problem, partly because I was reluctant to give the IUSR account full registry permissions. That seems like the same bad practice as just running IIS as a high-privilege user.
Note: This is actually really simple. In a programming language of your choice, you create a class with a function that returns an instance of the object you want (an ADODB.Connection, for example), and build a dll, which you register as a COM+ object. In your web server-side code, you create an instance of the class and use the function, and since it is running under a different security context, calls to network resources work.
Map drive letters to shares
This could theoretically work, but in my mind it's not really a good long-term strategy. Even though mappings can be created with specific credentials, and this can be done by others than a network admin, this also is going to mean that there are either way too many shared drives (small granularity) or too much permission is granted to entire file servers (large granularity). Also, I haven't figured out how to map a drive so that the IUSR gets the drives. Mapping a drive is for the current user, I don't know the IUSR account password to log in as it and create the mappings.
Move the resources local to the web server/database
There are times when I've done this, especially with Access databases. Does the database have to live out on the file share? Sometimes, it was just easiest to move the database to the web server or to the SQL database server (so the linked server to it would work). But I don't think this is a great all-around solution, either. And it won't work when the resource is a service rather than a file.
Move the service to the final web server/database
I suppose I could run a web server on my SQL Server database, so the web site can connect to it using impersonation and make me happy. But do we really want random extra web servers on our database servers just so this is possible? No.
Virtual directories in IIS
I know that virtual directories can help make remote resources look as though they are local, and this supports using custom credentials for each virtual directory. I haven't been able to come up with, yet, how this would solve the problem for system calls. Users could reach file shares directly, but this won't help, say, classic ASP code access resources. I could use a URL instead of a file path to read remote data files in a web page, but this isn't going to help me make a connection to an Access database, a SQL server database, or any other resource that uses a connection library rather than being able to just read all the bytes and work with them.
I wish there was some kind of "service tunnel" that I could create. Think about how a VPN makes remote resources look like they are local. With a richer aliasing mechanism, perhaps code-based, why couldn't even database connections occur under a defined security context? Why not a special Windows component that lets you specify, per user, what resources are available and what alternate credentials are used for the connection? File shares, databases, web sites, you name it. I guess I'm almost talking about a specialized local proxy server.
Anyway, so there's my list. I may update it if I think of more. Does anyone have any ideas for me?
My current problem today is, yet again, I need a web site to connect to an Access database on a file share. Here we go again...
I'd personally suggest pass through authentication and .net impersonation, using a unique Domain Account per application. Like you say this bottlenecks at the domain admin, but having every change to access permissions have to go through an admin can in fact be a good thing.
Presumably once the shares are set up per-application (part of a well defined set up process?) they won't really change much?
The admin can have a well defined process to go through that includes documentation of the access grant, with sign-off authorisation from management. This way everything is kept nice and secure, and very external-audit-friendly, if you happen to work in a regulatory environment.
See http://support.microsoft.com/kb/207671
Make all these network/remote resources local by either SQL replication/Log Shipping and/or file synchronization (Microsoft's Sync Framework is an option, although the symplicity of cygwin + rsync is more appealing, IMO).
Of course, all of this depends on how frequently the data changes and/or whether you need two-way synchronization or simply mirroring.
If you prefer not using Kerberos, you can impersonate the user when accessing the resource, using their token created when they were authenticated with forms-based authentication. This should use an SSL certificate, at least during the logon.
Presuming you don't have a security requirement to pass through authentication from the front end to the back-end shares, I would look at ways to get the network resources this app needs to access under one security root. Something like a DFS share might be perfect for this.
Second, I would assign security to an AD Security Group rather than an individual service account or computer.
From there I would probably use service accounts and computer names as they made sense. EG, you might just add the database server's service account (DOMAIN/MACHINENAME$) to the group but if you have a cluster of web app servers you would use a common service account.