その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

手っ取り早くウェブアプリケーションにOAuth2認証を導入する

bitly/oauth2_proxyを用いて,ウェブアプリケーションに手っ取り早くOAuth2認証を導入するという話です.
oauth2_proxyは良い感じでOAuth2による認証を肩代わりしてくれる君で,何らかのリバースプロキシの認証機構と組み合わせて利用すると簡単にOAuth2ログインを実現することができます.
今回は例としてKibanaにGoogleのOAuth2ログインを導入してみたいと思います.

構成
+------+              +-------+               +--------------+            +--------+
|      |              |       | ----auth----> |              |            |        |
| user | --request--> | nginx |               | oauth2_proxy | <--auth--> | Google |
|      |              |       | <--response-- |              |            |        |
+------+              +-------+               +--------------+            +--------+
                          |
                       access
                          |
                          v
                      +--------+
                      |        |
                      | kibana |
                      |        |
                      +--------+
流れ
  1. UserがKibanaが動いているドメインで待ち構えているnginxにアクセスする
  2. nginxはoauth2_proxyに認証処理を移譲する
  3. oauth2_proxyはGoogleに認証リクエストを送り,ログインフローに乗せる
  4. oauth2_proxyは認証の結果をnginxに戻す
  5. 認証が成功した場合はKibanaへリクエストを通す.失敗した場合はリジェクトする
  6. (OAuth2認証が成功した場合はその情報をcookieに保存しておき,cookieがexpireするまではOAuth2認証リクエストをバイパスする)
事前準備

https://console.developers.google.comにアクセスしてcredentialsを作り,Client IDとClient secretを取得します.この時,Authorized redirect URIsにリダイレクト先のURLを登録しておきます (e.g. https://kibana.example.com/oauth2/callback).

実際に動くサンプル

docker-composeで実際に動くものを示します.

nginx.conf

user nginx;
pid /var/run/nginx.pid;
worker_processes auto;
events {
use epoll;
}
http {
server {
listen 80;
server_name kibana.example.com;
location ~ ^/oauth2/(?:auth|start)$ {
internal;
proxy_pass http://oauth2-proxy:4180;
proxy_set_header Host $host;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
location = /oauth2/callback {
auth_request off;
proxy_pass http://oauth2-proxy:4180;
proxy_set_header Host $host;
}
location / {
satisfy any;
auth_request /oauth2/auth;
error_page 401 = /oauth2/start?rd=$uri;
allow 127.0.0.1;
deny all;
proxy_read_timeout 300;
proxy_pass http://kibana:5601;
}
}
}
daemon off;
view raw nginx.conf hosted with ❤ by GitHub
gist.github.com

auth_requestディレクティブでOAuth2認証を有効にしています.あとの部分は読むとだいたいわかると思います.このnginx.confの内容については以下の記事が詳しいです (というかパクりました,ありがとうございます).

lamanotrama.hateblo.jp


docker-compose.yml

version: '2.1'
services:
kibana:
image: 'docker.elastic.co/kibana/kibana-oss:6.1.0'
elasticsearch:
image: 'docker.elastic.co/elasticsearch/elasticsearch-oss:6.1.0'
oauth2-proxy:
image: 'a5huynh/oauth2_proxy:2.2'
command: >
'--http-address=0.0.0.0:4180'
'--cookie-secure=true'
'--cookie-expire=24h'
'--cookie-httponly=true'
'--cookie-domain=kibana.example.com'
'--cookie-name=<cookie-name>'
'--cookie-secret=<cookie-secret>'
'--upstream=http://kibana:5601'
'--email-domain=<email-domain>'
'--client-id=<client-id>'
'--client-secret=<client-secret>'
nginx:
image: 'nginx:1.13.5'
links:
- 'kibana'
- 'oauth2-proxy'
volumes:
- './nginx.conf:/etc/nginx/nginx.conf'
command: 'nginx'
https-portal:
image: 'steveltn/https-portal:1'
ports:
- '80:80'
- '443:443'
links:
- 'nginx'
restart: 'always'
environment:
DOMAINS: 'kibana.example.com -> http://nginx'
STAGE: 'local'
view raw docker-compose.yml hosted with ❤ by GitHub
gist.github.com

<>で囲われた部分については適宜書き換える必要があります.

ここで利用されているhttps-portalはLet's Encryptを利用してSSL/TLS証明書の取得を自動化しつつSSL終端してくれるコンポーネントです.これ利用している理由はGoogleのOAuth2認証がリダイレクトしてくる時にhttpsを利用するからです.もしnginxの側でSSL/TLS化してる場合や,ELB等の上流でSSL終端している場合などは不要です.
なお,STAGE: 'local'と記述しておくとLet's Encryptは利用せずにオレオレ証明書を利用するモードになるのでローカルでの検証等で便利です.つまり本番で使ってはならない.本番運用時にはSTAGE: 'production'などと記述する必要があるでしょう (ドキュメントを参照のこと: https://github.com/SteveLTN/https-portal).


docker-copose upしてアクセスするとこんな感じで動きます (kibana.example.comをそのまま利用する場合はhosts等を適宜書き換えておく必要があります).

f:id:moznion:20171214230153p:plain

Googleのログインページが開き,適宜やっていくと

f:id:moznion:20171214230312p:plain

という感じでKibanaが開きます.便利便利.よかったですね.