1
0

【keycloak on Fargate 2】keycloakとALB認証でWebアプリケーションを保護

Last updated at Posted at 2024-08-05

やること

  • https://qiita.com/yuta0003/items/c945fd89578b9f64bf2d
    にて作成したkeycloakをALB認証に使ってみる
  • ALB認証(OIDC)によって、後段のサーバに渡すヘッダをのぞいてみる
  • AWS公式リファレンスに記載されている前提事項は以下の通り。
    image.png
  • イメージ的にはこんな感じ
    image.png

本記事で作成するリソース

image.png

keycloakのOpenID Endpointを控えておく

image.png
keycloakにログインし、「Realm settings」メニューを開き、「OpenID Endpoint Configuration」を押下。
image.png
別タブにてJson形式で情報が表示されるため、「issuer」、「authorization_endpoint」、「token_endpoint」、「userinfo_endpoint」の値を控えておく。

keycloakのクライアント作成

image.png
「Clients」メニューを開き、「Create client」を押下。
image.png
Client IDは任意の名前を記載。
image.png
Client authenticationはオンにする。
image.png
Root URL、Home URLには

https://<webサーバドメイン>

Valid redirect URIsには

https://<keycloakサーバドメイン>/oauth2/idpresponse

を記載する。
ちなみにこの「/oauth2/idpresponse」はALB側で使うものであり、ユーザが実装する必要はない。
image.png

image.png
作成したクライアントの「Credentials」タブより、Client Secretをコピーしておく。

EC2インスタンスの作成

保護対象となるEC2インスタンスを作成する。
今回はnginx+phpの構成で簡易的なWebサーバを構築。
セキュリティグループは次に作成するALBからのHTTP接続を許容する設定で作成して割り当てる。

EC2にnginx、phpをインストール nginxのインストール
[ec2-user@ip-xxx ~]$ sudo yum update -y
[ec2-user@ip-xxx ~]$ sudo yum -y install nginx
[ec2-user@ip-xxx ~]$ sudo systemctl enable nginx
[ec2-user@ip-xxx ~]$ sudo systemctl start nginx.service
[ec2-user@ip-xxx ~]$ sudo systemctl status nginx.service
● nginx.service - The nginx HTTP and reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: disabl>
    Drop-In: /usr/lib/systemd/system/nginx.service.d
             └─php-fpm.conf
     Active: active (running) since Wed 2024-07-31 15:23:53 UTC; 4 days ago
   Main PID: 22747 (nginx)
      Tasks: 2 (limit: 1114)
     Memory: 3.3M
        CPU: 5.876s
     CGroup: /system.slice/nginx.service
             ├─ 22747 "nginx: master process /usr/sbin/nginx"
             └─158598 "nginx: worker process"

phpのインストール

[ec2-user@ip-xxx ~]$ sudo wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
[ec2-user@ip-xxx ~]$ sudo yum install php -y

/etc/nginx/nginx.confの編集
以下のlocation設定を追加

/etc/nginx/nginx.conf
server {
    (省略)
     location ~ \.php$ {
            root           /usr/share/nginx/html;
            fastcgi_pass    unix:/run/php-fpm/www.sock; # こちら派。パスの設
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME $document_root/$fastcgi_script_name;
            include        fastcgi_params;
        }
}

再起動

[ec2-user@ip-xxx ~]$ sudo nginx -s reload

/etc/php-fpm.d/www.confの編集
以下のlisten設定を編集

/etc/nginx/nginx.conf
<省略>
listen.owner = nginx
listen.group = nginx

再起動

[ec2-user@ip-xxx ~]$ sudo service php-fpm restart

indexファイルを設置

[ec2-user@ip-xxx ~]$ cd /usr/share/nginx/html
[ec2-user@ip-xxx ~]$ touch index.php
[ec2-user@ip-xxx ~]$ sudo vi index.php

中身は何でもよいが、ヘッダ情報を取得するため以下の通り記載

index.php
<?php
ini_set('mbstring.internal_encoding' , 'UTF-8');
?>

<html>
  <head>
    <title>Welcome to nginx!</title>
    <style>
      html {
        color-scheme: light dark;
      }
      body {
        width: 80%;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
      }
      table {
        width: 100%; /* テーブルが親要素の幅に合わせる */
        border-collapse: collapse; /* テーブルのボーダーを結合 */
      }
      th, td {
        border: 1px solid #ddd; /* ボーダーを追加 */
        padding: 8px; /* 内側の余白 */
        text-align: left; /* テキストを左寄せ */
        overflow-wrap: break-word; /* テキストの折り返し */
      }
      th {
        background-color: #f2f2f2; /* ヘッダーの背景色 */
      }
      .table-container {
        max-width: 100%; /* コンテナの最大幅を100%に設定 */
        overflow-x: auto; /* 横方向のスクロールを可能にする */
      }
    </style>
  </head>
  <body>
    <h1>ALB認証成功!!</h1>
    <h2>認証に係るHTTPヘッダ</h2>
    <div class="table-container">
    <table>
      <tr>
        <th>アクセストークン</th>
        <td>HTTP_X_AMZN_OIDC_ACCESSTOKEN</td>
        <td>
          <?php print_r($_SERVER['HTTP_X_AMZN_OIDC_ACCESSTOKEN']) ?>
        </td>
      </tr>
      <tr>
        <th>UUID</th>
        <td>HTTP_X_AMZN_OIDC_IDENTITY</td>
        <td>
          <?php print_r($_SERVER['HTTP_X_AMZN_OIDC_IDENTITY']) ?>
        </td>
      </tr>

      <tr>
        <th>ユーザークレーム (JWT)</th>
        <td>HTTP_X_AMZN_OIDC_DATA</td>
        <td>
          <?php print_r($_SERVER['HTTP_X_AMZN_OIDC_DATA']) ?>
        </td>
      </tr>
    </table>
    </div>

ドメイン、証明書作成

WebサーバにHTTPS接続するために、ドメインと証明書を作成しておく。

認証を行うALBを作成

コンテナの前段に配置するALBを作成する。
▷ターゲットグループ

# ##
基本的な設定 ターゲットタイプの選択 インスタンス
ターゲットグループ名 任意の名称
プロトコル : ポート HTTP: 80
IP アドレスタイプ IPv4
VPC 作成済みのVPCを選択
プロトコルバージョン HTTP1
ヘルスチェック ヘルスチェックプロトコル HTTP
ヘルスチェックパス /
ターゲット 先ほど作成したEC2インスタンスを選択

▷ロードバランサー

# ##
ロードバランサータイプ Application Load Balancer
基本的な設定 ロードバランサー名 任意の名称
スキーム インターネット向け
ネットワークマッピング VPC 作成済みのVPCを選択
VPC 作成済みのVPCを選択
マッピング パブリックサブネットを二つ選択する
セキュリティグループ 作成済みのSGを選択
リスナーとルーティング リスナー プロトコル:HTTPS/ポート:443/ターゲットグループ:作成済みのターゲットグループを選択
セキュアリスナーの設定 セキュリティポリシー ポリシー名:ELBSecurityPolicy-TLS13-1-2-2021-06
デフォルト SSL/TLS サーバー証明書 作成済みのACM証明書を選択

★認証設定★
作成したリスナーを選択し、「リスナーの編集」を押下。
image.png
先程控えた各値を記載し、保存する。
image.png

レコードセット登録

Route53にてエイリアスレコードを使用して、作成したドメインとALBを紐づける。

動作確認

Webサーバにアクセスしてみる。
image.png
realmユーザの認証情報を用いてログイン。
下記の通り、各ヘッダが表示されていればOK!
image.png
各ヘッダの役割は以下の通り。

ヘッダ 役割
HTTP_X_AMZN_OIDC_ACCESSTOKEN トークン エンドポイントからのアクセス トークン
HTTP_X_AMZN_OIDC_IDENTITY ユーザー情報エンドポイントに含まれるsubクレーム
HTTP_X_AMZN_OIDC_DATA ユーザークレーム

subクレームにはkeycloakのユーザIDが入るようですね。
せっかくなのでアクセストークンをデコードしてみます。
■ ヘッダ

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "G_U4yHAZfrrPi9a3e7lQG6gTi4jXjZKxThEHSPLaUCY"
}

■ ペイロード(個人情報はxxxでマスクしてます)

{
  "exp": 1722876090,
  "iat": 1722876030,
  "auth_time": 1722876029,
  "jti": "fb443470-e3aa-4570-8b58-260c971e1e66",
  "iss": "https://auth.testxxxx.xxxx/realms/albtest",
  "aud": "account",
  "sub": "9e9a7ed9-26c3-45fc-89a3-f112072fe8c7",
  "typ": "Bearer",
  "azp": "albtest",
  "sid": "e90a823f-c13d-44b3-bd1b-f103250feff4",
  "acr": "1",
  "allowed-origins": [
    ""
  ],
  "realm_access": {
    "roles": [
      "offline_access",
      "uma_authorization",
      "api_authentication",
      "default-roles-albtest"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid email profile",
  "email_verified": false,
  "name": "test user",
  "preferred_username": "test-user",
  "given_name": "test",
  "locale": "ja",
  "family_name": "user",
  "email": "xxxx@xxxx.com"
}

ユーザークレームもデコードしてみると、ユーザ情報を取得できていることが分かります。
ただし、IDトークンではないとの記載がドキュメントにありました。

まとめ

最後に、ALB認証の仕組みが、公式リファレンス上で分かりやすく解説されていたので紹介して終わりにします。
https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html

image.png

# 説明
1 ユーザーがALBの背後のWebサーバに HTTPSリクエスト。認証アクションは、リクエストヘッダー内のCookie をチェック。
2 Cookieが存在しない場合、ALBはユーザーをIdP認証エンドポイントにリダイレクト。
3 ユーザーが認証されると、IdPは認証付与コードとともにユーザーをALBに送り返す。
4 ALBは承認付与コードをIdPトークンエンドポイントに提示。
5 有効な認可付与コードを受け取ると、IdP はIDトークンとアクセストークンをALBに提供。
6 ALBはアクセス トークンをユーザー情報エンドポイントに送信。
7 ユーザー情報エンドポイントは、ユーザー要求のアクセストークンを交換。
8 ALBはAWSELB認証セッションCookieを持つユーザーを元のURIにリダイレクト。
9 ALBはCookieーを検証し、ユーザー情報をX-AMZN-OIDC-* HTTPヘッダーに設定してターゲットに転送。
10 ターゲットはALBに応答を返す。
11 ALBは最終応答をユーザーに送信。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0