Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify the TLS handling in the Vert.x client, make Vert.x client default #1546

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/actions/build-and-run-jvm-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ runs:
run: ./mvnw -B formatter:validate install -fae -ntp

# Same as the previous but only JVM tests and different default ConduitFactory
- name: QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=VertxHttpClientHTTPConduitFactory mvn -B test
- name: QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=URLConnectionHTTPConduitFactory mvn -B test
shell: bash
env:
QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: VertxHttpClientHTTPConduitFactory
QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: URLConnectionHTTPConduitFactory
run: ./mvnw -B clean install -fae -ntp

- name: 'Upload generated Antora docs site'
Expand Down
4 changes: 2 additions & 2 deletions .github/actions/run-native-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ runs:
fi

# Same as the previous but different default ConduitFactory
- name: Run integration test ${{ inputs.test-module-spec }} with QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=VertxHttpClientHTTPConduitFactory
- name: Run integration test ${{ inputs.test-module-spec }} with QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=URLConnectionHTTPConduitFactory
shell: bash
env:
QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: VertxHttpClientHTTPConduitFactory
QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: URLConnectionHTTPConduitFactory
# Skip native tests for CodeQL Security Scans
if: "${{ env.SKIP_NATIVE_TESTS != 'true' }}"
run: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ jobs:
-fae

# Same as the previous but different default ConduitFactory
- name: mvn test
- name: QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY=URLConnectionHTTPConduitFactory mvn test
env:
QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: VertxHttpClientHTTPConduitFactory
QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY: URLConnectionHTTPConduitFactory
shell: bash
run: |
./mvnw -B clean test \
Expand Down
5 changes: 0 additions & 5 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,6 @@
<artifactId>quarkus-cxf-services-sts-deployment</artifactId>
<version>${quarkus-cxf.version}</version>
</dependency>
<dependency>
<groupId>io.quarkiverse.cxf</groupId>
<artifactId>quarkus-cxf-vertx-client</artifactId>
<version>${quarkus-cxf.version}</version>
</dependency>
<dependency>
<groupId>io.quarkiverse.cxf</groupId>
<artifactId>quarkus-cxf-woodstox</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ quarkus.cxf.client.helloAllowAll.trust-store = client-truststore.pkcs12
quarkus.cxf.client.helloAllowAll.trust-store-password = client-truststore-password
quarkus.cxf.client.helloAllowAll.hostname-verifier = AllowAllHostnameVerifier


quarkus.tls.helloAllowAll.trust-store.p12.path = client-truststore.pkcs12
quarkus.tls.helloAllowAll.trust-store.p12.password = client-truststore-password
quarkus.tls.helloAllowAll.hostname-verification-algorithm = NONE
quarkus.cxf.client.helloAllowAllTlsConfig.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello
quarkus.cxf.client.helloAllowAllTlsConfig.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService
quarkus.cxf.client.helloAllowAllTlsConfig.tls-configuration-name = helloAllowAll

quarkus.cxf.client.helloCustomHostnameVerifier.client-endpoint-url = https://127.0.0.1:${quarkus.http.test-ssl-port}/services/hello
quarkus.cxf.client.helloCustomHostnameVerifier.service-interface = io.quarkiverse.cxf.it.security.policy.HelloService
quarkus.cxf.client.helloCustomHostnameVerifier.trust-store = client-truststore.pkcs12
Expand Down
17 changes: 15 additions & 2 deletions docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2002,12 +2002,25 @@ instead.

- One of the well known values: `AllowAllHostnameVerifier`, `HttpsURLConnectionDefaultHostnameVerifier`
- A fully qualified class name implementing `javax.net.ssl.HostnameVerifier` to look up in the CDI container.
- A bean name prefixed with `++#++` that will be looked up in the CDI container; example: `++#++myHostnameVerifier` If
not specified, then the creation of the `HostnameVerifier` is delegated to CXF, which boils down to
- A bean name prefixed with `++#++` that will be looked up in the CDI container; example: `++#++myHostnameVerifier`

If not specified, then the creation of the `HostnameVerifier` is delegated to CXF, which boils down to
`org.apache.cxf.transport.https.httpclient.DefaultHostnameVerifier` with the default
`org.apache.cxf.transport.https.httpclient.PublicSuffixMatcherLoader` as returned from
`PublicSuffixMatcherLoader.getDefault()`.

