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()