コンテナ上のマイクロサービスの認証強化 ~QuarkusとKeycloak~

2021年1月19日(火)
田畑 義之
連載6回目となる今回は、マイクロサービスの認証強化を実現する最先端の機能を紹介します。

第六回は、第五回に引き続き、内部向けのAPIであるマイクロサービスの認証を強化する最先端の機能を紹介します。

前回と同様、すべてのサービスをKubernetes(Minikube)上にデプロイする構成とし、堅牢化の対象としてIstioが提供しているBookinfoアプリケーションを取り上げます(図1)。

図1:システム構成

図1:システム構成

使用する各製品のバージョンを表 1に示します。

表1:使用する製品とバージョン

製品バージョン
Minikubev1.13.1
  (Kubernetes)v1.19.2
  (Docker)19.03.8
Keycloak11.0.3
Istio1.7.4
Quarkus1.9.2.Final

なお、Kubernetes環境(Minikube)はドキュメント等を参考にすでにインストールされているものとします。また、前回の記事でご紹介したKeycloakとIstioの導入/各種設定はなされているものとします。

QuarkusとKeycloak

Quarkusとは、KubernetesネイティブのJavaフレームワークです。Quarkusを使用することで、コンテナに最適化されたJavaアプリケーションを開発できます。ここではQuarkusとKeycloakを組み合わせた堅牢化方法をご紹介します(図2)。

図2:QuarkusとKeycloakを用いたBookinfoアプリケーションの堅牢化

図2:QuarkusとKeycloakを用いたBookinfoアプリケーションの堅牢化

前回は各サービスにそれぞれIstioのAuthorizationPolicyを作成し導入することで、各サービスにJWTによるアクセス制御を追加しました。Quarkusのkeycloak-authorizationエクステンションを使うと、各サービスのアクセス制御ロジックをKeycloakの認可サービスに集約することができます。

各サービスのアクセス制御ロジックを、各サービスで個別に持つべきか、認可サーバ側で集約して持つべきかについては一長一短があり、またシステムの要件に大きく依存する部分があるため、一概にどちらが良いとは言えません。アクセス制御ロジックを認可サーバに集約した場合、Keycloakのスコープやロール、グループといったリソースを、各サービス側で再定義することなくアクセス制御に使用することができ、またKeycloakのリソースの変更を即座にアクセス制御に反映させることができるという利点があります。

Quarkusアプリケーションの実装

まずは、Quarkusアプリケーションを実装していきます。Quarkusアプリケーションの実装には、Mavenが必要となるので、先にインストールしておきます。

リスト1:Mavenをインストール

1$ apt install maven
2$ mvn -v
3Apache Maven 3.6.3

次にプロジェクトを作成します。今回は、Keycloakの認可サービスを利用したり、アプリケーションをKubernetesにデプロイしたりするために、oidc、keycloak-authorization、resteasy-jsonb、kubernetes、container-image-dockerの5つのエクステンションを使います。

リスト2:プロジェクトの作成

1$ 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を実装します。

リスト3:Book.javaの実装

01package org.acme.security.keycloak.authorization;
02 
03import com.fasterxml.jackson.annotation.JsonProperty;
04 
05import io.quarkus.security.identity.SecurityIdentity;
06 
07public class Book {
08    @JsonProperty
09    private String type = "article";
10    @JsonProperty
11    private int pages = 3;
12    @JsonProperty
13    private String publisher;
14    @JsonProperty
15    private String language = "Japanese";
16    @JsonProperty("ISBN-10")
17    private String isbn10 = "1234567890";
18    @JsonProperty("ISBN-13")
19    private String isbn13 = "123-1234567890";
20 
21    public Book(SecurityIdentity identity) {
22        this.publisher = identity.getPrincipal().getName();
23    }
24}

security-keycloak-authorization-quickstart/src/main/java/org/acme/security/keycloak/authorization/BooksResource.javaを実装します。

リスト4:BooksResource.javaの実装

