The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

LDAPレルムを利用したセキュリティーの使用

このガイドでは、QuarkusアプリケーションがLDAPサーバーを使用してユーザーアイデンティティを認証および認可する方法を説明します。

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.15

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

アーキテクチャ

この例では、3つのエンドポイントを提供する非常にシンプルなマイクロサービスを構築します:

  • /api/public

  • /api/users/me

  • /api/admin

/api/public エンドポイントは匿名でアクセスできます。/api/admin エンドポイントは RBAC (Role-Based Access Control) で保護されており、adminRole ロールを付与されたユーザーのみがアクセスできます。このエンドポイントでは、@RolesAllowed アノテーションを使用して、アクセス制約を宣言的に適用します。/api/users/me エンドポイントも RBAC (Role-Based Access Control) で保護されており、standardRole ロールを付与されたユーザーのみがアクセスできます。レスポンスとして、ユーザーに関する詳細を含むJSONドキュメントを返します。

デフォルトでは、Quarkusは、Log4Shellと同様の将来の脆弱性を軽減するための予防措置として、アプリケーション内でのJNDIの使用を制限します。LDAPベースの認証にはJNDIが必要なため、この保護機能は自動的に無効化されます。

ソリューション

次のセクションの指示に従って、アプリケーションを段階的に作成することをお勧めします。ただし、完成した例に直接進むこともできます。

Gitリポジトリをクローン: git clone https://github.com/quarkusio/quarkus-quickstarts.git 、または アーカイブ をダウンロードしてください。

ソリューションは security-ldap-quickstart ディレクトリ にあります。

Mavenプロジェクトの作成

まず、新しいプロジェクトが必要です。以下のコマンドで新しいプロジェクトを作成します:

コマンドラインインタフェース
quarkus create app org.acme:security-ldap-quickstart \
    --extension='elytron-security-ldap,rest' \
    --no-code
cd security-ldap-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.35.2:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=security-ldap-quickstart \
    -Dextensions='elytron-security-ldap,rest' \
    -DnoCode
cd security-ldap-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=security-ldap-quickstart"

このコマンドはプロジェクトを生成し、Quarkusアプリケーション向けの wildfly-elytron-realm-ldap アダプターである elytron-security-ldap エクステンションをインポートします。

すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに elytron-security-ldap エクステンションを追加することができます:

コマンドラインインタフェース
quarkus extension add elytron-security-ldap
Maven
./mvnw quarkus:add-extension -Dextensions='elytron-security-ldap'
Gradle
./gradlew addExtension --extensions='elytron-security-ldap'

これにより、ビルドファイルに以下が追加されます:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-elytron-security-ldap</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-elytron-security-ldap")

アプリケーションの記述

まず、/api/public エンドポイントを実装することから始めましょう。以下のソースコードからわかるように、これは通常のJakarta RESTリソースに過ぎません:

package org.acme.elytron.security.ldap;

import jakarta.annotation.security.PermitAll;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/api/public")
public class PublicResource {

    @GET
    @PermitAll
    @Produces(MediaType.TEXT_PLAIN)
    public String publicResource() {
        return "public";
   }
}

/api/admin エンドポイントのソースコードも非常にシンプルです。ここでの主な違いは、adminRole ロールを付与されたユーザーのみがエンドポイントにアクセスできるように、@RolesAllowed アノテーションを使用していることです:

package org.acme.elytron.security.ldap;

import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/api/admin")
public class AdminResource {

    @GET
    @RolesAllowed("adminRole")
    @Produces(MediaType.TEXT_PLAIN)
    public String adminResource() {
         return "admin";
    }
}

最後に、/api/users/me エンドポイントについて考えてみましょう。以下のソースコードからわかるように、standardRole ロールを持つユーザーのみを信頼しています。SecurityContext を使用して、現在認証されているPrincipalへのアクセスを取得し、ユーザー名を返します。この情報はLDAPサーバーから読み込まれます。

package org.acme.elytron.security.ldap;

import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;

@Path("/api/users")
public class UserResource {