[IMPORTANT]
====
Setting this option when the conduit factory of this client is set to `VertxHttpClientHTTPConduitFactory`
(default since {quarkus-cxf-project-name} 3.16.0) leads to an exception at runtime.
The `AllowAllHostnameVerifier` value of this option can be replaced by using a
xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-tls-configuration-name[named TLS
configuration]
with
`{link-quarkus-docs-base}/tls-registry-reference#trusting-all-certificates-and-hostname-verification[hostname-verification-algorithm]`
set to `NONE`. Otherwise, there is no way to implement custom hostname verification for Vert.x HTTP client.
====

*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENT_NAME__HOSTNAME_VERIFIER+++` +
*Since Quarkus CXF*: 2.5.0

Expand Down
44 changes: 44 additions & 0 deletions docs/modules/ROOT/pages/release-notes/3.16.0.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
= {quarkus-cxf-project-name} 3.16.0 LTS release notes

This is the first release in the new 3.16 https://quarkus.io/blog/lts-releases/[LTS stream].
As usual, where we plan to produce patch releases with bug and security fixes for 12 months.

== Important dependency upgrades

* Quarkus 3.15.x -> 3.16.0 - https://quarkus.io/blog/quarkus-3-16-0-released/[release notes]

== New and noteworthy in {quarkus-cxf-project-name}

=== Vert.x HttpClient based HTTP Conduit is the new default

The `VertxHttpClientHTTPConduitFactory` was introduced in {quarkus-cxf-project-name} 3.13.0.
Since then, it went through some improvements and testing
so that we are confident enough to make it the default for the xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-http-conduit-factory[quarkus.cxf.client."client-name".http-conduit-factory] and
xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-http-conduit-factory[quarkus.cxf.http-conduit-factory] options.

==== SSL/TLS configuration

Prefer the new named TLS configurations for CXF clients.

==== Hostname verifiers not supported in combination with `VertxHttpClientHTTPConduitFactory`

Setting `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-hostname-verifier[quarkus.cxf.client."client-name".hostname-verifier]` together with `VertxHttpClientHTTPConduitFactory` leads to an exception at runtime.

The `AllowAllHostnameVerifier` value of that option can be replaced by using a
xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-tls-configuration-name[named TLS configuration]
with `{link-quarkus-docs-base}/tls-registry-reference#trusting-all-certificates-and-hostname-verification[hostname-verification-algorithm]`
set to `NONE`.

Otherwise, there is no way to implement custom hostname verification for Vert.x HTTP client.

==== Force the old default

There are three options how you can get back to the old default:

* Set the `QUARKUS_CXF_DEFAULT_HTTP_CONDUIT_FACTORY` environment variable to `URLConnectionHTTPConduitFactory`
* Set the global xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-http-conduit-factory[quarkus.cxf.http-conduit-factory]
* Set the per client xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-http-conduit-factory[quarkus.cxf.client."client-name".http-conduit-factory] option to `URLConnectionHTTPConduitFactory`

== Full changelog

https:/quarkiverse/quarkus-cxf/compare/3.15.0+++...+++3.16.0
12 changes: 11 additions & 1 deletion docs/src/test/java/io/quarkiverse/cxf/doc/it/AntoraTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package io.quarkiverse.cxf.doc.it;

import java.io.IOException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.TimeoutException;

