Keycloakで実現するAPIセキュリティ 6

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

連載6回目となる今回は、マイクロサービスの認証強化を実現する最先端の機能を紹介します。

田畑 義之

2021年1月19日 6:48

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

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

図1:システム構成

図1:システム構成

使用する各製品のバージョンを表 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)。

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

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

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

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

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

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

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

$ apt install maven
$ mvn -v
Apache Maven 3.6.3

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

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

$ 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の実装

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

リスト4: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)に設定を加えます。

リスト5: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にデプロイしましょう。

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

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

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

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

$ 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にルーティングするようにします。

リスト8:DestinationRuleの書き換え

……
---
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を適用します。

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

$ 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 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]ボタンをクリックします。

図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を組み合わせて、システムを堅牢化する方法を説明します。

この記事のキーワード

この記事をシェアしてください

人気記事トップ10

人気記事ランキングをもっと見る