01package org.acme.security.keycloak.authorization;
02 
03import javax.inject.Inject;
04import javax.ws.rs.GET;
05import javax.ws.rs.Path;
06import javax.ws.rs.core.MediaType;
07import javax.ws.rs.core.Response;
08 
09import org.jboss.resteasy.annotations.cache.NoCache;
10 
11import com.fasterxml.jackson.core.JsonProcessingException;
12import com.fasterxml.jackson.databind.ObjectMapper;
13 
14import io.quarkus.security.identity.SecurityIdentity;
15 
16@Path("/")
17public class BooksResource {
18 
19    @Inject
20    SecurityIdentity identity;
21 
22    @GET
23    @Path("/health")
24    public Response health() {
25        return Response.ok().type(MediaType.APPLICATION_JSON).entity("{\"status\": \"Details is healthy\"}").build();
26    }
27 
28    @GET
29    @Path("/details/0")
30    @NoCache
31    public Response details() {
32        ObjectMapper mapper = new ObjectMapper();
33        String json = null;
34        try {
35            json = mapper.writeValueAsString(new Book(identity));
36        } catch (JsonProcessingException e) {
37            e.printStackTrace();
38        }
39        return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
40    }
41 
42}

application.properties(security-keycloak-authorization-quickstart/src/main/ resources/application.properties)に設定を加えます。

リスト5:application.propertiesに設定を追加

01quarkus.http.port=9080
02quarkus.oidc.auth-server-url=http://172.30.10.93:31385/auth/realms/bookinfo
03quarkus.oidc.client-id=details
04quarkus.oidc.credentials.secret=<client secret>
05 
06# Enable Policy Enforcement
07quarkus.keycloak.policy-enforcer.enable=true
08 
09quarkus.kubernetes.part-of=details
10quarkus.container-image.registry=
11quarkus.container-image.group=
12quarkus.container-image.name=details
13quarkus.container-image.tag=1.0
14quarkus.kubernetes.image-pull-policy=never
15quarkus.kubernetes.labels.app=details
16quarkus.kubernetes.labels.version=v2

以上で、Quarkusアプリケーションの実装は完了です。

Quarkusアプリケーションのデプロイ

作成したQuarkusアプリケーションをビルドし、Kubernetesにデプロイしましょう。

リスト6:Quarkusアプリケーションのデプロイ

1$ cd security-keycloak-authorization-quickstart/
2$ ./mvnw clean package -Dquarkus.kubernetes.deploy=true

以上で、Detailsサービスのv2がデプロイされました。

リスト7:デプロイされていることを確認

1$ kubectl get pods
2NAME                             READY   STATUS    RESTARTS   AGE
3details-766f98fcf8-pc8fw         2/2     Running   1          10s
4$ kubectl get deployments
5NAME             READY   UP-TO-DATE   AVAILABLE   AGE
6details          1/1     1            1           10s

DestinationRule(destination-rule-all-mtls-v2.yaml)を書き換えて、Detailsサービスのv2にルーティングするようにします。

リスト8:DestinationRuleの書き換え

01……
02---
03apiVersion: networking.istio.io/v1alpha3
04kind: DestinationRule
05metadata:
06  name: details
07spec:
08  host: details
09  trafficPolicy:
10    tls:
11      mode: ISTIO_MUTUAL
12  subsets:
13  - name: v2
14    labels:
15      version: v2
16---

変更したDestinationRuleを適用します。

リスト9:変更したDestinationRuleの適用

1$ kubectl apply -f destination-rule-all-mtls-v2.yaml

以上で、Quarkusアプリケーションのデプロイは完了です。

Keycloakの認可サービスの設定

Keycloakの認可サービスでは、Resource(またはAuthorization Scope)、Policy、Permissionという3つのリソースを用いて、アクセス制御を設定することができます(図3)。

図3:Keycloakの認可サービス

図3:Keycloakの認可サービス

