NetScaler Gateway: Login with sAMAccountName in Multitenancy Deployment

Ich hatte diese Woche einen Kunden der sein bestehendes NetScaler Gateway Deployment mit einer zusätzlichen Active Directory Domain ausgestattet haben wollte. Bisher lief die Authentifizierung via LDAP (Primary) und RADIUS (Secondary) via SMS Passcode.

Um die Policies zu entschlacken und etwas Komplexität aus dem Setup zu nehmen haben wir das ganze auf RADIUS only (via SMS Passcode) umgestellt. Dadurch muss beim späteren hinzufügen einer weiteren Domain keine weitere LDAP Policy hinzugefügt werden. Der RADIUS bzw. SMS Passcode ist an mehreren Domänen angebunden und übernimmt zudem noch die Password Validation.

Problematisch war jedoch das der Login mit sAMAccountName weiterhin möglich sein sollte. SMS Passcode (bzw. der Windows NPS) akzeptiert zwar den Login sowohl via sAMAccountName als auch via UserPrincipalName, auf dem NetScaler kann dann aber nicht mehr zugeordnet werden in welcher Domäne der Benutzer zugeordnet wurde. In der RADIUS Antwort des NPS ist die Domain des Users nicht enthalten. Auch gibt es sonst keine Möglichkeit ohne ein zusätzlich Group Extraction via LDAP an die Domain des Users zu kommen.

Bei einem NetScaler Gateway SSO zu Citrix Storefront wird die Domäne jedoch immer benötigt. Andernfalls bedeutet funktioniert zwar der Login am NetScaler selbst, der „NetScaler Gateway SSO“ an Storefront schlägt aber fehlt und es erscheint ein weiteres Login Prompt (bei dem im Regelfall aber kein manueller Login möglich ist).

Eine einfache Lösung sind an dieser Stelle Traffic Policies, mit denen man auch die Möglichkeit hat einen Rewrite des Benutzernamens auszuführen. Diese werden bei jedem Request (und damit nach der Authentifizierung) evaluiert. Via Conditions können diese sogar auf bestimmte URLs/Pfade (z.B. /Citrix/Store) beschränkt werden.

Die Standard SSO Domain in der Session Policy bleibt leer. Stattdessen legt man für jede Kundendomain eine eigene Traffic Policy an.

# SSO Traffic Policies
add vpn trafficAction prof_traffic_tentant1_sso_domain http -SSO ON -userExpression "HTTP.REQ.USER.LOGIN_NAME + \"@tentant1.local\""
add vpn trafficAction prof_traffic_tentant2_sso_domain http -SSO ON -userExpression "HTTP.REQ.USER.LOGIN_NAME + \"@tentant2.local\""
add vpn trafficPolicy pol_traffic_tentant1_sso_domain ns_true prof_traffic_tentant1_sso_domain
add vpn trafficPolicy pol_traffic_tentant2_sso_domain ns_true prof_traffic_tentant2_sso_domain

Diese Traffic Policies können nun an Gruppen aus dem Active Directory gebunden werden. Wichtig ist lediglich das man eine Gruppe verwendet die eindeutig ist und in dieser Schreibweise nur in einer Domain vorhanden ist.

# Bind Traffic Policies to AAA Groups
bind aaa group Tentant1_Users -policy pol_traffic_tentant1_sso_domain -priority 100
bind aaa group Tentant2_Users -policy pol_traffic_tentant2_sso_domain -priority 200

Optional bietet es sich noch an den Rewrite des Benutzernamens nur dann durchzuführen, wenn nicht bereits ein „\“ (TENTANT\user1) oder ein „@“ (user1@tentant.local) enthalten ist.

Dieses Problem besteht lediglich in Szenarien in denen ausschließlich via RADIUS authentifiziert wird. Bei LDAP gibt es die Möglichkeit das SSO Feld unabhängig vom Login Feld anzupassen (z.B. dann eben auf den UserPrincipalName).

Securing openSSH (ssh-audit)

