modified: .gitignore

new file:   content/2018-06-04-quickstart-to-dkim-sign-email-with-python.rst
This commit is contained in:
russellballestrini 2018-06-04 17:26:19 -04:00
parent d2b1b2d39d
commit 226eaa0f1e
2 changed files with 147 additions and 0 deletions

1
.gitignore vendored
View file

@ -13,6 +13,7 @@ __pycache__/
.Python
env/
build/
build/*
develop-eggs/
dist/
downloads/

View file

@ -0,0 +1,146 @@
Quickstart to DKIM Sign Email with Python
################################################################
:author: Russell Ballestrini
:slug: quickstart-to-dkim-sign-email-with-python
:date: 2018-06-04 09:03
:tags: Code
:status: published
For a long time I have put off DKIM signing email sent from my web services because I couldn't wrap my head around all the indirection Postfix requires to make it work.
Honestly, I put it off for over 5 years...
Today a thought sprung into my head:
"Could I sign Email at the application level before passing to Postfix?"
As you may know, I primarily use the Python programming language, so I did some research and found a reference to a single library called ``dkimpy`` (previously ``pydkim``). The codebase started over 10 years ago and appeared stable and mature.
The part that sold me was that ``dkimpy`` seemed compatible with the two Python standard library modules which I already use:
* ``email`` which I use to prepare messages
* ``smtplib`` which I use to transport messages to Postfix running on localhost
One issue I did have with ``dkimpy`` was the complete lack of examples or even a quickstart guide.
For this reason, I have written this post!
The Missing dkimpy Quickstart Guide
======================================
1. To install ``dkimpy`` you may use ``pip`` (``requirements.txt``) or in my case I added it to my ``setup.py``.
2. Generate a public / private keypair. Don't let this step trip you up, the process easy. In a Unix -like environment you may run the following commands to create the keys.
generate private key (I name my file after the domain and DKIM selector I plan to use).
.. code-block:: bash
openssl genrsa -out remarkbox.com.20180605.pem 1024
generate public key from the private key.
.. code-block:: bash
openssl rsa -in remarkbox.com.20180605.pem -out remarkbox.com.20180605.pub -pubout
3. Install the public key (``.pub``) as a DNS TXT record, where the record name ("selector") is ``20180605._domainkey`` and the value body is:
.. code-block:: bash
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcplYPRsqIFwXuggtH2XgQDMX+e+6sGnWdV8ld/FR9zgRAxB+DeiCEVooVvYt2JRZUEokgDFvys82Q+JTbN4qHNz19bdcBGrnTsnIFaQYpgeQYmPLdDtcWRKzTYMRNCnRmmEXyGv7WIDcaTapIq9NFgLmy1QT7ZTxuNjQtDB/2LwIDAQAB;
You may choose any selector, I happen to like to use YearMonthDay. Additionally you will substitute your public key in place of mine. Put each the line of the public key on a single line in the TXT record.
4. On each of my application servers I store my private portion of my DKIM key in `/etc/dkim/remarkbox.com.20180605.pem`. You may store your key any where on the filesystem that is accessible to the user or group running you application.
5. This is how I used to send Email with Python:
.. code-block:: python
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# catch socket errors when postfix isn't running...
from socket import error as socket_error
def send_email(
to_email,
sender_email,
subject,
message_text,
message_html,
relay="localhost"
):
msg = MIMEMultipart("alternative")
msg.attach(MIMEText(message_text, "plain"))
msg.attach(MIMEText(message_html, "html"))
msg["Subject"] = subject
msg["From"] = sender_email
msg["To"] = to_email
# TODO: react if connecting to postfix is a socket error.
s = smtplib.SMTP(relay)
s.sendmail(sender_email, [to_email], msg.as_string())
s.quit()
return msg
6. This is how I now send DKIM signed Email with Python:
.. code-block:: python
import dkim
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# catch socket errors when postfix isn't running...
from socket import error as socket_error
def send_email(
to_email,
sender_email,
subject,
message_text,
message_html,
relay="localhost",
dkim_private_key_path="",
dkim_selector="",
):
sender_domain = sender_email.split("@")[-1]
msg = MIMEMultipart("alternative")
msg.attach(MIMEText(message_text, "plain"))
msg.attach(MIMEText(message_html, "html"))
msg["To"] = to_email
msg["From"] = sender_email
msg["Subject"] = subject
if dkim_private_key_path and dkim_selector:
with open(dkim_private_key_path) as fh:
dkim_private_key = fh.read()
headers = ["To", "From", "Subject"]
sig = dkim.sign(
message=msg.as_string(),
selector=str(dkim_selector),
domain=sender_domain,
privkey=dkim_private_key,
include_headers=headers,
)
msg["DKIM-Signature"] = sig.lstrip("DKIM-Signature: ")
# TODO: react if connecting to postfix is a socket error.
s = smtplib.SMTP(relay)
s.sendmail(sender_email, [to_email], msg.as_string())
s.quit()
return msg
Like always, if you have any questions feel free to leave a comment or contact me.