Für das Hosting einer Streamlit App über IIS als Proxy gibts eine fantastische Anleitung: https://gist.github.com/stanorama/bb52930e3cf70138d7f199338c064ae6, kurz zusammengefasst falls der Gist mal verschwinden sollte:

  • In der IIS Rolle WebSocket Protocol enablen
  • In der IIS Rolle Windows Authentication enablen
  • Application Request Routing installieren (https://www.iis.net/downloads/microsoft/application-request-routing) und in der IIS Console bei den Actions des Features in “Server Proxy Settings” Proxy einschalten
  • URL Rewrite installieren (https://www.iis.net/downloads/microsoft/url-rewrite)
  • Die User Site erzeugen, gerne auch mit https
  • Windows Authentication auf die Site enablen und Provider auf nur NTLM umstellen (jaaaa, grausam), Site Directory ACL setzen oder in den Authentication Rules entsprechende Gruppen/User hinterlegen
  • URL Rewrite Regel (inbound) erzeugen mit Match URL = .* Rewrite auf http://localhost:1234/{R:0} (dort wo halt die Streamlist App läuft) erzeugen

Resultierende web.config:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Reverse proxy to streamlit application running on localhost">
<match url=".*" />
<action type="Rewrite" url="http://localhost:8511/{R:0}" />
<conditions>
<add input="{HTTP_HOST}" pattern=".*" />
</conditions>
<serverVariables>
</serverVariables>
</rule>
</rules>
</rewrite>
<security>
<authentication>
<windowsAuthentication enabled="true">
<providers>
<clear />
<add value="NTLM" />
</providers>
</windowsAuthentication>
</authentication>
</security>
</system.webServer>
<system.web>
<authorization>
<allow roles="The_group_with_access" />
</authorization>
</system.web>
</configuration>

Im Home vom Streamlit App startenden User .streamlit Verzeichnis erzeugen und darin config.toml (sonst funktioniert WebSocket Weiterleitung nicht):

[server]
enableCORS = false
enableXsrfProtection = false

Streamlit App starten:

<pfad mit venv wo streamlist installiert ist>\streamlit.exe run C:\some_streamlist_app\app.py --server.headless true --server.port 8511 --browser.serverAddress localhost

In Anlehnung an https://gist.github.com/Chibitko/e5366851cf47b6fb238a49d6e0d37b94 zum Decodieren des NTLM Authorization Headers kann man st.context.headers["Authorization"] dieser Funktion übergeben und bekommt User, Domäne und Clientname geliefert:

VALID_CHRS = set(string.ascii_letters + string.digits + string.punctuation)
def clean_str(st):
return ''.join((s if s in VALID_CHRS else '?') for s in st)
class StrStruct(object):
def __init__(self, pos_tup, raw):
length, alloc, offset = pos_tup
self.length = length
self.alloc = alloc
self.offset = offset
self.raw = raw[offset:offset+length]
self.utf16 = False
if len(self.raw) >= 2 and self.raw[1] == 0:
self.string = self.raw.decode('utf-16')
self.utf16 = True
else:
self.string = self.raw
def __str__(self):
return clean_str(self.string)
def decode_ntlmssp(header_raw):
try:
header = base64.b64decode(header_raw[5:])
except:
return "","",""
if header[:8] != b"NTLMSSP\0":
return "","",""
ver_tup = struct.unpack("<i", header[8:12])
ver = ver_tup[0]
if ver != 3:
return "","",""
hdr_tup = struct.unpack("<hhihhihhihhihhi", header[12:52])
domain=StrStruct(hdr_tup[6:9], header).string
user=StrStruct(hdr_tup[9:12], header).string
client=StrStruct(hdr_tup[12:15], header).string
return client.upper(),domain.upper(),user.upper()