    @GET
    @RolesAllowed("standardRole")
    @Path("/me")
    public String me(@Context SecurityContext securityContext) {
        return securityContext.getUserPrincipal().getName();
    }
}

アプリケーションの設定

quarkus.security.ldap.enabled=true

quarkus.security.ldap.dir-context.principal=uid=admin,ou=system
quarkus.security.ldap.dir-context.url=ldaps://ldap.server.local (1)
%test.quarkus.security.ldap.dir-context.url=ldap://127.0.0.1:10389 (2)
quarkus.security.ldap.dir-context.password=secret

quarkus.security.ldap.identity-mapping.rdn-identifier=uid
quarkus.security.ldap.identity-mapping.search-base-dn=ou=Users,dc=quarkus,dc=io

quarkus.security.ldap.identity-mapping.attribute-mappings."cn".from=cn
quarkus.security.ldap.identity-mapping.attribute-mappings."cn".filter=(member=uid={0},ou=Users,dc=quarkus,dc=io) (3)
quarkus.security.ldap.identity-mapping.attribute-mappings."cn".filter-base-dn=ou=Roles,dc=quarkus,dc=io
1 LDAPサーバーのURLを指定する必要があります。この例では、LDAPサーバーが このLDIFファイル をインポートしている必要があります。
2 テストリソースで使用されるURL。テストでは、Quarkusによって提供される LdapServerTestResource を、このサンプルアプリケーションのテストカバレッジで 我々が行うように 活用できます。
3 {0}uid で置換されます。

quarkus-elytron-security-ldap エクステンションは、ユーザーとそのアイデンティティを認証するために、dir-contextと、少なくとも1つの属性マッピングを持つアイデンティティマッピングを必要とします。

デフォルトでは、QuarkusはLDAPディレクトリから取得したクレデンシャルをキャッシュしません。サービスへのリクエストごとに、LDAPサーバーへの追加のラウンドトリップが発生します。

パフォーマンスを向上させるためにこれらの結果をキャッシュするのは一般的な方法ですが、その代償として、LDAPでの変更がサービスに反映されるまでに遅延が生じます。

キャッシュを有効にするには、設定ファイルで quarkus.security.ldap.cache.enabled=true を設定してください。

デフォルトのキャッシュの最大有効期間は 60s です。これは quarkus.security.ldap.cache.max-age を設定することで構成できます。

キャッシュエントリの数は quarkus.security.ldap.cache.size によって制限され、デフォルトは 100 です。

LDAPグループを SecurityIdentity ロールにマップ

以前に説明したアプリケーション設定では、LDAP識別名グループの CN 属性をQuarkusの SecurityIdentity ロールにマッピングする方法を示しました。より具体的には、standardRole CNは SecurityIdentity ロールにマッピングされ、UserResource#me エンドポイントへのアクセスが許可されました。ただし、必要な SecurityIdentity ロールはアプリケーションによって異なる場合があり、以下の例のようにLDAPグループをローカルの SecurityIdentity ロールにマッピングする必要がある場合があります:

quarkus.http.auth.roles-mapping."standardRole"=user (1)
1 standardRole ロールをアプリケーション固有の SecurityIdentity ロール user にマッピングします。

アプリケーションのテスト

アプリケーションは保護され、アイデンティティはLDAPサーバーによって提供されるようになりました。アプリケーションを開発モードで起動しましょう:

コマンドラインインタフェース
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

まず最初に確認すべきは、匿名でのアクセスが可能かどうかということです。

$ curl -i -X GET http://localhost:8080/api/public
HTTP/1.1 200 OK
Content-Length: 6
Content-Type: text/plain;charset=UTF-8

public%

次に、保護されたリソースに匿名でアクセスしてみましょう。

$ curl -i -X GET http://localhost:8080/api/admin
HTTP/1.1 401 Unauthorized
Content-Length: 14
Content-Type: text/html;charset=UTF-8

Not authorized%

ここまでは順調です。次に、許可されたユーザーで試してみましょう。

