Amazon SES with Django not in UTC timezone - django

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.
The email backend is using django-anymail which is a simple wrapper for SES and the system uses send_mail from django core.
To support this I've opted for the following Django settings;
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. 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;
An error occurred (ExpiredToken) when calling the SendRawEmail operation: The security token included in the request is expired
Emails are sent successfully by changing settings to TIME_ZONE = 'UTC'.
I can only assume that the requests are being signed in UTC -4 which then hit AWS which is in UTC.
How can django run in a specific timezone, but boto operate with UTC timestamps?
The system is running in a docker container (pre-production);
docker compose 3.4 (unix host)
python 2.7
django 1.11
django-anymail 3.0
LocaleMiddleware is loaded

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.
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:
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).
So it looks like boto3 does use UTC for the signature calculations, despite the Django/environment time zone. 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?
Is that, in fact, the actual UTC datetime when you send the message?
(If not, the clock in your Docker container might be way off.)
Does that same date appear again correctly in the StringToSign, both as a full timestamp and a truncated date?
And does it appear again in the AWSPreparedRequest headers, both Authorization and X-Amz-Date? (If you see a Date header instead of X-Amz-Date, that would also be interesting.)
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.

Related

AWS Cognito cookie storage

I'm trying to set up Cognito to use cookies instead of localStorage for credentials so that I can keep the user logged in between domains, e.g. x.foo.com and y.foo.com. The first step is to get it working on localhost but I'm stuck.
The documentation shows a simple config change should do the trick?
The following debug messages are comitted to the console:
[DEBUG] 37:08.223 AuthClass
Object { idToken: {…}, refreshToken: {…}, accessToken: {…}, clockDrift: 0 }
ConsoleLogger.js:87
[DEBUG] 37:08.228 Credentials - No Cache module registered in Amplify ConsoleLogger.js:84
[DEBUG] 37:08.230 Credentials - set credentials from session ConsoleLogger.js:84
[DEBUG] 37:08.230 Credentials - No Cognito Federated Identity pool provided ConsoleLogger.js:84
[DEBUG] 37:08.230 AuthClass - cannot get cognito credentials No Cognito Federated Identity pool provided ConsoleLogger.js:94
[DEBUG] 37:08.231 AuthClass - Failed to get user from user pool ConsoleLogger.js:84
[ERROR] 37:08.232 AuthClass - Failed to get the signed in user No current user
It seems when you specify the cookieStorage config you need to manually apply a cache instance? How do I do that and will it solve the problem?
This config works:
{
region: 'eu-west-1',
userPoolId: 'eu-west-1_XXXXXX',
userPoolWebClientId: 'XXXXXX',
mandatorySignIn: false,
cookieStorage: {
domain: 'localhost',
secure: false,
path: '/',
expires: 365,
},
}
In particular, secure must be false for localhost unless you are using https (Firefox ignores this for localhost, but Chrome and Safari don't).

How to access S3 with profile on EC2? (sts.amazonaws.com:443 error)

I have EC2 and can access S3 using ec2-user with:
aws s3 ls --profile myprofile s3://
On the same EC2 I created docker container and installed aws cli tool. Created same ~./aws/config file with same content as on host ec2 but this command (created ec2-user to be consistent with host user/setup) from docker container now gives error:
- MainThread - botocore.auth - DEBUG - StringToSign:
AWS4-HMAC-SHA256
- MainThread - botocore.endpoint - DEBUG - Sending http request: <AWSPreparedRequest stream_output=False, method=POST, url=https://sts.amazonaws.com/, headers={'Content-Type': b'application/x-www-form-urlencoded; charset=utf-8', 'User-Agent': b'aws-cli/1.16.190 Python/3.7.3 Linux/3.10.0-957.10.1.el7.x86_64 botocore/1.12.180', 'X-Amz-Date': b'2019Z', 'X-Amz-Security-Token': b'1234=', 'Authorization': b'AWS4-HMAC-SHA256 Credential=abc/2019/us-east-1/sts/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token, Signature=abc', 'Content-Length': '160'}>
- MainThread - urllib3.util.retry - DEBUG - Converted retries value: False -> Retry(total=False, connect=None, read=None, redirect=0, status=None)
- MainThread - urllib3.connectionpool - DEBUG - Starting new HTTPS connection (1): sts.amazonaws.com:443

only OPTIONS request is sent with a 200 response

My situation is a bit similar to this question but it differs a bit nor there is an answer.
My backend is python and front-end is Angular. Live sever is Ngnix/Unix while dev is Windows. Every request just sends OPTIONS request, with successful response of 200 but then GET/POST are not followed. It was working perfectly fine and only on production sever is it not working. There are no CORS errors in the console and backend is debug= True for checking purposes but not problems so far cos obviously no get/post is being made.
On development machine, all is working. Previous team had added a custom header 'language':
const clonedRequest = req.clone({ headers: req.headers.set('Language', lang) });
which I noticed is never sent when connected to the live one. In the development setup, I see the following headers:
Accept application/json
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Connection keep-alive
Content-Length 21
Content-Type application/json
Host 127.0.0.1:9000
Language en
Origin http://localhost:4800
Referer http://localhost:4800/start/forgot-pwd
When connecting to the production from front end (Development or in production), the Language header is not being sent but I doubt it has issues. Regardless, my CORS on the backend looks like this:
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'language',
'x-requested-with',
)
You need to whitelist specific origins.
Using corsheaders such as:
CORS_ORIGIN_WHITELIST = [
'localhost:4800',
'127.0.0.1:4800'
]
You also need to add to you middlewares right after SecurityMiddleware
'corsheaders.middleware.CorsMiddleware',
And you also need to add 'corsheaders', to you INSTALLED_APPS.
Furthermore, you can simply have CORS_ALLOW_CREDENTIALS = True set and in addition to what I listed above to have CORS working.