Expand Down Expand Up @@ -29,12 +32,19 @@ public void antoraSite() throws TimeoutException, IOException, InterruptedExcept
@Test
public void externalLinks() {

Set<String> ignorables = Set.of(
Set<String> ignorables1 = Set.of(
/* known issue https:/quarkiverse/antora-ui-quarkiverse/issues/86 */
"http://quarkus.io/training",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role",
"http://schemas.xmlsoap.org/ws/2005/02/sc/dk/p_sha1",
"http://www.w3.org/2009/xmlenc11#aes256-gcm");
final Set<String> ignorables = new LinkedHashSet<>(ignorables1);

final ZonedDateTime deadline_2_16_0 = ZonedDateTime.parse("2024-10-31T23:59:59+01:00[Europe/Paris]");
if (ZonedDateTime.now(ZoneId.of("Europe/Paris")).isBefore(deadline_2_16_0)) {
ignorables.add("https://quarkus.io/blog/quarkus-3-16-0-released/");
ignorables.add("https:/quarkiverse/quarkus-cxf/compare/3.15.0...3.16.0");
}

AntoraTestUtils.assertExternalLinksValid(err -> ignorables.contains(err.uri()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
import io.quarkiverse.cxf.CXFClientInfo;
import io.quarkiverse.cxf.CXFRecorder;
import io.quarkiverse.cxf.ClientInjectionPoint;
import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl;
import io.quarkiverse.cxf.CxfClientProducer;
import io.quarkiverse.cxf.CxfFixedConfig;
import io.quarkiverse.cxf.CxfFixedConfig.ClientFixedConfig;
import io.quarkiverse.cxf.HTTPConduitImpl;
import io.quarkiverse.cxf.HttpClientHTTPConduitFactory;
import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkiverse.cxf.graal.QuarkusCxfFeature;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package io.quarkiverse.cxf.deployment.test;

import jakarta.inject.Inject;
import jakarta.jws.WebMethod;
import jakarta.jws.WebService;

import org.assertj.core.api.Assertions;
import org.jboss.logging.Logger;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkus.test.QuarkusUnitTest;
import io.smallrye.certs.Format;
import io.smallrye.certs.junit5.Certificate;
import io.smallrye.certs.junit5.Certificates;

@Certificates(baseDir = "target/classes", //
certificates = @Certificate( //
name = "fake-host", //
password = "secret", //
cn = "fake-host", //
formats = { Format.PKCS12 }))
public class BadHostnameTest {

@RegisterExtension
public static final QuarkusUnitTest test = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(HelloService.class, HelloServiceImpl.class))

/* Server TLS */
.overrideConfigKey("quarkus.tls.key-store.p12.path", "fake-host-keystore.p12")
.overrideConfigKey("quarkus.tls.key-store.p12.password", "secret")
.overrideConfigKey("quarkus.http.insecure-requests", "disabled")
/* Service */
.overrideConfigKey("quarkus.cxf.endpoint.\"/hello\".implementor",
HelloServiceImpl.class.getName())
.overrideConfigKey("quarkus.cxf.endpoint.\"/hello\".logging.enabled", "true")

/* Named TLS configuration for the clients */
.overrideConfigKey("quarkus.tls.client-pkcs12.trust-store.p12.path", "fake-host-truststore.p12")
.overrideConfigKey("quarkus.tls.client-pkcs12.trust-store.p12.password", "secret")

/* Client with VertxHttpClientHTTPConduitFactory */
.overrideConfigKey("quarkus.cxf.client.helloVertx.client-endpoint-url", "https://localhost:8444/services/hello")
.overrideConfigKey("quarkus.cxf.client.helloVertx.logging.enabled", "true")
.overrideConfigKey("quarkus.cxf.client.helloVertx.service-interface", HelloService.class.getName())
.overrideConfigKey("quarkus.cxf.client.helloVertx.http-conduit-factory", "VertxHttpClientHTTPConduitFactory")
.overrideConfigKey("quarkus.cxf.client.helloVertx.tls-configuration-name", "client-pkcs12")

/* Client with HttpClientHTTPConduitFactory */
.overrideConfigKey("quarkus.cxf.client.helloHttpClient.client-endpoint-url",
"https://localhost:8444/services/hello")
.overrideConfigKey("quarkus.cxf.client.helloHttpClient.logging.enabled", "true")
.overrideConfigKey("quarkus.cxf.client.helloHttpClient.service-interface", HelloService.class.getName())
.overrideConfigKey("quarkus.cxf.client.helloHttpClient.http-conduit-factory", "HttpClientHTTPConduitFactory")
.overrideConfigKey("quarkus.cxf.client.helloHttpClient.tls-configuration-name", "client-pkcs12")

/* Client with URLConnectionHTTPConduitFactory */
.overrideConfigKey("quarkus.cxf.client.helloUrlConnection.client-endpoint-url",
"https://localhost:8444/services/hello")
.overrideConfigKey("quarkus.cxf.client.helloUrlConnection.logging.enabled", "true")
.overrideConfigKey("quarkus.cxf.client.helloUrlConnection.service-interface", HelloService.class.getName())
.overrideConfigKey("quarkus.cxf.client.helloUrlConnection.http-conduit-factory", "URLConnectionHTTPConduitFactory")
.overrideConfigKey("quarkus.cxf.client.helloUrlConnection.tls-configuration-name", "client-pkcs12");

@CXFClient("helloVertx")
HelloService helloVertx;

@CXFClient("helloHttpClient")
HelloService helloHttpClient;

@CXFClient("helloUrlConnection")
HelloService helloUrlConnection;

@Inject
Logger logger;

@Test
void vertx() {
Assertions.assertThatThrownBy(() -> helloVertx.hello("Doe")).hasRootCauseMessage(
"No subject alternative DNS name matching localhost found.");
}

@Test
void httpClient() {
Assertions.assertThatThrownBy(() -> helloHttpClient.hello("Doe")).hasRootCauseMessage(
"No name matching localhost found");
}

@Test
void urlConnection() {
Assertions.assertThatThrownBy(() -> helloUrlConnection.hello("Doe")).hasRootCauseMessage(
"The https URL hostname does not match the Common Name (CN) on the server certificate in the client's truststore. Make sure server certificate is correct, or to disable this check (NOT recommended for production) set the CXF client TLS configuration property \"disableCNCheck\" to true.");
}

@WebService
public interface HelloService {

@WebMethod
String hello(String person);

}

@WebService(serviceName = "HelloService")
public static class HelloServiceImpl implements HelloService {

@Override
public String hello(String person) {
return "Hello " + person;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.apache.cxf.BusFactory;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduitFactory;
import org.apache.cxf.transport.http.HttpClientHTTPConduit;
import org.apache.cxf.transport.http.URLConnectionHTTPConduit;
import org.assertj.core.api.Assertions;
Expand All @@ -18,12 +17,10 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl;
import io.quarkiverse.cxf.URLConnectionHTTPConduitFactory;
import io.quarkiverse.cxf.HTTPConduitImpl;
import io.quarkiverse.cxf.HTTPConduitSpec;
import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkiverse.cxf.deployment.test.GlobalConduitFactoryTest.HelloService;
import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit;
import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduitFactory;
import io.quarkus.test.QuarkusUnitTest;

public class ClientConduitFactoryTest {
Expand Down Expand Up @@ -73,11 +70,10 @@ public class ClientConduitFactoryTest {
@Test
void conduitFactory() {
final Bus bus = BusFactory.getDefaultBus();
final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class);
final HTTPConduitImpl impl = HTTPConduitImpl.findDefaultHTTPConduitImpl();
Assertions.assertThat(factory).isInstanceOf(impl.newHTTPConduitFactory().getClass());
final HTTPConduitSpec registeredImpl = bus.getExtension(HTTPConduitSpec.class);
HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.HTTPConduitImpl.findDefaultHTTPConduitImpl();
Assertions.assertThat(registeredImpl.resolveDefault()).isEqualTo(defaultImpl);

HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl.findDefaultHTTPConduitImpl();
{
final HelloService service = helloDefault;
final Client client = ClientProxy.getClient(service);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.apache.cxf.BusFactory;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduitFactory;
import org.apache.cxf.transport.http.URLConnectionHTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.assertj.core.api.Assertions;
Expand All @@ -27,10 +26,10 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkiverse.cxf.CxfClientConfig;
import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl;
import io.quarkiverse.cxf.HTTPConduitImpl;
import io.quarkiverse.cxf.HTTPConduitSpec;
import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit;
import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduitFactory;
import io.quarkus.test.QuarkusUnitTest;

public class ClientHttpPolicyDefaultsTest {
Expand Down Expand Up @@ -85,9 +84,9 @@ public void defaults() throws IllegalArgumentException, IllegalAccessException,
@Test
void defaultConduitFactory() {
final Bus bus = BusFactory.getDefaultBus();
final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class);
HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl.findDefaultHTTPConduitImpl();
Assertions.assertThat(factory).isInstanceOf(defaultImpl.newHTTPConduitFactory().getClass());
final HTTPConduitSpec registeredImpl = bus.getExtension(HTTPConduitSpec.class);
HTTPConduitImpl defaultImpl = io.quarkiverse.cxf.HTTPConduitImpl.findDefaultHTTPConduitImpl();
Assertions.assertThat(registeredImpl.resolveDefault()).isEqualTo(defaultImpl);

final Client client = ClientProxy.getClient(helloService);
switch (defaultImpl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl;
import io.quarkiverse.cxf.HTTPConduitImpl;
import io.quarkiverse.cxf.HttpClientHTTPConduitFactory;
import io.quarkiverse.cxf.URLConnectionHTTPConduitFactory;
import io.quarkiverse.cxf.annotation.CXFClient;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkiverse.cxf.CxfClientConfig.HTTPConduitImpl;
import io.quarkiverse.cxf.HTTPConduitImpl;
import io.quarkiverse.cxf.HttpClientHTTPConduitFactory;
import io.quarkiverse.cxf.URLConnectionHTTPConduitFactory;
import io.quarkiverse.cxf.annotation.CXFClient;
Expand Down Expand Up @@ -66,7 +66,7 @@ public class GlobalConduitFactoryTest {
void conduitFactory() {
final Bus bus = BusFactory.getDefaultBus();
final HTTPConduitFactory factory = bus.getExtension(HTTPConduitFactory.class);
Assertions.assertThat(factory).isInstanceOf(URLConnectionHTTPConduitFactory.class);
Assertions.assertThat(factory).isNull();

{
final Client client = ClientProxy.getClient(helloGlobal);
Expand Down
Loading
Loading