コンテナ上のマイクロサービスの認証強化 ~QuarkusとKeycloak~
第六回は、第五回に引き続き、内部向けのAPIであるマイクロサービスの認証を強化する最先端の機能を紹介します。
前回と同様、すべてのサービスをKubernetes(Minikube)上にデプロイする構成とし、堅牢化の対象としてIstioが提供しているBookinfoアプリケーションを取り上げます(図1)。
使用する各製品のバージョンを表 1に示します。
製品 | バージョン |
---|---|
Minikube | v1.13.1 |
(Kubernetes) | v1.19.2 |
(Docker) | 19.03.8 |
Keycloak | 11.0.3 |
Istio | 1.7.4 |
Quarkus | 1.9.2.Final |
なお、Kubernetes環境(Minikube)はドキュメント等を参考にすでにインストールされているものとします。また、前回の記事でご紹介したKeycloakとIstioの導入/各種設定はなされているものとします。
QuarkusとKeycloak
Quarkusとは、KubernetesネイティブのJavaフレームワークです。Quarkusを使用することで、コンテナに最適化されたJavaアプリケーションを開発できます。ここではQuarkusとKeycloakを組み合わせた堅牢化方法をご紹介します(図2)。
前回は各サービスにそれぞれIstioのAuthorizationPolicyを作成し導入することで、各サービスにJWTによるアクセス制御を追加しました。Quarkusのkeycloak-authorizationエクステンションを使うと、各サービスのアクセス制御ロジックをKeycloakの認可サービスに集約することができます。
各サービスのアクセス制御ロジックを、各サービスで個別に持つべきか、認可サーバ側で集約して持つべきかについては一長一短があり、またシステムの要件に大きく依存する部分があるため、一概にどちらが良いとは言えません。アクセス制御ロジックを認可サーバに集約した場合、Keycloakのスコープやロール、グループといったリソースを、各サービス側で再定義することなくアクセス制御に使用することができ、またKeycloakのリソースの変更を即座にアクセス制御に反映させることができるという利点があります。
Quarkusアプリケーションの実装
まずは、Quarkusアプリケーションを実装していきます。Quarkusアプリケーションの実装には、Mavenが必要となるので、先にインストールしておきます。
$ apt install maven $ mvn -v Apache Maven 3.6.3
次にプロジェクトを作成します。今回は、Keycloakの認可サービスを利用したり、アプリケーションをKubernetesにデプロイしたりするために、oidc、keycloak-authorization、resteasy-jsonb、kubernetes、container-image-dockerの5つのエクステンションを使います。
$ mvn io.quarkus:quarkus-maven-plugin:1.9.2.Final:create -DprojectGroupId=org.acme -DprojectArtifactId=security-keycloak-authorization-quickstart -Dextensions="oidc, keycloak-authorization, resteasy-jsonb, kubernetes, container-image-docker"
既存のDetailsサービスを参考に、実装していきます。security-keycloak-authorization-quickstart/src/main/java/org/acme/security/keycloak/authorization/Book.javaを実装します。
package org.acme.security.keycloak.authorization; import com.fasterxml.jackson.annotation.JsonProperty; import io.quarkus.security.identity.SecurityIdentity; public class Book { @JsonProperty private String type = "article"; @JsonProperty private int pages = 3; @JsonProperty private String publisher; @JsonProperty private String language = "Japanese"; @JsonProperty("ISBN-10") private String isbn10 = "1234567890"; @JsonProperty("ISBN-13") private String isbn13 = "123-1234567890"; public Book(SecurityIdentity identity) { this.publisher = identity.getPrincipal().getName(); } }
security-keycloak-authorization-quickstart/src/main/java/org/acme/security/keycloak/authorization/BooksResource.javaを実装します。
package org.acme.security.keycloak.authorization; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.jboss.resteasy.annotations.cache.NoCache; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.security.identity.SecurityIdentity; @Path("/") public class BooksResource { @Inject SecurityIdentity identity; @GET @Path("/health") public Response health() { return Response.ok().type(MediaType.APPLICATION_JSON).entity("{\"status\": \"Details is healthy\"}").build(); } @GET @Path("/details/0") @NoCache public Response details() { ObjectMapper mapper = new ObjectMapper(); String json = null; try { json = mapper.writeValueAsString(new Book(identity)); } catch (JsonProcessingException e) { e.printStackTrace(); } return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build(); } }
application.properties(security-keycloak-authorization-quickstart/src/main/ resources/application.properties)に設定を加えます。
quarkus.http.port=9080 quarkus.oidc.auth-server-url=http://172.30.10.93:31385/auth/realms/bookinfo quarkus.oidc.client-id=details quarkus.oidc.credentials.secret=<client secret> # Enable Policy Enforcement quarkus.keycloak.policy-enforcer.enable=true quarkus.kubernetes.part-of=details quarkus.container-image.registry= quarkus.container-image.group= quarkus.container-image.name=details quarkus.container-image.tag=1.0 quarkus.kubernetes.image-pull-policy=never quarkus.kubernetes.labels.app=details quarkus.kubernetes.labels.version=v2
以上で、Quarkusアプリケーションの実装は完了です。
Quarkusアプリケーションのデプロイ
作成したQuarkusアプリケーションをビルドし、Kubernetesにデプロイしましょう。
$ cd security-keycloak-authorization-quickstart/ $ ./mvnw clean package -Dquarkus.kubernetes.deploy=true
以上で、Detailsサービスのv2がデプロイされました。
$ kubectl get pods NAME READY STATUS RESTARTS AGE details-766f98fcf8-pc8fw 2/2 Running 1 10s $ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE details 1/1 1 1 10s
DestinationRule(destination-rule-all-mtls-v2.yaml)を書き換えて、Detailsサービスのv2にルーティングするようにします。
…… --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: details spec: host: details trafficPolicy: tls: mode: ISTIO_MUTUAL subsets: - name: v2 labels: version: v2 ---
変更したDestinationRuleを適用します。
$ kubectl apply -f destination-rule-all-mtls-v2.yaml
以上で、Quarkusアプリケーションのデプロイは完了です。
Keycloakの認可サービスの設定
Keycloakの認可サービスでは、Resource(またはAuthorization Scope)、Policy、Permissionという3つのリソースを用いて、アクセス制御を設定することができます(図3)。
Resourceには、その名の通り、「/protected/*」や「/secret」といったリソースを設定し、Policyには、「adminロールを持つ」や「ユーザAである」といった条件を設定します。そして設定したResourceとPolicyをPermissionで結びつけることで、アクセス制御を実現します。もちろん、複数のResourceに対して同一の条件を設定したり、同一のResourceに対して複数の条件を設定したりすることもできます。
実際に設定していきます。Keycloakの認可サービスは、管理コンソールの各クライアント設定画面のAuthorizationタブで設定できます(図4)。
今回は、/details/*にadminロールチェックを導入してみましょう。つまり、Resourceとして「/details/*」、Policyとして「adminロールを持つ」を作成し、それらを結びつけるPermissionを作成します。
まずは事前準備として、adminロールとadminロールを持つユーザを作成し、Detailsサービス用クライアントの認可サービスを有効化します(表2)。
リソース | 設定項目名 | 設定値 | 備考 |
---|---|---|---|
クライアント | Client ID | details | Detailsサービス用クライアント。 |
Access Type | confidential | ― | |
Standard Flow Enabled | OFF | ― | |
Direct Access Grants Enabled | OFF | ― | |
Service Accounts Enabled | ON | ― | |
Authorization Enabled | ON | ― | |
ロール | Role Name | admin | ― |
ユーザ | Username | admin_user | ― |
ロール | Client | details | ― |
Assigned Roles | admin | ― |
まずは、Resourceを作成します。Resourcesタブの[Create]ボタンをクリックすると、Add Resource画面が表示されますので(図5)、Resource名(ここでは「Details Resource」とします)とURI(ここでは「/details/*」とします)を入力し、[Save]ボタンをクリックします。
次に、Policyを作成します。Policiesタブの[Create Policy…]をクリックし、[Role]をクリックすると、Add Role Policy画面が表示されますので(図 6)、Policy名(ここでは「Admin Role Policy」とします)とクライアント名(ここでは「details」とします)とクライアントロール名(ここでは「admin」とします)を入力し、[Save]ボタンをクリックします。
次に、Permissionを作成します。Permissionsタブの[Create Permission…]をクリックし、[Resource-based]をクリックすると、Add Resource Permission画面が表示されますので(図7)、Permission名(ここでは「Details Resource Permission」とします)とResource(ここでは「Details Resource」とします)とPolicy(ここでは「Admin Role Policy」とします)を入力し、[Save]ボタンをクリックします。
以上で、Keycloakの認可サービスの設定は完了です。試しに、sample_userを用いて発行したアクセストークンを付与してBookinfoアプリケーションのProductページにアクセスすると、Productページは表示されますが、Details部分(左半分)が表示されないこと(「Sorry, product details are currently unavailable for this book.」が表示されること)を確認できます(図8)。
次に、admin_userを用いて発行したアクセストークンを付与してBookinfoアプリケーションのProductページにアクセスすると、Details部分も表示されることを確認できます(図9)。またDetails部分には、今回実装した情報が表示されていることにもご注目ください。
次回は、コンテナ上のマイクロサービスの認証強化について、StrimziとKeycloakを組み合わせて、システムを堅牢化する方法を説明します。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- コンテナ上のマイクロサービスの認証強化 ~StrimziとKeycloak~
- コンテナ上のマイクロサービスの認証強化 ~IstioとKeycloak~
- NGINX Ingress Controllerの柔軟なアプリケーション制御、具体的なユースケースと設定方法を理解する
- Keycloakのクライアントポリシー(Client Policies)
- クラウドネイティブな環境でKeycloakによるシングルサインオンを実現
- APIファーストの設計ツール「Apicurio」「Microcks」を使ってみよう!
- Keycloakの最前線を体感できるイベント「Keyconf 23」レポート
- クライアントポリシーを利用したKeycloakの設定方法と、FAPIリファレンス実装の紹介
- Policy as Codeでインフラのコンプライアンスを自動実現! 「Pulumi CrossGuard」を活用してみよう
- Keycloakのインストールと構築例