signed urlを使っているわけでもないのにSignatureDoesNotMatchと言われる
ACLを設定していない、signed urlも使っていないバケットに対して接続してオブジェクトの取得をするコードを実装したら、 SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
と言われてしまいました。
同じようなエラーにハマった人の手助けになれば幸いです。
TL;DR
- バケット名に
/
を含めないようにする。
コード
func hogehoge() error {
config := aws.Config{
Region: aws.String(os.Getenv("REGION")),
}
sess := session.New(&config)
d := s3manager.NewDownloader(sess)
n, err := d.Download(file, &s3.GetObjectInput{
Bucket: aws.String("hogehoge/"), //!!!!!!!
Key: aws.String("piyopiyo/year=2020/month=04/fugafuga.gz"),
})
if err != nil {
return err //エラーになる
}
fmt.Printf("downloaded %d bytes", n)
}
エラーになる場合と、ならない場合
コードに書いてある通りですが、バケット名の後ろに間違えて /
をつけてしまっています。
このバケット名から /
を取り除けばすべての場合において正しく動作します。
ただし、 /
をつけていると、少し不思議な動作をしました。
エラーにならない
- バケットに対して
ListObjects
を実行し、オブジェクトのリストを取得できる。 - 上記で取得したオブジェクトのメタ(keyとか)を参照できる。
- keyに
=
を含まないS3オブジェクトをDLできる。
エラーになる
- keyに
=
を含むS3オブジェクトがDLできない。
直接的な原因はわかりませんでしたが、aws-sdk-go内部の実装を見る限りS3オブジェクトをDLする際はGETリクエストを発行しているようですので、パラメータのURLencode時に =
が悪さしているのではないかなと考えました。
https://github.com/aws/aws-sdk-go/issues/3110
また、こちらのissueで言及されているように、「バケット名として /
や =
は使用できない。」というS3自体のルールがあります。
aws-sdk-goでもその前提に則って実装した結果、今回のように「DLできたり、できなかったりする」不思議な挙動が起きたのではないかと考えられます。