AzureAD CloudSync Proxy

Wie kann ich mit AzureAD Connect CloudSync eine Verbindung des Agenten zum Server über einen HTTP-Proxy-Server einrichten?

Anforderungen

Große Firmen mit maximalem Funktionsumfang, insbesondere Device Management und Exchange Hybrid müssen auch im März 2023 den ADSync Connect-Service installieren während AzureAD Connect Cloud Sync für einfachere und oftmals kleinere Umgebungen gedacht ist. Oft soll der Agent sogar auf einem Domain Controller installiert werden, was supportet ist. Dennoch ist es auch hier nicht selbstverständlich, das der Prozess direkt die Gegenstellen bei Microsoft erreichen kann, sondern über einen Proxy-Server gehen muss.

Ein HTTP-Proxy kann den Client authentifizieren und sehr gut einen Missbrauch der Ports für andere Protokolle verhindern, da er "http" versteht und den angesprochenen Hostnamen mit einer Allow-Liste vergleichen kann.

Quellenstudium

Für den klassischen ADSync Connect gibt es einige Quelle, die eine Konfiguration über einen Proxy-Server beschreiben.

Für Azure AD Connect CloudSync ist die Quellenlage aber viel dünner. Eigentlich gibt es nur einen Hinweis auf einen Proxy server auf den Prerequisites

Hier gibt es die Überschrift "Firewall and Proxy requirements" und im Text steht auch noch einmal:

If your firewall or proxy allows you to specify safe suffixes, add connections:..."

Gefolgt von einer Liste von URLs, die AzureAD Connect CloudSync braucht.

Während es für den "ConnectHealthAgent" eine Powershell zum Setzen eines Proxy gibt, hat die AADCloudSync-Shell keine Einstellungen zu eine Proxy zu bieten:

Import-module -Name "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Utility\AADCloudSyncTools 
Get-Command -Module aadcloudsynctools

CommandType     Name
-----------     ----
Function        Connect-AADCloudSyncTools
Function        Export-AADCloudSyncToolsLogs
Function        Get-AADCloudSyncToolsInfo
Function        Get-AADCloudSyncToolsJob
Function        Get-AADCloudSyncToolsJobSchedule
Function        Get-AADCloudSyncToolsJobSchema
Function        Get-AADCloudSyncToolsJobScope
Function        Get-AADCloudSyncToolsJobSettings
Function        Get-AADCloudSyncToolsJobStatus
Function        Get-AADCloudSyncToolsServiceAccount
Function        Get-AADCloudSyncToolsServicePrincipal
Function        Install-AADCloudSyncToolsPrerequisites
Function        Invoke-AADCloudSyncToolsGraphQuery
Function        Remove-AADCloudSyncToolsGroupMembers
Function        Repair-AADCloudSyncToolsAccount
Function        Restart-AADCloudSyncToolsJob
Function        Resume-AADCloudSyncToolsJob
Function        Set-AADCloudSyncToolsJobSchema
Function        Set-AADCloudSyncToolsTenantId
Function        Start-AADCloudSyncToolsVerboseLogs
Function        Stop-AADCloudSyncToolsVerboseLogs
Function        Suspend-AADCloudSyncToolsJob

Also läuft es wohl wieder auf eigene Versuche hinaus, ob die Einstellungen zu einem HTTP-Proxy in der machine.config oder der "AADConnectProvisioningAgent.exe.config" im Programmverzeichnis ausreichen.

Vorarbeiten

Solche Änderungen sollten Sie besser nicht in einer Produktionsumgebung testen. Ich habe daher einfach einen Windows 2016 DC mit einem Developer-Tenant genutzt, um AzureAD CloudSync einzurichten, indem ich folgende Schritte durchlaufen habe:

  • "Fiddler" als Proxy auf "localhost:8888" mit SSL-Inspection installiert
  • Ausgehende Verbindungen von "Fiddler" in der Windows Firewall erlaubt
    Dazu habe ich den Prozess "fiddler.exe" zugelassen, ehe ich dann danach die Verbote setzt
  • Ausgehende Verbindungen zu Fiddler erlaubt
    Zudem habe ich Verbindungen auf den Port 8888 erlaubt, damit der Proxy erreichbar ist. Auf eine Limitierung auf 127.0.0.1 habe ich verzichtet
  • Blockiere Ausgehende Verbindungen auf Port 80/443/8080
    Dazu habe ich in der Windows Advanced Firewall einfach eine "Block"-Regel angelegt, die alle Verbindungen zu 80,443,8080 unterbindet

  • Proxy-Konfiguration des Servers entsprechende angepasst
    Dazu habe ich in einer Admin-Powershell folgende Befehle eingegeben:
# WinHTTP-Einstellungen für den Proxy angpasst
netsh winhttp set proxy proxy-server="localhost:8888" bypass-list="<-loopback>;<local>"

# PowerShell Default Proxy angepasst
[system.net.webrequest]::defaultwebproxy = New-Object system.net.webproxy('http://localhost:8888')
[system.net.webrequest]::defaultwebproxy.credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
[system.net.webrequest]::defaultwebproxy.BypassProxyOnLocal = $true

