Problemas de disponibilidad entre sitios (Exchange)
Por Adrian Kruss
Trabajar con problemas de disponibilidad a través de múltiples sitios es una tarea complicada. Adicionar Exchange Online (EXO) a esta mezcla aumenta el desafío. Mientras que el asistente de configuración hibrida (HCW) se encarga d la parte de configuración hibrida, como la creación de las relaciones organizacionales, el establecimiento de la relación de confianza y la adición del dominio al Microsoft Federation Gateway (también conocido como el Azure Active Directory ACS), pero este no prueba si la disponibilidad trabaja entre el ambiente local y Exchange Online trabaja ni se encarga de configurar los parámetros locales del firewall.
Generalmente al completar el asistente de configuración hibrida la disponibilidad desde el ambiente local hacia EXO trabaja sin cambios, la disponibilidad desde EXO al ambiente local puede fallar y ciertos ajustes son necesarios. Si el HCW termino sin errores significa que el registro externo de Autodiscover resuelve el servidor de Exchange CAS híbrido, sin embargo, si se está utilizando un DNS dividido (Split DNS) el registro interno de Autodiscover y el punto de conexión se servicio (service connection point SCP) tiene que resolver al servidor CAS más cercano al usuario.
Tenga en cuenta que el servidor Exchange con el role CAS más cercano al usuario es el responsable de la solicitud de disponibilidad para la organización externa (en este caso Exchange Online), por lo que, si este no tiene permiso para salir a la internet, todos los pedidos de disponibilidad entre este sitio local y EXO van a fallar para todos los usuarios hospedados en este sitio.
Usando la herramienta Aka.ms/EXRCA (Remote Connectivity Analyzer) y hacer una prueba de disponibilidad en la pestaña de Office 365 ofrecerá información sobre el error y dará una posibilidad de cómo arreglarlo; Pero en ciertos casos esta prueba terminará sin problemas y la disponibilidad desde EXO hacia el ambiente local fallará. Para este ejemplo solo la disponibilidad desde EXO hacia el ambiente local fallaba (solo para los servidores CAS que no están publicados a la internet), pero los pedidos de disponibilidad desde el ambiente local hacia EXO trabajaban sin problema. Al revisar los registros en el visor de eventos en el servidor hibrido podíamos ver que al solicitar la disponibilidad de un usuario en otro sitio local (el CAS no está publicado en el Internet) el siguiente error:
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.
Utilizando Fiddler podíamos ver:
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 = <User, Local>
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
Revisando ambos registros podemos ver que muestra un problema de seguridad. Este enlace https://support.microsoft.com/es-es/kb/2752387 muestra el mismo error recibido y la solución es asegurarse que el WSSecurityAuthentication está habilitada no solo en el CAS hibrido, pero en todos los servidores CAS en la organización (los que están publicados y los que no estén a la internet) en los directorios virtuales de Autodiscover y WebService. En este caso en particular verificamos que la seguridad WSSecurityAuthentication estaba habilitada en todos los servidores Exchange CAS. Volviendo al servidor CAS en el sitio remoto que fallaba estábamos viendo:
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.
También el mirar los registros de HTTPERR veíamos varias entradas con:
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.
Siguiendo este error encontramos https://support.microsoft.com/es-es/kb/2988444 lo cual explica “Este problema puede producirse si el usuario es miembro de muchos grupos de Active Directory y este error puede producirse durante el proceso de proxy del Exchange Server 2016 o Exchange Server 2013 CAS al CAS de Exchange Server 2010.”
*Nota: Tener usuarios el cual es miembro de muchos grupos en AD aumentara el tamaño de Kerberos y el tamaño del campo PAC lo cual se encapsula en la cabecera HTTP. Si el encabezado HTTP no es lo suficientemente grande para contener todos los campos del PAC la solicitud de autenticación fallará ya que se considerará incompleta.
Así entonces, como lo indica el artículo, “Para resolver este problema, siga uno de estos procedimientos:
· Reducir los grupos de Active Directory asignados al usuario.
· En todas las CA de Exchange 2010, aumente las entradas MaxFieldLength y MaxRequestBytes en los siguientes valores. Este cambio requiere el reinicio de los servidores de acceso de cliente. El valor recomendado para la coexistencia de Exchange 2010 es 65536.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters
DWORD MaxRequestBytes 65536 (Decimal)
DWORD MaxFieldLength 65536 (Decimal)
Nota: si las entradas no existen para MaxFieldLength y MaxRequestBytes crear manualmente las claves.”
Mientras que estos pasos deberían resolver el problema no lo hizo en este caso en particular, nos ayudó para confirmar que el problema es un problema de seguridad. Investigando sobre los errores recibidos pudimos ver que al procesar el token de WSSecurity podría haber inconvenientes si el Certificado de la Federación fue recientemente modificado o añadido, si se realiza algún cambio en la federación (tal como agregar un servidor CAS nuevo) o si se realizó algún cambio en la relación de confianza. Para asegurarnos que todos los servidores CAS tengan la información más actualizada de la federación es necesario reciclar el MSExchangeServicesAppPool en el/los servidores CAS encargados de procesar la disponibilidad. Como en este caso nos dimos cuenta que los servidores CAS no habían sido reiniciados por más de 30 días corrimos los pasos siguientes en los servidores CAS que no podían procesar la disponibilidad cuando los buzones de EXO la pedían:
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: MSExchangeServicesAppPool
También podemos ejecutar los siguientes comandos para cambiar el estado del WSSecurity para el directorio virtual de Autodiscover y WebService para todos los servidores CAS de la organización:
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 }}
Después de que el MSExchangeServicesAppPool reinicio la disponibilidad desde EXO hacia el ámbito local comenzó a trabajar.