Problema de verificação de Disponibilidade (Free/Busy) em ambiente Híbrido

Por: Ramon Rocha, artigo original por Adrian Kruss

Trabalhar com Free/Busy em múltiplos sites pode ser uma tarefa desafiadora. Adicionando Exchange Online (EXO) aumenta ainda mais o nível do desafio. O Hybrid Configuration Wizard (HCW) cuida de configurar alguns aspectos como: relação de confiança entre as organizações, adicionar o domínio no Microsoft Federation Gateway (conhecido como Azure Active Directory ACS). Porém, o HCW não valida se Free/Busy está configurado corretamente (verificação de disponibilidade entre on-premise e nuvem, firewall, etc).

Enquanto um usuário on-premise verificando disponibilidade de um usuário na nuvem funciona na maioria dos casos após finalizar o HCW, o processo inverso (usuário com mailbox na nuvem verificando disponibilidade de um usuário on-premise) pode falhar e são necessários alguns ajustes para o serviço funcionar corretamente. Se o HCW finalizar com sucesso, isso significa que no DNS externo o Autodiscover para o domínio federado resolveu corretamente o CAS server utilizado na configuração híbrida, porém se você está usando um split-brain DNS, o Autodiscover interno, e também o Service Connection Point (SCP), deverá resolver para o CAS server mais próximo na sua rede.

Tenha em mente que o CAS server mais próximo do usuário (no ambiente on-premise) é o responsável pela verificação de disponibilidade solicitada pela organização externa (nesse caso, o Exchange Online). Isso significa que se o CAS Server não está publicado na Internet todas as verificações de Free/Busy feitas pelo Exchange Online irão falhar.

Você pode utilizar o www.exrca.com para fazer um teste de Free/Busy através da aba Office 365. O teste lhe dará informações como: em qual ponto houve a falha no teste e a possível solução para corrigir o problema.

No exemplo a seguir, apenas o teste Free/Busy feito por um usuário que sua mailbox está hospedada no Exchange Online para um usuário que a mailbox está localizada On-premise está falhando (CAS server não está publicados na Internet), porém todos os testes de On-premise para o Exchange Online funcionam. Revisando os logs no Event Viewer do servidor Híbrido, podemos perceber que há a tentativa de consulta de disponibilidade, porém devido à circunstância de o CAS server não está publicado na Internet, podemos ver que essa consulta falha.

Log Name: Application

Source: MSExchange Availability

Date: 18/01/2016 15:43:24

Event ID: 4002

Task Category: Availability Service

Level: Error

Keywords: Classic

User: N/A

Computer: HybridCAS.domain.local

Description:

Process 10004: ProxyWebRequest CrossSite from Cloud.User@Contoso.mail.onmicrosoft.com to https://remote.site1CAS.domain.local:443/ews/exchange.asmx failed. Caller SIDs: WSSecurity. The exception returned is Microsoft.Exchange.InfoWorker.Common.Availability.ProxyWebRequestProcessingException: System.Web.Services.Protocols.SoapHeaderException: An error occurred when verifying security for the message.

at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)

