I've posted this on stackoverflow, but posting it here as I want some help from folks that are familiar with kerberos delegation and IIS. I am currently trying to figure out how to get my flask app to handle active directory attribute updates on behalf of users in a domain, such as their phone numbers. I currently have this running in IIS 10 on a Windows Server 2019 VM. I have a small virtual lab on Hyper-V that replicates a vanilla Active Directory domain, where I have a domain controller called dc1, a web server called webserver1, and a client machine called client1.
The web application is ran under a service account named service-acct in IIS. Currently, the HTTP request provides me with the windows auth token of the requesting user (via asp net core module), which allows me to impersonate them.
Web.config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
<authentication>
<anonymousAuthentication enabled="false" />
<windowsAuthentication enabled="true" useKernelMode="true" useAppPoolCredentials="true" />
</authentication>
</security>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" requireAccess="Script" />
</handlers>
<aspNetCore processPath="C:\apps\Test\venv\scripts\python.exe" arguments="app.py" startupTimeLimit="10" stdoutLogEnabled="true" stdoutLogFile=".\logs\log.log" processesPerApplication="10" forwardWindowsAuthToken="true">
<environmentVariables>
</environmentVariables>
</aspNetCore>
<httpErrors errorMode="Detailed" />
</system.webServer>
<system.web>
<identity impersonate="true" />
</system.web>
</configuration>
The workflow looks something like this:
User hits IIS --> Web Server receives HTTP request --> Flask parses the header and gets the windows authentication token --> continue with endpoint python logic
In terms of impersonation, I have been able to use the win32security python module to impersonate the user and perform limited operations within the web server (eg. create folders); however, attempting to update the user's active directory credentials through a flask endpoint leads to a permission error (via the pyad python module):
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, 'Active Directory', 'An operations error occurred.\r\n', None, 0, -2147217865), None)
The relevant endpoint python code I used is below:
@app.route("/TestImpersonation")
def TestImpersonation():
key = 'Ms-Aspnetcore-Winauthtoken'
if key in request.headers.keys():
handle_str = request.headers[key]
handle = int(handle_str, 16) # need to convert from Hex / base 16
win32security.ImpersonateLoggedOnUser(handle)
user = win32api.GetUserName()
from pyad import pyad
user_obj = pyad.from_cn(user)
description = "Changed by "+ str(user) + " on " + datetime.datetime.today().strftime("%Y/%m/%d %H:%M:%S")
user_obj.update_attribute('description', description)
win32security.RevertToSelf() # undo impersonation
win32api.CloseHandle(handle) # don't leak resources, need to close the handle!
# Continue...
Searching for the error suggests that I have permission issues trying to do the active directory operation, which makes me think it is a double-hop problem. I tried allowing kerberos delegation for the service account in ADUC and also created SPNs for it as such:
setspn -s HTTP/webSERVER1 contoso\service-acct
setspn -s HTTP/webserver1.contoso.com contoso\service-acct
However, that seems to still have issues and I seem to be stuck. Any suggestions?