The Line Up
Serious topics like DNS and email security do indeed require us to get our ducks in a row. In this article, we discuss what DNS records should be in place for Exchange Online, why these records are needed, and how to validate that DNS is properly configured. First, we review DNS Domain components for Exchange Online, what the records do, review methods to query DNS records, and build a PowerShell script using the best method. With this script, we use PowerShell to create a report to verify that these records exist. The Domain Health Checker PowerShell module also provides some feedback for DNS records, which we can use to rate whether the domain follows best practices. We end with a report covering DNS health.
Duck One – DNS Domains, Records, and CompAuth
Mail flow and mail security rely on several DNS records to function properly. Below is a list of the records that an organization would use for Exchange Online, and while not all are required, they should be configured for each Accepted Domain in Exchange Online. Here are those records and some additional concepts to keep in mind:
MX Record: Record used to lookup the destination mail server for an email domain.
Sender Policy Framework (SPF) Record: important for telling SMTP recipient servers what SMTP sending servers are authorized for a domain and as such. Without a properly configured SPF record a domain’s SMTP reputation is at risk. The SPF record exists in the form of a TXT record which could look like this:
v=spf1 include:spf.protection.outlook.com -all
DKIM Record: Use in conjunction with an SPF record to reduce malicious spoofing. Sample DKIM Record:
Host name: selector1._domainkey Points to address or value: selector1-myoffice365domain.com._domainkey.tenantdomain.onmicrosoft.com TTL: 3600
DMARC Record: Works together with SPF and DKIM to authenticate a sender’s email server. Sample DMARC Record:
_dmarc.practical365.com. 3600 IN TXT "v=DMARC1; p=none; pct=100; rua=mailto:dmarc@practical365.com; ruf=mailto:dmarc@practical365.com; fo=1"
Note that there were recent changes to Microsoft 365 support which you can read about on this blog.
DNSSEC: Adds a level of protection to your email Domains.
CompAuth: Exchange Online checks the SPF, DMARC, and DKIM records and combines them into a concept known as CompAuth. CompAuth can determine if a message has been successfully authenticated.
On Demand Migration
Migrate all your workloads and Active Directory with one comprehensive Office 365 tenant-to-tenant migration solution.
Duck Two – The Preparation
Many ways exist to check DNS records and validate an organization’s DNS configuration:
Manually: The DNS Provider website checks each domain manually for the records and verifies that the txt is valid [free SPF checker]. The downside is knowing if a record is misconfigured, or even missing.
Built-In PowerShell: Use the Resolve-DnsName cmdlet to resolve DNS names for a domain. The downside is that the cmdlet does not provide feedback other than the DNS record itself.
NSLookup: Queries DNS records and can be used to resolve email DNS records.
DomainHealthChecker: Custom PowerShell module written and maintained by Martien van Dijk which can perform queries for email-related DNS records and also provides advisory feedback on these records. With ease of use and consistent updates since mid-2021, this module should be added to your PowerShell toolkit for DNS queries.
After validating DNS records, a chart of a DNS domain’s health is useful for reporting and auditing.
To overcome the limitations of the available methods, this article uses cmdlets from the Exchange Online and Domain Health Checker PowerShell modules to fetch data, analyze the results, and output an easy-to-read HTML chart.
Duck Three – PowerShell
Let’s look at the code to check a domain’s DNS records and assess an organization’s DNS health for Exchange Online.
After connecting to Exchange Online and loading the DomainHealthChecker module, we retrieve a list of Accepted Domains:
$AcceptedDomains = (Get-AcceptedDomain).Name
With a list of Accepted domains we can analyze the DNS configuration for each domain using a Foreach loop:
Foreach ($AcceptedDomain in $AcceptedDomains) { $SpfRecord = Get-SPFRecord $AcceptedDomain $DmarcRecord = Get-DMARCRecord $AcceptedDomain $DkimRecord = Get-DKIMRecord $AcceptedDomain $DnsSec = Get-DNSSec $AcceptedDomain
First up is the base MX record. We use a system of points to determine the health of a domain. In the report, the lower the point total for a domain, the healthier its status is. For example, for the MX record, if a record exists, then no points are added whereas if there isn’t an MX record, we add 2 points. Accepted domains should always have an MX record. It is possible to have a domain without an MX record. Scoring these assessment of the DNS configuration raises awareness about potential issues for the domain.
Try { $MXRecord = Resolve-DnsName $AcceptedDomain -Server $DNSServer -Type MX -ErrorAction STOP $MX = 'X' # To be marked on the report for this domain $MXRecordResult = 0 # MX record found (o pts) } Catch { $MXRecordResult = 2 # No MX record found (2 pts) }
Next up is the SPF record, which requires some more analysis. We examine an SPF record (txt record, and not the deprecated SPF DNS type) using several criteria. One is to see if the record exists. Second, is the record over 255 characters? We check the length for the variable $SpfRecord using the SPFRecordLenght property(misspelled in the PS module – reported), but also to check if the record exists (length > 0). The Domain Health Checker module also provides an ‘advisory’ which can be used for additional scoring:
$SPFRecordLength = [int32]$SpfRecord.SPFRecordLenght #SPF Length Analysis If ($SPFRecordLength -eq 0) { $SpfRecordLengthResult = 2 # Red / Risk $SpfRecordExists = $False } If (($SPFRecordLength -gt 0) -and ($SPFRecordLength -lt 150)) { $SpfRecordLengthResult = 0 # Green / No Risk $SpfRecordExists = $True } If ($SPFRecordLength -gt 200) { $SpfRecordExists = $True If ($SPFRecordLength -lt 250) { $SpfRecordLengthResult = 1 # Yellow / Low Risk } Else { $SpfRecordLengthResult = 2 # Red / Risk / Too large } }
Now we rate the Advisory. There are three levels and the script adds an increased number of points to the domain’s overall score based on the level:
# If the record exists, check the If ($SpfRecordExists) { # SPF Advisory Analysis If ($SPFRecord.SPFAdvisory -like '*not sufficiently*') { $SPFAdvisoryLevel = 1 # Yellow / Low Risk } Else { $SPFAdvisoryLevel = 0 # Green / No Risk } If ($SPFRecord.SPFAdvisory -eq 'Domain does not have an SPF record. To prevent abuse of this domain, please add an SPF record to it.') { $SPFAdvisoryLevel = 2 # Red / Risk } }
Last, for reporting purposes, we take the overall score for the SPF record and apply an appropriate color code (Yellow representing Caution while Red is a Warning).
If ($SpfRecordExists) { $SPF = 'X' # To be marked on the report for this domain $SpfOverallResults = $SpfRecordLengthResult + $SPFAdvisoryLevel If ($SpfOverallResults -eq 4) { $SPFColor = "$Red" } If (($SpfOverallResults -gt 0) -and ($SpfOverallResults -lt 4)) { $SPFColor = "$Yellow" } }
Next, we analyze the data stored in the $DmarcRecord variable. The script checks first to see if the record exists, and if it exists and what is the DMARC ‘p’ value:
# DMARC Existence If ([string]::IsNullOrWhiteSpace($DmarcRecord.DmarcRecord) ) { $DmarcRecordExists = $False $DmarcColor = "$Red" } Else { $DmarcRecordExists = $True $DmarcExistResult = 0 } If ($DmarcRecordExists) { # DMARC Record setting check / rating $DMARCAdvisoryLevel = Switch ($DmarcRecord.DmarcRecord) { { $_ -like "*p=none*"} {"2"} { $_ -like "*p=quarantine*"} {"1"} { $_ -like "*p=reject*"} {"0"} } # Prep for chart $Dmarc = 'X' # To be marked on the report for this domain $DmarcOverallResults = $DMARCExistResult + $DMARCAdvisoryLevel If ($DmarcOverallResults -eq 1) { $DmarcColor = "$Yellow" } If ($DmarcOverallResults -eq 2) { $DmarcColor = "$Red" } }
The last check validates if the DNS domain is secured according to the DNS SEC standard, which is now supported by Exchange Online – see the ICANN standard and this article for further information.
If ($DnsSec -like '*DNSSEC is enabled on your domain.') { $DnsSec = 'X' # To be marked on the report for this domain $DNSSecResult = 0 } Else { $DNSSecResult = 2 $DnsSec = '' }
The Last Duck – The Report
Taking all of the above data, we can convert it into a chart using either straight PowerShell to output line of an HTML file or use the PSWriteHTML module. With the first method, I created a sample chart (Figure 1)
Figure 1: Sample output from a DNS assessment
All the Ducks in a Row
At the beginning, we discussed reviewing DNS settings for Exchange Online before explaining how to discover, rate, and report the DNS data using PowerShell. The end result is a report that administrators can use to validate a good configuration or to find flaws to fix (evident in Figure 1). With malicious actors constantly attempting new attack vectors, the least we can do is to make sure our DNS Ducks are in a Row. Hopefully, the script (downloadable from GitHub) will help you to do just that.