KeycloakをAWS上に構築してセキュリティ検証してみた

##Keycloakを構築してセキュリティ検証

Keycloakはシングルサインオン(SSO)やAPIアクセスの認証・認可制御をするオープンソースソフトウェアです。
今回は案件で使用する可能性があるので、2021/2頃にAWS上で構築して検証してみました。

検証用に構築したEC2インスタンス上にWildflyをセットアップしてサンプルアプリケーションをデプロイし、クライアントアダプターを適用してSSOが適用されるように設定していきます。

##検証環境情報

今回はAWSで環境を構築したので、一通りの環境情報をまとめてみました。
(AWS上でなくても検証は可能)
費用があまりかからないよう、基本的に無料枠ベースで構築していきます。

###Keycloak

| 環境情報 | バージョン・タイプ |
|-----------|------------|
| keycloak| 12.0.2|

※検証開始時点での最新を使用

###VPC

| 環境情報 | バージョン・タイプ |
|-----------|------------|
| IPv4 CIDR ブロック| 10.0.0.0/24|
| テナンシー| デフォルト|
| サブネット| パブリック|

パブリックサブネットなので、インターネットゲートウェイをルートテーブルに追加してパブリックIPの割り当てを有効化しておきます。
デフォルトのサブネットを使う場合は特に準備は不要です。

###EC2

| 環境情報 | バージョン・タイプ |
|-----------|------------|
| プラットフォーム| Amazon Linux 64ビット|
| インスタンスタイプ| t2.micro|
| ストレージ| EBS 汎用SSD 8G|
| Java| OpenJDK 1.8|
| Wildfly| 22.0.0.Final|
| Maven| 3.6.1|
| Git| 2.23.3|

EC2は上記のVPC上に構築したパブリックサブネット上にアタッチし、念の為終了保護を有効化しておきます。
また、MavenとGitに関してはセキュリティ保護の検証用アプリをクローン・ビルドするためのものなので、Keycloakの動作としては必須ではないです。

###セキュリティグループ

| タイプ | プロトコル |ポート |用途 |
|-----------|------------|------------|------------|
| SSH| TCP| 22| SSH接続用|
| HTTP| TCP| 80| HTTPアクセス用|
| カスタム TCP| TCP| 8080| セキュリティー保護のサンプルアプリケーション用|
| カスタム TCP| TCP| 8180| Keycloakアクセス用|

フルオープンだと全世界からアクセス可能になるため、基本それぞれポート毎に有効なIPアドレスの範囲を設定する必要があります。
Keycloakアクセス用の8180ポートについてはEC2インスタンスのグローバルIPアドレスに対してリクエストされ、JSONのレスポンスが返却されるため、設定時に注意が必要です。
使用しているネットワークのグローバルIPアドレス以外に、EC2へ直接リクエストできるようにしないといけないです。

##EC2セットアップ

※前提として操作は全てルートユーザーで行う想定
　ec2-userのworkディレクトリを使用するため、EC2以外の場合は適宜読み替えが必要

1.作業用フォルダを作成してkeycloakをダウンロード・解凍する

```bash
mkdir /home/ec2-user/work
cd /home/ec2-user/work
wget https://github.com/keycloak/keycloak/releases/download/12.0.2/keycloak-12.0.2.zip
unzip keycloak-12.0.2.zip -d /opt
```

2.Java8をインストールしてバージョンを確認する

```bash
yum -y install java-1.8.0-openjdk-devel
java -version
```

3.Mavenをダウンロードしてシンボリックリンク作成後に配置し、パスを追加する

```bash
cd /home/ec2-user/work
curl https://archive.apache.org/dist/maven/maven-3/3.6.1/binaries/apache-maven-3.6.1-bin.tar.gz | tar zxv
mkdir /opt/maven
mv apache-maven-3.6.1 /opt/maven/
ln -s /opt/maven/apache-maven-3.6.1 /opt/maven/default
echo 'export PATH=$PATH:/opt/maven/default/bin' | sudo tee -a /etc/profile
source /etc/profile
mvn --version
```

4.Gitをインストールしてバージョンを確認する

```bash
yum install git-all
git --version
```

※Keycloakの起動時に80番ポートを使用するため、Apacheはインストールしない
　インストールすると以降の手順でKeycloakが起動しなくなるため注意

##Keycloakセットアップ

1.Keycloak用のログディレクトリを作成する

```bash
mkdir /var/log/keycloak
```

**※Keycloakの起動等でエラーになった場合はこちらに出力されているログを確認する**

2.Keycloakのサービスを作成

```bash
vim /etc/systemd/system/keycloak.service
```

コマンド実行後、エディタが起動するので以下をそのまま貼り付けて保存する
**※「-Djboss.http.port=80」で起動時のポートを指定している**

```
[Unit]
Description=Jboss Application Server After=network.target
After=network.target
[Service]
Type=idle Environment=JBOSS_HOME=/opt/keycloak-12.0.2
JBOSS_LOG_DIR=/var/log/keycloak "JAVA_OPTS=-server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true"
User=root
Group=root
ExecStart=/opt/keycloak-12.0.2/bin/standalone.sh -Djboss.http.port=80
TimeoutStartSec=600
TimeoutStopSec=600
[Install]
WantedBy=multi-user.target
```

3.Keycloakの設定ファイルを再読み込みおよび自動起動の有効化する

```bash
systemctl daemon-reload
systemctl enable keycloak
```

4.Keycloakに内包されているWildflyの設定ファイルを編集し、外部からアクセスできるように設定する

```bash
vi /opt/keycloak-12.0.2/standalone/configuration/standalone.xml
```

viエディタが起動するので以下のように編集する
※設定するIPアドレスはEC2インスタンスのプライベートIPアドレスなので、グローバルIPアドレスと間違えないよう注意

```xml
<interface name="management">	
	<inet-address value="${jboss.bind.address.management:<EC2インスタンスのプライベートIPアドレス>}"/>
</interface>	
<interface name="public">	
	<inet-address value="${jboss.bind.address:<EC2インスタンスのプライベートIPアドレス>}"/>
</interface>	
```

※viエディターのコマンドモード「/」で「interface name="management"」・「interface name="public"」を検索してペーストすると簡単に編集可能

5.Keycloakを起動する

```bash
systemctl start keycloak
```

6.起動後に管理アカウントを作成する
※以下は管理者ID/PASSをadmin/passwordとする場合の例

```bash
./bin/add-user-keycloak.sh -r master -u admin -p password
```

7.Keycloakを再起動する

```bash
systemctl daemon-reload
systemctl restart keycloak
```

8.Keycloakにログインする
※以下は管理者ID/PASSをadmin/passwordとする場合の一例

```bash
./bin/kcadm.sh config credentials --server http://<EC2インスタンスのプライベートIPアドレス>/auth --realm master --user admin --password password
```

9.外部からのアクセス時にHTTPでアクセスできるように設定を変更する

```bash
./bin/kcadm.sh update realms/master -s sslRequired=NONE
```

10.以下のURLでブラウザからアクセスし、作成した管理者ユーザーでログインする

```
http://<EC2インスタンスのグローバルIPアドレス>/auth/admin/
```

![Sign in to Keycloak - Google Chrome 2021-02-04 18.17.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/3e0f29de-19f6-a218-56c8-a39eb0536955.png)

###日本語化手順

必要に応じて日本語化の設定が可能です。
以降の手順についても日本語化を前提として記載しています。

1.MasterのThemesタブを開き、赤字のオプションをONにして「Save」押下

![日本語化1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/7b920b40-a0ca-68c2-7330-7efbe2d91b11.png)

2.ヘッダー右上のManege accountをクリックすると有効化される

![日本語化2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/bc89daa7-db90-3b50-5f44-0745122e73f1.png)

##レルム・ユーザー追加

構築初回のログインはmasterレルムでログインされているため、別のレルムを作成していきます。
レルム作成後は紐づくユーザーを作成し、実際のログイン時に作成したユーザー情報を使用します。

###レルム作成

1.レルムの追加をクリックする

![レルム追加1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/fa31d6dc-017e-9406-ac12-ba7b71422183.png)

2.任意の名前を入力して「作成」押下

![レルム追加2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/72486af5-142e-5635-8d1f-f4a221c3832a.png)

3.レルムが作成される
※サイドバーの上でレルムの切り替えができる

![レルム追加3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/0a129391-076b-adc6-a43a-c0a49e0b2aef.png)

※デフォルトだとSSLのリクエストが強要されてしまうので、HTTPでアクセスできるように設定を変更しておく

![レルム追加4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/e11f2305-6f23-d133-342f-a8fa4404f150.png)

###ユーザー作成

1.サイドバーのユーザーをクリックして「ユーザーの追加」を押下

![ユーザー追加1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/a6eb7d4d-e8a0-758c-991e-36b2b1d1814d.png)

2.任意の名前を入力して「保存」を押下

![ユーザー追加2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/425267e8-bc1d-90ca-228b-74dd5906b626.png)

3.作成したユーザーにパスワードを設定する
※以下の場合はワンタイムパスワードにしているため、ログイン後にパスワード変更を求められる

![ユーザー追加3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/96ffd7ae-8389-e63c-f505-ce2e27bd9a49.png)

4.一度サインアウトして以下のURLへアクセスし、作成したユーザーでログインする
※ログイン後にパスワード変更を求められる

```
http://<EC2インスタンスのグローバルIPアドレス>/auth/realms/<作成したレノム名>/account
```

![ユーザー追加4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/67767519-7cbc-c2c1-4aaf-17e02fafc233.png)

##検証用Wildflyのセットアップ

###Wildflyのインストール

1.Wildflyをダウンロードして解凍する

```bash
cd /home/ec2-user/work
wget https://download.jboss.org/wildfly/22.0.0.Final/wildfly-22.0.0.Final.zip
unzip wildfly-22.0.0.Final.zip -d /opt
```

2.Keycloakを起動する

```bash
cd /opt/keycloak-12.0.2
./bin/standalone.sh -Djboss.socket.binding.port-offset=100
```

3.以下のURLでアクセスできることを確認する

```
http://<EC2インスタンスのグローバルIPアドレス>:8180/auth/admin/
```

###WildFlyクライアント・アダプターのセットアップ

1.WildFly OpenID Connect Client Adapter の配布物をダウンロードしてWildflyのルートディレクトリに解凍する

```bash
cd /home/ec2-user/work
wget https://github.com/keycloak/keycloak/releases/download/12.0.2/keycloak-oidc-wildfly-adapter-12.0.2.zip
unzip keycloak-oidc-wildfly-adapter-12.0.2.zip -d /opt/wildfly-22.0.0.Final
```

2.以下のスクリプトを実行する

```bash
cd /opt/wildfly-22.0.0.Final
./bin/jboss-cli.sh --file=adapter-elytron-install-offline.cli
```

3.Wildflyを起動する

```bash
./bin/standalone.sh
```

###WildFlyアプリケーションの登録

1.以下のURLにアクセスして管理者ユーザーでログイン

```
http://<EC2インスタンスのグローバルIPアドレス>:8180/auth/admin/
```

2.先ほど作成したレルムを選択してサイドバーのクライアントをクリックし、「作成」を押下
※以下はレルムはDemoで作成する場合の例

![App登録1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/bca9e8b4-09ae-2df5-35cb-8cd182c094c0.png)

2.以下のように入力して「保存」を押下
※ルートURLにはグローバルIPアドレスを指定

![App登録2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/e0c3749b-989c-c899-1af8-c96334814151.png)

3.認証用のJSONファイルを自動生成してダウンロードする
※セキュリティ検証時にサンプルアプリケーションの中に組み込んで使用する

![App登録3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/f6a176fc-5c8c-84a2-a3b5-b7101e4152cf.png)

※XMLでもセキュリティ保護設定用に自動生成することができる

![App登録4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/830e7f27-6250-99cc-25bb-6fd11410ce5d.png)

###WildFlyのセキュリティ保護設定

1.standalone.xmlを開く

```bash
vi /opt/wildfly-22.0.0.Final/standalone/configuration/standalone.xml
```

エディタが起動後、以下のように編集

①EC2のグローバルIPアドレス・レルムを指定してセキュリティ保護設定を追加する
■編集前

```xml
<subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
```

■編集後

```xml
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
  <secure-deployment name="vanilla.war">
    <realm>demo</realm>
    <auth-server-url>http://<EC2インスタンスのグローバルIPアドレス>:8180/auth</auth-server-url>
    <public-client>true</public-client>
    <ssl-required>NONE</ssl-required>
    <resource>vanilla</resource>
  </secure-deployment>
</subsystem>
```

※viエディターのコマンドモード「/」で「urn:jboss:domain:keycloak:1.1」を検索してペーストすると簡単に編集可能
　また、上記の場合はレルムはdemoを指定する例

②外部アクセスを許可されるように設定を変更する

```xml
<interface name="management">	
	<inet-address value="${jboss.bind.address.management:<EC2インスタンスのプライベートIPアドレス>}"/>
</interface>	
<interface name="public">	
	<inet-address value="${jboss.bind.address:<EC2インスタンスのプライベートIPアドレス>}"/>
</interface>	
``````

※①と同じくviエディターのコマンドモード「/」で「interface name="management"」・「interface name="public"」を検索してペーストすると簡単に編集可能

2.Wildflyを再度起動する

```bash
cd /opt/wildfly-22.0.0.Final
./bin/standalone.sh
```

※失敗する場合は起動したプロセスが残っている可能性があるので終了させる

```bash
lsof -i:8080
kill -9 <プロセスID>
```

##サンプルアプリケーションのクローン・ビルド

1.アプリケーションのサンプルコードをGitからクローンする

```bash
cd /home/ec2-user/work
git clone https://github.com/keycloak/keycloak-quickstarts
```

2.以下のコマンドでディレクトリを移動し、クライアント作成時に保存した認証用のJSONファイルを配置する

```bash
cd /home/ec2-user/work/keycloak-quickstarts/app-profile-jee-vanilla/config
```

3.以下のコマンドでディレクトリを移動し、アプリケーションをデプロイする

```bash
cd /home/ec2-user/work/keycloak-quickstarts/app-profile-jee-vanilla
mvn clean wildfly:deploy
```

※Wildflyが起動している状態で行う必要がある
　また、JAVA_HOMEに設定のパスがJREの場合はビルドに失敗するのでJDKが設定されていることを確認する

##セキュリティ検証

1.以下のURLからデプロイされた検証用のアプリケーションにアクセスする

```
http://<EC2インスタンスのグローバルIPアドレス>:8080/vanilla
```

![セキュリティテスト1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/0e911710-8ce7-9c7a-3c39-4cae08f9db51.png)

2.Keycloakの認証を求められるので、作成したユーザーとパスワードを入力してログインする

![セキュリティテスト2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/21617128-84fb-775d-e015-707980c0dd1c.png)

3.Keycloakを使用してサンプルアプリケーションがセキュリティー保護されていることが確認できる

![セキュリティテスト3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492152/674c7c36-25c1-ed68-ec2e-3c9ed9584683.png)


##まとめ

Keycloakをセットアップ後にSSOの対象となるアプリケーションがデプロイされているAPサーバーにセキュリティ保護設定を加えることで、作成したレルム内のユーザー情報をもとにログインできることが確認できました。
今回はWildflyを使用して検証を行いましたが、別のAPサーバー用にクライアント・アダプターが提供されているのでTomcat等でも同じように導入可能です。
業務上ではアプリケーションの実装面でセキュリティを意識することがありましたが、外部と連携する形でセキュリティ強化することも非常に重要だと感じました。

近年だとAWS Cognitoなどで簡単にユーザープールを管理できたりする仕組みもありますが、Keycloakに関してもチラホラみかけるようになったので使い方を知っておいても損はないと思います。
また、今回の検証環境構築は進めるうちにAPサーバーの設定についても少し強くなれた気がします。
気になった方は是非試してみてください。

##参考

https://keycloak-documentation.openstandia.jp/master/ja_JP/getting_started/index.html

https://www.ipride.co.jp/blog/2229

https://qiita.com/ryota_i/items/69ded3fd950c7f9b70f5