前提
- Client -> ElasticBeanstalk(EC2) -> S3 という構成
- ClientとEB間にBasic認証をかける
- EBとS3間は、Authorization Headerを使った認証をかける
- ElasticBeanstalkではnginxを用いた
要件
- 実際はこのClientとEBの間にCloudFrontをはさみます
- 特定のpathだけBasic認証をかけたいという要望があり、基本的にはCF -> S3な構成なのですが、一部EBを経由させてここでBasic認証をかけるという狙い(この記事を書いた時点では、CFを介しての検証はしてないですが、CFを介したBasic認証の事例はたくさんあったので大丈夫という想定)
課題
最初は単純に、Basic認証とS3の認証をそれぞれクリアすればよいと思って以下のような設定にしました。
s3_auth.luaの中で、S3へアクセスするためのAuthorization Headerの値を生成してます。
...
location ~ ^/(.*)$ {
auth_basic "Restricted";
auth_basic_user_file /usr/local/nginx/conf/.htpasswd;
rewrite_by_lua_file '/usr/local/nginx/lua/s3_auth.lua';
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_intercept_errors on;
proxy_pass http://$bucket.s3.amazonaws.com/$1;
}
...
参考までにs3_auth.luaはこんな感じ
local date = ngx.http_time(ngx.time())
local string_to_sign = ngx.req.get_method() .. "\n\n\n" .. date .. "\n/" .. ngx.var.bucket .. "/" .. ngx.var.full_filename
ngx.req.set_header("Date", date)
local digest = ngx.hmac_sha1(ngx.var.secret_key, string_to_sign)
local signature = ngx.encode_base64(digest)
ngx.req.set_header("Authorization", "AWS " .. ngx.var.access_key .. ":" .. signature)
ngx.req.clear_header("x-amz-cf-id")
これだと、S3用のAuthorization Headerに書き換えてしまいBasic認証が通らなくなってしまうという罠にハマってしまいました。
内部的にproxyさせてあげることで、Basic認証とS3への認証を別々に扱うことができました。
...
location ~ ^/(.*)$ {
auth_basic "Restricted";
auth_basic_user_file /usr/local/nginx/conf/.htpasswd;
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_intercept_errors on;
proxy_pass http://127.0.0.1:8081/$1;
}
}
server {
listen 8081 default_server;
sendfile on;
tcp_nopush on;
send_timeout 120;
access_log off;
set_by_lua $bucket 'return os.getenv("BUCKET")';
set_by_lua $access_key 'return os.getenv("AWS_ACCESS_KEY")';
set_by_lua $secret_key 'return os.getenv("AWS_SECRET_KEY")';
location ~ ^/(.+)$ {
set $full_filename $1;
rewrite_by_lua_file '/usr/local/nginx/lua/s3_auth.lua';
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_hide_header Cache-Control;
proxy_set_header Host $bucket.s3.amazonaws.com;
proxy_pass http://$bucket.s3.amazonaws.com/$1;
error_page 415 /50x.html;
}
...