2017-12-10 11 views
1

AWS.S3.upload()403エラー

TLをマルチパートアップロードしよう; DR

組み合わせるブラウザにJavaScriptが AWS SDKによって提供s3.upload()方法を使用してブラウザから直接ファイルをアップロードしようとしIAMクレジットAWS.STS.getFederationToken()への呼び出しによって生成されます。マルチパート以外のアップロードではすべて正常に動作し、はマルチパートアップロードの最初の部分であるになります。

しかしときS3403 Access Deniedエラーで応答マルチパートアップロードの第二の部分を送信するためにs3.upload()試み。

なぜですか?



コンテキスト

は私がマルチパート(チャンク)を有効に私のS3バケットにブラウザから直接アップロードします私のアプリでアップローダーを実施しています。

これを達成するために、私はnew AWS.S3.ManagedUpload()の基本的な利用のために砂糖以上であると理解しているAWS SDK for Javascript in the Browsers3.upload()メソッドを利用しています。私がしようとしてるかの

簡単な図がここで見つけることができます:https://aws.amazon.com/blogs/developer/announcing-the-amazon-s3-managed-uploader-in-the-aws-sdk-for-javascript/

はさらに、私はまた、アップロードを許可するために、私のAPI層からの一時的IAMユーザー資格情報を、販売するための手段としてAWS.STS.getFederationToken()を使用しています。

は1,2,3:

  1. ユーザーは、標準のHTML <input type="file">を経由してファイルを選択してアップロードを開始します。
  2. このアクションを実行するために、自分のシステムに必要な特権があることを確認するために、APIレイヤーへの最初のリクエストがトリガーされます。AWS.STS.getFederationToken()Policy paramを呼び出してアップロードするだけです提供されたキーへのファイルそして結果の一時的な信用をブラウザに返します。
  3. ブラウザに必要な一時信用があるので、新しいAWS.S3クライアントを作成してAWS.S3.upload()メソッドを実行して、ファイルの(複数の)自動マートマルチパートアップロードを実行することができます。



コード

api.myapp.com/vendUploadCreds.js

これは一時的なアップロードcredsをを生成し、販売すると呼ばれるAPI層方法です。プロセスのこの時点で、アカウントはすでに認証されており、信用を受け取り、ファイルをアップロードする権限を持っています。

module.exports = function vendUploadCreds(request, response) { 

    var account = request.params.account; 
    var file = request.params.file; 
    var bucket = 'cdn.myapp.com'; 

    var sts = new AWS.STS({ 
     AccessKeyId : process.env.MY_AWS_ACCESS_KEY_ID, 
     SecretAccessKey : process.env.MY_AWS_SECRET_ACCESS_KEY 
    }); 

    /// The following policy is *exactly* the same as the S3 policy 
    /// attached to the IAM user that executes this STS request. 

    var policy = { 
     Version : '2012-10-17', 
     Statement : [ 
      { 
       Effect : 'Allow', 
       Action : [ 
        's3:ListBucket', 
        's3:ListBucketMultipartUploads', 
        's3:ListBucketVersions', 
        's3:ListMultipartUploadParts', 
        's3:AbortMultipartUpload', 
        's3:GetObject', 
        's3:GetObjectVersion', 
        's3:PutObject', 
        's3:PutObjectAcl', 
        's3:PutObjectVersionAcl', 
        's3:DeleteObject', 
        's3:DeleteObjectVersion' 
       ], 
       Resource : [ 
        'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name 
       ], 
       Condition : { 
        StringEquals : { 
         's3:x-amz-acl' : ['private'] 
        } 
       } 
      } 
     ] 
    }; 

    sts.getFederationToken({ 
     DurationSeconds : 129600, /// 36 hours 
     Name : account._id + '-uptoken', 
     Policy : JSON.stringify(policy) 
    }, function(err, data) { 

     if (err) console.log(err, err.stack); // an error occurred 

     response.send(data); 

    }); 

} 


console.myapp.com/uploader.js

これは最初vendUploadCreds APIメソッドを呼び出し、次に、得られた一時的を使用し、ブラウザ側のアップローダの切断図でありますマルチパートアップロードを実行するためのcreds。

uploader.getUploadCreds(account, file) { 

    /// A request is sent to api.myapp.com/vendUploadCreds 
    /// Upon successful response, the creds are returned. 

    request('https://api.myapp.com/vendUploadCreds', { 
     params : { 
      account : account, 
      file : file 
     } 
    }, function(error, data) { 
     upload.credentials = data.credentials; 
     this.uploadFile(upload); 
    }); 

} 

