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 11+ がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.8.1+

  • 使用したい場合、 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ドキュメントを返します。

By default, Quarkus will restrict the use of JNDI within an application, as a precaution to try and mitigate any future vulnerabilities similar to Log4Shell. Because LDAP based auth requires JNDI this protection will be automatically disabled.

ソリューション

次のセクションで紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.gitアーカイブ をダウンロードします。

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

Mavenプロジェクトの作成

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

CLI
quarkus create app org.acme:security-ldap-quickstart \
    --extension=elytron-security-ldap,resteasy-reactive \
    --no-code
cd security-ldap-quickstart

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

Quarkus CLIのインストール方法については、Quarkus CLIガイドをご参照ください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:2.11.1.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=security-ldap-quickstart \
    -Dextensions="elytron-security-ldap,resteasy-reactive" \
    -DnoCode
cd security-ldap-quickstart

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

This command generates a project, importing the elytron-security-ldap extension which is a wildfly-elytron-realm-ldap adapter for Quarkus applications.

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

CLI
quarkus extension add 'elytron-security-ldap'
Maven
./mvnw quarkus:add-extension -Dextensions="elytron-security-ldap"
Gradle
./gradlew addExtension --extensions="elytron-security-ldap"

This will add the following to your build file:

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 エンドポイントの実装から始めましょう。以下のソースコードから分かるように、通常のJAX-RSリソースです:

package org.acme.elytron.security.ldap;

import javax.annotation.security.PermitAll;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.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 javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

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

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

Finally, let’s consider the /api/users/me endpoint. As you can see from the source code below, we are trusting only users with the standardRole role. We are using SecurityContext to get access to the current authenticated Principal, and we return the user’s name. This information is loaded from the LDAP server.

package org.acme.elytron.security.ldap;

import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.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=tool,ou=accounts,o=YourCompany,c=DE
quarkus.security.ldap.dir-context.url=ldaps://ldap.server.local
quarkus.security.ldap.dir-context.password=PASSWORD

quarkus.security.ldap.identity-mapping.rdn-identifier=uid
quarkus.security.ldap.identity-mapping.search-base-dn=ou=users,ou=tool,o=YourCompany,c=DE

quarkus.security.ldap.identity-mapping.attribute-mappings."0".from=cn
quarkus.security.ldap.identity-mapping.attribute-mappings."0".to=groups
quarkus.security.ldap.identity-mapping.attribute-mappings."0".filter=(member=uid={0})
quarkus.security.ldap.identity-mapping.attribute-mappings."0".filter-base-dn=ou=roles,ou=tool,o=YourCompany,c=DE

{0}uid で置換され、 {1} はユーザーエントリーの dn で置換されます。

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

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

The application is now protected and the identities are provided by our LDAP server. Let’s start the application in dev mode:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

The very first thing to check is to ensure the anonymous access works.

$ 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%

Now, let’s try to hit a protected resource anonymously.

$ 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

boolean

false

The elytron realm name

Environment variable: QUARKUS_SECURITY_LDAP_REALM_NAME

string

Quarkus

Provided credentials are verified against ldap?

Environment variable: QUARKUS_SECURITY_LDAP_DIRECT_VERIFICATION

boolean

true

The url of the ldap server

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_URL

string

required

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

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_PRINCIPAL

string

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

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_PASSWORD

string

how ldap redirects are handled

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_REFERRAL_MODE

ignore, follow, throw

ignore

The connect timeout

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_CONNECT_TIMEOUT

Duration

5S

The read timeout

Environment variable: QUARKUS_SECURITY_LDAP_DIR_CONTEXT_READ_TIMEOUT

Duration

60S

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

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_RDN_IDENTIFIER

string

uid

The dn where we look for users

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_SEARCH_BASE_DN

string

required

If the child nodes are also searched for identities

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_SEARCH_RECURSIVE

boolean

false

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

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__FROM

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

string

groups

The filter (also named "roleFilter")

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__FILTER

string

required

The filter base dn (also named "rolesContextDn")

Environment variable: QUARKUS_SECURITY_LDAP_IDENTITY_MAPPING_ATTRIBUTE_MAPPINGS__ATTRIBUTE_MAPPINGS__FILTER_BASE_DN

string

required

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

期間のフォーマットは標準の java.time.Duration フォーマットを使用します。詳細は Duration#parse() javadoc を参照してください。

数値で始まる期間の値を指定することもできます。この場合、値が数値のみで構成されている場合、コンバーターは値を秒として扱います。そうでない場合は、 PT が暗黙的に値の前に付加され、標準の java.time.Duration 形式が得られます。

リファレンス