简体   繁体   English

Django的Amazon SES不在UTC时区

[英]Amazon SES with Django not in UTC timezone

I'm developing a django project for use in America, specifically the New York timezone and the system is hosted on AWS, with SES sending email. 我正在开发一个在美国使用的django项目,特别是纽约时区,该系统在AWS上托管,SES发送电子邮件。 The email backend is using django-anymail which is a simple wrapper for SES and the system uses send_mail from django core. 电子邮件后端使用的是django-anymail ,它是SES的一个简单包装器,系统使用django core的send_mail

To support this I've opted for the following Django settings; 为了支持这一点,我选择了以下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,
    },
}

With the above settings django calls tzset() on startup which modifies the system timezone. 通过上述设置,django在启动时调用tzset()来修改系统时区。 This then means the timestamp used by botocore to sign the requests for SES is not UTC, because the following error is received from message sending; 这意味着botocore用于签署SES请求的时间戳不是UTC,因为从消息发送中收到以下错误;

An error occurred (ExpiredToken) when calling the SendRawEmail operation: The security token included in the request is expired 调用SendRawEmail操作时发生错误(ExpiredToken):请求中包含的安全令牌已过期

Emails are sent successfully by changing settings to TIME_ZONE = 'UTC' . 通过将设置更改为TIME_ZONE = 'UTC'来成功发送电子邮件。

I can only assume that the requests are being signed in UTC -4 which then hit AWS which is in UTC. 我只能假设这些请求是以UTC-4签署的,然后以UTC格式登录AWS。

How can django run in a specific timezone, but boto operate with UTC timestamps? django如何在特定时区运行,但是boto是否以UTC时间戳运行?

The system is running in a docker container (pre-production); 系统在docker容器中运行(预生产);

  • docker compose 3.4 (unix host) docker compose 3.4(unix主机)
  • python 2.7 python 2.7
  • django 1.11 django 1.11
  • django-anymail 3.0 django-anymail 3.0
  • LocaleMiddleware is loaded LocaleMiddleware已加载

I'm not able to reproduce the error you're seeing with the settings you've described, but I can show you what is working correctly for me with extra logging, and you could compare that to your failing case to try to see what's different. 我无法使用您所描述的设置重现您所看到的错误,但我可以通过额外的日志记录向您展示正常工作的内容,您可以将其与失败案例进行比较,以尝试查看什么是不同。

I ran this code in the Django shell ( python manage.py shell ) just for convenience, but you could put it in a debugging view or anywhere else that works for you. 我在Django shellpython manage.py shell )中运行此代码只是为了方便,但您可以将它放在调试视图或其他适合您的地方。

Our working theory is that boto is using the wrong time zone to calculate timestamps for signing the API request, so let's enable some detailed boto3 logging that covers that area: 我们的工作原理是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)

Now try to send a message: 现在尝试发送消息:

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

You should see log output that looks something like this: 您应该看到如下所示的日志输出:

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'}>

The important parts here are the dates: 这里的重要部分是日期:

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', ...}>

Notice the signature calculations are all based on the UTC date (2019-03-20)—not the current local date in my Django timezone (2019-03-19). 请注意,签名计算均基于UTC日期(2019-03-20) - 而不是我的Django时区(2019-03-19)中的当前本地日期。

So it looks like boto3 does use UTC for the signature calculations, despite the Django/environment time zone. 所以看起来boto3 确实使用UTC进行签名计算,尽管有Django /环境时区。 And indeed, the send works for me without error. 事实上,发送对我没有错误。

So the question is, what's different when you see the problem? 所以问题是,当你看到问题时有什么不同?

  • What is the x-amz-date in the CanonicalRequest? CanonicalRequest中的x-amz-date是什么?
  • Is that, in fact, the actual UTC datetime when you send the message? 实际上,这是发送消息时的实际UTC日期时间吗? (If not, the clock in your Docker container might be way off.) (如果没有,Docker容器中的时钟可能会关闭。)
  • Does that same date appear again correctly in the StringToSign, both as a full timestamp and a truncated date? 同一日期是否在StringToSign中再次正确显示,作为完整时间戳和截断日期?
  • And does it appear again in the AWSPreparedRequest headers, both Authorization and X-Amz-Date ? 它是否会再次出现在AWSPreparedRequest标头中,包括AuthorizationX-Amz-Date (If you see a Date header instead of X-Amz-Date , that would also be interesting.) (如果您看到Date标头而不是X-Amz-Date ,那也会很有趣。)

Hope that helps you either get a little closer to a solution, or at least figure out what detail is essential to reproducing the problem. 希望能帮助您更接近解决方案,或者至少弄清楚哪些细节对于重现问题至关重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM