oauth2_proxyとNginxのauth_requestを組み合わせると便利

oauth2_proxy、便利でググると導入手順も結構出てくるのだけど、今回紹介するのは(環境によっては)さらに便利に使えるナウい方法です。

なにをするのかというとREAMDEに書いてあります。

Configuring for use with the Nginx auth_request directive The Nginx auth_request directive allows Nginx to authenticate requests via the oauth2_proxy's /auth endpoint, which only returns a 202 Accepted response or a 401 Unauthorized response without proxying the request through. For example:

サンプル設定もあるので、なんとなく雰囲気は伝わるんだけど、はっきり言ってこれだけ見てすんなり出来る人はなかなかいないだろう(自分がそうだった)し、何が便利なのかもイマイチわからんので、もうちょい丁寧に説明してみようと思います。


まず以前からQuipperでもやっていた一般的な構成はこうです。

  1. nginxで特定locationやserver全体でproxy_passにoauth2_proxyサーバを指定する
  2. oauth2_proxyがGoogle Appsの認証ページにリダイレクトする
  3. 認証成功したら、oauth2_proxyのupstreamで設定しているbackendのアプリケーションにproxyされる

しかし、これだと下記のような面倒が発生することがあります。

  • 認証を導入したいbackendのアプリケーションが複数ある場合、その分複数のoauth2_proxy設定を用意し、複数のプロセスを起動しなくてはいけない
    • nginx側のproxy_passも変えないといけない
  • nginx側の設定だけを見るとどこに最終的にproxyされるのかわからん
  • 特定pathのみ認証をいれたいような場合に、設定が煩雑になる

それがこうなります。

  1. nginxのauth_requestディレクティブ使って、oauth2_proxyに対して認証(ログイン)済みかどうか確認する
  2. 未認証の場合はoauth2_proxyに自動的に認証を依頼する
  3. 認証がすんだらnginxのproxy_passに設定しているbackendアプリケーションにproxyされる

ポイントは「oauth2_proxyには認証だけしてもらってbackendへのproxyには一切ノータッチ」になるってとこです。これによって上述した問題は全て解決されます。oauth2_proxy沢山起動する必要もないし、nginxの設定も見通しが良くなります。

また、auth_requestはsatisfyに対応しているので、allowディレクティブと組み合わせることで、「特定のクライアントのみ認証不要にする」ということも簡単にできるようになります。

設定例

前置きが長くなった。ここからそれをやるための設定例です。尚、設定を作るにあたって、同じような仕組みで動作するクックパットさんのnginx_omniauth_adapterの紹介エントリが大変参考になりました。ありがとうございます。

oauth2_proxy側の設定についてはauth_requstを使わない場合とほぼ変わらないので省きますが、2点だけ注意点があります。

  • まだauthエンドポイントがリリースされてない(releasesにあるバイナリでは未サポート)ので、masterからbuildすること
  • oauth2_proxyでは一切proxyしないけど、configのupstreamsにはなんでもいいので値をsetしとくこと。しないと起動しない

です。

common/oauth2_proxy.conf

oauth2_proxyの各種エンドポイントへproxyするための設定。

# for checking login status.
# only returns a 202 Accepted response or a 401 Unauthorized response;
location = /oauth2/auth {
  internal;
  proxy_pass http://127.0.0.1:4180;
  proxy_set_header Host $host;
  proxy_pass_request_body off;
  proxy_set_header Content-Length "";
}

# to start oauth cycle
location = /oauth2/start {
  internal;
  proxy_pass http://127.0.0.1:4180;
  proxy_set_header Host $host;
  proxy_pass_request_body off;
  proxy_set_header Content-Length "";
}

# to complete oauth cycle
location = /oauth2/callback {
  auth_request off;
  proxy_pass http://127.0.0.1:4180;
  proxy_set_header Host $host;
}

common/oauth2_enable.conf

locationコンテクストでincludeして、認証を有効化する設定。

satisfy any;

# 認証済みかどうか確認
auth_request /oauth2/auth;

# ^で401がかえってきたら認証を開始
# rdパラメータにpathをセットしないと、最終的に/にredirectされちゃうよ
error_page 401 = /oauth2/start?rd=$uri;

# オフィスなど認証が不要なクライアントIPをallow
allow 119.172.242.131;
deny all;

virtual host

上記の2つの設定をincludeして一部のpathのみ認証を有効にする設定例。

server {
  listen 80;
  server_name my.domain.com;

  include conf.d/common/oauth2_proxy.conf;

  location /secret/path {
    include conf.d/common/oauth2_enable.conf;
    # 認証こけたらproxyされないよ
    proxy_pass http://backend;
  }

  location / {
    proxy_pass http:/backend;
  }
}

おわり。