uploader.uploadFile : function(upload) { 

    var uploadID = upload.id; 

    /// The `upload` object coming through via the args has 
    /// a `credentials` property containing the creds obtained 
    /// via the `vendUploadCreds` method above. 

    var credentials = new AWS.Credentials({ 
     accessKeyId : upload.credentials.AccessKeyId, 
     secretAccessKey : upload.credentials.SecretAccessKey, 
     sessionToken : upload.credentials.SessionToken 
    }); 

    AWS.config.region = 'us-east-1'; 

    var s3 = new AWS.S3({ 
     credentials, 
     signatureVersion : 'v2', /// 'v4' also attempted 
     params : { 
      Bucket : 'cdn.myapp.com' 
     } 
    }); 

    var uploader = s3.upload({ 
     Key : upload.key, 
     ACL : 'private', 
     ContentType : upload.file.type, 
     Body : upload.file 
    },{ 
     queueSize : 3, 
     partSize : 1024 * 1024 * 5 
    }); 

    uploader.on('httpUploadProgress', function(event) { 
     var total = event.total; 
     var loaded = event.loaded; 
     var percent = loaded/total; 
     percent = Math.ceil(percent * 100); 
     console.log('Uploaded ' + percent + '% of ' + upload.key); 
    }); 

    uploader.send(function(error, result) { 
     console.log(error, result); 
    }); 

} 


cdn.myapp.com S3バケットCORS設定

CORSは問題ではありませんので、これまでのところ、私が言うことができるように、これは、広く開いているのですか?私はファイルをアップロードしようとすると

<?xml version="1.0" encoding="UTF-8"?> 
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 
<CORSRule> 
    <AllowedOrigin>*</AllowedOrigin> 
    <AllowedMethod>GET</AllowedMethod> 
    <AllowedMethod>PUT</AllowedMethod> 
    <AllowedMethod>POST</AllowedMethod> 
    <AllowedMethod>DELETE</AllowedMethod> 
    <MaxAgeSeconds>3000</MaxAgeSeconds> 
    <ExposeHeader>ETag</ExposeHeader> 
    <AllowedHeader>*</AllowedHeader> 
</CORSRule> 
</CORSConfiguration> 


エラー

わかりましたが、そう、それは本当に混乱取得します。

  1. うまく5Mbのアップロードの下で任意のファイルを。 5Mb(S3マルチパートアップロードの最小パーツサイズ)以下のファイルはマルチパートアップロードを必要としないため、s3.upload()はそれらを標準のPUTリクエストとして送信します。理にかなって、うまくいく。
  2. 5MBを超えるファイルは、アップロードするにはと思われますが、最初の部分のみです。そして、s3.upload()が2番目の部分を送信しようとすると、S3は403 Access Deniedエラーで応答します。

私はここで私はアストラッド・ジルベルトの憂鬱古典"So Nice (Summer Samba)"(MP3、6をアップロードしようとしたとき、私はChromeから取得エラーのダンプですので、あなたは情報のファンだ願っています。6MB):

一般

Request URL:https://s3.amazonaws.com/cdn.myapp.com/5a2cbda70b9b741661ad98df/files/Astrud-Gilberto-So-Nice-1512903188573.mp3?partNumber=2&uploadId=ljaviv9n25aRKwc4HKGhBbbXTWI3wSGZwRRi39fPSEvU2dcM9G7gO6iu5w7va._dMTZil4e_b53Iy5ngojJqRr5F6Uo_ZXuF27yaqizeARmUVf5ZVeah8ZjYwkZV8C0i3rhluYoxFHUPxlLMjaKLww-- 
Request Method:PUT 
Status Code:403 Forbidden 
Remote Address:52.216.165.77:443 
Referrer Policy:no-referrer-when-downgrade 

レスポンスヘッダ

Access-Control-Allow-Methods:GET, PUT, POST, DELETE 
Access-Control-Allow-Origin:* 
Access-Control-Expose-Headers:ETag 
Access-Control-Max-Age:3000 
Connection:close 
Content-Type:application/xml 
Date:Sun, 10 Dec 2017 10:53:12 GMT 
Server:AmazonS3 
Transfer-Encoding:chunked 
Vary:Origin, Access-Control-Request-Headers, Access-Control-Request-Method 
x-amz-id-2:0Mzo7b/qj0r5Is7aJIIJ/U2VxTTulWsjl5kJpTnEhy/B0fQDlRuANcursnxI71LA16AdePVSc/s= 
x-amz-request-id:DA008A5116E0058F 

リクエストヘッダ

