Это продолжение моего предыдущего вопроса о подписании документа политики с использованием профилей экземпляров.
Я разрабатываю систему, которая позволяет перетаскивать и выгружать файлы непосредственно в ведро S3; сначала выполняется запрос AJAX на мой сервер, содержащий метаданные файла. После проверки мой сервер отвечает параметрами формы, которые используются для завершения загрузки.
Процесс настройки загрузок на основе браузера объясняется здесь, и все это работает в локальной тестовой среде.
Однако, как только мое приложение будет развернуто в экземпляре EC2, я вижу эту ошибку, когда браузер пытается загрузить файл:
<Error> <Code>InvalidAccessKeyId</Code> <Message>The AWS Access Key Id you provided does not exist in our records.</Message> <RequestId>...</RequestId> <HostId>...</HostId> <AWSAccessKeyId>ASIAxxxyyyzzz</AWSAccessKeyId> </Error>
Значение ASIAxxxyyyzzz
здесь происходит из учетных данных роли экземпляра , полученных из службы метаданных; кажется, что эти учетные данные не могут использоваться вне EC2 для облегчения загрузки на основе браузера.
Я также просмотрел службу маркеров безопасности, чтобы создать другой набор временных учетных данных, выполнив следующие действия:
$token = $sts->assumeRole(array( 'RoleArn' => 'arn:aws:iam::xyz:role/mydomain.com', 'RoleSessionName' => 'uploader', )); $credentials = new Credentials($token['Credentials']['AccessKeyId'], $token['Credentials']['SecretAccessKey']);
Этот вызов дает мне новый набор учетных данных, но при его использовании он дает ту же ошибку, что и выше.
Я надеюсь, что кто-то это сделал раньше и может рассказать мне, какую тупую вещь я пропустил 🙂
Документы AWS очень сбивают с толку, но я подозреваю, что вам нужно включить параметр x-amz-security-token
в запросе POST для загрузки S3 и что его значение соответствует SessionToken
вы получаете из STS ( $token['Credentials']['SessionToken']
).
Временные учетные данные STS действительны только при включении соответствующего токена безопасности.
Документация AWS для запроса POST гласит, что:
Для каждого запроса, использующего Amazon DevPay, требуется два поля формы для маркеров безопасности: один для токена продукта и один для токена пользователя.
но этот параметр также используется вне DevPay, чтобы передать токен STS, и вам нужно будет только один раз передать его в полях формы.
Как указано в ответе dcro , токен сеанса должен быть передан службе, которую вы используете при использовании временных учетных данных. В официальной документации упоминается поле x-amz-security-token
, но, похоже, предполагается, что оно используется только для DevPay; это, вероятно, потому, что DevPay использует один и тот же тип временных учетных данных и поэтому требует токена безопасности сеанса.
2013-10-16 : Amazon обновила свою документацию, чтобы сделать это более очевидным.
Как оказалось, даже не нужно использовать STS; учетные данные, полученные службой метаданных, также имеют такой токен сеанса. Этот токен автоматически передается для вас, когда SDK используется вместе с временными учетными данными, но в этом случае окончательный запрос выполняется браузером и, следовательно, должен быть явно передан.
Ниже приведен мой рабочий код:
$credentials = Credentials::factory(); $signer = new S3Signature(); $policy = new AwsUploadPolicy(new DateTime('+1 hour', new DateTimeZone('UTC'))); $policy->setBucket('upload.mydomain.com'); $policy->setACL($policy::ACL_PUBLIC_READ); $policy->setKey('uploads/test.jpg'); $policy->setContentType('image/jpeg'); $policy->setContentLength(5034); $fields = array( 'AWSAccessKeyId' => $credentials->getAccessKeyId(), 'key' => $path, 'Content-Type' => $type, 'acl' => $policy::ACL_PUBLIC_READ, 'policy' => $policy, ); if ($credentials->getSecurityToken()) { // pass security token $fields['x-amz-security-token'] = $credentials->getSecurityToken(); $policy->setSecurityToken($credentials->getSecurityToken()); } $fields['signature'] = $signer->signString($policy, $credentials);
Я использую вспомогательный класс для создания политики, называемой AwsUploadPolicy
; на момент написания это не является полным, но это может помочь другим с аналогичной проблемой.
Разрешения были последней проблемой; мой код устанавливает ACL для public-read
и для этого требуется дополнительное разрешение s3:PutObjectAcl
.
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:PutObject", "s3:PutObjectAcl" ], "Sid": "Stmt1379546195000", "Resource": [ "arn:aws:s3:::upload.mydomain.com/uploads/*" ], "Effect": "Allow" } ] }