第6章 Quarkus CXF セキュリティーガイド
この章では、Quarkus CXF エクステンションを使用する際のセキュリティーに関する情報を提供します。
6.1. セキュリティーガイド
セキュリティーガイドには、Quarkus CXF のセキュリティーに関連するさまざまな側面が記載されています。
6.1.1. SSL、TLS、および HTTPS
このセクションでは、SSL、TLS、HTTPS に関連するさまざまなユースケースを説明します。
このセクションで使用されているサンプルコードスニペットは、Quarkus CXF のソースツリーにある WS-SecurityPolicy インテグレーションテスト からのものです。
6.1.1.1. クライアント SSL 設定
クライアントが、クライアントのオペレーティングシステムによって SSL 証明書が信頼されていないサーバーと通信する場合は、クライアント用にカスタムトラストストアを設定する必要があります。
トラストストアの作成と維持には、openssl
や Java keytool
などのツールがよく使用されます。
Quarkus CXF ソースツリーには、両方のツールの例があります。
トラストストアを準備したら、それを使用するようにクライアントを設定する必要があります。
6.1.1.1.1. application.properties
にクライアントトラストストアを設定する
これは、最も簡単にクライアントトラストストアを設定できる方法です。次のプロパティーには、重要な役割があります。
以下に例を示します。
application.properties
# Client side SSL quarkus.cxf.client.hello.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/hello quarkus.cxf.client.hello.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService 1 quarkus.cxf.client.hello.trust-store-type = pkcs12 2 quarkus.cxf.client.hello.trust-store = client-truststore.pkcs12 quarkus.cxf.client.hello.trust-store-password = client-truststore-password
- 1
pkcs12
とjks
は、よく使用される 2 つのキーストア形式です。PKCS12 は、Java 9 以降の デフォルトの Java キーストア形式 です。PKCS12 はより強力な暗号化アルゴリズムを提供し、拡張可能で、標準化されており、言語に中立で、広くサポートされているため、JKS ではなく PKCS12 を使用することを推奨します。- 2
- 参照される
client-truststore.pkcs12
ファイルは、クラスパスまたはファイルシステムのいずれかで使用可能である必要があります。
6.1.1.2. サーバー SSL 設定
HTTPS プロトコル経由でサービスを利用できるようにするには、まずサーバーキーストアを設定する必要があります。サーバーの SSL 設定は、Quarkus の HTTP レイヤーである Vert.x によって実行されます。Quarkus HTTP ガイド では、設定オプションに関する情報が提供されています。
以下に基本的な例を示します。
application.properties
# Server side SSL quarkus.tls.key-store.p12.path = localhost-keystore.pkcs12 quarkus.tls.key-store.p12.password = localhost-keystore-password quarkus.tls.key-store.p12.alias = localhost quarkus.tls.key-store.p12.alias-password = localhost-keystore-password
6.1.1.3. 相互 TLS (mTLS) 認証
これまでは、サーバーのみが SSL 証明書を通じてアイデンティティーを証明し、クライアントがその証明書を信頼するように設定する必要がある単純なケース、つまり片側だけのケースを説明しました。相互 TLS 認証では、クライアントにも同じ公開鍵暗号化手段を使用してアイデンティティーを証明させます。
したがって、相互 TLS (mTLS) 認証の場合、上記のようにサーバーキーストアとクライアントトラストストアをセットアップすることに加えて、クライアント側のキーストアとサーバー側のトラストストアをセットアップする必要があります。
ストアを作成および維持するためのツールは同じであり、使用する設定プロパティーは Simple TLS の場合に使用されるものとほぼ類似しています。
Quarkus CXF ソースツリーの mTLS インテグレーションテスト は、適切なスタートポイントになります。
キーストアとトラストストアは、openssl
(または Java Java keytool
) を使用して作成されます。
application.properties
ファイルは次のとおりです。
application.properties
# Server keystore for Simple TLS quarkus.tls.localhost-pkcs12.key-store.p12.path = localhost-keystore.pkcs12 quarkus.tls.localhost-pkcs12.key-store.p12.password = localhost-keystore-password quarkus.tls.localhost-pkcs12.key-store.p12.alias = localhost quarkus.tls.localhost-pkcs12.key-store.p12.alias-password = localhost-keystore-password # Server truststore for Mutual TLS quarkus.tls.localhost-pkcs12.trust-store.p12.path = localhost-truststore.pkcs12 quarkus.tls.localhost-pkcs12.trust-store.p12.password = localhost-truststore-password # Select localhost-pkcs12 as the TLS configuration for the HTTP server quarkus.http.tls-configuration-name = localhost-pkcs12 # Do not allow any clients which do not prove their indentity through an SSL certificate quarkus.http.ssl.client-auth = required # CXF service quarkus.cxf.endpoint."/mTls".implementor = io.quarkiverse.cxf.it.auth.mtls.MTlsHelloServiceImpl # CXF client with a properly set certificate for mTLS quarkus.cxf.client.mTls.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/mTls quarkus.cxf.client.mTls.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService quarkus.cxf.client.mTls.key-store = target/classes/client-keystore.pkcs12 quarkus.cxf.client.mTls.key-store-type = pkcs12 quarkus.cxf.client.mTls.key-store-password = client-keystore-password quarkus.cxf.client.mTls.key-password = client-keystore-password quarkus.cxf.client.mTls.trust-store = target/classes/client-truststore.pkcs12 quarkus.cxf.client.mTls.trust-store-type = pkcs12 quarkus.cxf.client.mTls.trust-store-password = client-truststore-password # Include the keystores in the native executable quarkus.native.resources.includes = *.pkcs12,*.jks
6.1.1.4. WS-SecurityPolicy を通じて SSL を強制する
クライアントが HTTPS 経由で接続するための要件は、ポリシーで定義できます。
この機能は、quarkus-cxf-rt-ws-security
エクステンションにより提供されます。
以下は、ポリシーファイルの例です。
https-policy.xml
<?xml version="1.0" encoding="UTF-8"?> <wsp:Policy wsp:Id="HttpsSecurityServicePolicy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <wsp:ExactlyOne> <wsp:All> <sp:TransportBinding> <wsp:Policy> <sp:TransportToken> <wsp:Policy> <sp:HttpsToken RequireClientCertificate="false" /> </wsp:Policy> </sp:TransportToken> <sp:IncludeTimestamp /> <sp:AlgorithmSuite> <wsp:Policy> <sp:Basic128 /> </wsp:Policy> </sp:AlgorithmSuite> </wsp:Policy> </sp:TransportBinding> </wsp:All> </wsp:ExactlyOne> </wsp:Policy>
ポリシーは、サービスエンドポイントインターフェイス (SEI) から参照される必要があります。
HttpsPolicyHelloService.java
package io.quarkiverse.cxf.it.security.policy; import jakarta.jws.WebMethod; import jakarta.jws.WebService; import org.apache.cxf.annotations.Policy; /** * A service implementation with a transport policy set */ @WebService(serviceName = "HttpsPolicyHelloService") @Policy(placement = Policy.Placement.BINDING, uri = "https-policy.xml") public interface HttpsPolicyHelloService extends AbstractHelloService { @WebMethod @Override public String hello(String text); }
このセットアップを行うと、HTTP 経由で配信されるすべてのリクエストは PolicyVerificationInInterceptor
によって拒否されます。
ERROR [org.apa.cxf.ws.pol.PolicyVerificationInInterceptor] Inbound policy verification failed: These policy alternatives can not be satisfied: {http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}TransportBinding: TLS is not enabled ...
6.1.2. 認証および認可
このセクションに示されているサンプルコードは、Quarkus CXF のソースツリーにある Client and server integration test からの抜粋です。これは、実行可能な例として使用できます。
6.1.2.1. クライアント HTTP Basic 認証
quarkus-cxf
エクステンションによって提供される次のクライアント設定オプションを使用して、HTTP Basic 認証のユーザー名とパスワードを渡します。
以下に例を示します。
application.properties
quarkus.cxf.client.basicAuth.wsdl = http://localhost:${quarkus.http.test-port}/soap/basicAuth?wsdl quarkus.cxf.client.basicAuth.client-endpoint-url = http://localhost:${quarkus.http.test-port}/soap/basicAuth quarkus.cxf.client.basicAuth.username = bob quarkus.cxf.client.basicAuth.password = bob234
6.1.2.1.1. Basic 認証で保護された WSDL へのアクセス
デフォルトでは、quarkus.cxf.client."client-name".secure-wsdl-access
を true
に設定しない限り、Quarkus CXF によって作成されたクライアントは Authorization
ヘッダーを送信しません。
application.properties
quarkus.cxf.client.basicAuthSecureWsdl.wsdl = http://localhost:${quarkus.http.test-port}/soap/basicAuth?wsdl quarkus.cxf.client.basicAuthSecureWsdl.client-endpoint-url = http://localhost:${quarkus.http.test-port}/soap/basicAuthSecureWsdl quarkus.cxf.client.basicAuthSecureWsdl.username = bob quarkus.cxf.client.basicAuthSecureWsdl.password = ${client-server.bob.password} quarkus.cxf.client.basicAuthSecureWsdl.secure-wsdl-access = true
6.1.2.2. 相互 TLS (mTLS) 認証
SSL、TLS、HTTPS ガイドの 相互 TLS (mTLS) 認証 セクションを参照してください。
6.1.2.3. サービスエンドポイントの保護
特に以下の点において、サーバー側の認証と認可は Quarkus Security によって実行されます。
具体的な例は、Client and server integration test を参照してください。主に以下が含まれます。
-
アイデンティティープロバイダーとしての
io.quarkus:quarkus-elytron-security-properties-file
依存関係 Basic 認証の有効化と、
application.properties
でロールが設定されているユーザー。application.properties
quarkus.http.auth.basic = true quarkus.security.users.embedded.enabled = true quarkus.security.users.embedded.plain-text = true quarkus.security.users.embedded.users.alice = alice123 quarkus.security.users.embedded.roles.alice = admin quarkus.security.users.embedded.users.bob = bob234 quarkus.security.users.embedded.roles.bob = app-user
-
@RolesAllowed
アノテーションによって強制されるロールベースのアクセス制御。
BasicAuthHelloServiceImpl.java
package io.quarkiverse.cxf.it.auth.basic; import jakarta.annotation.security.RolesAllowed; import jakarta.jws.WebService; import io.quarkiverse.cxf.it.HelloService; @WebService(serviceName = "HelloService", targetNamespace = HelloService.NS) @RolesAllowed("app-user") public class BasicAuthHelloServiceImpl implements HelloService { @Override public String hello(String person) { return "Hello " + person + "!"; } }
6.1.3. WS-SecurityPolicy によって強制される認証
クライアント と サービス に対して、相互 TLS と Basic HTTP 認証の代わりに、WS-SecurityPolicy を通じて認証を強制できます。
WS-SecurityPolicy を通じて認証を強制するには、次の手順に従います。
- WSDL コントラクトのエンドポイントにサポートトークンポリシーを追加します。
-
サーバー側では、認証コールバックハンドラーを実装し、
application.properties
または環境変数を介してエンドポイントに関連付けます。クライアントから受信した認証情報は、コールバックハンドラーによって認証されます。 -
クライアント側では、
application.properties
内の設定または環境変数を通じて認証情報を提供します。または、認証コールバックハンドラーを実装して認証情報を渡すこともできます。
6.1.3.1. 認証ポリシーの指定
サービスエンドポイントで認証を強制する場合は、サポートトークン ポリシーアサーションを関連するエンドポイントバインディングに関連付け、その下に 1 つ以上の トークンアサーション を指定します。
サポートトークンポリシーアサーションにはいくつかの種類があり、その XML 要素名はすべて SupportingTokens
で終わります (たとえば、SupportingTokens
、SignedSupportingTokens
など)。完全なリストは、WS-SecurityPolicy 仕様の Supporting Tokens のセクションを参照してください。
6.1.3.2. UsernameToken
ポリシーアサーションの例
このセクションで使用されるサンプルコードスニペットは、Quarkus CXF のソースツリーにある WS-SecurityPolicy インテグレーションテスト からのものです。これは、実行可能な例として使用できます。
次のリストは、WS-Security UsernameToken
(ユーザー名/パスワードの認証情報を含む) をセキュリティーヘッダーに含めることを要求するポリシーの例を示しています。
username-token-policy.xml
<?xml version="1.0" encoding="UTF-8"?> <wsp:Policy wsp:Id="UsernameTokenSecurityServicePolicy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:sp13="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200802" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <wsp:ExactlyOne> <wsp:All> <sp:SupportingTokens> <wsp:Policy> <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> <wsp:Policy> <sp:WssUsernameToken11 /> <sp13:Created /> <sp13:Nonce /> </wsp:Policy> </sp:UsernameToken> </wsp:Policy> </sp:SupportingTokens> </wsp:All> </wsp:ExactlyOne> </wsp:Policy>
このポリシーファイルをサービスエンドポイントに関連付けるには、次の 2 つの方法があります。
次のように、サービスエンドポイントインターフェイス (SEI) のポリシーを参照します。
UsernameTokenPolicyHelloService.java
@WebService(serviceName = "UsernameTokenPolicyHelloService") @Policy(placement = Policy.Placement.BINDING, uri = "username-token-policy.xml") public interface UsernameTokenPolicyHelloService extends AbstractHelloService { ... }
-
WSDL 契約に ポリシーを含め、
PolicyReference
要素 を介して参照します。
ポリシーを設定したら、サービスエンドポイントとクライアントで認証情報を設定します。
application.properties
# A service with a UsernameToken policy assertion quarkus.cxf.endpoint."/helloUsernameToken".implementor = io.quarkiverse.cxf.it.security.policy.UsernameTokenPolicyHelloServiceImpl quarkus.cxf.endpoint."/helloUsernameToken".security.callback-handler = #usernameTokenPasswordCallback # These properties are used in UsernameTokenPasswordCallback # and in the configuration of the helloUsernameToken below wss.user = cxf-user wss.password = secret # A client with a UsernameToken policy assertion quarkus.cxf.client.helloUsernameToken.client-endpoint-url = https://localhost:${quarkus.http.test-ssl-port}/services/helloUsernameToken quarkus.cxf.client.helloUsernameToken.service-interface = io.quarkiverse.cxf.it.security.policy.UsernameTokenPolicyHelloService quarkus.cxf.client.helloUsernameToken.security.username = ${wss.user} quarkus.cxf.client.helloUsernameToken.security.password = ${wss.password}
上記のリストでは、usernameTokenPasswordCallback
は、javax.security.auth.callback.CallbackHandler
を実装する @jakarta.inject.Named
Bean の名前です。Quarkus CXF は、CDI コンテナー内でこの 名前 の Bean を検索します。
以下は Bean の実装例です。
UsernameTokenPasswordCallback.java
package io.quarkiverse.cxf.it.security.policy; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Named; import org.apache.wss4j.common.ext.WSPasswordCallback; import org.eclipse.microprofile.config.inject.ConfigProperty; @ApplicationScoped @Named("usernameTokenPasswordCallback") /* We refer to this bean by this name from application.properties */ public class UsernameTokenPasswordCallback implements CallbackHandler { /* These two configuration properties are set in application.properties */ @ConfigProperty(name = "wss.password") String password; @ConfigProperty(name = "wss.user") String user; @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { if (callbacks.length < 1) { throw new IllegalStateException("Expected a " + WSPasswordCallback.class.getName() + " at possition 0 of callbacks. Got array of length " + callbacks.length); } if (!(callbacks[0] instanceof WSPasswordCallback)) { throw new IllegalStateException( "Expected a " + WSPasswordCallback.class.getName() + " at possition 0 of callbacks. Got an instance of " + callbacks[0].getClass().getName() + " at possition 0"); } final WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; if (user.equals(pc.getIdentifier())) { pc.setPassword(password); } else { throw new IllegalStateException("Unexpected user " + user); } } }
セットアップ全体をテストするには、単純な @QuarkusTest
を作成します。
UsernameTokenTest.java
package io.quarkiverse.cxf.it.security.policy; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import io.quarkiverse.cxf.annotation.CXFClient; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest public class UsernameTokenTest { @CXFClient("helloUsernameToken") UsernameTokenPolicyHelloService helloUsernameToken; @Test void helloUsernameToken() { Assertions.assertThat(helloUsernameToken.hello("CXF")).isEqualTo("Hello CXF from UsernameToken!"); } }
mvn test -Dtest=UsernameTokenTest
でテストを実行すると、Username
と Password
を含む Security
ヘッダーを含む SOAP メッセージがログに記録されます。
UsernameTokenTest のログ出力
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soap:mustUnderstand="1"> <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-bac4f255-147e-42a4-aeec-e0a3f5cd3587"> <wsse:Username>cxf-user</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">secret</wsse:Password> <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">3uX15dZT08jRWFWxyWmfhg==</wsse:Nonce> <wsu:Created>2024-10-02T17:32:10.497Z</wsu:Created> </wsse:UsernameToken> </wsse:Security> </soap:Header> <soap:Body> <ns2:hello xmlns:ns2="http://policy.security.it.cxf.quarkiverse.io/"> <arg0>CXF</arg0> </ns2:hello> </soap:Body> </soap:Envelope>
6.1.3.3. SAML v1 および v2 ポリシーアサーションの例
WS-SecurityPolicy インテグレーションテスト には、SAML v1 および SAML v2 アサーションを使用した類似の例も含まれています。