通过示例展示我遇到问题的地方可能是最简单的,所以我会直接进入......
此代码段将按原样用于启用了重写/授权模块的 Nginx。所以希望这个问题在几乎所有 Nginx 安装上都能快速轻松地重现......
server {
listen 8081;
add_header x-user bar;
return 200;
}
server {
listen 8080;
location = /auth {
internal;
proxy_pass http://localhost:8081;
}
location / {
auth_request /auth;
auth_request_set $foo $upstream_http_x_user;
add_header x-test $foo;
add_header success true;
proxy_pass http://example.com/;
}
}
上面的示例 Nginx 站点配置执行以下操作:
/auth
通过auth_request
呼叫发送任何请求- /auth 位置将请求发送到添加标头的另一台服务器
x-user bar
auth_request_set
$foo
根据上面步骤 2 中设置的上游标头值设置一个新的 varx-user
。- 设置了一个新的标头,
x-test
该标头的值为$foo
- 请求继续到外部目的地。
响应正是我所期望的,并确认$foo
var 设置正确:
$ curl -s --head http://localhost:8080 | grep -E 'HTTP|x-test'
HTTP/1.1 200 OK
x-test: bar
所以,问题来了……
我需要调整这个配置,以便在上游标头的值不正确时返回 403。
似乎是一项简单的任务。所以我添加了一个if{}
条件来检查标题:
location / {
auth_request /auth;
auth_request_set $foo $upstream_http_x_user;
# this 'if' is the only part added to the original config
if ($foo != bar) {
return 403;
}
add_header x-test $foo;
add_header success true;
proxy_pass http://example.com/;
}
条件if
评估为真,所以我得到了 403,这不是我所期望的。所以,这不起作用:
$ curl -s --head http://localhost:8080 | grep -E 'HTTP|x-test'
HTTP/1.1 403 Forbidden
我意识到if 是邪恶的但是我似乎只是用它来返回应该没问题。我愿意使用任何方法来实现相同的目标——有或没有if,所以我对想法持开放态度!!
我试过做一些事情,比如将auth_request
和/或if
语句移动到块中server{}
,但似乎没有任何东西可以按照我期望的方式进行评估。
进一步的故障排除/详细信息:
我已经验证了问题是在isif
之前对 is 进行了评估auth_request_set
location / {
auth_request /auth;
auth_request_set $foo $upstream_http_x_user;
if ($foo != bar) {
# x-test never gets set because $foo is null when if evaluates
add_header x-test $foo always;
add_header success false always;
return 403;
}
add_header x-test $foo;
add_header success true;
proxy_pass http://example.com/;
}
$ curl -s --head http://localhost:8080 | grep -E 'HTTP|x-test|success'
HTTP/1.1 403 Forbidden
success: false
set
如果使用而不是,我已经验证这不是问题auth_request_set
。这有效(但没有实现目标):
# set works, but not auth_request_set
location / {
set $foo bar;
if ($foo != bar) {
return 403;
}
add_header x-test $foo;
add_header success true;
proxy_pass http://example.com/;
}
这个配置有效。set
之前被评估if
:
$ curl -s --head http://localhost:8080 | grep -E 'HTTP|x-test'
HTTP/1.1 200 OK
x-test: bar
即使在上下文auth_request
中,该行为仍然存在server{}
:
server {
listen 8081;
add_header x-user bar;
return 200;
}
server {
listen 8080;
auth_request /auth;
auth_request_set $foo $upstream_http_x_user;
location = /auth {
internal;
proxy_pass http://localhost:8081;
}
location / {
if ($foo != bar) {
return 403;
}
add_header x-test $foo;
add_header success true;
proxy_pass http://example.com/;
}
}
$ curl -s --head http://localhost:8080 | grep -E 'HTTP|x-test|success'
HTTP/1.1 403 Forbidden
success: false
我查看了以下文档和问题:
- 在 nginx 中返回 auth_request 后,我可以比较 auth_request_set 设置的变量吗?
- https://stackoverflow.com/questions/73431103/cant-access-added-headers-using-nginx-auth-request-set
- Nginx $upsteam_cache_status 自定义标头不会出现
- auth_request 不阻止 return 指令,不能返回状态?
- https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil
- http://nginx.org/en/docs/http/ngx_http_auth_request_module.html
- http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if
- https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
这
if
是有问题的,因为它是在其他声明性配置中的命令式构造。因此它并不总是按预期工作。可以在IfIsEvil文章中找到更多信息。
在这种情况下,
set
和auth_request_set
发生在 nginx 请求处理的不同阶段,并且if
处理发生在这两个阶段之间。不幸的是我不知道如何在 nginx 中实际做你想做的事。也许这需要在您将请求代理到的上游服务器中完成。
这没有回答问题,请在此处查看 Tero 的回答以获取实际答案(剧透:它确实
if
是邪恶的,即使与 一起使用也是如此)。return
但是,作为提供解决方法的功能性答案:这至少是一种使无效请求发送 403 的方法。proxy_pass 端点是动态的,具体取决于测试的标头。这似乎解决了用例......