Pages

Sunday, April 26, 2020

Securing Microsoft ADFS on Windows Server 2019 with Duo Authentication MFA

As a continuation of the AD FS deployment from two of my previous posts:

Deploying a redundant Active Directory Federation Services (ADFS) Web Application Proxy servers on Windows Server 2019
http://terenceluk.blogspot.com/2020/04/deploying-redundant-active-directory_21.html

Deploying a redundant Active Directory Federation Services (ADFS) farm on Windows Server 2019
http://terenceluk.blogspot.com/2020/04/deploying-redundant-active-directory.html

This post serves to demonstrate the deployment of Duo to provide two-factor authentication for ADFS services using browser-based federated logins.

Deployment instructions as demonstrated in this post can be found directly from Duo here: https://duo.com/docs/adfs

Prerequisites

Download the DUO AD FS installer package for Windows 2012 R2 and later here: https://dl.duosecurity.com/duo-adfs3-latest.msi

Copy the file to your internal ADFS in your farm.

image

View the checksums for the Duo downloads here: https://duo.com/docs/checksums#duo-ad-fs

Retrieve Duo Configuration Parameters

Begin by logging onto the Duo Admin Panel (https://admin.duosecurity.com/) with an administrator account:

image

 

Navigate to Applications and click on Protect an Application

image

Type ADFS into the search field to locate Microsoft ADFS in the applications list then click on the Protect button to the right:

image

The following details will be displayed:

  • Integration key
  • Secret key
  • API hostname

Copy the 3 text strings down into notepad as you will need them for the deployment later:

image

Install DUO MFA Adapter onto ADFS Servers

Log onto your internal ADFS server hosting the primary WIN database and run the duo-adfs3-1.2.0.17.msi MSI installer:

image

image

Enter the previously documented strings for:

  • Integration key
  • Secret key
  • API hostname

Then decide whether you want to enable or disable the following 2 configuration parameters:

  • Bypass Duo authentication when offline
  • Use UPN username format
image

If you only have one ADFS server in your farm then select either of the option would not matter. However, if you have more than one ADFS server or plan to deploy an additional one in the near future then select Enter shared session key option and generate a unique key with the following PowerShell cmdlets:

$bytes = new-object "System.Byte[]" 30

(new-object System.Security.Cryptography.RNGCryptoServiceProvider).GetBytes($bytes)

[Convert]::ToBase64String($bytes)

image

image

Place the unique key into notepad so you can use it for the deployment of the next ADFS server and then paste it into the Enter shared session key field:

image

Proceed with the install:

imageimage

The following prompt will be displayed upon completing the install:

imageimage

Repeat the steps above for the additional ADFS servers in the farm.

Configuring AD FS to use DUO for MFA

Launch the AD FS Management console:

image

Navigate to AD FS > Service > Authentication Methods and click on the Edit link for Additional Authentication Methods:

image

In the Edit Authentication Methods window, select Duo Authentication for AD FS 1.2.0.17 and click OK:

image

The ADFS farm is now ready to leverage the Duo Authentication for two-factor authentication.

Depending on the requirements in your environment, the default Access Control Policies may be sufficient but if it isn’t, you can configure additional ones by navigating to ADFS > Access Control Policies:

Note how the default Permit everyone and require MFA policy is not currently in use by any applications in this environment.

image

imageimage

This environment also does not have any Relying Party Trusts (applications using AD FS for claims based authentication) configured:

image

Configuring Content Security Policy (CSP) on AD FS 2019

As of Windows Server 2019, the Content Security Policy security feature was introduced to secure ADFS and therefore the inline DUO prompt will not load properly without adding the Duo API hostname with the format api-xxxxxxxx.duosecurity.com into the Content Security Policy security configuration:

image

Option #1 – If Content Security Policy (CSP) has not yet been set on AD FS 2019, run the following command to set CSP allowing the Duo Prompt:

Set-AdfsResponseHeaders -SetHeaderName "Content-Security-Policy" -SetHeaderValue "default-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self'; frame-src api-xxxxxx.duosecurity.com "

Option #2 – If you have set existing CSP in AD FS 2019, run this PowerShell script to append the necessary changes:

$apihostname = "api-XXXXX.duosecurity.com"

$CSP = ((Get-AdfsResponseHeaders | Select -ExpandProperty ResponseHeaders).'Content-Security-Policy')

$CSP = $CSP + "; frame-src " + $apihostname

Set-AdfsResponseHeaders -SetHeaderName "Content-Security-Policy" -SetHeaderValue $CSP

Please remember to replace api-xxxxxx.duosecurity.com in both of the options above with the Duo hostname you copied from the Duo administration portal earlier.

Testing Duo two-factor authentication on the idpinitiatedsignon.htm page

If you do not have any Relaying Party Trusts configured but want to test the newly deployed Duo, you can use the idpinitiatedsignon.htm page to test.

Begin by enabling the page by executing the following cmdlet as ADFS 2016 and newer disables the page by default:

Set-AdfsProperties -EnableIdPInitiatedSignonPage $true

Verify that the sign on page is displayed when browsing to the URL: https://<ADFSServer>/adfs/ls/idpinitiatedsignon.htm

Navigate to AD FS > Relying Party Trusts and click on Add Relying Party Trust… under the Actions pane on the right:

image

Select Claims Aware and click Start:

image

Select Import data about the relying party published online or on a local network and paste the following under the Federation metadata address (host name or URL): text field:

https://<ADFSServerFQDN>/federationmetadata/2007-06/federationmetadata.xml

image

Specify a Display name and an optional description in the Notes field:

image

Select Permit everyone and require MFA under the Choose an access control policy options:

image

Verify the configuration and click Next:

image

Clear the Configure claims issuance policy for this application and click Close:

image

Navigate to the URL: https://<ADFSServer>/adfs/ls/idpinitiatedsignon.htm and you should see an additional Sign in to one of the following sites: option. Ignore that option and proceed to sign with the Sign in to this site option:

image

Enter your Active Directory credentials:

image

You should now see the Duo 2-factor authentication prompt:

image

The following page will be displayed upon successfully signed in:

image

Thursday, April 23, 2020

Configuring AD FS claims-based authentication with Exchange Server 2019 Outlook on the web fails to display and redirect /owa and /ecp logon portals

Problem

You’ve completed deploying the configuration required that allow clients to use AD FS (on Windows Server 2019) claims-based authentication to connect to Exchange Server 2019 Outlook on the web (formerly known as Outlook Web App) and the Exchange admin center (EAC) as outlined in the following documentation:

Use AD FS claims-based authentication with Outlook on the web
https://docs.microsoft.com/en-us/exchange/clients/outlook-on-the-web/ad-fs-claims-based-auth?view=exchserver-2019

… but notice that attempting to navigate to /owa throws two different errors depending on the browser you use:

Google Chrome:

The page isn’t working

mail.contoso.com redirected you too many times.

ERR_TOO_MANY_REDIRECTS

image

Internet Explorer:

Server Error in '/owa' Application.


Encryption certificate is absent

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: Microsoft.Exchange.Security.Authentication.AdfsConfigurationException: Encryption certificate is absent
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[AdfsConfigurationException: Encryption certificate is absent]
   Microsoft.Exchange.Security.Authentication.Utility.GetCertificates() +130
   Microsoft.Exchange.Security.Authentication.AdfsSessionSecurityTokenHandler.CreateTransforms() +12
   Microsoft.Exchange.Security.Authentication.AdfsFederationAuthModule.FederatedAuthentication_ServiceConfigurationCreated(Object sender, ServiceConfigurationCreatedEventArgs e) +143
   Microsoft.IdentityModel.Web.FederatedAuthentication.get_ServiceConfiguration() +177
   Microsoft.IdentityModel.Web.HttpModuleBase.Init(HttpApplication context) +17
   System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +580
   System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +165
   System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +267
   System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +341

[HttpException (0x80004005): Encryption certificate is absent]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +523
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +107
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +688

image

Attempting to navigate to /ecp throws the following error:

Server Error in '/ecp' Application.


Runtime Error

Description: An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed remotely (for security reasons). It could, however, be viewed by browsers running on the local server machine.
Details: To enable the details of this specific error message to be viewable on remote machines, please create a <customErrors> tag within a "web.config" configuration file located in the root directory of the current web application. This <customErrors> tag should then have its "mode" attribute set to "Off".

<!-- Web.Config Configuration File -->

<configuration>

<system.web>

<customErrors mode="Off"/>

</system.web>

</configuration>

Notes: The current error page you are seeing can be replaced by a custom error page by modifying the "defaultRedirect" attribute of the application's <customErrors> configuration tag to point to a custom error page URL.

<!-- Web.Config Configuration File -->

<configuration>

<system.web>

<customErrors mode="RemoteOnly" defaultRedirect="mycustompage.htm"/>

</system.web>

</configuration>

image

Reviewing the event logs shows the following error consistently logged on the Exchange server:

image

Log Name: Application

Source: ASP.NET 4.0.30319.0

Event ID: 1309

Level: Warning

Event code: 3005

Event message: An unhandled exception has occurred.

Event time: 4/23/2020 2:41:17 PM

Event time (UTC): 4/23/2020 5:41:17 PM

Event ID: 034a726997a145338d6d7f878e25b637

Event sequence: 2

Event occurrence: 1

Event detail code: 0

Application information:

Application domain: /LM/W3SVC/1/ROOT/owa-20-132321372689777583

Trust level: Full

Application Virtual Path: /owa

Application Path: E:\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\

Machine name: OSMIUM

Process information:

Process ID: 14576

Process name: w3wp.exe

Account name: NT AUTHORITY\SYSTEM

Exception information:

Exception type: AdfsConfigurationException

Exception message: Encryption certificate is absent

at Microsoft.Exchange.Security.Authentication.Utility.GetCertificates()

at Microsoft.Exchange.Security.Authentication.AdfsSessionSecurityTokenHandler.CreateTransforms()

at Microsoft.Exchange.Security.Authentication.AdfsFederationAuthModule.FederatedAuthentication_ServiceConfigurationCreated(Object sender, ServiceConfigurationCreatedEventArgs e)

at Microsoft.IdentityModel.Web.FederatedAuthentication.get_ServiceConfiguration()

at Microsoft.IdentityModel.Web.HttpModuleBase.Init(HttpApplication context)

at System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers)

