Aditya Agrawal
Aditya Agrawal

Reputation: 445

DKIM fails when sending mails with smtplib

I'm trying to send emails with smtplib and they seem to be delivering fine. The only problem is that DKIM fails and the mails usually go straight to the spam folder.

DKIM is enabled on my shared hosting (host is a2hosting, if that helps) and the process works fine when sending individual emails with Thunderbird, and DKIM passes, suggesting that the problem lies on my end.

I even tried using dkimpy to explicitly sign the emails using the private key but I still get dkim=fail under ARC-Authentication-Results. Some posts and answers I referred to suggested "logging in" as the solution but I am already logging in using SMTP.login() and as I mentioned earlier, the emails are being sent.

An answer I referred to mentioned that it is the server's job to sign the email and it's worth mentioning that the raw email output includes the DKIM signature, even without explicitly signing it with dkimpy, indicating that the server is signing as expected.

But the problem remains that DKIM fails affecting the email deliverability, and the raw output does not provide any details as to why DKIM failed for the domain.

I use the following code snippet to send an email

    msg = MIMEMultipart()
    msg['From'] = '[email protected]'
    msg['To'] = '[email protected]'
    msg['Subject'] = "Subject"
    msg.attach(MIMEText("SomeText", "plain"))

    s = smtplib.SMTP_SSL("mydomain.tld:465")
    s.login("[email protected]", "mypassword")
    s.sendmail("[email protected]", '[email protected]',msg.as_string())

I tried signing the message as follows

headers = ["To", "From", "Subject"]
with open("cert.pem") as fh:
    dkim_private = fh.read()
sig = dkim.sign(
            message=msg.as_string().encode("ascii"),    
            selector=str(dkim_selector).encode("ascii"),
            domain="robogyan.tech".encode("ascii"),
            privkey=dkim_private.encode("ascii"),
            include_headers=headers,)
msg["DKIM-Signature"] = sig.decode("ascii").lstrip("DKIM-Signature: ")

The raw output did reflect the signature with the above code but DKIM still failed.

There seems to be no problem with the authentication whatsoever since the server replies with "Authentication succeeded"

Edit:

DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;

    d=mydomain.tld; s=default; h=Subject:To:From:MIME-Version:Content-Type:

    Sender:Reply-To:Date:Message-ID:Cc:Content-Transfer-Encoding:Content-ID:

    Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc

    :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:

    List-Subscribe:List-Post:List-Owner:List-Archive;

    bh=giCDGo/0duFr1Ex65l7Ixc3N45EAULK+gw5cHV8pO0k=; b=DR08Q+CjgOLqo8WkLJs/XROfTw

    Z7+ph+qnzi5p49cT3+UwQolcL1CKIVPk7XRkL8WZ3FFa9hZuc6TumquRSiYd5uR0AC5Z3lopEfnQe

    fdbOOTRnks2ZzoOnQusy/gmydUttypu8wTthFhy7vTWXMFcdI29X/HkrokCtiGKCoD2u2kWBtn2sm

    3/aP83lBbMpcWsNbvo3HTsL71o8QPd6bVKpqRGyAy89cAwMLwP4dnJ9WcCxxNzowlJNPQja3o5W16

    t3rG/KizcRehjaDUXhPPRF/4RdYUSIi/SGNwmIPwvkZNc17k3wQpszKeG6/Ujgax/i7Li7V7dLJBT

    Fu/x6xDA==;

Signed-by: [email protected]

Expected-Body-Hash: giCDGo/0duFr1Ex65l7Ixc3N45EAULK+gw5cHV8pO0k=

Here's the DKIM of the failing email if that helps. The expected body hash and the received body hash match too. I am not sure what the problem is then.

Upvotes: 9

Views: 3351

Answers (2)

woozly
woozly

Reputation: 1369

Important note: you need to add your smtp client's machine IP address to InternalHosts list, because OpenDKIM will check client's permission with these rules.

The you need to add this line to your /etc/opendkim.conf:

InternalHosts    file:/etc/opendkim/TrustedHosts   # or any location you want

Content of /etc/opendkim/TrustedHosts could look like:

127.0.0.1
::1
localhost
<server_ip>
hostname.example1.com
example1.com
hostname.example2.com
example2.com
...

It's just for example. You need to put here your python smtplib-client machine's address (ip/host).

Then just restart your opendkim:

$ sudo service opendkim restart

Upvotes: 1

Aditya Agrawal
Aditya Agrawal

Reputation: 445

After a lot of research and brute force approaches, I finally found the solution to my problem. I needed to include the Message-ID and the date in the headers as well. Adding the following lines to the code helped me pass the verification.

msg['Date'] = email.utils.formatdate()
msg['Message-ID'] = email.utils.make_msgid(domain='mydomain.tld')

Upvotes: 16

Related Questions