第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
quarkus.cxf.client.hello.trust-store-type = pkcs12
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 アサーションを使用した類似の例も含まれています。