How To: Проксирование в приватный AWS S3 через Nginx
Проблема
Иногда нам надо давать доступ к приватному баккету в Amazon S3 с авторизацией в наших сервисах, например LDAP или банальный Basic Auth. Стандартный Nginx, к сожалению, этого не позволяет сделать. Но в интернете полно инструкций как это сделать с помощью LUA модуля Nginx. Тут я расскажу как собрать Nginx с модулем LUA и всем необходимым, чтоб можно было проксировать запросы в Amazon S3 и в конце укажу на ошибку в конфигурации из сети, которая валяется почти на каждом углу. После чего мы эту ошибку исправим.
Сборка Nginx
Для сборки нам понадобятся исходники самого Nginx, модуля LUA, Nginx Development Kit и Set Misc модуль для Nginx. Итак, устанавливаев необходимое программное обеспечение
apt install build-essential automake libgd-dev libluajit-5.1-dev libgeoip-dev
Скачиваем необходимые модули и исходники:
mkdir -p ~/tmp/nginx
wget -O ~/tmp/nginx/nginx.tar.gz http://nginx.org/download/nginx-1.11.1.tar.gz
cd ~/tmp/nginx
tar -vxzf ~/tmp/nginx/nginx.tar.gz
git clone https://github.com/simpl/ngx_devel_kit.git
git clone https://github.com/openresty/lua-nginx-module.git
git clone https://github.com/openresty/set-misc-nginx-module.git
Собираем Nginx со всеми модулями:
./configure --with-cc-opt='-g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_sub_module --with-http_xslt_module --with-mail --with-mail_ssl_module --add-module=${HOME}/tmp/nginx/ngx_devel_kit --add-module=${HOME}/tmp/nginx/set-misc-nginx-module --add-module=${HOME}/tmp/nginx/lua-nginx-module
Останавливаем сервис Nginx и заменяем бинарный файл:
sudo service nginx stop
sudo cp -f objs/nginx /usr/sbin/nginx
Теперь рассмотрим конфигурацию. Я приведу только location, т.к. если вы задумались о проксировании в AWS, то уже должны знаете, что такое конфигурация Nginx:
location ~* ^/(.*) {
set $bucket 'bucket-name';
set $aws_access 'AWS_ACCESS_TOKEN';
set $aws_secret 'AWS_ACCESS_SECRET';
set $url_full "$1";
set_by_lua $now "return ngx.cookie_time(ngx.time())";
set $string_to_sign "$request_method\n\n\n\nx-amz-date:${now}\n/$bucket/$url_full";
set_hmac_sha1 $aws_signature $aws_secret $string_to_sign;
set_encode_base64 $aws_signature $aws_signature;
resolver 8.8.8.8 valid=300s;
resolver_timeout 10s;
proxy_http_version 1.1;
proxy_set_header Host $bucket.s3.amazonaws.com;
proxy_set_header x-amz-date $now;
proxy_set_header authorization "AWS $aws_access:$aws_signature";
proxy_buffering off;
proxy_intercept_errors on;
rewrite .* /$url_full break;
proxy_pass http://$bucket.s3.amazonaws.com;
}
Нужно подставить ваши ключи доступа к AWS и все должно заработать как надо. Но тут есть одна проблема. Если вы храните в баккете файлы с именами без пробелов, то вы ее не заметите. Все будет работать и так. Но если вам попадется файл с пробелом в имени, то Амазон вам его не отдаст с ошибкой о некорректной сигнатуре.
Это и есть та самая проблема о которой я говорил выше. Почему так происходит? Все дело в том, что Амазон пробел заменяет на знак "+", а мы кодируем его как "%20". Т.е. сервис Амазона не видит такого файла вообще.
Поскольку проблема понятна, то и решить ее можно достаточно легко с помощью все того же LUA:
set_by_lua $url_decoded "return ngx.re.gsub(ngx.var.url_full, '%20', '+')";
Что происходит в этой строке:
- Устанавливаем переменную \$url_decoded с помощью LUA
- Пишем в нее результат от ngx.re.gsub - это функции замены
- ngx.var.url_full - переменная в которой будет делаться поиск
- '%20' - то, что ищем
- '+' - то, на что меняем
И дальше в конфиге заменить везеде \$url_full на \$url_decoded. Результирующий конфиг будет такой:
location ~* ^/(.*) {
set $bucket 'bucket-name';
set $aws_access 'AKIAJAL2RFUF73T66RBA';
set $aws_secret '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';
set $url_full "$1";
set_by_lua $url_decoded "return ngx.re.gsub(ngx.var.url_full, '%20', '+')";
set_by_lua $now "return ngx.cookie_time(ngx.time())";
set $string_to_sign "$request_method\n\n\n\nx-amz-date:${now}\n/$bucket/$url_decoded";
set_hmac_sha1 $aws_signature $aws_secret $string_to_sign;
set_encode_base64 $aws_signature $aws_signature;
resolver 8.8.8.8 valid=300s;
resolver_timeout 10s;
proxy_http_version 1.1;
proxy_set_header Host $bucket.s3.amazonaws.com;
proxy_set_header x-amz-date $now;
proxy_set_header authorization "AWS $aws_access:$aws_signature";
proxy_buffering off;
proxy_intercept_errors on;
rewrite .* /$url_decoded break;
proxy_pass http://$bucket.s3.amazonaws.com;
}
Теперь можно запускать сервис Nginx:
sudo service nginx start
Авторизацию, я думаю, вы и сами уже умеете прикручивать к Nginx. ;)
Комментарии: