先日社内で開催した勉強会にてKeycloakのハンズオンを行いましたのでその時のテキストを公開します。
Dockerを使ってKeycloakで認証サーバーを建て、WEBサーバーに対してリバースプロキシ方式で認証をかけます。
1. Keycloak作成
早速Keycloakを使って認証サーバーを建てます。
1-1. ネットワーク作成
コンテナ間で通信を行うので、最初にコンテナが所属するネットワークを作成します。
$ docker network create --driver bridge keycloak
1-2. コンテナ作成
Keycloakのコンテナを建てます。Keycloakの開発元がコンテナイメージを提供してくれているので、それを使います。
version: "3"
services:
keycloak:
image: jboss/keycloak:12.0.2
environment:
KEYCLOAK_USER: administrator
KEYCLOAK_PASSWORD: "00000000"
ports:
- 8080:8080
networks:
- keycloak
networks:
keycloak:
external: true
KEYCLOAK_USERとKEYCLOAK_PASSWORDは管理コンソールへのログインIDとパスワードです。
1-3. コンテナ起動
$ docker-compose -f keycloak.yaml up -d
1-4. 起動確認
ブラウザを開き、http://localhost:8080
にアクセスするとウェルカムページが表示されます。
2. 認証サーバーの設定
Keycloakの設定をしていきます。
2-1. 管理コンソールへのログイン
1-4でアクセスしたウェルカムページのAdministration Console
をクリックしてください。
ログインページが表示されるので、KEYCLOAK_USER
とKEYCLOAK_PASSWORD
で設定したユーザーIDとパスワードを入力してログインしてください。
ログインに成功すると管理コンソールの画面へ遷移します。
2-2. 日本語化
ひとまず、分かりづらいかと思いますので、日本語化します。
右側にあるタブのThemes
をクリックしてください。
Internationalization Enabled
をON
にするとSupported Locales
とDefault Locale
が表示されます。Default Locale
をja
にしてSave
ボタンを押してください。
日本語に変わらない場合は、右上のAdministrator
(もしくはKEYCLOAK_USER
で設定した名前)をクリックして、Manage account
をクリックしてください。アカウント管理画面が日本語で表示されたら、セキュリティ管理コンソールに戻る
で日本語化した管理画面に遷移します。
2-3. レルムの作成
現在表示されてるのはMaster
レルムの管理ページです。レルムというのはKeycloakのルートデータで、それぞれが独立した認証プロバイダとなります。Master
レルムは全てのレルムへのアクセス権をデフォルトで持ってるレルムで、ここにログインすると全てのレルムの管理を行うことができます。
基本的にMasterレルム
はKeycloakの管理を行う場所で、ここを認証プロバイダとして使うことはないので、新しいレルムを作りましょう。左のサイドメニューの上にあるMaster
にマウスオーバーすると、レルムの追加
というボタンが出てくるので押してください。レルムの追加画面に遷移するので、名前
にレルム名を入力して作成
を押すとレルムが作られます。今回はhandson
で作成します。
2-4. レルムの設定
作成すると、サイドメニューの上のMaster
がHandson
(もしくは設定したレルム名)になってると思います。ここがHandson
レルムの管理ページです。
右側の設定ページの下の方にエンドポイント
とあります。そこのOpenIDエンドポイントの設定
をクリックしてください。OpenID Connectに必要な各種設定値が記載されたJSONを取得できます。
{
"issuer": "http://localhost/auth/realms/handson",
"authorization_endpoint": "http://localhost:8080/auth/realms/handson/protocol/openid-connect/auth",
"token_endpoint": "http://localhost:8080/auth/realms/handson/protocol/openid-connect/token",,
"userinfo_endpoint": "http://localhost:8080/auth/realms/handson/protocol/openid-connect/userinfo",
...
}
この設定値を取得するためのエンドポイントはOpenID Connect
の仕様に定められており、OpenID Connect
に準拠してる認証サービスにはこの[ベースURL]/.well-known/openid-configuration
というエンドポイントは必ず存在します。例えば、Googleの場合はhttps://accounts.google.com/.well-known/openid-configuration にあります。ここに記述される属性はこちらで定義されています。
この設定値のうち、issuer
の値は後で使いますので控えておいてください。
では、いくつか基本的な設定をします。右側のタブのログイン
をクリックしてください。遷移後の画面で以下をON
にします。
- ユーザー登録
- パスワード忘れ
- ログイン状態の保存
以上の設定で、よくあるログインページを表示できるようになります。ON
にしたら保存
を押してください。
2-5. クライアント作成
認証するアプリ(RP)の情報を登録していきます。左側のサイドメニューからクライアント
をクリックしてください。右側の画面に登録されてるクライアントの一覧が表示されたら、右上の作成
を押してください。
クライアントの追加
画面が表示されたら、クライアントID
を入力して、保存
を押してください。今回はnginx
とします。
2-6. クライアント設定
クライアントが作成されるとクライアントの設定画面に遷移しますので、セキュアに認証するためにいくつか設定していきます。
まず、アクセスタイプ
をpublic
からconfidential
に変えてください。これでクライアントシークレット
が発行されます。
次に、有効なリダイレクトURI
を入力します。これは認証後にサービスに戻るURIなので、RPとなるサービスのURLになるかと思います。今回はローカルのWEBサーバーなので、http://localhost/*
と入れてください。
入力したら一番下の保存
を押してください。保存
を押すと一番上のタブにクレデンシャル
というものが出てきます。クレデンシャル
タブに移動するとシークレット
というものがあります。これは認証時に使用するクライアントシークレット
となるので控えておいてください。
3. WEBサーバー
WEBサーバーを作成します。Nginxを使用します。
3-1. Nginxコンテナ作成
version: "3"
services:
nginx:
image: nginx
ports:
- 80:80
networks:
- keycloak
networks:
keycloak:
external: true
3-2. コンテナ起動
$ docker-compose -f client.yaml up -d
3-3. 起動確認
ブラウザを開き、 http://localhost
にアクセスしてください。
Nginxのウェルカムページが表示されれば問題ありません。
4. 認証プロキシ
Nginxの代わりに認証プロセスを行ってくれるリバースプロキシを建てます。
公式が推奨してるオープンソースのoauth2-proxyを使用します。
4-1. コンテナ作成
docker-composeの設定にプロキシコンテナを追加します。[クライアントシークレット]
には先程クライアントの設定ページで控えたシークレットを入れてください。その他、レルム名やクライアント名を変えてる場合は適時置き換えてください。
version: "3"
services:
nginx:
image: nginx
# 80番ポートはプロキシで使うのでnginxから削除
- ports:
- - 80:80
networks:
- keycloak
+ proxy:
+ image: quay.io/oauth2-proxy/oauth2-proxy
+ environment:
+ OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180
+ OAUTH2_PROXY_COOKIE_SECRET: 01234567890123456789012345678912
+ OAUTH2_PROXY_COOKIE_SECURE: "false"
+ OAUTH2_PROXY_COOKIE_NAME: "oauth2_proxy"
+ OAUTH2_PROXY_EMAIL_DOMAINS: "*"
+ OAUTH2_PROXY_UPSTREAMS: http://nginx/
+ OAUTH2_PROXY_PROVIDER: oidc
+ OAUTH2_PROXY_OIDC_ISSUER_URL: http://keycloak:8080/auth/realms/handson
+ OAUTH2_PROXY_CLIENT_ID: nginx
+ OAUTH2_PROXY_CLIENT_SECRET: [クライアントシークレット]
+ OAUTH2_PROXY_REDIRECT_URL: http://localhost/oauth2/callback
+ OAUTH2_PROXY_SCOPE: openid email profile
+ OAUTH2_PROXY_PASS_ACCESS_TOKEN: "true"
+ OAUTH2_PROXY_PASS_USER_HEADERS: "true"
+ OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"
+ OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"
+ OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL: "true"
+ ports:
+ - 80:4180
+ networks:
+ - keycloak
networks:
keycloak:
external: true
一個一個説明すると時間がかかるのでポイントだけ説明すると、OAUTH2_PROXY_OIDC_ISSUER_URL
には先程のOpenID Connect設定値エンドポイント
で控えたissuer
のURLを入力します。ただし、このプロキシから見たKeycloak
のURLにしてください。docker-composeはサービス名でルーティングできるので、localhost
をkeycloak
に変更します。認証時にリダイレクトされるURLもkeycloak
になるので、以下のコマンドでホスト名を追加しておきます。
$ echo 127.0.0.1 keycloak >> /etc/hosts
OAUTH2_PROXY_SCOPE
は認証後にRPに与えてほしい操作権限です。openid
はOpenID Connect
による認証処理の許可、email
やprofile
はユーザーデータの取得許可です。
OAUTH2_PROXY_UPSTREAMS
には認証後にリクエストを流すサーバーを指定します。今回はNginx
に渡します。
PASS~
の設定は認証後のデータをUpstreamに渡すかどうかのフラグです。
OAUTH2_PROXY_REDIRECT_URL
には認証後にリダイレクトしてほしいURLを指定します。oauth2-proxy
の標準のコールバックエンドポイントは/oauth2/callback
となります。
3-2. コンテナ起動
コンテナを起動します。先程起動したNginxが生きてる場合は、ポートが使われてるので、停止してから起動します。
$ docker-compose -f client.yaml down
$ docker-compose -f client.yaml up -d
3-3. 動作確認
ブラウザを開き、 http://localhost
にアクセスしてください。
Keycloakのログインページへリダイレクトされます。ユーザーは登録してないので、下の登録
からユーザー登録をしてください。ユーザー名
は半角英数字にしてください。登録後、入力したユーザー名
とパスワード
でログインしてください。ログインが成功したらNginx
のウェルカムページに飛びます。
4. WEBサーバーに飛んでるリクエストの中身を確認
実際に使う時にoauth2-proxy
がどのように認証後のデータを投げてくるのか知ってないと、その後の処理ができないので、リクエストの中身を確認してみましょう。httpbinというアプリケーションを使います。
4-1. コンテナ作成
version: "3"
services:
nginx:
image: nginx
networks:
- keycloak
+ httpbin:
+ image: kennethreitz/httpbin
+ networks:
+ - keycloak
proxy:
image: quay.io/oauth2-proxy/oauth2-proxy
environment:
...
ports:
- 80:4180
networks:
- keycloak
networks:
keycloak:
external: true
4-2. Nginxの設定
Nginx
がリクエストをhttpbin
に流すように設定します。
以下でNginxの設定ファイルを作成します。
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://httpbin/;
}
}
設定した作成ファイルをNginx
のコンテナにマウントします。また、httpbin
が起動する前にNginx
が起動するのは具合が悪いので依存関係を設定します。
version: "3"
services:
nginx:
image: nginx
+ volumes:
+ - ./nginx/conf.d:/etc/nginx/conf.d
+ depends_on:
+ - httpbin
networks:
- keycloak
httpbin:
image: kennethreitz/httpbin
networks:
- keycloak
proxy:
image: quay.io/oauth2-proxy/oauth2-proxy
environment:
...
ports:
- 80:4180
networks:
- keycloak
networks:
keycloak:
external: true
4-2. コンテナ起動
コンテナを起動します。
$ docker-compose -f client.yaml up -d
4-3. 動作確認
ブラウザを開き、 http://localhost/get
にアクセスしてください。以下のようなJSONが取得できるはずです。
{
"args": {},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "ja,en-US;q=0.7,en;q=0.3",
"Authorization": "Bearer xxx",
"Cache-Control": "no-cache",
"Connection": "close",
"Cookie": "...",
"Host": "httpbin",
"Pragma": "no-cache",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "...",
"X-Forwarded-Access-Token": "xxx",
"X-Forwarded-Email": "foo@example.com",
"X-Forwarded-Preferred-Username": "account_name",
"X-Forwarded-User": "00000000-0000-0000-0000-000000000000"
},
"origin": "0.0.0.0",
"url": "http://httpbin/get"
}
httpbin
に飛んできたリクエストの中身がJSONで返ってきます。oauth2-proxy
によって追加されたヘッダーは以下の通りです。
- Authorization
- X-Forwarded-Access-Token
- X-Forwarded-Email
- X-Forwarded-Preferred-Username
- X-Forwarded-User
Preferred-Username
とUser
の違いは、Preferred-Username
がログイン認証等にも使われる"ユーザーが設定したユーザーID"で、User
が"認証サービス側が自動的に割り当てているユーザーID"です。
後始末
$ docker-compose -f keycloak.yaml -f client.yaml down