«Чистый» раствор nginx
К сожалению, nginx не предоставляет расширенный набор строковых операций. Я думаю, что нет способа сделать глобальный поиск и замену через некоторую строку (что может быть решением, если мы могли бы заменить все %2B
на +
, %2F
на /
и %3D
на =
). Однако существуют обстоятельства, при которых nginx выполняет urldecoding некоторой строки - когда эта строка становится частью URI, который будет перенаправлен на прокси-сервер вышестоящего уровня.
Таким образом, мы можем добавить значение BASIC_AUTH
аргумента запроса к URI и сделать запрос прокси для себя:
# Main server block
server {
listen 80 default_server;
...
location / {
if ($arg_basic_auth) {
# "basic_auth" request argument is present,
# append "/decode_basic_auth/<BASE64_token>" to the URI
# and go to the next location block
rewrite ^(.*)$ /decode_basic_auth/$arg_basic_auth$1 last;
}
# No "basic_auth" request argument present,
# can do a proxy call from here without setting authorization headers
...
}
location /decode_basic_auth/ {
# This will be an internal location only
internal;
# Remove "basic_auth" request argument from the list of arguments
if ($args ~* (.*)(^|&)basic_auth=[^&]*(\2|$)&?(.*)) {
set $args $1$3$4;
}
# Some hostname for processing proxy subrequests
proxy_set_header Host internal.basic.auth.localhost;
# Do a subrequest to ourselfs, preserving other request arguments
proxy_pass http://127.0.0.1$uri$is_args$args;
}
}
# Additional server block for proxy subrequests processing
server {
listen 80;
server_name internal.basic.auth.localhost;
# Got URI in form "/decode_basic_auth/<BASE64_token>/<Original_URI>"
location ~ ^/decode_basic_auth/([^/]+)(/.*)$ {
proxy_set_header Authorization "Basic $1";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>$2$is_args$args;
}
# Do not serve other requests
location / {
return 444;
}
}
Возможно, это не очень элегантное решение, но оно протестировано и работает.
OpenResty / ngx_http_lua_module
Эту проблему легко решить с помощью openresty или ngx_http_lua_module с использованием функции ngx.escape_uri :
server {
listen 80 default_server;
...
location / {
set $auth $arg_basic_auth;
if ($args ~* (.*)(^|&)basic_auth=[^&]*(\2|$)&?(.*)) {
set $args $1$3$4;
}
rewrite_by_lua_block {
ngx.var.auth = ngx.unescape_uri(ngx.var.auth)
}
proxy_set_header Authorization "Basic $auth";
# Setup other HTTP headers here
...
proxy_pass http://<upstream_server>;
}
}