at System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context)

at System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context)

at System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext)

Request information:

Request URL: https://mail.contoso.bm:443/owa/

Request path: /owa/

User host address: 172.16.10.105

User:

Is authenticated: False

Authentication Type:

Thread account name: NT AUTHORITY\SYSTEM

Thread information:

Thread ID: 18

Thread account name: NT AUTHORITY\SYSTEM

Is impersonating: False

Stack trace: at Microsoft.Exchange.Security.Authentication.Utility.GetCertificates()

at Microsoft.Exchange.Security.Authentication.AdfsSessionSecurityTokenHandler.CreateTransforms()

at Microsoft.Exchange.Security.Authentication.AdfsFederationAuthModule.FederatedAuthentication_ServiceConfigurationCreated(Object sender, ServiceConfigurationCreatedEventArgs e)

at Microsoft.IdentityModel.Web.FederatedAuthentication.get_ServiceConfiguration()

at Microsoft.IdentityModel.Web.HttpModuleBase.Init(HttpApplication context)

at System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers)

at System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context)

at System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(sIntPtr appContext, HttpContext context)

at System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext)

Custom event details:

image

Solution

This issue took quite a bit of my time to troubleshoot as I thought I had made a mistake for the configuration outlined in the document. What I later determined from the event constantly logged shown above was that the self-signed Microsoft Exchange Server Auth Certificate on the Exchange 2019 servers have expired and it was causing the authentication redirect to the AD FS server to fail. To quickly determine whether this is the case for an environment, simply launch the event viewer on one of the Exchange servers, navigate to the Application logs and filter based on Event ID 24:

You should see an event similar to the following:

image

Log Name: Application

Source: MSExchange Web Services

Event ID: 24

Level: Error

The Exchange certificate [Subject]

CN=Microsoft Exchange Server Auth Certificate

[Issuer]

CN=Microsoft Exchange Server Auth Certificate

[Serial Number]

2BF79A269A16ACB047DE62C555EAF2B7

[Not Before]

3/18/2015 2:36:19 PM

[Not After]

2/20/2020 1:36:19 PM

[Thumbprint]

E60327C0FFB444247A6CD5E2BAD6AD58083BD6F1

expired on 2/20/2020 1:36:19 PM.

image

The certificate in this environment expired on February 20, 2020 and while this Exchange Server 2019 wasn’t installed 5 years ago (this is the default lifetime of the self-signed certificate), the first Exchange Server 2013 or 2016 was probably installed sometime in 2015. This certificate is installed on every Exchange server in the organization and shared as it is not unique to the identity of any of the servers.

To renew this certificate, begin by executing the following cmdlet to display the certificates on the Exchange server and confirm that the Microsoft Exchange Server Auth Certificate listed has indeed expired:

Get-ChildItem cert:\localmachine\my | ft Thumbprint,Notafter,Subject

image

Use the Get-AuthConfig cmdlet to confirm that the expired certificate is being used:

Note the matching thumbprint.

image

Use the following cmdlet to retrieve the thumbprint of the expired certificate:

Get-AuthConfig | fl Currentcer*

Copy the Thumbprint down as you will need this later.

image

Note that if the certificate has expired then you can simply renew it directly from the EAC console but I had issues accessing the EAC so I decided to use PowerShell to perform the renewal as outlined in the documentation here:

Renew an Exchange Server certificate
https://docs.microsoft.com/en-us/exchange/architecture/client-access/renew-certificates?view=exchserver-2019

Execute the following cmdlet to list all of the certificates the Exchange server is using and confirm that the previously copied thumbprint matches the one listed as Subject: CN=Microsoft Exchange Server Auth Certificate :

Get-ExchangeCertificate | where {$_.IsSelfSigned -eq $true} | Format-List FriendlyName,Subject,CertificateDomains,Thumbprint,NotBefore,NotAfter

image

Execute the following cmdlet to retrieve the expired certificate as an object then renew it with the New-ExchangeCertificate cmdlet:

Get-ExchangeCertificate -Thumbprint <paste the thumbprint previous copied> | New-ExchangeCertificate -Force -PrivateKeyExportable $true

Here is an example of the cmdlet ran with the thumbprint copied previously:

Get-ExchangeCertificate -Thumbprint E60327C0FFB444247A6CD5E2BAD6AD58083BD6F1 | New-ExchangeCertificate -Force -PrivateKeyExportable $true

image

Copy the thumbprint to notepad and clearly label this is the renewed certificate.

Executing the previous cmdlet:

Get-ExchangeCertificate | where {$_.IsSelfSigned -eq $true} | Format-List FriendlyName,Subject,CertificateDomains,Thumbprint,NotBefore,NotAfter

… will now show the expired and renewed certificate:

image

If you’re like me where you’re unable to access EAC, you can view the new certificate in the IIS console as well:

image

Execute the Get-AuthConfig cmdlet and make a note of the that the CurrentCertificateThumbprint is populated while the PreviousCertificateThumbprint and NextCertificateThumbprint are empty:

image

Proceed to replace the expired certificate by executing the following cmdlets:

$thumb = "< The New Certificate Thumbprint>"

$date = get-date

Set-AuthConfig -NewCertificateThumbprint $thumb -NewCertificateEffectiveDate $date

Here is an example of the cmdlet to execute with the information above:

$thumb = “BDE4266ED877E95F38D303878A048C0EA0183C7B”

$date = get-date

Set-AuthConfig -NewCertificateThumbprint $thumb -NewCertificateEffectiveDate $date

image

image

Execute the Get-AuthConfig cmdlet and notice how that the CurrentCertificateThumbprint hasn’t changed but the NextCertificateThumbprint field is now populated with the new certificate’s thumbprint:

image

Proceed to publish the new certificate and clearing the old expired certificate from the configuration by executing the following cmdlets:

Set-AuthConfig -PublishCertificate

Set-AuthConfig -ClearPreviousCertificate

Execute the Get-AuthConfig cmdlet and notice how that the CurrentCertificateThumbprint has been updated with the new certificate’s thumbprint and the NextCertificateThumbprint field is cleared:

image

Proceed and confirm that these events are no longer logged:

image

image

… then test to confirm that the /owa and /ecp are now being redirected to AD FS.