Dank einem Kollegen habe ich das nette Python Script „ssh-audit“ zum Auditing des SSH Daemons auf GitHub gefunden. Das Tool prüft Host Keys und Ciphers einer SSH Instanz und gibt direkt sinnvolle Empfehlungen zur Absicherung aus.

Die Absicherung von openSSH ist tatsächlich etwas, mit dem man sich im Normalfall recht wenig beschäftigt. Dennoch macht es massiv Sinn die paar Minuten Zeit zu investieren.

Ich hatte bereits einige Ciphers in meiner SSH Config hinterlegt, dennoch war etwas Raum für Verbesserung. Mit meiner ursprünglichen Konfiguration hatte ich in Sachen versuchter SSH Angriffsversuche bereits merklich Besserung gegenüber der CentOS Default Konfiguration gespürt (in Verbindung mit fail2ban). Statt fast 1000 Angriffen und Blocks pro Tag hatte ich mit meiner Cipher Konfiguration nur noch rund 100 Einträge im fail2ban Log. Mit der neuen Konfiguration (und dem deaktivieren von DSA und ECDSA) konnte ich die Anzahl nochmal auf ca. 10 pro Tag minimieren.

Das liegt insbesondere daran, das meist ältere (nicht mit Sicherheitsupdates versorgte) Systeme gehackt und für derartige SSH Scans und Angriffe verwendet werden. Die dort installierten SSH Clients unterstützen jedoch in der Regel nicht die aktuellsten SSH Sicherheitsstandards und kommen somit gar nicht zum Verbindungsaufbau.

Eine mögliche Konfiguration für ein CentOS 7 System sieht folgendermaßen aus.

# disable DSA and ECDSA host keys
#HostKey /etc/ssh/ssh_host_dsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

# force strong ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
KexAlgorithms curve25519-sha256@libssh.org
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

Das ganze setzt einen aktuellen SSH Client voraus. Ältere Putty Installationen müssen u.U. geupdated werden.

Prepare Windows (WinRM) for Ansible

Bekanntlich lassen sich mit Ansible auch Windows Maschinen via WinRM managen.

Bei einer Standardinstallation von Windows ist WinRM deaktiviert. Das ganze lässt sich zwar relativ einfach via „winrm quickconfig“ aktivieren, jedoch wird mit diesem Kommando lediglich der Plaintext Port geöffnet. Dies ist gerade für Produktionsumgebungen nicht empfehlenswert.

Als mögliche Alternative können die benötigen Zertifikate entweder via Windows CA enrolled werden (sofern vorhanden), oder man nutzt einfach diesen nützlichen PowerShell Oneliner, der WinRM automatisch mit Self Signed Zertifikaten konfiguriert.

