stages/email: add helper for AWS-specific smtp password handling
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
bc6706016b
commit
c1b4c785c2
|
@ -82,7 +82,7 @@ from authentik.flows.views.executor import QS_KEY_TOKEN
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.tasks import send_mails
|
from authentik.stages.email.tasks import send_mails
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils.template import TemplateEmailMessage
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
|
@ -39,7 +39,7 @@ from authentik.lib.sentry import SentryIgnoredException
|
||||||
from authentik.lib.utils.http import get_client_ip, get_http_session
|
from authentik.lib.utils.http import get_client_ip, get_http_session
|
||||||
from authentik.lib.utils.time import timedelta_from_string
|
from authentik.lib.utils.time import timedelta_from_string
|
||||||
from authentik.policies.models import PolicyBindingModel
|
from authentik.policies.models import PolicyBindingModel
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils.template import TemplateEmailMessage
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
from authentik.tenants.utils import DEFAULT_TENANT
|
from authentik.tenants.utils import DEFAULT_TENANT
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.core.management.base import BaseCommand, no_translations
|
||||||
|
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.tasks import send_mail
|
from authentik.stages.email.tasks import send_mail
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils.template import TemplateEmailMessage
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
|
@ -14,6 +14,7 @@ from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.flows.models import Stage
|
from authentik.flows.models import Stage
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
|
from authentik.stages.email.utils.aws import aws_calculate_password
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
@ -106,11 +107,17 @@ class EmailStage(Stage):
|
||||||
"""Get fully configured Email Backend instance"""
|
"""Get fully configured Email Backend instance"""
|
||||||
if self.use_global_settings:
|
if self.use_global_settings:
|
||||||
CONFIG.refresh("email.password")
|
CONFIG.refresh("email.password")
|
||||||
|
host = CONFIG.get("email.host")
|
||||||
|
password = CONFIG.get("email.password")
|
||||||
|
# Special case for AWS Email passwords
|
||||||
|
if host.endswith("amazonaws.com"):
|
||||||
|
region = host.replace(".amazonaws.com", "").split(".")[-1]
|
||||||
|
password = aws_calculate_password(password, region)
|
||||||
return self.backend_class(
|
return self.backend_class(
|
||||||
host=CONFIG.get("email.host"),
|
host=host,
|
||||||
port=CONFIG.get_int("email.port"),
|
port=CONFIG.get_int("email.port"),
|
||||||
username=CONFIG.get("email.username"),
|
username=CONFIG.get("email.username"),
|
||||||
password=CONFIG.get("email.password"),
|
password=password,
|
||||||
use_tls=CONFIG.get_bool("email.use_tls", False),
|
use_tls=CONFIG.get_bool("email.use_tls", False),
|
||||||
use_ssl=CONFIG.get_bool("email.use_ssl", False),
|
use_ssl=CONFIG.get_bool("email.use_ssl", False),
|
||||||
timeout=CONFIG.get_int("email.timeout"),
|
timeout=CONFIG.get_int("email.timeout"),
|
||||||
|
|
|
@ -18,7 +18,7 @@ from authentik.flows.stage import ChallengeStageView
|
||||||
from authentik.flows.views.executor import QS_KEY_TOKEN
|
from authentik.flows.views.executor import QS_KEY_TOKEN
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.tasks import send_mails
|
from authentik.stages.email.tasks import send_mails
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils.template import TemplateEmailMessage
|
||||||
|
|
||||||
PLAN_CONTEXT_EMAIL_SENT = "email_sent"
|
PLAN_CONTEXT_EMAIL_SENT = "email_sent"
|
||||||
PLAN_CONTEXT_EMAIL_OVERRIDE = "email"
|
PLAN_CONTEXT_EMAIL_OVERRIDE = "email"
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
"""AWS Helpers"""
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
|
||||||
|
# These values are required to calculate the signature. Do not change them.
|
||||||
|
AWS_DATE = "11111111"
|
||||||
|
AWS_SERVICE = "ses"
|
||||||
|
AWS_MESSAGE = "SendRawEmail"
|
||||||
|
AWS_TERMINAL = "aws4_request"
|
||||||
|
AWS_VERSION = 0x04
|
||||||
|
|
||||||
|
|
||||||
|
# https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html#smtp-credentials-convert
|
||||||
|
|
||||||
|
|
||||||
|
def aws_sign(key: bytes, msg: bytes) -> bytes:
|
||||||
|
"""Hmac sign"""
|
||||||
|
return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
|
||||||
|
|
||||||
|
|
||||||
|
def aws_calculate_password(secret_access_key: str, region: str) -> str:
|
||||||
|
"""Calculate AWS SMTP password from secret key"""
|
||||||
|
signature = aws_sign(("AWS4" + secret_access_key).encode("utf-8"), AWS_DATE)
|
||||||
|
signature = aws_sign(signature, region)
|
||||||
|
signature = aws_sign(signature, AWS_SERVICE)
|
||||||
|
signature = aws_sign(signature, AWS_TERMINAL)
|
||||||
|
signature = aws_sign(signature, AWS_MESSAGE)
|
||||||
|
signature_and_version = bytes([AWS_VERSION]) + signature
|
||||||
|
smtp_password = base64.b64encode(signature_and_version)
|
||||||
|
return smtp_password.decode("utf-8")
|
Reference in New Issue