AWS BYODKIM & other nerdisms
Cookin' up some opsec
There are a couple of ways to implement DKIM identity verification on your mail relay service. The most common being the use of CNAME records automatically generated by yourself or a third-party provider, such as AWS. These records can be inserted automatically via Route 53 or manually with the use of a third-party DNS service.
The manual method is trivial if you know not to insert the entire name & value strings in their respective fields with your DNS provider, but counter-intuitive as one of my colleague's recently discovered.
However, as with most things, there is another way, which provides a bit more control and flexibility around how you verify your domain identity.
The alternative being BYODKIM. The following one-liner (three) creates a mechanism to generate a DKIM key pair and format a nice DNS TXT record, trivializing the process of setting up DKIM for a domain. I'll go through and break it all down, then present an alternative script that's a bit more user-friendly.
Have a gander at mr misery and let's deconstruct it together.
openssl genrsa -out myawesomedomain.com.au.private.key 2048
openssl rsa -in myawesomedomain.com.au.private.key -pubout -out myawesomedomain.com.au.public.key
grep -v -- "----" myawesomedomain.com.au.public.key | tr -d '\n' | sed 's/^.*\(MI.*\)$/"v=DKIM1; k=rsa; p=\1"/' | xargs -I {} echo "selector1._domainkey.myawesomedomain.com.au. IN TXT {}"
The output should be as follows...
writing RSA key
selector1._domainkey.myawesomedomain.com.au. IN TXT v=DKIM1; k=rsa; p=MIIBCgKCAQEAzVsPIdHpblobBVOX7jtDtyq...
void@void: /home/void/working
➜ ls
myawesomedomain.com.au.private.key myawesomedomain.com.au.public.key
Private Key Generation
Probably the easiest line to understand - we use the openssl binary and the genrsa utility operation to output the private key file for myawesomedomain. We also set the bit size to 2048 because any lower would be madness.
openssl genrsa -out myawesomedomain.com.au.private.key 2048
Public Key Generation
Pretty straightforward, but here we're using openssl to read in the private key rather than output, and output a new public key against it. This public key will be as the name implies, made public to our TXT record later.
openssl rsa -in myawesomedomain.com.au.private.key -pubout -out myawesomedomain.com.au.public.key
Text manipulation
For the sauce. This command sequence formats the public key for inclusion in a DNS TXT record. There are a lot of subcommands here so we'll break them down individually.
grep -v -- "----" myawesomedomain.com.au.public.key | tr -d '\n' | sed 's/^.*\(MI.*\)$/"v=DKIM1; k=rsa; p=\1"/' | xargs -I {} echo "selector1._domainkey.myawesomedomain.com.au. IN TXT {}"
Grepping the public key file
grep -v takes input line by line. To ensure that strings containing hyphens don't break the command, we use the -- double delimiter. This way we can search the public key file and filter lines with the begin or end key headers.
grep -v -- "----" myawesomedomain.com.au.public.key
Trimming the new lines
tr -d looks for literal new lines \n and strips them from the file
| tr -d '\n'
Formatting the public key
Here we use sed to format the remaining public key string according to DKIM requirements, wrapping it with the necessary prefix and quotes.
| sed 's/^.*\(MI.*\)$/"v=DKIM1; k=rsa; p=\1"/'
Printing the DNS TXT record
Xargs is used here to take the formatted public key string and pass it as an argument to echo. The -I operator tells xargs to replace the contents of the placeholder in the echo command with the input it receives from the previous command in the pipe.
Then the echo command is used to construct a DNS TXT record by embedding the formatted public key received from the pipe into a template that defines how the TXT record should appear in the DNS configuration. This should then be added to the DNS configuration with the third-party provider for DKIM to work.
| xargs -I {} echo "selector1._domainkey.myawesomedomain.com.au. IN TXT {}
To clean this all up and make it a bit less academic I've created a gist for anyone to use.
We can do this by breaking out private & public key generation, and the DKIM record creation into separate functions. Then we can use the output of the public key script to create the DKIM record.
The output should be as follows..
void@void: /home/void/working
➜ touch dkim-id-verification.sh # Create the file
void@void: /home/void/working
➜ chmod +x dkim-id-verification.sh # Set the permissions to executable
void@void: /home/void/working
➜ nano dkim-id-verification.sh # Paste the code example
void@void: /home/void/working
➜ ./dkim-id-verification.sh # Run the code example
Generating RSA private key of size 2048 bits in pkcs1 format...
Cleaning the key...
Cleaned key saved to myawesomedomain.com.au.selector1.private.pem.cleaned
Generating the public key from the private key...
writing RSA key
Cleaning the key...
Cleaned key saved to myawesomedomain.com.au.selector1.private.public.pem.cleaned
########################################################
Enter the following DNS record with your DNS provider:
########################################################
Type: TXT
Domain: selector1._domainkey.myawesomedomain.com.au
Value: "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtNtRJr..."
TTL: 86400 (1 day recommended)
########################################################
void@null: /home/void/working
➜ ls
dkim-id-verification.sh myawesomedomain.com.au.selector1.private.pem.cleaned myawesomedomain.com.au.selector1.private.public.pem.cleaned
myawesomedomain.com.au.selector1.private.pem myawesomedomain.com.au.selector1.private.public.pem
To retrieve the public key entry for submission with your third-party DNS provider
void@null: /home/void/working
➜ cat myawesomedomain.com.au.selector1.private.pem.cleaned
The public key can be added as a TXT entry with your DNS provider be pre-filling the following fields
Type - TXT
Name - selector1._domainkey
Data - v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
TTL - 1/2 Hour
To retrieve the cleaned private key entry to be used with AWS SES BYODKIM by dropping it into the private key field while creating a verified identity.
void@null: /home/void/working
➜ cat myawesomedomain.com.au.selector1.private.public.pem
If everything's a hit you should be able to query the DNS record and see the public key in the TXT record.
dig +short TXT selector1._domainkey.myawesomedomain.com.au
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." "mh9GQCxSPyWta4CPTG72WW..."
If you want to make this process even easier, I recommend having a look at my other guide on automating AWS SES identities