Accept:*/* 
Accept-Encoding:gzip, deflate, br 
Accept-Language:en-US,en;q=0.9 
Authorization:AWS ASIAJAR5KXKAOPTC64PQ:Wo9lbflZuVVS9+UTTDSjU0iPUbI= 
Cache-Control:no-cache 
Connection:keep-alive 
Content-Length:1314943 
Content-Type:application/octet-stream 
DNT:1 
Host:s3.amazonaws.com 
Origin:http://132.12.23.145:8080 
Pragma:no-cache 
Referer:http://132.12.23.145:8080/ 
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 
X-Amz-Date:Sun, 10 Dec 2017 10:53:09 GMT 
x-amz-security-token:FQoDYXdzENT//////////wEaDK9srK2+5FN91W+T+SLSA/LdEwpOiY7wDkgggOMhuGEiqIXAQrFMk/EqvZFl8Npqx414WsL9E310rj5mU1RGXsxuN+ers1r6NVPpJIlXSDG7bnwlGabejNvDL9vMX5HJHGbZOEVUoaL60/T5NM+0TZtH61vHAEVmRVFKOB0tSez8TEU1jQ2cJME0THn5RuV/6CuIpA9dlEYO7/ajB5UKT3F1rBkt12b0DeWmKG2pvTJRwa8nrsF6Hk6dk1B1Hl1fUwAh9rD17O9Roi7MFLKisPH+96WX08liC8k+n+kPPOox6ZZM/lOMwlNinDjLc2iC+JD/6uxyAGpNbQ7OHAUsF7DOiMvw6Nv6PrImrBvnK439BhLOk1VXCfxxmtTWGim8TD1w1EciZcJhsuCMpDF8fMnhF/JFw3KNOJXHUtpTGRjNbOPcPojVs3FgIt+9MllIA0pGMr2bYmA3HvKewnhD2qeKkG3DPDIbpwuRoY4wIXCP5OclmoHp5nE5O94aRIvkBvS1YmqDQO+jTiI7/O7vlX63q9sGqdIA4nwzh5ASTRJhC2rKgxepFirEB53dCev8i9f1pwXG3/4H3TvPCLVpK94S7/csNJexJP75bPBpo4nDeIbOBKKIMuUDK1pQsyuGwuUolKS00QU= 
X-Amz-User-Agent:aws-sdk-js/2.164.0 callback 

クエリ文字列のparams

partNumber:2 
uploadId:ljaviv9n25aRKwc4HKGhBbbXTWI3wSGZwRRi39fPSEvU2dcM9G7gO6iu5w7va._dMTZil4e_b53Iy5ngojJqRr5F6Uo_ZXuF27yaqizeARmUVf5ZVeah8ZjYwkZV8C0i3rhluYoxFHUPxlLMjaKLww-- 

実際の応答ボディ

そしてここでは、S3からの応答の本文です:

<?xml version="1.0" encoding="UTF-8"?> 
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>8277A4969E955274</RequestId><HostId>XtQ2Ezv0Wa81Rm2jymB5ZwTe+OHfwTcnNapYMgceqZCJeb75YwOa1AZZ5/10CAeVgmfeP0BFXnM=</HostId></Error> 


質問

  1. sts.generateFederationToken()リクエストによって作成されたクレジットに問題はないことは明らかです。小さい方の(マルチパートでない)アップロードも失敗するからです。
  2. cdn.myapp.comバケットのCORS設定に問題がないことは明らかです。小さい方の(マルチパート以外の)アップロードも失敗する可能性があるからです。
  3. S3が複数アップロードのpartNumber=1を受け入れ、同じアップロードのpartNumber=2で403を受け入れるのはなぜですか?

答えて

0

Aソリューション

私は問題は、私は私のAWS.STS.getFederationToken()Policyのparamとしてを通じて送信されたIAMポリシーConditionブロックであったことを考え出しこれでレスリングの多くの時間後要求。具体的には、AWS.S3.upload()は、S3.initiateMultipartUpoadへの呼び出しであるの最初のPUT要求に対してx-amz-aclヘッダーのみを送信します。

x-amz-aclヘッダーは、アップロードの実際の部分の次のPUT要求に含まれるではありません。です。

私は任意のアップロードは「プライベート」のACLを持たなければならないことを保証するために使用していた私のIAMポリシー、上の次の条件を持っていた:

Condition : { 
    StringEquals : { 
     's3:x-amz-acl' : ['private'] 
    } 
} 

だからS3.initiateMultipartUploadへの初期PUT要求は大丈夫でしたしかし、PUTは、x-amz-aclヘッダーがないために失敗しました。

解決方法は、一時的なユーザーに添付していたポリシーを編集し、s3:PutObjectのアクセス許可を独自のステートメントに移動してから、対象の値が存在する場合にのみ適用するように条件を調整することでした。最終的なポリシーは次のようになります。

var policy = { 
    Version : '2012-10-17', 
    Statement : [ 
     { 
      Effect : 'Allow', 
      Action : [ 
       's3:PutObject' 
      ], 
      Resource : [ 
       'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name 
      ], 
      Condition : { 
       StringEqualsIfExists : { 
        's3:x-amz-acl' : ['private'] 
       } 
      } 
     }, 
     { 
      Effect : 'Allow', 
      Action : [ 
       's3:AbortMultipartUpload' 
      ], 
      Resource : [ 
       'arn:aws:s3:::' + bucket + '/' + account._id + '/files/' + file.name 
      ] 
     } 
    ] 
}; 

これは3日間を無駄にするのに役立ちます。