我正在尝试使用 nginx 代理对存储在 S3 上的文件的请求,但是我不想暴露我们的存储桶结构,并且我需要授权用户访问特定文件。除了隐藏存储桶结构外,我已设法使所有功能“正常工作”。
到目前为止,用户将请求https://example.com/s3/FILE_ID,它将被传递到授权部分(使用auth_request
)。然后它授权用户,并设置标题Content-Type
、S3Authorization
标题和其他数据(例如 S3 上的实际位置)。我希望能够rewrite .* $result_from_auth_request
,但它似乎不起作用,可能是因为rewrite
和auth_request
发生在不同的阶段?我已经到了可以从 S3 获得响应的地步,但由于我似乎无法更改请求的 URL,S3 授权失败。
基本上,我想映射example.com/s3/FILEID
到bucket.s3.amazonaws.com/some/bucket/path/NOT_RELATED_FILE_NAME
,但不向用户公开映射。有没有办法根据 的结果重写 url auth_request
?
这是我的示例配置:
server {
listen 0.0.0.0:443 ssl http2 default_server;
root /var/www;
ssl_certificate /etc/nginx/certs/localhost.crt;
ssl_certificate_key /etc/nginx/certs/localhost.key;
location /s3/ {
# Auth the request first
auth_request /auth;
set $bucket mybucket;
# Request will set these headers which we can pass to S3
auth_request_set $s3_host $upstream_http_x_s3_host;
auth_request_set $auth_status $upstream_status;
auth_request_set $ct $upstream_http_content_type;
auth_request_set $name $upstream_http_content_disposition;
auth_request_set $amzAuth $upstream_http_authorization;
auth_request_set $amzDate $upstream_http_x_amz_date;
auth_request_set $amzContent $upstream_http_x_amz_content_sha256;
# The auth handler sets this header as a way of specifying the the location on S3
auth_request_set $s3path $upstream_http_x_s3_path;
# Send these to the client so that the file will "download"
add_header Content-Type $ct;
add_header Content-Disposition $name;
proxy_http_version 1.1;
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
# Set these to send to S3
proxy_set_header Connection '';
proxy_set_header Host $bucket.s3.amazonaws.com;
proxy_set_header Authorization $amzAuth;
proxy_set_header x-amz-date $amzDate;
proxy_set_header x-amz-content-SHA256 $amzContent;
proxy_buffering off;
proxy_intercept_errors on;
proxy_pass_request_headers off;
# !!!!---------------
# Rewrite the url request to S3 to be the "correct" one
# This doesn't work, "$s3path" always seems to be empty
# !!!!---------------
rewrite .* "/$s3path" break;
resolver 8.8.8.8 valid=300s;
resolver_timeout 10s;
recursive_error_pages on;
error_page 301 302 307 = @handle_redirect;
proxy_pass https://$s3_host;
}
# Sometimes S3 does a redirect, so follow
location @handle_redirect {
error_log /dev/stdout debug;
resolver 8.8.8.8 valid=300s;
resolver_timeout 10s;
set $redirect_url $upstream_http_location;
proxy_pass $redirect_url;
}
location = /auth {
#error_log /dev/stdout debug;
internal;
proxy_http_version 1.1;
proxy_pass http://auth-service:8911/auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_pass_request_headers on;
}
location / {
#error_log /dev/stdout debug;
proxy_http_version 1.1;
proxy_redirect off;
proxy_read_timeout 6000s;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://app:1854;
}
}
答案1
我最终将请求传递给 varnish,然后在交给 s3 之前使用它进行 url 重写。(不过,我确实想知道是否有可能在没有 varnish 的情况下使用内部重定向)。
我的配置改变如下:
nginx.conf
upstream varnish_s3 {
server varnish_cache:80;
}
server {
listen 0.0.0.0:443 ssl http2 default_server;
root /var/www;
ssl_certificate /etc/nginx/certs/localhost.crt;
ssl_certificate_key /etc/nginx/certs/localhost.key;
location /s3/ {
# Auth the request first
auth_request /auth;
# Request will set these headers which we can pass to S3
auth_request_set $s3_host $upstream_http_x_s3_host;
auth_request_set $auth_status $upstream_status;
auth_request_set $ct $upstream_http_content_type;
auth_request_set $name $upstream_http_content_disposition;
auth_request_set $amzAuth $upstream_http_authorization;
auth_request_set $amzDate $upstream_http_x_amz_date;
auth_request_set $amzContent $upstream_http_x_amz_content_sha256;
# The auth handler sets this header as a way of specifying the the location on S3
auth_request_set $s3path $upstream_http_x_s3_path;
# Send these to the client so that the file will "download"
add_header Content-Type $ct;
add_header Content-Disposition $name;
proxy_http_version 1.1;
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
# Set these to send to S3
proxy_set_header Connection '';
proxy_set_header Host $bucket.s3.amazonaws.com;
proxy_set_header Authorization $amzAuth;
proxy_set_header x-amz-date $amzDate;
proxy_set_header x-amz-content-SHA256 $amzContent;
# Send to varnish
proxy_set_header X-S3-Path $s3path;
proxy_buffering off;
proxy_intercept_errors on;
proxy_pass_request_headers off;
# resolver 8.8.8.8 valid=300s;
#resolver_timeout 10s;
recursive_error_pages on;
error_page 301 302 307 = @handle_redirect;
proxy_pass http://varnish_s3;
}
# Sometimes S3 does a redirect, so follow
location @handle_redirect {
#error_log /dev/stdout debug;
set $redirect_url $upstream_http_location;
proxy_pass $redirect_url;
}
location = /auth {
#error_log /dev/stdout debug;
internal;
proxy_http_version 1.1;
proxy_pass http://auth-service:8911/auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_pass_request_headers on;
}
location / {
#error_log /dev/stdout debug;
proxy_http_version 1.1;
proxy_redirect off;
proxy_read_timeout 6000s;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://app:1854;
}
}
varnish.default.vcl
vcl 4.0;
backend s3 {
.host = "s3.amazonaws.com";
.port = "80";
}
sub vcl_recv {
if (req.http.X-S3-Path) {
set req.url = req.http.X-S3-Path;
return (pipe);
}
return (synth(401, "Forbidden"));
}
sub vcl_pipe {
unset bereq.http.x-s3-path;
unset bereq.http.x-varnish;
unset bereq.http.x-forwarded-for;
}