Zuletzt habe ich noch die Datei "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\Machine-config" um die folgenden Einträge erweitert:

Wenn es schon einen "<system.net>"-Bereich gibt, dann müssen Sie dies zusammenführen. Die Einträge sollten ans Ende gesetzt werden, damit sie nach der vorhandene generelle Definition sind.

<configuration>
  <system.net>
    <defaultProxy>
      <proxy proxyaddress="http://localhost:8888" bypassonlocal="True" usesystemdefault="True"/>
    </defaultProxy>
  </system.net>
</configuration>

Um die Funktion generell zu testen, habe ich per Powershell einfach eine Office 365 URL abgerufen.

# test um den Request in Fiddler zu sehen
(Invoke-WebRequest -Uri login.microsoftonline.com).StatusDescription

So konnte ich zumindest für eine PowerShell sehen, dass der Proxy über Fiddler genutzt wurde.

Installation und Konfiguration

Danach habe ich die Installation von "AADConnectProvisioningAgentSetup.exe" gestartet und die normale Installation durchgeführt. Ich bin kurz an der RC4-Abschaltung (Siehe Kerberos RC4 Abschaltung) mit dem GroupManagedServiceAccount hängen geblieben aber danach war die Installation abgeschlossen. Während der Installation habe ich in Fiddler gut sehen können, dass die Authentifizierung am AzureAD als Administrator als auch das Provisoining im AzureAD-Tenant erfolgreich gelaufen sind. In dem Auszug können Sie aber erkennen, dass eine Authentifizierung erfolgt und auch per Graph die Roles aus dem Tenant abgerufen werden und ein "On-PremisesPublishinProfiles" erfolgt.

Allerdings war der Status des Agenten im AzureAD-Portal unter https://portal.azure.com/#view/Microsoft_AAD_Connect_Provisioning/CloudSyncMenuBlade/~/Agents immer noch auf "Provisioning quarantined "

Die Details zu dem Fehler sind eindeutig:

Error code HybridIdentityServiceNoActiveAgents Error message We are unable to find an active agent for the domain you are trying to sync. Please check to see if the agent is running by going to the server where the agent is installed and check to see if Azure AD Connect Provisioning Agent under Services is running.

Mein Agent spricht noch nicht mit dem AzureAD Provisioning Service, obwohl die Einrichtung erfolgreich war.

Fehlersuche

Wir sehen aber jede Menge "/nnectorBootstrap"-Einträge mit 0 Bytes. Der Request ist überschaubar:

Wer aber auf das Feld "Content-Length" achtet, weiß um eine Payload des Posts, in der sehr ausführliche Daten über den Agenten an Microsoft gesendet werden. Neben der Agent-Version und ConnectorID werden auch Daten zum Windows ServerOS, .Net Version, und sogar CPU-Performancedaten etc. gesendet. Allerdings findet sich in der Payload auch keine Authentifizierung. Passend dazu finde ich im Trace-File auf C:\ProgramData\Microsoft\Azure AD Connect Provisioning Agent\Trace\AzureADConnectProvisioningAgent_*.log" auch die Fehlermeldungen:

AADConnectProvisioningAgent.exe Error: 0 : Service bootstrap request failed with exception: 
  'System.ServiceModel.Security.MessageSecurityException: The HTTP request was forbidden with 
   client authentication scheme 'Anonymous'. ---> 
   System.Net.WebException: The remote server returned an error: (403) Forbidden.
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)
   --- End of inner exception stack trace ---
   at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass7_0`1.<CreateGenericTask>b__0(IAsyncResult asyncResult)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.ApplicationProxy.Connector.Bootstrap.BootstrapManager.<SendBootstrapRequestAsync>d__39.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.ApplicationProxy.Connector.Bootstrap.BootstrapManager.<SendBootstrapRequestAsync>d__30.MoveNext()' due to security issues of the certificate.
    ThreadId=22
      DateTime=2023-03-08T20:33:48.6069286Z

Azure AD AppProxy und Proxy

Der AzureAD CloudSync nutzt im Unterbau die gleiche Technik wie der Azure AD Application Proxy, wie sich schon aus den URLs und dem Programmverzeichnis ersehen lässt:

Zur Frage der Proxy-Nutzung mit einem AzureAD App Proxy gibt es deutlich mehr Hinweise auf die Konfiguration eines HTTP-Proxy.

Connectors have underlying OS components that make outbound requests. These components automatically attempt to locate a proxy server on the network using Web Proxy Auto-Discovery (WPAD). The OS components attempt to locate a proxy server by carrying out a DNS lookup for wpad.domainsuffix. If the lookup resolves in DNS, an HTTP request is then made to the IP address for wpad.dat. This request becomes the proxy configuration script in your environment. The connector uses this script to select an outbound proxy server. However, connector traffic might still not go through, because of additional configuration settings needed on the proxy.
https://learn.microsoft.com/en-us/azure/active-directory/app-proxy/application-proxy-configure-connectors-with-proxy-servers#bypass-outbound-proxies

Interessant auch die Aussage, dass eine Authentifizierung am Proxy nicht unterstützt wird. Das dürfte dann auch für AzureAD Cloud Sync gelten:

Application Proxy does not support authentication to other proxies. The connector/updater network service accounts should be able to connect to the proxy without being challenged for authentication.
Quelle https://learn.microsoft.com/en-us/azure/active-directory/app-proxy/application-proxy-configure-connectors-with-proxy-servers#use-the-outbound-proxy-server

Die Proxy-Konfiguration scheint er entweder aus der globalen machine.config zu lesen oder pro Programm. Das verifiziere ich später noch einmal.

The Connector service evaluates the defaultProxy configuration for usage in %SystemRoot%\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config, if the defaultProxy is not configured (by default) in ApplicationProxyConnectorService.exe.config. The same applies to the Connector Updater service (ApplicationProxyConnectorUpdaterService.exe.config) too.
Quelle :https://learn.microsoft.com/en-us/azure/active-directory/app-proxy/application-proxy-configure-connectors-with-proxy-servers#step-1-configure-the-connector-and-related-services-to-go-through-the-outbound-proxy

Und von den Analysen zu Azure AD ation Proxy weiß ich, dass der ApplicationProxy sich mit einem Client-Zertifikat authentifiziert. Im Zertifikatspeicher des Computers habe ich dann auch ein passendes Zertifikat gefunden:

LeidLeider ist das Zertifikat so nicht exportierbar, so dass ich mit Fiddler diese nicht auch noch untermogeln konnte.

Nachdem ich aber die SSL-Decryption in Fiddler abgeschaltet habe, ist Agent auch im AzureAD von "inactive" auf "online" gegangen.


Quelle: https://portal.azure.com/#view/Microsoft_AAD_Connect_Provisioning/CloudSyncMenuBlade/~/Agents

Über den Ressource Monitor konnte ich auch gut sehen, dass der Prozess wirklich nur die Verbindung zum Proxy aufgebaut hat:

Allerdings sehen Sie im Process Monitor auch, dass Azure Ad Cloud Sync selbst kein Webserver mit einem "Listening Port" ist, auf sie sich der AzureAD AppProxy verbinden kann. Diese Kommunikation wird wohl nur intern abgewickelt.

Proxy Rückbau

Um zu wissen, welche Einstellung für die Nutzung des Proxy-Servers relevant ist, habe ich die vorher gemachten Einstellungen wieder rückgängig gemacht. Meine Hoffnung war, dass ein Eintrag in der Datei "AADConnectProvisioningAgent.exe.config" ausreichen würde.

netsh winhttp reset proxy
[system.net.webrequest]::defaultwebproxy = $null

und dann noch die folgenden Zeilen aus der Datei "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\Machine-config" wieder entfernt und stattdessen in "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\AADConnectProvisioningAgent.exe.config" addiert.

Wenn es schon einen "<system.net>"-Bereich gibt, dann müssen Sie dies zusammenführen. Die Einträge sollten ans Ende gesetzt werden, damit sie nach der vorhandene generelle Definition sind.

<configuration>
  <system.net>
    <defaultProxy>
      <proxy proxyaddress="http://localhost:8888" bypassonlocal="True" usesystemdefault="True"/>
    </defaultProxy>
  </system.net>
</configuration>

Auch diesmal konnte ich im Process Monitor sehen, dass der Dienst den Proxy genutzt hat.

Allein für die Funktion des AADConnectProvisioningAgent.exe über einen Proxy reicht es, den Proxy in der Datei "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\AADConnectProvisioningAgent.exe.config" zu addieren.

Das bedeutet nicht, dass Sie die anderen Proxy Einträge vielleicht zur Installation nicht doch brauchen. Der Assistent muss sich ja an der Cloud über den Browser authentifizieren. Eine grundlegende Kommunikation des Betriebssystems über den Proxy zum Internet muss schon gewährleistet sein.

Als Beifang kann ich zudem sagen, dass der Prozess neben der Kommunikation über den Proxy-Server nur noch Verbindungen per LDAP (389/TCP) und RPC 135/TCP zum Domaincontroller aufgebaut hat.

Zusammenfassung

Ich habe bei Microsoft nur Anleitungen für den klassischen AzureAD Connect Sync zur Einrichtung eines Proxy-Servers gefunden. Für den AzureAD Cloud Sync ist die Quellenlage dürftig bis nicht vorhanden. Nur wenige Spuren lassen auf eine Proxy-Unterstützung hoffen. Eine klare Aussage dazu gibt es nicht auch wenn es durch die Nutzung des AzureAD AppProxy als Unterbau zu erwarten ist.

Die Konfiguration des Proxy muss .NET-typisch in der Datei "AADConnectProvisioningAgent.exe.config" erfolgen und der Proxy darf eine Authentifizierung des Service gegen die Cloud mittels Client-Zertifikat (MTLS) nicht verhindern.

Weitere Links