Site icon Franky's Web

Managing Sender Policy Framework (SPF) entries (Part 2)

In the first part The first part of this article dealt with the requirements and the potential problems associated with them. The second part now deals with the management of SPF entries. In a relatively small and static environment, you will have very few points of contact with the Sender Policy Framework. As a rule, there are only 2 to 3 entries in the SPF DNS record in small environments, and these usually do not change. However, if, as described in the first article, there are many entries and flattened / smoothed SPF entries are also used, you need to think about how to organize them.

Sender Policy Framework (SPF) as a managed service

Probably the easiest way to keep your SPF entries up to date and manage them according to your requirements is to buy SPF as a managed service. I'll just name three providers at random that I have found:

I haven't tested any of them yet. The providers all work quite similarly: The domain is authorized in a portal and the DNS entries for Sender Policy Framework are then managed by the provider. The providers usually support several services such as DMARC, MTA-STS and SPF.

Script for creating the SPF entries

Another way to create and manage your SPF records at a glance is to use a script. Some providers allow DNS records to be created or updated via API. Here you could use a small script that updates the DNS records when required. Here is an example of what this could look like in PowerShell:

# Definiere SPF Einträge
$SPFPrefix = "_spf"
$Domain = "frankysweb.de"
$SPFEntries = @(
    "ip4:185.3.235.230",
    "include:secure-mailgate.com",
    "include:spf.protection.outlook.com"
)

$Policy = "~all"

# Funktion zum Auflösen von SPF-Einträgen
function Resolve-SPF {
    param (
        [string[]]$SPFRecords
    )

    $resolvedIPs = @()

    foreach ($entry in $SPFRecords) {
        if ($entry -like "ip4:*") {
            # Direkt IP-Adressen
            $ip = $entry -replace "ip4:", ""
            $resolvedIPs += $ip
        } elseif ($entry -like "include:*") {
            # Auflösen von DNS SPF Include-Einträgen
            $domain = $entry -replace "include:", ""
            $spfTxtRecords = (Resolve-DnsName -Type TXT -Name $domain).Strings -match "v=spf1"
            
            foreach ($record in $spfTxtRecords) {
                $tokens = $record -split "\s+"
                foreach ($token in $tokens) {
                    if ($token -like "ip4:*") {
                        $ip = $token -replace "ip4:", ""
                        $resolvedIPs += $ip
                    }
                }
            }
        }
    }

    return $resolvedIPs
}

# Funktion zum Aufteilen von SPF Einträgen in 255 Zeichen lange Teile
function Split-SPF {
    param (
        [string]$SPFString,
        [string]$Policy,
        [int]$MaxLength = 255
    )

    $spfParts = @()
    $currentPart = ""

    $tokens = $SPFString -split "\s+"

    foreach ($token in $tokens) {
        # Prüfen, ob das Hinzufügen des Tokens und der Policy die Länge überschreitet
        if (($currentPart.Length + $token.Length + $Policy.Length + 1) -le $MaxLength) {
            $currentPart += $token + " "
        } else {
            # Wenn die Länge überschritten wird, den aktuellen Teil speichern und neuen anfangen
            $spfParts += ($currentPart.TrimEnd() + " " + $Policy)
            $currentPart = $token + " "
        }
    }

    # Den letzten Teil hinzufügen
    if ($currentPart.TrimEnd().Length -gt 0) {
        $spfParts += ($currentPart.TrimEnd() + " " + $Policy)
    }

    return $spfParts
}

# Funktion zum Erstellen der flachen SPF Einträge und Aufteilen
function Create-SPF {
    param (
        [string[]]$ResolvedIPs,
        [string]$Policy
    )

    # Erstellen des vollständigen SPF-Eintrags
    $FlatSPF = ""
    foreach ($IP in $ResolvedIPs) {
        $entry = "ip4:" + $IP + " "
        $FlatSPF = $FlatSPF + $entry
    }

    # Aufteilen des SPF-Eintrags, falls er zu lang ist
    return Split-SPF -SPFString $FlatSPF -Policy $Policy
}

# Beispielhafte Nutzung
$resolvedIPs = Resolve-SPF -SPFRecords $SPFEntries
$SPFEntries = Create-SPF -ResolvedIPs $resolvedIPs -Policy $Policy

# Haupt SPF für die Domain erzeugen
$Counter = 1
$MainDNS = "IN TXT `"v=spf1 "
$SubDNS = @()
foreach ($SPFEntry in $SPFEntries) {
    # Erstellen des Haupt SPF Eintrags mit Includes für Sub-SPF-Einträge
    $MainDNS = $MainDNS + "include:" + "$SPFPrefix" + $Counter + "." + $Domain + " "
    
    # Sub-SPF-Einträge ohne 'v=spf1' hinzufügen, da es bereits im Haupt-SPF steht
    $SubDNS += "$SPFPrefix" + $Counter + "." + $Domain + " IN TXT " + '"' + "v=spf1 " + $SPFEntry + '"'
    $Counter++
}
$MainDNS = $MainDNS + $Policy + '"'

# Ausgabe des Haupt SPF Eintrags
Write-Host "Main SPF Entry for Domain $Domain`:" -foregroundcolor green
write-host  "$MainDNS"

# Ausgabe der Sub SPF Einträge
foreach ($entry in $SubDNS) {
    write-host "Sub SPF Entry:" -foregroundcolor green
	Write-Host $entry
}

The normal readable SPF entries are specified in the first lines of the script. Flattened SPFs are then generated and displayed from this information. The part with updating the DNS records is of course missing here, as the APIs of the providers are different, so you still have to do this yourself if necessary.

However, it is probably easier to use a managed service, as you usually also get additional features such as DMARC reporting, hosted MTA-STS etc. there.

Exit mobile version