at System.Web.Services.Protocols.SoapHttpClientProtocol.EndInvoke(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.Proxy.Service.EndGetUserAvailability(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.FreeBusyApplication.EndProxyWebRequest(ProxyWebRequest proxyWebRequest, QueryList queryList, Service service, IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.ProxyWebRequest.EndInvoke(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.AsyncWebRequest.EndInvokeWithErrorHandling(). The request information is ProxyWebRequest type = CrossSite, url = https://remote.site1CAS.domain.local:443/ews/exchange.asmx

Mailbox list = <User, Local>SMTP:Local.User@contoso.com, Parameters: windowStart = 02/01/2016 10:00:00, windowEnd = 01/02/2016 10:00:00, MergedFBInterval = 30, RequestedView = Detailed

. ---> System.Web.Services.Protocols.SoapHeaderException: An error occurred when verifying security for the message.

at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)

at System.Web.Services.Protocols.SoapHttpClientProtocol.EndInvoke(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.Proxy.Service.EndGetUserAvailability(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.FreeBusyApplication.EndProxyWebRequest(ProxyWebRequest proxyWebRequest, QueryList queryList, Service service, IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.ProxyWebRequest.EndInvoke(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.AsyncWebRequest.EndInvokeWithErrorHandling()

--- End of inner exception stack trace ---

. Name of the server where exception originated: HybridCAS. Make sure that the Active Directory site/forest that contain the user's mailbox has at least one local Exchange 2010 server running the Availability service. Turn up logging for the Availability service and test basic network connectivity.

Como complemento, utilizando a ferramenta Fiddler nós pudemos encontrar mais informações:

HTTP/1.1 200 OK

Cache-Control: private

Transfer-Encoding: chunked

Content-Type: text/xml; charset=utf-8

Server: Microsoft-IIS/8.0

request-id: 64c7446a-b8e7-4637-80db-e53ccb74e795

Set-Cookie: ClientId=FEOI3CMFK0UBFDNPHVHVVQ; expires=Tue, 17-Jan-2017 17:42:25 GMT; path=/; secure; HttpOnly

X-CalculatedBETarget: GRUPR80MB0972.lamprd80.prod.outlook.com

X-BackEndHttpStatus: 200

Set-Cookie: exchangecookie=48abc0d6cafe418fa2512610def114d8; expires=Wed, 18-Jan-2017 17:42:26 GMT; path=/; HttpOnly

x-EwsHandler: GetUserAvailability

X-AspNet-Version: 4.0.30319

X-DiagInfo: GRUPR80MB0972

X-BEServer: GRUPR80MB0972

Set-Cookie: ClientId=FEOI3CMFK0UBFDNPHVHVVQ; expires=Tue, 17-Jan-2017 17:42:25 GMT; path=/; secure; HttpOnly

X-Powered-By: ASP.NET

X-FEServer: CP1PR80CA0115

Date: Mon, 18 Jan 2016 17:42:27 GMT

193

<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/"><s:Header><h:ServerVersionInfo MajorVersion="15" MinorVersion="1" MajorBuildNumber="365" MinorBuildNumber="24" Version="V2015_10_05" xmlns:h="https://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"/>

135

</s:Header><s:Body><GetUserAvailabilityResponse xmlns="https://schemas.microsoft.com/exchange/services/2006/messages" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"><FreeBusyResponseArray><FreeBusyResponse><ResponseMessage ResponseClass="Error"><MessageText>

72

System.Web.Services.Protocols.SoapHeaderException: An error occurred when verifying security for the message.

10c

at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)

at System.Web.Services.Protocols.SoapHttpClientProtocol.EndInvoke(IAsyncResult asyncResult)

7c

at Microsoft.Exchange.InfoWorker.Common.Availability.Proxy.Service.EndGetUserAvailability(IAsyncResult asyncResult)

c5

at Microsoft.Exchange.InfoWorker.Common.Availability.FreeBusyApplication.EndProxyWebRequest(ProxyWebRequest proxyWebRequest, QueryList queryList, Service service, IAsyncResult asyncResult)

71

at Microsoft.Exchange.InfoWorker.Common.Availability.ProxyWebRequest.EndInvoke(IAsyncResult asyncResult)

105

at Microsoft.Exchange.InfoWorker.Common.Availability.AsyncWebRequest.EndInvokeWithErrorHandling(). The request information is ProxyWebRequest type = CrossSite, url = https://remote.site1CAS.domain.local:443/ews/exchange.asmx

Mailbox list = &lt;User, Local&gt;

189

SMTP:Local.User@Contoso.com, Parameters: windowStart = 02/01/2016 10:00:00, windowEnd = 01/02/2016 10:00:00, MergedFBInterval = 30, RequestedView = Detailed

., inner exception: An error occurred when verifying security for the message.</MessageText><ResponseCode>ErrorProxyRequestProcessingFailed</ResponseCode><DescriptiveLinkKey>0</DescriptiveLinkKey><MessageXml><ExceptionType xmlns="

196

https://schemas.microsoft.com/exchange/services/2006/errors">Microsoft.Exchange.InfoWorker.Common.Availability.ProxyWebRequestProcessingException</ExceptionType><ExceptionCode xmlns="https://schemas.microsoft.com/exchange/services/2006/errors">5016</ExceptionCode><ExceptionServerName xmlns="https://schemas.microsoft.com/exchange/services/2006/errors">
HybridCAS</ExceptionServerName><ResponseSource xmlns="

17b

https://schemas.microsoft.com/exchange/services/2006/errors">https://HybridCAS.Contoso.com/ews/exchange.asmx/WSSecurity</ResponseSource></MessageXml></ResponseMessage><FreeBusyView><FreeBusyViewType xmlns="https://schemas.microsoft.com/exchange/services/2006/types">None</FreeBusyViewType></FreeBusyView></FreeBusyResponse></FreeBusyResponseArray></GetUserAvailabilityResponse>

16

</s:Body></s:Envelope>

0

Através do Event Viewer e Fiddler nós podemos perceber que o problema é relacionado a um erro de segurança. O link https://support.microsoft.com/en-us/kb/2752387 retrata sobre esse problema e mostra que o WSSecurityAuthentication deve estar habilitado, não apenas no servidor CAS híbrido, mas também em todos os CAS servers da organização on-premise, em ambos Autodiscover e WebServices Virtual Directory. Neste caso, nós conseguimos validar que WSSecurityAuthentication está habilitado em todos os CAS servers. Voltando ao Event Viewer, nós revisamos os logs do CAS Server remote.site1CAS. Nesse servidor pudemos encontrar:

Log Name: Application

Source: MSExchange Availability

Date: 19/01/2016 14:23:57

Event ID: 4002

Task Category: Availability Service

Level: Error

Keywords: Classic

User: N/A

Computer: remote.site1CAS.domain.local

Description:

Process 13052: ProxyWebRequest CrossSite from S-1-5-21-154472496-1843795937-1001802626-60749 to https://HybridCAS.domain.local:443/ews/exchange.asmx failed. Caller SIDs: NetworkCredentials. The exception returned is Microsoft.Exchange.InfoWorker.Common.Availability.ProxyWebRequestProcessingException: System.Net.WebException: The request failed with HTTP status 400: Bad Request.

at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)

at System.Web.Services.Protocols.SoapHttpClientProtocol.EndInvoke(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.Proxy.Service.EndGetMailTips(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.MailTips.MailTipsApplication.EndProxyWebRequest(ProxyWebRequest proxyWebRequest, QueryList queryList, Service service, IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.ProxyWebRequest.EndInvoke(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.AsyncWebRequest.EndInvokeWithErrorHandling():. The request information is ProxyWebRequest type = CrossSite, url = https://HybridCAS.domain.localt:443/ews/exchange.asmx

Mailbox list = <Local, User>SMTP:User.Local@Contoso.com, 41205359 SMTP:Local.User2@Contoso.com

. ---> System.Net.WebException: The request failed with HTTP status 400: Bad Request.

at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)

at System.Web.Services.Protocols.SoapHttpClientProtocol.EndInvoke(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.Proxy.Service.EndGetMailTips(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.MailTips.MailTipsApplication.EndProxyWebRequest(ProxyWebRequest proxyWebRequest, QueryList queryList, Service service, IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.ProxyWebRequest.EndInvoke(IAsyncResult asyncResult)

at Microsoft.Exchange.InfoWorker.Common.Availability.AsyncWebRequest.EndInvokeWithErrorHandling()

--- End of inner exception stack trace ---

. Name of the server where exception originated: remote.site1CAS. Make sure that the Active Directory site/forest that contain the user's mailbox has at least one local Exchange 2010 server running the Availability service. Turn up logging for the Availability service and test basic network connectivity.

Verificando o log HTTPERRs podemos encontrar múltiplas entradas:

2016-01-19 14:16:39 10.21.106.6 57409 10.21.96.219 443 HTTP/1.1 POST /ews/exchange.asmx 400 1 BadRequest MSExchangeServicesAppPool

400.1 - Invalid Destination Header

BadRequest A parse error occurred while processing a request.

De acordo com o erro, vimos através do artigo https://support.microsoft.com/en-us/kb/2988444 que diz "Esse problema pode ocorrer se o usuário é membro de vários grupos no Active Directory e essa falha pode ocorrer durante o procedimento de proxy do Exchange Server 2016 ou Exchange 2013 CAS para o Exchange 2010 CAS".

*Nota: se o usuário for membro de vários grupos no AD isso irá aumentar o tamanho to ticket fornecido pelo Kerberos e também do arquivo PAC no qual será encapsulado pelo HTTP header.

Para resolver esse problema, execute a seguinte ação:

  • Reduzir a quantidade de grupos que o usuário é membro.
  • Em todos os Exchange 2010 CAS, aumente o valor dos atributos MaxFieldLenght e MaxRequestBytes. Essa mudança exige que os servidores CAS sejam reiniciados. O valor recomendado para coexistencia no Exchange 2010 é 65536.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters

MaxRequestBytes DWORD 65536 (Decimal)

MaxFieldLength DWORD 65536 (Decimal)

Nota: Se as entradas MaxFieldLenght e MaxRequestBytes não existirem, crie-as manualmente.

Isso deveria resolver esse problema, porém não foi o caso nesse cenário em particular. Nos ajudou a confirmar que é um problema de segurança. Analisando os logs coletados pudemos constatar que enquanto processando o token WSSecurity, poderiam haver problemas se o certificado da federação foi recentemente modificado ou adicionado. Se quaisquer alterações foram feitas na Federação (como adicionar um novo CAS) ou qualquer outra alteração na Federation Trust. Para ter certeza que os CAS servers possuem a versão mais recente com as informações da Federação, nós precisariamos fazer o recycle do MSExchangeServicesAppPool no CAS server configurado para processar requisições de teste de disponibilidade (Free/Busy).

No nosso caso pudemos perceber que o CAS server não foi reiniciado por mais de 30 dias então executamos os cmdlets em todos os CAS servers que apresentava falha para checar Free/Busy do Exchange Online para o Exchange on-premise.

Get-WebServicesVirtualDirectory -Server $env:computername | Set-WebServicesVirtualDirectory -WSSecurityAuthentication $False

Get-AutodiscoverVirtualDirectory -Server $env:computername | Set-AutodiscoverVirtualDirectory -WSSecurityAuthentication $False

Get-WebServicesVirtualDirectory -Server $env:computername | Set-WebServicesVirtualDirectory -WSSecurityAuthentication $True

Get-AutodiscoverVirtualDirectory -Server $env:computername | Set-AutodiscoverVirtualDirectory -WSSecurityAuthentication $True

cd C:\Windows\System32\inetsrv\; ./appcmd recycle apppool /apppool.name:MSExchangeSyncApppool

Em adição nós podemos executar o seguinte cmdlet para alterar o WSSecurity Virtual Directory e reciclar todos os MSExchangeServicesAppPool de todos os servidores na organização.

Get-ExchangeServer |where{$_.ServerRole -like "*ClientAccess*"}| Set-AutodiscoverVirtualDirectory -WSSecurityAuthentication $False

Get-ExchangeServer |where{$_.ServerRole -like "*ClientAccess*"}| Set-AutodiscoverVirtualDirectory -WSSecurityAuthentication $True

Get-ExchangeServer |where{$_.ServerRole -like "*ClientAccess*"}| Set-WebServicesVirtualDirectory -WSSecurityAuthentication $False

Get-ExchangeServer |where{$_.ServerRole -like "*ClientAccess*"}| Set-WebServicesVirtualDirectory -WSSecurityAuthentication $True

Get-ExchangeServer | %{invoke-command -ComputerName $_.Name -ScriptBlock {cd C:\Windows\System32\inetsrv\; ./appcmd recycle apppool /apppool.name:MSExchangeServicesAppPool }}

Depois que o MsExchangeServicesAppPool finalizou o recycle, nós conseguimos receber as informações de Free/Busy corretamente.