$ curl -i -X GET -u adminUser:adminUserPassword http://localhost:8080/api/admin
HTTP/1.1 200 OK
Content-Length: 5
Content-Type: text/plain;charset=UTF-8

admin%

adminUser:adminUserPassword クレデンシャルを提供することで、エクステンションはユーザーを認証し、そのロールをロードしました。adminUser ユーザーは、保護されたリソースへのアクセスを認可されています。

ユーザー adminUser は、この役割を持っていないので、 @RolesAllowed("standardRole") で保護されたリソースへのアクセスを禁止する必要があります。

$ curl -i -X GET -u adminUser:adminUserPassword http://localhost:8080/api/users/me
HTTP/1.1 403 Forbidden
Content-Length: 34
Content-Type: text/html;charset=UTF-8

Forbidden%

最後に、ユーザー standardUser を使用すると動作し、セキュリティーコンテキストにはプリンシパルの詳細 (例えばユーザー名) が含まれます。

$ curl -i -X GET -u standardUser:standardUserPassword http://localhost:8080/api/users/me
HTTP/1.1 200 OK
Content-Length: 4
Content-Type: text/plain;charset=UTF-8

user%

設定リファレンス

ビルド時に固定される設定プロパティー - 他のすべての設定プロパティーは実行時にオーバーライド可能

Configuration property

デフォルト

The option to enable the ldap elytron module

Environment variable: QUARKUS_SECURITY_LDAP_ENABLED

Show more

boolean

false

The elytron realm name

Environment variable: QUARKUS_SECURITY_LDAP_REALM_NAME

Show more

string

Quarkus

Provided credentials are verified against ldap?

Environment variable: QUARKUS_SECURITY_LDAP_DIRECT_VERIFICATION

Show more

boolean

true

The url of the ldap server

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_URL

Show more

string

required

The principal: user which is used to connect to ldap server (also named "bindDn")

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_PRINCIPAL

Show more

string

The password which belongs to the principal (also named "bindCredential")

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_PASSWORD

Show more

string

how ldap redirects are handled

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_REFERRAL_MODE

Show more

ignore, follow, throw

ignore

The connect timeout

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_CONNECT_TIMEOUT

Show more

Duration 

5S

The read timeout

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_READ_TIMEOUT

Show more

Duration 

60S

If set to true, request to the LDAP server are cached

Environment variable: QUARKUS_SECURITY_LDAP_CACHE_ENABLED

Show more

boolean

false

The duration that an entry can stay in the cache

Environment variable: QUARKUS_SECURITY_LDAP_CACHE_MAX_AGE

Show more

Duration 

60S

The maximum number of entries to keep in the cache

Environment variable: QUARKUS_SECURITY_LDAP_CACHE_SIZE

Show more

int

100

The identifier which correlates to the provided user (also named "baseFilter")

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_RDN_IDENTIFIER

Show more

string

uid

The dn where we look for users

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_SEARCH_BASE_DN

Show more

string

required

If the child nodes are also searched for identities

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_SEARCH_RECURSIVE

Show more

boolean

false

The roleAttributeId from which is mapped (e.g. "cn")

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__FROM

Show more

string

required

The identifier whom the attribute is mapped to (in Quarkus: "groups", in WildFly this is "Roles")

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__TO

Show more

string

groups

The filter (also named "roleFilter")

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__FILTER

Show more

string

required

The filter base dn (also named "rolesContextDn")

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__FILTER_BASE_DN

Show more

string

required

期間フォーマットについて

期間の値を書くには、標準の java.time.Duration フォーマットを使います。 詳細は Duration#parse() Java API documentation を参照してください。

数字で始まる簡略化した書式を使うこともできます:

  • 数値のみの場合は、秒単位の時間を表します。

  • 数値の後に ms が続く場合は、ミリ秒単位の時間を表します。

その他の場合は、簡略化されたフォーマットが解析のために java.time.Duration フォーマットに変換されます:

  • 数値の後に hms が続く場合は、その前に PT が付けられます。

  • 数値の後に d が続く場合は、その前に P が付けられます。

関連コンテンツ