ANCM is architected as a generic reverse proxy, but a couple of implementation choices currently limit its usefulness to only ASP.NET Core apps.
The repository nil4/IISIntegration
contains
an ANCM V2 fork that adds a few opt-in settings to enable any other application to run
with ANCM, and be provided access to the IIS authenticated user identity.
The ancmv2_extra/Readme.md
file describes:
- the handshake protocol between ANCM and its backend app
- the protocol used to securely pass requests to the backend app
- the implications of the ANCM design choice to pass the IIS user identity (a Windows token/handle) to the backend app, which restricts its applicability to only ASP.NET Core apps
It also describes the few extra features added by the ANCM fork.
This repository contains demo apps that use these extra ANCM features to show how any other application type, in addition to ASP.NET Core, could be hosted on IIS/Windows.
⚠️ The forked ANCM binaries are stored in this repo, under /ancmv2_extra, and are used to run these demo apps.
IIS Express 10 and the .NET Core 2.1 Runtime & Hosting Bundle (which installs ANCM) are required to run these demos.
In addition, a one-time setup step needs to be performed. The demos use ANCM version 2 (which is not installed by the .NET Core 2.1 bundle), and the extra features implemented by the ANCM fork require an additional IIS Express schema file.
If the schema files are not installed, IIS Express will not recognize the extra attributes
of the <aspNetCore>
element in Web.config
and will fail to start the demo apps.
To install the two schemas, copy these files to %ProgramFiles%\IIS Express\config\schema
:
The batch files that launch the demo apps will test the prerequisites, and remind you to install/copy them if needed.
If you have NodeJS installed, start
run-node.cmd
in a command prompt.
This starts IIS Express, listening on http://localhost:50690
, writing detailed logs to the console window,
and ANCM will launch node/server.js
as the backend
application. Like all other demos, the batch file will also open http://localhost:50690
in your browser.
Things to note:
-
A greeting similar to:
Hello from NodeJS, YourDomain\UserName!
should be displayedIIS Express is configured to use Windows authentication, and the
forwardUserName="true"
setting innode/Web.config
requests ANCM to pass the IIS authenticated user name to the NodeJS application as a request header. -
Below the greeting, all request headers forwarded by ANCM are displayed, and among them you will find
ms-aspnetcore-user
.This is the header that the ANCM fork uses, by default, to pass the name of the authenticated IIS user, which makes it useful regardless of the backend app technology.
In contrast, the handle/token passed by ANCM by default (e.g. to an ASP.NET Core app) requires the app to call Win32 APIs to convert the handle into a user identity, and then close the handle at the end of the request to avoid resource leaks. This is disabled explicitly here by
forwardWindowsAuthToken="false"
. -
Edit
node/Web.config
and setforwardUserDomain="false"
, then refresh the browser page.You should see the greeting update to just
Hello from NodeJS, UserName!
.Note that whenever you change Web.config, ANCM stops the NodeJS application, and starts it again on the next request. You can see the relevant ANCM process management messages in the console window.
-
Edit
node/Web.config
and setforwardUserName="false"
, then refresh the browser page.The user name header (
ms-aspnetcore-user
) is no longer passed to the back-end app, and the greeting is no longer shown. -
Edit
node/Web.config
, setforwardUserName="true"
, and then disable Windows authentication (<windowsAuthentication enabled="false" />
) and enable anonymous instead (<anonymousAuthentication enabled="true">
).The greeting is not shown (as the anonymous user name is empty), but note that the
ms-aspnetcore-user
is still passed. Regardless of the active IIS authentication method, the header is always send whenforwardUserName
is set totrue
, such that the backend app can reliably use it for authorization purposes.In contrast to the out-of-the-box ANCM functionality, which only forwards a Windows authentication tokens to ASP.NET Core apps, the
forwardUserName
setting works with all authentication methods supported by IIS. -
The
node/server.js
script implements the same request security protocol that ASP.NET Core applications implement.To test this, search the IIS Express console log for a line similar to:
Application '/LM/W3SVC/1/ROOT' started process '1234' successfully and process '1234' is listening on port '44836'.
Here, the NodeJS application is running on port 44836; open
http://localhost:44836
in your browser.Because direct requests do not include the correct
ms-aspnetcore-token
token, you will see an error message:Invalid request: ANCM token mismatch
.
Press Q in the console window to stop IIS Express before trying the next demo.
If you have Python 3 installed, start
run-python.cmd
in a command prompt.
Similar to the previous demo, this will launch python/server.py
under ANCM/IIS Express, and open http://localhost:50690
in your browser.
The Python demo has the same functionality as the NodeJS demo (including the ANCM security handshake protocol). All the experiments in the NodeJS demo are applicable here as well.
If you have Docker for Windows installed,
with experimental features enabled, i.e. LCOW (Linux Containers on Windows) available,
start run-docker.cmd
in a command prompt.
This will pull the paddycarey/go-echo image (a simple HTTP server, written in Go and running on Alpine Linux, that echoes request headers as a JSON-formatted response).
It will then hopefully run the image under ANCM and IIS Express (the Docker LCOW support is still experimental).
If all else fail, restart Docker for Windows, and set stdoutLogEnabled="true"
in docker/Web.config
,
which should create log files with hints about any issue.
If it works, you should see a JSON response in your browser, with the ANCM-forwarded request headers,
including ms-aspnetcore-token
and ms-aspnetcore-user
.
Things to note:
-
Currently, ANCM does not replace
%ASPNETCORE_PORT%
in the command line of the process it starts. But Docker expects the port mapping (-pHostPort:ContainerPort
) to be passed through the command line.Fortunately, ANCM allows specifying the port to use (as opposed to generating a random port every time) through an environment variable.
Therefore, in this demo, the backend app port has to be hardcoded in both
run-docker.cmd
anddocker/Web.config
. -
Note the
disableProcessIdCheck="true"
setting indocker/Web.config
This is a setting added by the ANCM fork, and is required here because ANCM version 2 expects the process it launched (or one of its child processes) to be listening on the configured port. However, while the user program
docker.exe
triggers a container to start, the running container is actually hosted by a system service (com.docker.service
—the Docker daemon), and it is this service that actually opens the port that ANCM needs to proxy to.When the
disableProcessIdCheck
is set tofalse
(the default value), ANCM will successfully launchdocker.exe
, but will then try to find the listening port (18000
) attached to thedocker.exe
process. As the port is actually hosted bycom.docker.service
, ANCM will eventually time out and declare the process launch failed.Setting
disableProcessIdCheck="true"
turns off this check, allowing ANCM to proxy to an existing port, regardless of which process owns it. -
The
run-docker.cmd
file explicitly stops the Docker container when IIS Express exits, to release the port it listens on.
If you have .NET Core SDK 2.1 installed,
start run-dotnet.cmd
in a command prompt.
This will build dotnet/TestANCM.csproj
and run the netcoreapp2.1
application under the ANCM fork.
Just like the regular ANCM, Windows authentication tokens are passed to the application; in your browser, you should see:
HttpContext.User.Identity.Name = YourDomain\UserName
Header[MS-ASPNETCORE-USER] =
ASPNETCORE_IIS_HTTPAUTH = windows;
Try the following:
-
set
forwardWindowAuthToken="false"
indotnet/Web.config
; note thatHttpContext.User.Identity.Name
is now empty. -
set
forwardUserName="true"
; note thatHeader[MS-ASPNETCORE-USER]
is now set toYourDomain\UserName
.
If you have Docker for Windows with LCOW support enabled,
start run-dotnet-docker.cmd
in a command prompt.
This will build and deploy dotnet/TestANCM.csproj
to an Alpine Linux container image
using dotnet/Dockerfile
,
and then attach to the running container under IIS Express/ANCM.
In your browser, you should see similar output to the previous demo, but HttpContext.User.Identity.Name
will
be empty (expected, since an application running in a container cannot use a Windows user handle),
while Header[MS-ASPNETCORE-USER]
will have the correct user name.
A more realistic example would be to run a .NET application in a Windows Docker container,
under a Group Managed Service Account identity
that enables it to use integrated Windows authentication against SQL Server databases or other remote resources.
In that scenario, the application running inside the container would still need to have access
to the identity of the authenticated IIS user, and the forwardUserName
setting enables that.
If you have WSL (aka Bash-on-Windows) installed, start wsl.exe ASPNETCORE_PORT=18000 python/server.py
in a command prompt.
This should run the same Python server script used earlier, but this time under Bash/WSL.
If it works, there will be no output; the Python server waits silently for a request.
Open http://localhost:18000
in your browser, and you should see the request headers
listed, and server logs for the requests processed.
Stop the server (and Bash) by pressing Ctrl+C.
Start run-python-wsl.cmd
in a command prompt.
For reasons that are unclear, but likely related to IIS Express/ANCM starting processes under
Windows job objects
that may be incompatible with Bash-on-Windows, the same command that runs well interactively,
fails under IIS Express/ANCM.
You will likely see the IIS Express error page (HTTP Error 502.5 - Process Failure
), with
a cryptic message logged in python-wsl/python-wsl_<date>_<pid>.log
:
Error: 0x80070006
This looks (at least remotely) related to microsoft/WSL#2 or microsoft/WSL#1259.