注意点
残念ながら今の所明確な結論、原因は書かれていません。
概要
nginxはその圧倒的な設定項目の量ゆえ、整っているように見えるのだがなんか細かい挙動がよくわからんという話。
対象バージョン
nginx 1.15.4
問題の内容
nginxはバックエンドにポストするAPIに対して自前の認証・認可をかけることができる。
location = /auth {
internal;
proxy_pass http://auth_server/auth;
}
location = /v1/api {
auth_request /auth;
proxy_pass http://api_server;
}
こういうふうに書くとhttp://api_server/v1/api
というapiを実行する前にhttp://auth_server/auth
を実行して2xxならapiを実行それ以外ならエラーのステータスコードを返すみたいな挙動ができるわけだ。
大変便利なんだがふとしたときにauth_serverから返ってきたレスポンスが401の場合そのheaderを利用して挙動を変えたいという要求が出た。
例えば401がauthから返ってきた時に再認証画面にリダイレクトさせるか、システムやアプリケーションアカウントからのアクセスなので、そのまま401を返すかとかだ。
どうもauth_requestはレスポンスボディを返すことができないようなので(多分)レスポンスヘッダーにシステムからのアクセスの場合System-Error: system
というカスタムヘッダーを追加する事でnginx上で挙動の変更をさせてたかった。
多分auth_serverもまぁupstreamの一種だろうと雑に考えた結果、以下のような設定なら期待する挙動になるかなと思った。
error_page 401 = @error401;
location @error401 {
if ($upstream_http_system_error = 'system') {
return 401;
}
# 人の場合認証サーバーにリダイレクトさせる
rewrite ^ http://認証サーバー/login;
break;
}
以下upstream_http_の説明
$upstream_http_name
keep server response header fields. For example, the “Server” response header field is available through the $upstream_http_server variable. The rules of converting header field names to variable names are the same as for the variables that start with the “$http_” prefix. Only the header fields from the response of the last server are saved.
ここでいうkeep server response header fieldというのがauthサーバーからのレスポンスだと思ったのだがどうにもupstream_http_ではauthからのレスポンスヘッダを取得することができなかった。
そこで以下のように設定すればauthからのレスポンスヘッダを取得することができた。
error_page 401 = @error401;
location @error401 {
if ($system_error = 'system') {
# こうも行けます if ($sent_http_system_error = 'system') {
return 401;
}
# 人の場合認証サーバーにリダイレクトさせる
rewrite ^ http://認証サーバー/login;
break;
}
location = /auth {
internal;
proxy_pass http://auth_server/auth;
}
location = /v1/api {
auth_request /auth;
auth_request_set $system_error $sent_http_system_error;
proxy_pass http://api_server;
}
auth_request_setディレクティブで$system_error変数に$sent_http_system_errorの値を入れて
location @error401
で出し分ける。みたいなことができる。
でこのsent_http_system_errorとは
http://nginx.org/en/docs/http/ngx_http_core_module.html#var_sent_http_
$sent_http_name
arbitrary response header field; the last part of a variable name is the field name converted to lower case with dashes replaced by underscores
これだけ。
任意のレスポンスヘッダってだいぶ範囲広いっすね。snipptでも書いたけどauth_request_setで定義すると
その他のロケーションでも$sent_http_system_error
が読めるようになってこれもまた謎。
(auth_request_setディレクティブで設定しないとerror401 locationで$sent_http_system_error
にヘッダが設定されない。)
動くには動くのだけれども何故auth_requestのレスポンスヘッダが$sent_http_に入るのかは結局良く理解できなかった。
いまいち$sent_http_はどのタイミングでどういう値が入るのかがちょっと理解できなかった。
だれか教えて(他力本願)
参考リンク
http://mogile.web.fc2.com/nginx/http/ngx_http_auth_request_module.html
http://nginx.org/en/docs/http/ngx_http_core_module.html#var_sent_http_
http://nginx.org/en/docs/http/ngx_http_upstream_module.html