Resourceには、その名の通り、「/protected/*」や「/secret」といったリソースを設定し、Policyには、「adminロールを持つ」や「ユーザAである」といった条件を設定します。そして設定したResourceとPolicyをPermissionで結びつけることで、アクセス制御を実現します。もちろん、複数のResourceに対して同一の条件を設定したり、同一のResourceに対して複数の条件を設定したりすることもできます。

実際に設定していきます。Keycloakの認可サービスは、管理コンソールの各クライアント設定画面のAuthorizationタブで設定できます(図4)。

図4:Authorizationタブ

図4:Authorizationタブ

今回は、/details/*にadminロールチェックを導入してみましょう。つまり、Resourceとして「/details/*」、Policyとして「adminロールを持つ」を作成し、それらを結びつけるPermissionを作成します。

まずは事前準備として、adminロールとadminロールを持つユーザを作成し、Detailsサービス用クライアントの認可サービスを有効化します(表2)。

表2:作成するKeycloakのリソース

リソース設定項目名設定値備考
クライアントClient IDdetailsDetailsサービス用クライアント。
 Access Typeconfidential
 Standard Flow EnabledOFF
 Direct Access Grants EnabledOFF
 Service Accounts EnabledON
 Authorization EnabledON
 ロールRole Nameadmin
ユーザUsernameadmin_user
 ロールClientdetails
 Assigned Rolesadmin

まずは、Resourceを作成します。Resourcesタブの[Create]ボタンをクリックすると、Add Resource画面が表示されますので(図5)、Resource名(ここでは「Details Resource」とします)とURI(ここでは「/details/*」とします)を入力し、[Save]ボタンをクリックします。

図5:Add Resource画面

図5:Add Resource画面

次に、Policyを作成します。Policiesタブの[Create Policy…]をクリックし、[Role]をクリックすると、Add Role Policy画面が表示されますので(図 6)、Policy名(ここでは「Admin Role Policy」とします)とクライアント名(ここでは「details」とします)とクライアントロール名(ここでは「admin」とします)を入力し、[Save]ボタンをクリックします。

図6:Add Role Policy画面

図6:Add Role Policy画面

次に、Permissionを作成します。Permissionsタブの[Create Permission…]をクリックし、[Resource-based]をクリックすると、Add Resource Permission画面が表示されますので(図7)、Permission名(ここでは「Details Resource Permission」とします)とResource(ここでは「Details Resource」とします)とPolicy(ここでは「Admin Role Policy」とします)を入力し、[Save]ボタンをクリックします。

図7:Add Resource Permission画面

図7:Add Resource Permission画面

以上で、Keycloakの認可サービスの設定は完了です。試しに、sample_userを用いて発行したアクセストークンを付与してBookinfoアプリケーションのProductページにアクセスすると、Productページは表示されますが、Details部分(左半分)が表示されないこと(「Sorry, product details are currently unavailable for this book.」が表示されること)を確認できます(図8)。

図8:BookinfoアプリケーションのProductページ(DetailsサービスAccess Denied)

図8:BookinfoアプリケーションのProductページ(DetailsサービスAccess Denied)

次に、admin_userを用いて発行したアクセストークンを付与してBookinfoアプリケーションのProductページにアクセスすると、Details部分も表示されることを確認できます(図9)。またDetails部分には、今回実装した情報が表示されていることにもご注目ください。

図9:BookinfoアプリケーションのProductページ

図9:BookinfoアプリケーションのProductページ

次回は、コンテナ上のマイクロサービスの認証強化について、StrimziとKeycloakを組み合わせて、システムを堅牢化する方法を説明します。

株式会社 日立製作所
OSSソリューションセンタにて、API管理や認証周りのOSSの開発/サポート/普及活動に従事。3scaleおよびkeycloakコミュニティのコントリビュータであり、多数のコードをコミットしている。

連載バックナンバー

運用・管理技術解説
第7回

コンテナ上のマイクロサービスの認証強化 ~StrimziとKeycloak~

2021/2/16
前回に引き続き、マイクロサービスの認証強化を実現する最先端の機能を紹介します。
運用・管理技術解説
第6回

コンテナ上のマイクロサービスの認証強化 ~QuarkusとKeycloak~

2021/1/19
連載6回目となる今回は、マイクロサービスの認証強化を実現する最先端の機能を紹介します。
セキュリティ技術解説
第5回

コンテナ上のマイクロサービスの認証強化 ~IstioとKeycloak~

2020/12/15
連載5回目となる今回は、Istioを用いたマイクロサービスのシステムをKeycloakを用いて認証強化する手順を紹介します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています