Amazon SES с Django не в часовом поясе UTC

Я разрабатываю проект django для использования в Америке, в частности в часовом поясе Нью-Йорка, и система размещена на AWS, а SES отправляет электронную почту. Бэкэнд электронной почты использует django-anymail, которая является простой оболочкой для SES, и система используетsend_mailиз ядра Джанго.

Чтобы поддержать это, я выбрал следующие настройки Django;

EMAIL_BACKEND = "anymail.backends.amazon_ses.EmailBackend"

LANGUAGE_CODE = 'en'
TIME_ZONE = 'America/New_York'
USE_I18N = False
USE_L10N = True
USE_TZ = True

ANYMAIL = {
    "AMAZON_SES_CLIENT_PARAMS": {
        "region_name": AWS_SES_REGION_NAME,
    },
}

С указанными выше настройками звонков djangotzset() при запуске, который изменяет часовой пояс системы. Это означает, что отметка времени используется botocore подписывать запросы на SES не UTC, поскольку при отправке сообщения получена следующая ошибка;

Произошла ошибка (ExpiredToken) при вызове операции SendRawEmail: срок действия маркера безопасности, включенного в запрос, истек

Письма успешно отправляются путем изменения настроек на TIME_ZONE = 'UTC',

Я могу только предположить, что запросы подписываются в UTC -4, который затем попадает в AWS, который находится в UTC.

Как django может работать в определенном часовом поясе, а boto работать с временными метками UTC?

Система работает в док-контейнере (подготовка к производству);

  • докер compose 3.4 (хост unix)
  • Python 2.7
  • Джанго 1,11
  • django-anymail 3.0
  • LocaleMiddleware загружен

1 ответ

Я не могу воспроизвести ошибку, которую вы видите, с настройками, которые вы описали, но я могу показать вам, что работает правильно для меня, с помощью дополнительной регистрации, и вы можете сравнить это с вашим неудачным случаем, чтобы попытаться увидеть, что разные.

Я запустил этот код в оболочке Django (python manage.py shell) просто для удобства, но вы можете поместить его в режим отладки или в любое другое удобное для вас место.

Наша рабочая теория заключается в том, что boto использует неправильный часовой пояс для вычисления временных отметок для подписания запроса API, поэтому давайте включим некоторые подробные журналы boto3, которые охватывают эту область:

import boto3
boto3.set_stream_logger('botocore.auth')  # log the signature logic
boto3.set_stream_logger('botocore.endpoint')  # log the API request
# boto3.set_stream_logger('botocore.parsers')  # log the API response (if you want)

Теперь попробуйте отправить сообщение:

from django.core.mail import send_mail
send_mail("Test", "testing", None, ['success@simulator.amazonses.com'])

Вы должны увидеть вывод журнала, который выглядит примерно так:

2019-03-19 20:48:32,321 botocore.endpoint [DEBUG] Setting email timeout as (60, 60)
2019-03-19 20:48:32,580 botocore.endpoint [DEBUG] Making request for OperationModel(name=SendRawEmail) with params: {'body': {'Action': u'SendRawEmail', 'Version': u'2010-12-01', 'RawMessage.Data': [base64 message omitted]'}, 'url': u'https://email.us-east-1.amazonaws.com/', 'headers': {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'User-Agent': 'Boto3/1.9.117 Python/2.7.15 Darwin/18.2.0 Botocore/1.12.117 django-anymail/3.0-amazon-ses'}, 'context': {'auth_type': None, 'client_region': 'us-east-1', 'has_streaming_input': False, 'client_config': <botocore.config.Config object at 0x10dadd1d0>}, 'query_string': '', 'url_path': '/', 'method': u'POST'}
2019-03-19 20:48:32,581 botocore.auth [DEBUG] Calculating signature using v4 auth.
2019-03-19 20:48:32,581 botocore.auth [DEBUG] CanonicalRequest:
POST
/

content-type:application/x-www-form-urlencoded; charset=utf-8
host:email.us-east-1.amazonaws.com
x-amz-date:20190320T064832Z

content-type;host;x-amz-date
[redacted]
2019-03-19 20:48:32,582 botocore.auth [DEBUG] StringToSign:
AWS4-HMAC-SHA256
20190320T064832Z
20190320/us-east-1/ses/aws4_request
[redacted]
2019-03-19 20:48:32,582 botocore.auth [DEBUG] Signature:
[redacted]
2019-03-19 20:48:32,582 botocore.endpoint [DEBUG] Sending http request: <AWSPreparedRequest stream_output=False, method=POST, url=https://email.us-east-1.amazonaws.com/, headers={'Content-Length': '437', 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': 'AWS4-HMAC-SHA256 Credential=[key id redacted]/20190320/us-east-1/ses/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=[redacted]', 'X-Amz-Date': '20190320T064832Z', 'User-Agent': 'Boto3/1.9.117 Python/2.7.15 Darwin/18.2.0 Botocore/1.12.117 django-anymail/3.0-amazon-ses'}>

Важными частями здесь являются даты:

2019-03-19 20:48:32,581 botocore.auth [DEBUG] CanonicalRequest:
...
x-amz-date:20190320T064832Z

2019-03-19 20:48:32,582 botocore.auth [DEBUG] StringToSign:
...
20190320T064832Z
20190320/...

2019-03-19 20:48:32,582 botocore.endpoint [DEBUG] Sending http request: <AWSPreparedRequest ...
  headers={
    'Authorization': '.../20190320/...',
    'X-Amz-Date': '20190320T064832Z', ...}>

Обратите внимание, что все расчеты подписи основаны на дате UTC (2019-03-20), а не на текущей локальной дате в моем часовом поясе Django (2019-03-19).

Похоже, что boto3 использует UTC для вычислений подписи, несмотря на часовой пояс Django/ среды. И действительно, отправка у меня работает без ошибок.

Итак, вопрос в том, что отличается, когда вы видите проблему?

  • Что такое x-amz-date в CanonicalRequest?
  • Действительно ли это фактическая дата-время UTC, когда вы отправляете сообщение? (Если нет, то часы в вашем контейнере Docker могут быть далеко.)
  • Эта же дата снова отображается правильно в StringToSign, как полная отметка времени, так и усеченная дата?
  • И появляется ли он снова в заголовках AWSPreparedRequest, оба Authorization а также X-Amz-Date? (Если вы видите Date заголовок вместо X-Amz-Date, это тоже было бы интересно.)

Надеюсь, что это поможет вам либо приблизиться к решению, либо хотя бы выяснить, какие детали необходимы для воспроизведения проблемы.

Другие вопросы по тегам