OAuth2 Proxy と Keycloak で認証をお試し

# はじめに
OpenID ConnectとKeycloakの勉強をかねて、今回は「OAuth2 Proxy」+「Keycloak」を構築してみました。  
全く知識がないのでお試し程度に考えていただければと思います。

# 構成
今回は、oauth2-proxyに渡ってきたhttpリクエストに認証情報がなかった際に、Keycloakを利用して認証、何かしらのウェブページを表示させることを目標にします。 

![現在の構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/ce072bef-becb-7730-d5f5-54a448e440ec.png)

構成の中のanyには実際に認証したいコンテンツを設置します。
今回は[こちら](https://httpbin.org/ "https://httpbin.org/")を利用し、リクエストを可視化できるようにしておきます。

https://httpbin.org/

# 作成したdocker-compose
今回作成したdokcer-composeは以下の通りです。  
oauth2-proxyの環境変数にある`XXXXXXXXXXXXXXXXXXX`については、Keycloakを設定後に取得できる内容となっているので、  
ここについては一旦上記の形で書いてあります。  
また、keycloakのデータや設定値の永続化は設定しておらず、設定用コンテナを起動し、設定値をexport、コンテナ起動時に自動的にimportさせるようにもできますが、今回は認証を動かすことを目的にしているため、そのような形の構成にはしていません。  

```yml:docker-compose.yml
version: '3.9'

services:
  keycloak:
    image: quay.io/keycloak/keycloak:20.0.1
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: Pa55w0rd
      PROXY_ADDRESS_FORWARDING: "true"
    ports:
      - 8080:8080
      - 8443:8443
    entrypoint: ["/opt/keycloak/bin/kc.sh", "start-dev"]

  any:
    image: kennethreitz/httpbin:latest
    ports:
      - "3000:80"

  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:v7.4.0-amd64
    ports:
      - 80:4180
    environment:
      OAUTH2_PROXY_PROVIDER: oidc
      OAUTH2_PROXY_CLIENT_ID: oauth2-proxy
      OAUTH2_PROXY_CLIENT_SECRET: XXXXXXXXXXXXXXXXXXX
      OAUTH2_PROXY_REDIRECT_URL: http://localhost/oauth2/callback
      OAUTH2_PROXY_OIDC_ISSUER_URL: http://keycloak:8080/realms/DemoRealm
      OAUTH2_PROXY_COOKIE_SECRET: "01234567890123456789012345678901"
      OAUTH2_PROXY_COOKIE_SECURE: "false"
      OAUTH2_PROXY_COOKIE_NAME: "demo_cookie"
      OAUTH2_PROXY_EMAIL_DOMAINS: "*"
      OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180
      OAUTH2_PROXY_UPSTREAMS: http://any/
      OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"
```
また、Dockerネットワークの名前解決を利用していますが、うまくいかなかったため、`/etc/hosts`を設定しています。

```:/etc/hosts
127.0.0.1 localhost keycloak
```


# 準備
認証ができているか確認するためには、Keycloakの設定を先に行っておく必要があります。
そのため、keycloakのみを起動し、設定を行います。

```
docker-compose up -d keycloak
```

## 1.Realmを作成する
起動ができたら、`http://localhost:8080/`のkeycloakにアクセスします。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/fac15ccf-daec-8390-d5eb-fd3138cb8dd9.png">

`Administration Console`からログインを行います。
今回は環境変数で下記の通り指定していますので、こちらがadmin ユーザーのIDとパスワードになります。
```
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: Pa55w0rd
```

ログインができたら、realmを作成する作業になります。
`Create Realm`からrealmを作成していきます。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/18b20262-2edf-4bb4-eca4-a9cbb0a1f501.png">

Realm nameには`DemoRealm`と入力して`Create`を押します。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/e1377706-fa5a-34a9-9ec7-895455e20828.png">

これでRealmが作成することができました。

## 2.Client IDとClient Secretを設定、確認する

次にoidc周りの設定（`Client ID` `Client Secret`の発行）を行っていきます。
まず、`Clients`にある`Create client`からClientを作成します。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/d15fb308-fdd9-0378-4cb4-c51d07c09499.png">

`Client ID`に`oauth2-proxy`と入力し、`Next`を押します。
これが、Client IDとなります。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/258844e8-4c1b-584f-74be-66c2538a54d3.png">

`Client authentication`にチェックを入れ、`Save`を押します。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/ef769321-e6ef-8798-29d3-830122ac4033.png">

そうすると、`Client secret`が確認できるようになりますので、`Credentials`タブの`Client secret`を確認します。
ここの値を`docker-compose.yml`の`OAUTH2_PROXY_CLIENT_SECRET`に入力します。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/fb6809b0-d082-0a33-b91c-3310b87c212e.png">

## 3.Valid redirect URIs を設定する
`Clients`の`Settings`にある`Valid redirect URIs`を設定します。
```
http://localhost/*
```
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/0d1944ad-d8fd-08a8-4c20-75b7811579ea.png">


## 4.ログインを行うテストアカウントを作成する

ログインを行うテストアカウントを作成します。
`Users`の`Create new user`からアカウントを作成します。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/5cef6484-d987-81d4-de19-75eb854ff809.png">

必要事項を入力し、`Create`を押します。
`Email verified`は`On`にしておきます。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/7b7de5fa-19b4-0ea2-2631-2d8d9fe55fa2.png">

テストアカウントのパスワードを設定します。
`Credentials`の`Set password`からパスワードを設定します。

<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/84a323a6-80aa-72da-4eb9-4d6d05af1f98.png">

パスワードを設定します。
今回は`password`とし、`Temporary`はOffにします。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/9a55d35d-f139-7b21-25f9-1131e7d2880d.png">

これで、準備は完了です。

# 動作確認
Keycloak以外を起動し、動作の確認を行います。
```
docker-compose up -d any oauth2-proxy
```
起動ができたら、`http://localhost/`にアクセスします。
`Sign in with OpenID Connect`を押してログインを行います
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/9ec7ba4c-4b9e-2ff8-c821-7c744c953a22.png">
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/114d0635-9efe-8f9e-340f-2f905bd825cb.png">

表示されました。
<img width="700" alt="keycloak" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/1464967d-61fa-3b51-bd8d-8b9f9208d905.png">

`http://localhost/bearer`にアクセスしてみます。

```
{
  "authenticated": true, 
  "token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoQTNFY2Q0aFRJQVMwOHRNUng4dnJCZnBHSmppcHd0VFZNSDRnWTV0a09NIn0.eyJleHAiOjE2NzAzMjcxMDcsImlhdCI6MTY3MDMyNjgwNywiYXV0aF90aW1lIjoxNjcwMzI2NjE4LCJqdGkiOiJkZjEzYmQ4OS03MjA4LTQwYTEtYWU0Yi1lN2YwMmUyZWNlZWIiLCJpc3MiOiJodHRwOi8va2V5Y2xvYWs6ODA4MC9yZWFsbXMvRGVtb1JlYWxtIiwiYXVkIjoib2F1dGgyLXByb3h5Iiwic3ViIjoiNzk0YzMxZGYtZTI1MC00NmQ4LWE2OTUtZWJkNGZlNDJiYTNiIiwidHlwIjoiSUQiLCJhenAiOiJvYXV0aDItcHJveHkiLCJzZXNzaW9uX3N0YXRlIjoiY2M4OWM5NTYtMzdiZS00MTBlLTgzNGEtYjExYmY1YjgxMGI0IiwiYXRfaGFzaCI6InpKOWs3X01XMXRQUmlBR0ZGWjc0TkEiLCJhY3IiOiIwIiwic2lkIjoiY2M4OWM5NTYtMzdiZS00MTBlLTgzNGEtYjExYmY1YjgxMGI0IiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6IuOBpuOBmeOBqOOChuODvOOBluODvCIsImdpdmVuX25hbWUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImVtYWlsIjoidGVzdEB0ZXN0LmFhYSJ9.AvRU4LPjTqYqmXVSeHAmkruNemKJHNtYrmF8tEzh6XLHt-NOKK2ezy9GgoJSN-UKEqKXXu1kYgo2D5PtZG4l31Uio0i-pCo227iXcEUelp6aZevL5YFVnvN7iEvZ1pC5F3jtDhmrgbQ1j01Pxept_OKWYaGKQKsA-DDCgj4ryrxW3cS6qkYuxfg360_NpH-h2ieIp60lFB_GcaVrmynDJ1ZRCh-S3EayWD7e8D14aAWS8Dj_9XvtVJ48tAyRRJ7-SJjEtkhmZcyA7ATWYOlLOezWSEN0LYZmfgKwATfBUy04fN02nOR2s3Gu9UHnANsThXDWcD9zzT5_Is0Er5qIIA"
}
```

jwtのデコードをしてみます。

https://jwt.io/


```
{
  "exp": 1670327107,
  "iat": 1670326807,
  "auth_time": 1670326618,
  "jti": "df13bd89-7208-40a1-ae4b-e7f02e2eceeb",
  "iss": "http://keycloak:8080/realms/DemoRealm",
  "aud": "oauth2-proxy",
  "sub": "794c31df-e250-46d8-a695-ebd4fe42ba3b",
  "typ": "ID",
  "azp": "oauth2-proxy",
  "session_state": "cc89c956-37be-410e-834a-b11bf5b810b4",
  "at_hash": "zJ9k7_MW1tPRiAGFFZ74NA",
  "acr": "0",
  "sid": "cc89c956-37be-410e-834a-b11bf5b810b4",
  "email_verified": true,
  "preferred_username": "てすとゆーざー",
  "given_name": "",
  "family_name": "",
  "email": "test@test.aaa"
}
```

正しく発行されているようです。
`http://localhost/cookies`にもアクセスしてみます。

```
{
  "cookies": {
    "demo_cookie": "xDN81GmyNvGWXI4-h0qSh6BVqdehbnhoejp5_DY0hlJW6m3-GzN_3gr_OVNboHtHDa05-AZrqXlUPnbAWsho24Hu5Ov6T-oMdTjGUTfjsldKRemfRPeViB95rfrUqtRzu-03joF7X1DSbayJ8nxdKMrwDcwvLpfCkWkQss1PYtY-Q09GW9dXTdxuO9GhhMPZqkvK3C2bZOgotrL8scvEe4ei_VHkXZ7nZThPC1b_J685JS3GwVi_u9eZ58rPx_FdZcs4oWxxh83NfDDMmgNzyUk2k1TK3NuCx7wQw32anzY9XmspKIEjWsy2PJXF9-zx1UVVoPunuPkDU2H9E8WHYSEJFVXOcHblLtYn7ocgoSk3ud_2s1ygSTUplLYEG1-RmlZoL8__WjH7xNPD3UUXDrBNv02_G6D-LsBnM1aXQ6SCFpGGgAg3bST88p_nBn6VR2iu7SrSJUYkv4eH_0_y_42-sfHM9cyxmaKupI1LWA5HXl3NrdyDMV7K13_XCq2L3IOALiUQro0AAsAtu8z_OucxWRp_ctvQFuLdnL50GgIl3M05a2FDV7vxVP7HbYiEXp_ZXF9XlxzjbAx-Xe04l3SCOFWGukpaxHBKeLx3xDBuwprRrqEpsfdE8o9zLGs2vfVSjsoydDaC1pFOwR205WonMv8u0wRXu3Fs_wFPuYt_2y31dHjOvULHkV0vKtZ90bzDKmNhORByJfvLJohDruZtd0KNUUXkuam4EpIIEr04FjDobR786nsIvuEahF-JQmVRV_1j5K3Aa7AKF5-RiwMqCAHsLpe-a7uvg18qf9AZBgLkkZPFfv3U8jOV_m3ibM1oJ4mqOAgv3hgurVb0lGpJegr3UxsgOwyw57DXbRNpYwa9PmtC2rGahIAFXaG7_KO8eGcrdM204SmXQ3gMuE7VszGVcL7msljP37N1ThwLlciQhZM2gaQDxWf52QPtCfuZ8-Ez4LVCw4tk5_8hkwyfq3NxNMcmZiylIRoO2jzD-iyiWfoQ5mldBwIhtge8k65nReDsU94zm223MH1cA_xglWGI8mWQd3iMvGmkStpvdSAZx2d88YiUQXJW_gV0vADWzYNv7XWsV1T5F4rn6eeqDKqt__8s5v1w7O7N88R28cFOk4qqJMq2Vz8UncuB0M7M5gjt5dos9DULayu_ij4HqTNqaCGIa2S-wrUfBmyZpljp7xrSMzK96dbFHy3RO9X5t2AjcaWXnXaraFBob7ttgHwUUiJjoDRe41o5m4JGh1OkjjaJnahUP4_LGdbsRmbcWtY6WSt8-1dhHSnw7wUShxLEf9hZksFpV4So62hLCBAbbammaMDn4wpZY6h8g4Ks7CJ5KXxtFP-pQ7SlIn_ZtUNOmYZUxckAr2fkjwPl4vNgjWsokUA57VnTfsYlqA2svB1IYn4fPQUvU0pOgiDL1Sm-xb71N7acY4ZER_ApxYxiQQthwS5JYLjoBXI3b3bJ8ACa1YicIxXRdcR9gLVf8nQ6g2Yo66LFFUHbAsQ8xev924uoD67rz67Fs-NQ53MneI0U1hFXFWO_9MTq7aNHTKCYeHhqdMYjGnis9B49G-rmHglTeBWEAFszMwW32bHvoK-lLg9jL2KvEXhEePV6KQZqsqOxxClHdjygpSlRWo7o8fRSs7dllRR016GnYNftVJ3T29dD6EjyFuKvT0Xfwt5R-bvzzIL8XH6bYFkmysg41a5wxQKHkS3uWlvhc126W5Z-XLnhLxDre_k0honG7dtAVaavx7DAJTVsmbJo2Xarv800NJO4OCkFjnnHLvenImKzmdqz4MpZeVs-kaQVT0sQB5SpuTqiAQHz_of3HZ0xIMSBUciLdZp0jQqChPPDRld0m9LHXylruVj9JS9FcETzI7WW4gKY0Wv1UhowMvboL2XJDvNCkCoVzWUO3kgOE2wVmaC4Z1TfehqOoYh8AJL-pEpSWOCzt8p8T35fXtEfWzajq9kiYG6ayjnyoDjYyHKAvEqyHWHy0h3WUWiwYCAEIF-UjEPI8SvrhuJUkDgAltmLTRs8dNsdWl7PKT_P7EhwyU7Sn-ZiJJpQk6qfd6tCgK3C24JLs6WqTlTNpBUxSV3EHDwAhLrzUegjzQJBxJ6kfsw3a0T-MMWM2Q-58h1JWRtcINLelJn9OjKb2T50WrKl_JttvBIn51yK6fCcQdOW91Dc4jLVj_7_WXKA9wAYaan0D2C3P5YXaagJIJlfWv6ZuYYInDyby79aN-XxUQKar1DYJdu0H-RaUkg-eWXqdEJJYo6GFYE8yIzs1-tTZdR6Dx-U49p_mM0R7W1OuRFui9wPGhn_lMSrTizDoFmlK-NVfGsUj2RMl36kJf1O8V7Ex0tz2goS-ZcJ3-yj8dKHebVImrjiXmHKH1v5Z43gmplEvJhke7I5GivaiDVUSUDJlWmggGuac5-FXGqobqJ5Sa10lZF7xK21aYN-2WGRINJqREOaCawYIDuOgc213kbu7LVuV91AwWHd2n2Wq7xp2-fAdNTUCH7lSriCEBrEhq0cMchWGWyKEn7hQtEYa88rYqKS_KkIvfMRxZUgzySH38Htjib4XijkcpygwQfStUzP6LTyR3h7Dysx721XcQiWajIVMnygtLtf6vwrD_Rh3LrrbkbUuzDXrT_cj52ITa1mYOVXciMK7L7s-CROQF1_KgOg6k4kj67u7s2stVIgMHnybsyDw03_AotBfZSan5ELLtINYNKgWiXxw1B7mZu_gDp7SEMVt7Y0ThqDTUUHqqJcwIx0MSRHZqoiR6iXQLH9OVIuWCuR4f5hlhROPDJjF86uQTva9pzawuI7tUUGvEsV8zfXnYM3oE8VipvrDdY6VI5u1-sKGOzNDte5tIpGztwR4QXw4W4J3J9CSdqd_qdf2ZRHm06u0ixY4bapJLd6A1wnfC3ikQpbEo3cd9CJrcvQ8xK0exBMlhIXq9MRvF0yo3jz4viQXplOCpwwOqmadAbtynoMUSw-8z5bEO_SEOLvir_p49CcGOm9dY674Uz2Ors9iowB_FuTUznejLxMhe11kLfWEYLBVLSXKYGi7EOpLjg1vqUBpPisdT0S_hQX09OEQEamzOPZwM_1qzpLhwik6Aag99sP32Pz1cqfScqPww2pJgM9Z0PmoWukq0kGaYbk4k3kX7Qg9_UK1DOWvUq-xKLUCw_3mRfUus07nUVfAA6LP2SjhxlAxWfEwV5HWnBTDp8wiVpQ2vIlsCZuxAtTiJxgDBO2wBh8pglTz3F4eHmDnT20hevhxEgbg-qh2faq-hVWbdrRSjLESAshAbNkEGV6W0b6iAfrXnO7AZyTQFLE3kzM-sj0355SJRJPjcUazmofjWfeUiDCSmMpViN0iZOXmc8pZe0odXnul7Bf09OsFm5kNZ1G_PJgOZExtpCk05incuX7c2DTNIOTn1BBI7Xw6p8Syo_iZF997850Czt8smB8sXzW5hiJwfvheJeyfJbuYpqq_u6txbjs6DVQ2irQAo6OmOYxJnx5wHU2vwtb5QLtxXjlMLw7X_DXvMmAkKXInsrCKQHXMMEV-ed1rAE5wd1YRZLGj5lmV1RUbNqUcHYXE3D5LV-eAFhZvaGWrz8GsSFPHwtMxHjNb74zuHXgLWE5hDcS4UW4jt3MsDYNFljybe2zsPrrO3uewM7PEsZs|1670326807|K9mF97EVHUEyWCNwStiX9rSmH4tN-_RXYsXAOx9u33M="
  }
}
```

設定した`OAUTH2_PROXY_COOKIE_NAME: "demo_cookie"`で発行されてることがわかりました。

# 感想
簡単に認証に触れることができて、とても勉強になりました。
また、控えているコンテナに対して手を加えず、認証・認可が行えるため、簡易的ではありますが、いろいろな場面で使用できると感じました。

# 参考
https://redj.hatenablog.com/entry/2022/03/19/231840
https://qiita.com/inajob/items/7099e539078c38c60844
https://qiita.com/tosier/items/c6f64811fde067bfd3c3