██▒   █▓ ▒█████   ██▓▓█████▄ 
▓██░   █▒▒██▒  ██▒▓██▒▒██▀ ██▌
 ▓██  █▒░▒██░  ██▒▒██▒░██   █▌
  ▒██ █░░▒██   ██░░██░░▓█▄   ▌
   ▒▀█░  ░ ████▓▒░░██░░▒████▓ 
   ░ ▐░  ░ ▒░▒░▒░ ░▓   ▒▒▓  ▒ 
   ░ ░░    ░ ▒ ▒░  ▒ ░ ░ ▒  ▒ 
     ░░  ░ ░ ░ ▒   ▒ ░ ░ ░  ░ 
      ░      ░ ░   ░     ░    
     ░                 ░      
    

 ___      ___ ________  ___  ________     
|\  \    /  /|\   __  \|\  \|\   ___ \    
\ \  \  /  / | \  \|\  \ \  \ \  \_|\ \   
 \ \  \/  / / \ \  \\\  \ \  \ \  \ \\ \  
  \ \    / /   \ \  \\\  \ \  \ \  \_\\ \ 
   \ \__/ /     \ \_______\ \__\ \_______\
    \|__|/       \|_______|\|__|\|_______|
    

Yane ✖ Karov

  • 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

    © 2024 Yane ✖ Karov