Amazon SES с Django не в часовом поясе UTC - PullRequest
3 голосов
/ 15 марта 2019

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

Для поддержки этого я выбрал следующие настройки 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,
    },
}

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

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

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

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

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

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

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

1 Ответ

1 голос
/ 20 марта 2019

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

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

Наша рабочая теория заключается в том, что boto использует неправильный часовой пояс для вычисления временных отметок при подписании запроса API, поэтому давайте включим некоторые подробные loging 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? (Если вместо X-Amz-Date вы видите заголовок Date, это также будет интересно.)

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

...