Error 302 running cron and login:admin in app.yaml in google app engine

I added login: Admin to app.yaml like documentation is described, but I still have a 302 erro when runing a task with cron in google app engine.
I have this app.yaml
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: main.app
login: admin
And this cron.yaml
cron:
- description: "Dashboard"
url: /processdate?from=2016-03-01&until=2016-03-31
schedule: every day 23:46
timezone: Europe/Madrid
I obtain this error
0.1.0.1 - - [26/Oct/2018:00:49:40 +0200] "GET /processdate?from=2016-03-01&until=2016-03-31 HTTP/1.1" 302 355 - "AppEngine-Google; (+http://code.google.com/appengine)" "p20000.appspot.com" ms=74 cpu_ms=12 cpm_usd=3.9674e-8 loading_request=0 instance=00c61b117c78f767097d6896daa1f8967a815c14a94d54578ac19efa9d50a5077d5a app_engine_release=1.9.65 trace_id=3c92edad090b5a57d249bd92be246e58
httpRequest: {
status: 302
}
insertId: "5bd248840005a3aae7fa2111"
labels: {
clone_id: "00c61b117c78f767097d6896daa1f8967a815c14a94d54578ac19efa9d50a5077d5a"
}
logName: "projects/p201309/logs/appengine.googleapis.com%2Frequest_log"
operation: {
first: true
id: "5bd2488400ff047fe69ec5d94d0001657e62692d70682d3230313330390001323031383130323674303033383339000100"
last: true
producer: "appengine.googleapis.com/request_id"
}
protoPayload: {
#type: "type.googleapis.com/google.appengine.logging.v1.RequestLog"
appEngineRelease: "1.9.65"
appId: "e~myappname"
cost: 3.9674e-8
endTime: "2018-10-25T22:49:40.369327Z"
finished: true
first: true
host: "p200000.appspot.com"
httpVersion: "HTTP/1.1"
instanceId: "00c61b117c78f767097d6896daa1f8967a815c14a94d54578ac19efa9d50a5077d5a"
instanceIndex: -1
ip: "0.1.0.1"
latency: "0.074441s"
megaCycles: "12"
method: "GET"
requestId: "5bd2488400ff047fe69ec5d94d0001657e62692d70682d3230313330390001323031383130323674303033383339000100"
resource: "/processdate?from=2016-03-01&until=2016-03-31"
responseSize: "355"
startTime: "2018-10-25T22:49:40.294886Z"
status: 302
taskName: "25ed634cde05b07d9a7906f2161d2b16"
taskQueueName: "__cron"
traceId: "3c92edad090b5a57d249bd92be246e58"
traceSampled: true
urlMapEntry: "main.app"
userAgent: "AppEngine-Google; (+http://code.google.com/appengine)"
versionId: "20181026t003839"
}
receiveTimestamp: "2018-10-25T22:49:40.376251430Z"
resource: {
labels: {
module_id: "default"
project_id: "myappname"
version_id: "20181026t003839"
zone: "eu2"
}
type: "gae_app"
}
timestamp: "2018-10-25T22:49:40.294886Z"
trace: "projects/myappname/traces/3c92edad090b5a57d249bd92be246e58"
traceSampled: true
}
Any idea about what could be the problem or how to solve it?
I don't have any problem runing this script in my local machine.
------ UPDATED WITH A MUCH MORE SIMPLE APP ------
I tested something much more simple. This is working when I run https://myappname.appspot.com/hellocron and http://myappname.appspot.com/hellocron
With this the result it's what expected.
But when I run it with cron error 302 it's returned again
----------- app.yaml
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /hellocron
script: main.app
login: admin
secure: always
----------- cron.yaml
cron:
- description: "hellocron"
url: /hellocron
schedule: every day 23:46
timezone: Europe/Madrid
------------ I had the same error result
0.1.0.1 - - [01/Nov/2018:12:29:49 +0100] "GET /hellocron HTTP/1.1" 302 267 - "AppEngine-Google; (+http://code.google.com/appengine)" "myappname.appspot.com" ms=8 cpu_ms=9 cpm_usd=2.9839e-8 loading_request=0 instance=00c61b117cb863320ce80ff59a2b5b4b20ee440529428f43d612baa0e980733727302b27 app_engine_release=1.9.65 trace_id=9b9fa8ad127ad41f7907529c3863a0a9
{
httpRequest: {
status: 302
}
insertId: "5bdae3ad0004cfe52d64d457"
labels: {
clone_id: "00c61b117cb863320ce80ff59a2b5b4b20ee440529428f43d612baa0e980733727302b27"
}
logName: "projects/myappname/logs/appengine.googleapis.com%2Frequest_log"
operation: {
first: true
id: "5bdae3ad00ff04ac4decaecea60001657e62692d70682d3230313330390001323031383131303174313135363535000100"
last: true
producer: "appengine.googleapis.com/request_id"
}
protoPayload: {
#type: "type.googleapis.com/google.appengine.logging.v1.RequestLog"
appEngineRelease: "1.9.65"
appId: "e~myappname"
cost: 2.9839e-8
endTime: "2018-11-01T11:29:49.315161Z"
finished: true
first: true
host: "myappname.appspot.com"
httpVersion: "HTTP/1.1"
instanceId: "00c61b117cb863320ce80ff59a2b5b4b20ee440529428f43d612baa0e980733727302b27"
instanceIndex: -1
ip: "0.1.0.1"
latency: "0.008908s"
megaCycles: "9"
method: "GET"
requestId: "5bdae3ad00ff04ac4decaecea60001657e62692d70682d3230313330390001323031383131303174313135363535000100"
resource: "/hellocron"
responseSize: "267"
startTime: "2018-11-01T11:29:49.306253Z"
status: 302
taskName: "b0467e8a57f53a8ee2b827ca35db275f"
taskQueueName: "__cron"
traceId: "9b9fa8ad127ad41f7907529c3863a0a9"
traceSampled: true
urlMapEntry: "main.app"
userAgent: "AppEngine-Google; (+http://code.google.com/appengine)"
versionId: "20181101t115655"
}
receiveTimestamp: "2018-11-01T11:29:49.321937019Z"
resource: {
labels: {
module_id: "default"
project_id: "myappname"
version_id: "20181101t115655"
zone: "eu2"
}
type: "gae_app"
}
timestamp: "2018-11-01T11:29:49.306253Z"
trace: "projects/myappname/traces/9b9fa8ad127ad41f7907529c3863a0a9"
traceSampled: true
}
The code for /hellocron in python is this one basically:
decorator = OAuth2DecoratorFromClientSecrets(
os.path.join(os.path.dirname(__file__), 'client_secrets.json'),
scope='https://www.googleapis.com/auth/bigquery')
class hellocron (webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
self.response.write('hellocron')
app = webapp2.WSGIApplication([
('/hellocron', hellocron),
(decorator.callback_path, decorator.callback_handler()) ], debug=True)
You want to drop the #decorator.oauth_required from the cron url handler code.
The cron service does not have any user credentials (it doesn't run as a user) so that decorator will cause a re-direction to a login service - hence the 302 response. You should be able to verify this by re-trying your manual check but from an incognito browser window.
To secure the cron service URLs you can't use regular user authentication for this reason. But you can use login: admin in the app.yaml file and, if you want, also check for the X-Appengine-Cron: true header or the 0.1.0.1 source IP address, see Securing URLs for cron.
Similarly you might need to drop the secure: always from the respective app.yaml handler definition (I don't have it enabled for my app): I'm not sure if the cron service makes its GET request using http or https. If it makes it using http the secure: always config will also cause a redirection to a https URL. You can easily check if this is correct or not following my earlier comment after you drop the decorator.
My name is Dan I'm from google cloud support.
As I could understand, you're receiving a 302 response when launching Cron jobs.
Have you a custom domain with SSL enabled? I ask you this because we have received this report from some customers that are using HTTP instead of HTTPS.
Are you running your application on App Engine flexible? This information will help me to better understand your current scenario.
I will be awaiting your reply.
I wanted to post this solution as I was pulling my hair out yesterday trying to figure out why my GAE cron jobs kept failing and this thread helped me fine tune the solution that worked.
I am using Python, but the idea seems the same. In my cron script (i.e. the page visited with the cron logic), I had a redirect after successful execution of a report, due to this the cron would state it failed in GAE, but the logic would still execute correctly.
To fix the issue, all I did was return a simple HTTP response so the cron thought everything loaded correctly on the page instead of the redirect or not returning an HTTP response all together.
I was trying to execute a django admin endpoint from gcloud app engine. It seems there is no way to make gcloud crons login with any credentials before running so here is what you have to do if you find yourself in this situation (receiving 302 in your crons executions):
Make the url public, that means: no credentials needed to run it.
Gcloud crons send request including X-AppEngine-Cron in it's headers. They strip 'X-' from all their headers so you may trust this particular one. All you have to do is check for this header in your endpoint request as this guy does.
Doc: https://cloud.google.com/appengine/docs/flexible/go/scheduling-jobs-with-cron-yaml?hl=es-419#validating_cron_requests

Sending emails using django-SES (Amazon SES)

I've been trying to configure the django-SES services to send outgoing emails and am not sure what is wrong here. Am receving a strange error message,
settings.py
# Email Configuration using Amazon SES Services
EMAIL_BACKEND = 'django_ses.SESBackend'
# These are optional -- if they're set as environment variables they won't
# need to be set here as well
AWS_SES_ACCESS_KEY_ID = 'xxxxxxx'
AWS_SES_SECRET_ACCESS_KEY = 'xxxxxxxxxxxxx'
# Additionally, you can specify an optional region, like so:
AWS_SES_REGION_NAME = 'us-east-1'
AWS_SES_REGION_ENDPOINT = 'email-smtp.us-east-1.amazonaws.com'
In my design, am inserting all emails into a table and then using celery task to go through all pending emails and firing them.
here is my tasks.py
#task(name='common_lib.send_notification', ignore_result=True)
#transaction.commit_manually
def fire_pending_email():
try:
Notification = get_model('common_lib', 'Notification')
NotificationEmail = get_model('common_lib', 'NotificationEmail')
pending_notifications=Notification.objects.values_list('id', flat=True).filter(status=Notification.STATUS_PENDING)
for email in NotificationEmail.objects.filter(notification__in=pending_notifications):
msg = EmailMultiAlternatives(email.subject, email.text_body, 'noreply#xx.com.xx', [email.send_to, ])
if email.html_body:
msg.attach_alternative(email.html_body, "text/html")
msg.send()
transaction.commit()
return 'Successful'
except Exception as e:
transaction.rollback()
logging.error(str(e))
finally:
pass
yet in the celery debug console am seeing the following error
[2012-11-13 11:45:28,061: INFO/MainProcess] Got task from broker: common_lib.send_notification[4dc71dee-fc7c-4ddc-a02c-4097c73e4384]
[2012-11-13 11:45:28,069: DEBUG/MainProcess] Mediator: Running callback for task: common_lib.send_notification[4dc71dee-fc7c-4ddc-a02c-4097c73e4384]
[2012-11-13 11:45:28,069: DEBUG/MainProcess] TaskPool: Apply <function trace_task_ret at 0x9f38a3c> (args:('common_lib.send_notification', '4dc71dee-fc7c-4ddc-a02c-4097c73e4384', [], {}, {'retries': 0, 'is_eager': False, 'task': 'common_lib.send_notification', 'group': None, 'eta': None, 'delivery_info': {'priority': None, 'routing_key': u'celery', 'exchange': u'celery'}, 'args': [], 'expires': None, 'callbacks': None, 'errbacks': None, 'hostname': 'ubuntu', 'kwargs': {}, 'id': '4dc71dee-fc7c-4ddc-a02c-4097c73e4384', 'utc': True}) kwargs:{})
[2012-11-13 11:45:28,077: DEBUG/MainProcess] Task accepted: common_lib.send_notification[4dc71dee-fc7c-4ddc-a02c-4097c73e4384] pid:8256
[2012-11-13 11:45:28,097: DEBUG/MainProcess] (0.001) SELECT `common_lib_notification_email`.`id`, `common_lib_notification_email`.`notification_id`, `common_lib_notification_email`.`send_to`, `common_lib_notification_email`.`template`, `common_lib_notification_email`.`subject`, `common_lib_notification_email`.`html_body`, `common_lib_notification_email`.`text_body` FROM `common_lib_notification_email` WHERE `common_lib_notification_email`.`notification_id` IN (SELECT U0.`id` FROM `common_lib_notification` U0 WHERE U0.`status` = 'P' ); args=(u'P',)
[2012-11-13 11:45:28,103: DEBUG/MainProcess] Method: POST
[2012-11-13 11:45:28,107: DEBUG/MainProcess] Path: /
[2012-11-13 11:45:28,107: DEBUG/MainProcess] Data: Action=GetSendQuota
[2012-11-13 11:45:28,107: DEBUG/MainProcess] Headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
[2012-11-13 11:45:28,109: DEBUG/MainProcess] Host: email-smtp.us-east-1.amazonaws.com
[2012-11-13 11:45:28,109: DEBUG/MainProcess] establishing HTTPS connection: host=email-smtp.us-east-1.amazonaws.com, kwargs={}
[2012-11-13 11:45:28,109: DEBUG/MainProcess] Token: None
[2012-11-13 11:45:28,702: DEBUG/MainProcess] wrapping ssl socket; CA certificate file=/home/mo/projects/garageenv/local/lib/python2.7/site-packages/boto/cacerts/cacerts.txt
[2012-11-13 11:45:29,385: DEBUG/MainProcess] validating server certificate: hostname=email-smtp.us-east-1.amazonaws.com, certificate hosts=[u'email-smtp.us-east-1.amazonaws.com']
[2012-11-13 11:45:39,618: ERROR/MainProcess] <unknown>:1:0: syntax error
[2012-11-13 11:45:39,619: INFO/MainProcess] Task common_lib.send_notification[4dc71dee-fc7c-4ddc-a02c-4097c73e4384] succeeded in 11.5491399765s: None
UPDATE
when I changed the setting to
AWS_SES_REGION_ENDPOINT = 'email.us-east-1.amazonaws.com'
I got a different error, as below
[2012-11-13 13:24:05,907: DEBUG/MainProcess] Method: POST
[2012-11-13 13:24:05,916: DEBUG/MainProcess] Path: /
[2012-11-13 13:24:05,917: DEBUG/MainProcess] Data: Action=GetSendQuota
[2012-11-13 13:24:05,917: DEBUG/MainProcess] Headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
[2012-11-13 13:24:05,918: DEBUG/MainProcess] Host: email.us-east-1.amazonaws.com
[2012-11-13 13:24:05,918: DEBUG/MainProcess] establishing HTTPS connection: host=email.us-east-1.amazonaws.com, kwargs={}
[2012-11-13 13:24:05,919: DEBUG/MainProcess] Token: None
[2012-11-13 13:24:06,511: DEBUG/MainProcess] wrapping ssl socket; CA certificate file=/home/mo/projects/garageenv/local/lib/python2.7/site-packages/boto/cacerts/cacerts.txt
[2012-11-13 13:24:06,952: DEBUG/MainProcess] validating server certificate: hostname=email.us-east-1.amazonaws.com, certificate hosts=['email.us-east-1.amazonaws.com', 'email.amazonaws.com']
[2012-11-13 13:24:07,177: ERROR/MainProcess] 403 Forbidden
[2012-11-13 13:24:07,178: ERROR/MainProcess] <ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<Error>
<Type>Sender</Type>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
</Error>
<RequestId>41c15592-2d7c-11e2-a590-f33d1568f3ea</RequestId>
</ErrorResponse>
[2012-11-13 13:24:07,180: ERROR/MainProcess] BotoServerError: 403 Forbidden
<ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<Error>
<Type>Sender</Type>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
</Error>
<RequestId>41c15592-2d7c-11e2-a590-f33d1568f3ea</RequestId>
</ErrorResponse>
[2012-11-13 13:24:07,184: INFO/MainProcess] Task common_lib.send_notification[3b6a049e-d5cb-45f4-842b-633d816a132e] succeeded in 1.31089687347s: None
Can you try using this
AWS_SES_REGION_ENDPOINT = 'email.us-east-1.amazonaws.com'
And not the smtp server setting on AWS's dashboard?
(You used AWS_SES_REGION_ENDPOINT = 'email-smtp.us-east-1.amazonaws.com' as mentioned above)
Once you have updated this, you got a new error as updated in your question. This confirms that you now have the correct AWS_SES_REGION_ENDPOINT setting set.
The reason you are getting this new error is most likely because you are confusing the access keys and giving amazon a wrong set of credentials - see detailed comments here - https://github.com/boto/boto/issues/476#issuecomment-7679158
Follow the solution prescribed in the comment and you should be fine, I think.