Sign DKIM at app level

- add DKIM_PRIVATE_KEY_PATH param
- create email_utils.add_dkim_signature
- add DKIM signature for transactional emails
- add DKIM signature for forward & reply emails. In reply phase, only non-custom-domain emails have DKIM added.
This commit is contained in:
Son NK 2019-12-17 18:48:06 +02:00
parent 18751d452b
commit a61b900675
6 changed files with 75 additions and 4 deletions

View File

@ -28,12 +28,15 @@ EMAIL_SERVERS_WITH_PRIORITY=[(10, "email.hostname.")]
# these emails are ignored when computing stats
# IGNORED_EMAILS = ["my_email@domain.com"]
# the DKIM private key used to compute DKIM-Signature
DKIM_PRIVATE_KEY_PATH=local_data/dkim.key
# <<< END Email related settings >>>
# <<< Database >>>
# delete and recreate sqlite database, for local development
RESET_DB=true
# RESET_DB=true
# DB Connection
DB_URI=sqlite:///db.sqlite

View File

@ -57,6 +57,14 @@ if os.environ.get("IGNORED_EMAILS"):
else:
IGNORED_EMAILS = []
DKIM_PRIVATE_KEY_PATH = get_abs_path(os.environ["DKIM_PRIVATE_KEY_PATH"])
DKIM_SELECTOR = b"dkim"
with open(DKIM_PRIVATE_KEY_PATH) as f:
DKIM_PRIVATE_KEY = f.read()
DKIM_HEADERS = [b'from', b'to', b'subject']
# Database
DB_URI = os.environ["DB_URI"]

View File

@ -3,9 +3,18 @@ from email.message import EmailMessage
from email.utils import make_msgid, formatdate
from smtplib import SMTP
import dkim
from jinja2 import Environment, FileSystemLoader
from app.config import SUPPORT_EMAIL, ROOT_DIR, POSTFIX_SERVER, NOT_SEND_EMAIL
from app.config import (
SUPPORT_EMAIL,
ROOT_DIR,
POSTFIX_SERVER,
NOT_SEND_EMAIL,
DKIM_SELECTOR,
DKIM_PRIVATE_KEY,
DKIM_HEADERS,
)
from app.log import LOG
@ -123,7 +132,12 @@ def send_email(to_email, subject, plaintext, html):
LOG.d("Date header: %s", date_header)
msg["Date"] = date_header
smtp.send_message(msg, from_addr=SUPPORT_EMAIL, to_addrs=[to_email])
# add DKIM
email_domain = SUPPORT_EMAIL[SUPPORT_EMAIL.find("@") + 1 :]
add_dkim_signature(msg, email_domain)
msg_raw = msg.as_string().encode()
smtp.sendmail(SUPPORT_EMAIL, to_email, msg_raw)
def get_email_name(email_from):
@ -146,3 +160,22 @@ def get_email_part(email_from):
return email_from[email_from.find("<") + 1 : email_from.find(">")].strip()
return email_from
def add_dkim_signature(msg: EmailMessage, email_domain: str):
# Specify headers in "byte" form
# Generate message signature
sig = dkim.sign(
msg.as_string().encode(),
DKIM_SELECTOR,
email_domain.encode(),
DKIM_PRIVATE_KEY.encode(),
include_headers=DKIM_HEADERS,
)
sig = sig.decode()
# remove linebreaks from sig
sig = sig.replace("\n", " ").replace("\r", "")
# Add the DKIM-Signature
msg.add_header("DKIM-Signature", sig[len("DKIM-Signature: ") :])

View File

@ -39,7 +39,12 @@ from smtplib import SMTP
from aiosmtpd.controller import Controller
from app.config import EMAIL_DOMAIN, POSTFIX_SERVER, URL
from app.email_utils import get_email_name, get_email_part, send_email
from app.email_utils import (
get_email_name,
get_email_part,
send_email,
add_dkim_signature,
)
from app.extensions import db
from app.log import LOG
from app.models import GenEmail, ForwardEmail, ForwardEmailLog
@ -179,6 +184,8 @@ class MailHandler:
envelope.rcpt_options,
)
add_dkim_signature(msg, EMAIL_DOMAIN)
# smtp.send_message has UnicodeEncodeErroremail issue
# encode message raw directly instead
msg_raw = msg.as_string().encode()
@ -242,6 +249,10 @@ class MailHandler:
)
del msg["DKIM-Signature"]
# add DKIM-Signature for non-custom-domain alias
if alias.endswith(EMAIL_DOMAIN):
add_dkim_signature(msg, EMAIL_DOMAIN)
# email seems to come from alias
msg.replace_header("From", alias)
msg.replace_header("To", forward_email.website_email)

15
local_data/dkim.key Normal file
View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCxhcKgFHz+HbZiuUhH7iGCVsaZYQ7xzf64ui+09QFlSYzl7d28
LVlr7nvM0+xDbwwsgu2D1vweklroWM5FjbfVtJX3HvSnNbwceX5du/m8RHelmX0/
vLSfsEcnvdNjBmwl/gSIUb660pEp2yo6dUBDTzTDUBNoL6qmnnTNhriRoQIDAQAB
AoGAdhGEtHtr9odEerzIei7DUrDsPa70BZcAR1Rtzmj1mKwmbfaad0GiK8rdxAlf
JiqBaklaN0mRPbQRil8mMdRj4z8gBYbiHWIL7q6zEjjo8f6CUnNqKgs2trTApqLq
L4l110fFSHCmIava5Ly9hJhdOWuJ+PUbcbp0l3j2yoz7RhECQQDa8IMEB/qqeM+e
FTz2+F3HhPI3tGALKWYyCbqcip9UUePfPQ/m547YXsdc1ATzb8OsI7emqTcZLu7H
joX+8WN/AkEAz5J9uFnp2+fWvmkNV1imoys38OwOq7yYUBSfgDymuzWrf8D2L5mt
gSK2LToIjfMRwdJ1RFLGv6oCy6ge3aga3wJAaEKKkZvfIdkgPY6tloqV1hKYajCK
YCZZ1VBOvodA8p2An2lrrjDtFFqmI62PogHCM7JanZINe/+elAdqBgsbrwJBAIN1
wY2Z1FRjlkttePeSu6anXnyE5B28CbLd/M5YmzgBm6YDbWdkKtCYTUyDbpuID/zy
7zXgPuNwJukYhsPXDX0CQGD3laRUSRZiVSD/rJwsJTG2o1FZcsv13CO/0jY7sYxk
IjBK29XMHhTB/dip+beU0RCLFjB3nNK8VyMWmmn1WJ0=
-----END RSA PRIVATE KEY-----

View File

@ -10,6 +10,7 @@ ADMIN_EMAIL=to_fill
# Max number emails user can generate for free plan
MAX_NB_EMAIL_FREE_PLAN=3
EMAIL_SERVERS_WITH_PRIORITY=[(10, "email.hostname.")]
DKIM_PRIVATE_KEY_PATH=local_data/dkim.key
# Database
RESET_DB=true