Invoke-Expression ((New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1'))

Das Script öffnet auch direkt den nötigen SSL Listener Port in der Windows Firewall, der bei der Verwendung von SSL manuell freigeschaltet werden muss.

Gefunden in der Doku von pywinrm.

NetScaler Gateway sends wrong Host-Header to StoreFront

In gewissen Konstellationen kommt es bei NetScaler Gateway Deployments trotz korrekter Konfiguration dazu das Requests an Storefront mit dem externen DNS-Namen des NetScaler Gateways an Storefront weitergeleitet werden.

Dabei scheint es sich um einen Bug im Build 11.1-51.21 zu handeln. Der Bug kommt jedoch nur dann zum Vorschein wenn im UserAgent „CitrixReceiver“ vorkommt. Dabei ist es irrelevant wie die Session Policies konfiguriert sind, daher vermute ich eine defekte Clienterkennung innerhalb des NetScalers.

Das ganze lässt sich nachvollziehen indem man z.B. mein NetScaler Nagios Plugin um einen UserAgent erweitert. Wie genau dieser lautet ist egal, solange irgendwo im String „CitrixReceiver“ vorkommt.

$lwp->agent("CitrixReceiver/MyTestClient");

Das Resultat in einem Test Setup mit einer Sophos UTM als Loadbalancing sieht folgendermaßen aus.

root@web01:/opt/check_netscaler_gateway 
-bash# ./check_netscaler_gateway.pl -H gateway.linux-dev.ext -u username -p password -v
** POST https://gateway.linux-dev.ext/cgi/login ==> 302 Object Moved
** POST https://gateway.linux-dev.ext/cgi/setclient?wica ==> 200 OK
** POST https://gateway.linux-dev.ext/Citrix/StoreWeb/Home/Configuration ==> 403 Forbidden
NetScaler Gateway CRITICAL - request to https://gateway.linux-dev.extCitrix/StoreWeb/Home/Configuration failed with HTTP 403

Der „fehlerhafte“ Request wird von der Sophos UTM als möglicher Angriffsversuch erkannt und geblockt. Das ganze könnte jedoch auch bei anderen Loadbalancern oder einem Setup mit aktiviertem SNI auftreten.

Als Workaround kann eine Rewrite Policy genutzt werden, die den Host-Header mit dem internen DNS-Namen ersetzt (z.B. storefront.linux-dev.local).

add rewrite action act_rewrite_hostname replace HTTP.REQ.HOSTNAME "storefront.linux-dev.local"
add rewrite policy pol_rewrite_hostname true act_rewrite_hostname
bind vpn vserver vs_vpn_citrix -policy pol_rewrite_hostname -priority 100 -gotoPriorityExpression END -type REQUEST

Diese Policy ersetzt den Header bei jedem HTTP-Request in das Backend mit dem korrekten Namen. Diese Policy funktioniert so nur in „ICA Only“ Deployments. Für Setups mit Smartaccess (Clientless VPN) sind weitere Anpassungen notwendig.

SMS Passcode: Login mit sAMAccountName unterbinden

SMS Passcode ist eine Software für die 2-Faktor-Authentifizierung. Sie generiert OTPs die dem User auf verschiedenen Wegen zugestellt werden können, z.B. via Software Token App auf dem Handy, E-Mail, Telefonanruf, API, oder eben SMS. SMS Passcode setzt dabei auf den Network Policy Server (den RADIUS Dienst von Microsoft) auf. SMS Passcode lässt sich durch die RADIUS Architektur sehr einfach an jeden RADIUS kompatiblen Dienst anbinden, u.a. an den Citrix NetScaler.

SMS Passcode kann Passwörter bzw. User Accounts mit „Windows NT“ über das Active Directory (via Computer Account in der Domäne) oder LDAP (via LDAP Bind) auflösen. Auch im LDAP Mode ist aber trotz richtiger Konfiguration auch immer der Login via sAMAccountName möglich, selbst wenn der sAMAccountName explizit nicht als Benutzername angegeben oder importiert wird.

Problematisch ist dies in Multi-Domain-Setups, in denen ausschließlich mit einem Login mit dem userPrincipalName gerechnet wird, etwa wenn Citrix StoreFront ohne Default Domains betrieben wird. Hier funktioniert nur der Login mit dem Domänenprefix (DOMAIN\username) bzw. dem userPrincipalName (user@domain.local).

Falls User aus der Gewohnheit heraus den Login mittels sAMAccountName versuchen klappt dieser zwar, das NetScaler Gateway kennt jedoch die für das SSO nötige Domain des Users nicht. Die Folge ist eine „Permission Denied“ Fehlermeldung von Storefront nach erfolgreicher Authentifizierung.

Es gibt jedoch eine einfache Möglichkeit Loginversuche via SAM auf der Windows NPS Ebene zu unterbinden, um Benutzer nicht in dieses Problem laufen zu lassen.

SMS Passcode - NPS Policy

In der Connection Request Policy im NPS kann eine Condition für den Usernamen konfiguriert werden. Hier sind auch Regular Expressions supported.

# Regular Expression für den userPrincipalName
^.*\@.*$

Mit dieser Condition wird geprüft ob der Username mindestens ein @-Zeichen enthält. Falls nicht, greift die Policy nicht. Gibt es keine andere machende Policy, schlägt der Login komplett fehl.