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

Allow using null peer host for TLS and disabling host and port inference and SNI altogether. #1561

Merged
merged 7 commits into from
Jun 8, 2021

Conversation

chemicL
Copy link
Contributor

@chemicL chemicL commented May 13, 2021

Motivation:

There are cases when clients require connecting to a secure server which doesn't
identify itself in the TLS certificate with the same address as used for
connection establishment, or multiple identities can be presented from
diffrent underlying backends. In such case the hostname verification would fail
and would need to be disabled. However, the peerHost which is inferred from the
connection address would unnecessarily cache SSL sessions, which could lead
to issues in session resumption. Therefore, we want to allow explicitly disabling
peerHost inference and either allow setting it to a desired address or to null.

Modifications:

  • Added ClientSslConfig#isPeerHostSet method,
  • ClientSslConfigBuilder#setPeerHost now accepts null and disables address inference,
  • Added test in SslAndNonSslConnectionsTest validating the mentioned scenario,
  • Added test in SslAndNonSslConnectionsTest for ensuring hostname validation
    works as expected when hostname is used (existing tests used only IP).

Result:

It is now possible to explicitly use null for peerHost
and disable its' inference.

@chemicL chemicL force-pushed the allow-null-peerhost branch from 5727fea to 0d5e0a3 Compare May 18, 2021 14:10
@chemicL chemicL force-pushed the allow-null-peerhost branch from 0d5e0a3 to 31043de Compare May 25, 2021 14:57
@chemicL chemicL changed the title Allow setting peerHost to null in ClientSslConfig. Allow using null peer host for TLS and disabling host and port inference and SNI altogether. May 25, 2021
@tkountis tkountis added breaking-change PR with breaking changes, removed APIs or other binary incompatibilities. API PR with API changes (New or Deprecated) labels May 25, 2021
@chemicL chemicL marked this pull request as ready for review May 25, 2021 16:35
* via {@link #sslConfig(ClientSslConfig)}.
* @return {@code this}
*/
public abstract SingleAddressHttpClientBuilder<U, R> disableSni();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disableSni -> disableSniInference? Can you clarify why this method is different than the other new methods?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the following names to allow users set/unset the value on the builder:

peerHostInference(boolean)
peerPortInference(boolean)
sniHostnameInference(boolean)

or

inferPeerHost(boolean)
inferPeerPort(boolean)
inferSniHostname(boolean)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch my reasoning was the following: the inference for SNI is less straightforward than for peerHost – internally, the X509TrustManagerImpl does a fallback to peerHost when checking the certificate identity. The internals also explicitly add the peerHost to the SNI list.
The existing fallback may seem confusing, hence I decided disabling SNI is the functionality that might be useful. Yet still, my attempt at allowing disabling is not really effective because of the fallback in JDK and Netty. I'll change to the inference related method.

@@ -256,7 +256,7 @@ public ClientSslConfig asSslConfig() {
}

if (hostnameVerificationAlgorithm == null) {
builder.disableHostnameVerification();
builder.hostnameVerificationAlgorithm("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add at the end of @deprecated description of ClientSslConfigBuilder#disableHostnameVerification() that if users are sure they need to disable it, they can use hostnameVerificationAlgorithm("").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* via {@link #sslConfig(ClientSslConfig)}.
* @return {@code this}
*/
public abstract SingleAddressHttpClientBuilder<U, R> disableSni();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the following names to allow users set/unset the value on the builder:

peerHostInference(boolean)
peerPortInference(boolean)
sniHostnameInference(boolean)

or

inferPeerHost(boolean)
inferPeerPort(boolean)
inferSniHostname(boolean)

chemicL and others added 2 commits May 27, 2021 13:36
Motivation:

There are cases when clients require connecting to a secure server which doesn't
identify itself in the TLS certificate with the same address as used for
connection establishment, or multiple identities can be presented from
diffrent underlying backends. In such case the hostname verification would fail
and would need to be disabled. However, the peerHost which is inferred from the
connection address would unnecessarily cache SSL sessions, which could lead
to issues in session resumption. Therefore, we want to allow explicitly disabling
peerHost inference and either allow setting it to a desired address or to null.

Modifications:

- Added ClientSslConfig#isPeerHostSet method,
- ClientSslConfigBuilder#setPeerHost now accepts null and disables address inference,
- Added test in SslAndNonSslConnectionsTest validating the mentioned scenario,
- Added test in SslAndNonSslConnectionsTest for ensuring hostname validation
  works as expected when hostname is used (existing tests used only IP).

Result:

It is now possible to explicitly use `null` for `peerHost`
and disable its' inference.
…rence and SNI altogether.

Motivation:

There are cases when clients require connecting to a secure server which doesn't
identify itself in the TLS certificate with the same address as used for
connection establishment, or multiple identities can be presented from
diffrent underlying backends. In such case the hostname verification would fail
and would need to be disabled. However, the peerHost which is inferred from the
connection address would unnecessarily cache SSL sessions, which could lead
to issues in session resumption. Therefore, we want to allow explicitly disabling
peerHost inference and either allow setting it to a desired address or leaving `null`.

Modifications:

- Added SingleAddressHttpClientBuilder#disablePeerHostInference method,
- Added SingleAddressHttpClientBuilder#disablePeerPortInference method,
- Added SingleAddressHttpClientBuilder#disableSni method,
- ClientSslConfigBuilder#setPeerHost now accepts explicit `null`,
- Added case in SslAndNonSslConnectionsTest validating the mentioned scenario,
- Added case in SslAndNonSslConnectionsTest for ensuring hostname validation
  works as expected when hostname is used (existing tests used only IP),
- Added case in SniTest validating behaviour for disabled SNI and explicitly
  disabling it in existing `noSni` cases.

Result:

It is now possible to explicitly use `null` for `peerHost` in `ClientSslConfig`
and disable its' inference using `SingleAddressHttpClientBuilder`'s API.
@chemicL chemicL force-pushed the allow-null-peerhost branch from 31043de to c2c8e1a Compare May 27, 2021 11:40
* via {@link #sslConfig(ClientSslConfig)}.
* @return {@code this}
*/
public abstract SingleAddressHttpClientBuilder<U, R> disableSni();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch my reasoning was the following: the inference for SNI is less straightforward than for peerHost – internally, the X509TrustManagerImpl does a fallback to peerHost when checking the certificate identity. The internals also explicitly add the peerHost to the SNI list.
The existing fallback may seem confusing, hence I decided disabling SNI is the functionality that might be useful. Yet still, my attempt at allowing disabling is not really effective because of the fallback in JDK and Netty. I'll change to the inference related method.

@chemicL chemicL force-pushed the allow-null-peerhost branch from dc204ed to ef843cf Compare May 27, 2021 14:39
@chemicL chemicL force-pushed the allow-null-peerhost branch from ef843cf to a725289 Compare May 27, 2021 14:43
Copy link
Member

@Scottmitch Scottmitch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one comment then lgtm


@Nullable
private String evaluatePeerHost() {
return (configPeerHost == null && inferPeerHost) ? fallbackPeerHost : configPeerHost;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider inlining these definitions or making the anonymous type non-anonymous and adding constructor to clarify we don't need to retain references to configPeerHost and related variables outside the scope of this class.

)
.listenBlockingAndAwait((ctx, request, responseFactory) -> responseFactory.ok());
BlockingHttpClient client = HttpClients.forSingleAddress(
HostAndPort.of(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider removing HostAndPort.of( and use the method overload that takes String, int

Copy link
Member

@idelpivnitskiy idelpivnitskiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work!

Doesn't look like this PR breaks any API now. Should we remove breaking-change label now?

* @param shouldInfer value indicating whether inference is on ({@code true}) or off ({@code false}).
* @return {@code this}
*/
public abstract SingleAddressHttpClientBuilder<U, R> inferPeerHost(boolean shouldInfer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We previously discussed in #1561 (comment), but doesn't look like we come to a conclusion. Does it make sense to join peer host and port together on ClientSslConfig API into a single method HostAndPort peerHostAndPort()? If yes, we can have a single method here: inferPeerHostAndPort(boolean) instead of two.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Scottmitch wdyt about single ClientSslConfig#peerHostAndPort() and inferPeerHostAndPort(boolean)?

Copy link
Contributor Author

@chemicL chemicL Jun 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my understanding the peer port is not used when peer host is not inferred (and also is null). I think it's reasonable to provide an API where both are affected. And yes, the breaking API change would need to go to ClientSslConfig. Perhaps we could do it in a separate PR and for now add this feature with no breakage?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that if we merge host and port into one method, we can provide only one "infer" method and won't need to change client API later.
Let's make the final decision tomorrow morning. Everything else looks good in the PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed with @Scottmitch offline. He suggested to keep host and port independent because sometimes users don't know what is the port number (SRV resolutions or use of Unix Domain Sockets).

@chemicL chemicL force-pushed the allow-null-peerhost branch from 064d4ce to 90553c4 Compare June 7, 2021 16:34
Copy link
Member

@idelpivnitskiy idelpivnitskiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

FAILURE: Build failed with an exception.
428 actionable tasks: 353 executed, 75 up-to-date
What went wrong:
Execution failed for task ':servicetalk-http-netty:pmdTest'.
> 4 PMD rule violations were found. See the report at: file:///home/runner/work/servicetalk/servicetalk/servicetalk-http-netty/build/reports/pmd/test.html

The build fails because of #1596. Please, fix PMD warnings and 🚢

@idelpivnitskiy idelpivnitskiy removed the breaking-change PR with breaking changes, removed APIs or other binary incompatibilities. label Jun 8, 2021
@chemicL
Copy link
Contributor Author

chemicL commented Jun 8, 2021

Fixed PMD errors, merged with main branch and ready to go.

@chemicL chemicL merged commit 4c8e681 into apple:main Jun 8, 2021
bondolo pushed a commit to bondolo/servicetalk that referenced this pull request Jun 8, 2021
…rence and SNI altogether. (apple#1561)

Allow using `null` peer host for TLS and disabling host and port inference and SNI altogether.

Motivation:

There are cases when clients require connecting to a secure server which doesn't
identify itself in the TLS certificate with the same address as used for
connection establishment, or multiple identities can be presented from
diffrent underlying backends. In such case the hostname verification would fail
and would need to be disabled. However, the peerHost which is inferred from the
connection address would unnecessarily cache SSL sessions, which could lead
to issues in session resumption. Therefore, we want to allow explicitly disabling
peerHost inference and either allow setting it to a desired address or leaving `null`.

Modifications:

- Added `SingleAddressHttpClientBuilder#inferPeerHost` method,
- Added `SingleAddressHttpClientBuilder#inferPeerPort` method,
- Added `SingleAddressHttpClientBuilder#inferSniHostname` method,
- `ClientSslConfigBuilder#setPeerHost` now accepts explicit `null`,
- Added case in `SslAndNonSslConnectionsTest` validating the mentioned scenario,
- Added case in `SslAndNonSslConnectionsTest` for ensuring hostname validation
  works as expected when hostname is used (existing tests used only IP),
- Added case in SniTest validating behaviour for disabled SNI and explicitly
  disabling it in existing `noSni` cases.

Result:

It is now possible to explicitly use `null` for `peerHost` in `ClientSslConfig`
and disable its' inference using `SingleAddressHttpClientBuilder`'s API.
idelpivnitskiy added a commit to idelpivnitskiy/servicetalk that referenced this pull request Jun 21, 2021
…1561

Motivation:

`ClientSslConfigBuilder#disableHostnameVerification()` option was
deprecated in apple#1561 and can be removed now. Users can use
`hostnameVerificationAlgorithm("")` as an alternative, but should avoid
disabling hostname verification in general.

Modifications:

- Remove `ClientSslConfigBuilder#disableHostnameVerification()`;

Result:

No deprecated `ClientSslConfigBuilder#disableHostnameVerification()` API.
idelpivnitskiy added a commit that referenced this pull request Jun 22, 2021
…1637)

Motivation:

`ClientSslConfigBuilder#disableHostnameVerification()` option was
deprecated in #1561 and can be removed now. Users can use
`hostnameVerificationAlgorithm("")` as an alternative, but should avoid
disabling hostname verification in general.

Modifications:

- Remove `ClientSslConfigBuilder#disableHostnameVerification()`;

Result:

No deprecated `ClientSslConfigBuilder#disableHostnameVerification()` API.
chemicL pushed a commit to chemicL/servicetalk that referenced this pull request Jul 19, 2021
Motivation:

This change follows from [a change to HTTP client builders](apple#1561)
and exposes the same capabilities to GRPC client builders.

Modifications:

- `SingleAddressGrpcClientBuilder` has new methods: `inferPeerHost`,
 `inferPeerPort`, and `inferSniHostname` which allow configuring the
 behaviour of the TLS handshake and inference of the corresponding
 configurations.

Result:

Users are able to disable peerHost and peerPort inference and can now
use an empty peerHost when they so desire. The SNI hostname inference
from the provided address can also be disabled in cases when the
address used for connection should not be passed in the SNI extension.
chemicL pushed a commit that referenced this pull request Jul 21, 2021
…nts (#1678)

Motivation:

This change follows from [a change to HTTP client builders](#1561)
and exposes the same capabilities to GRPC client builders.

Modifications:

- `SingleAddressGrpcClientBuilder` has new methods: `inferPeerHost`,
 `inferPeerPort`, and `inferSniHostname` which allow configuring the
 behaviour of the TLS handshake and inference of the corresponding
 configurations.

Result:

Users are able to disable peerHost and peerPort inference and can now
use an empty peerHost when they so desire. The SNI hostname inference
from the provided address can also be disabled in cases when the
address used for connection should not be passed in the SNI extension.
chemicL pushed a commit that referenced this pull request Jul 21, 2021
…nts (#1678)

Motivation:

This change follows from [a change to HTTP client builders](#1561)
and exposes the same capabilities to GRPC client builders.

Modifications:

- `SingleAddressGrpcClientBuilder` has new methods: `inferPeerHost`,
 `inferPeerPort`, and `inferSniHostname` which allow configuring the
 behaviour of the TLS handshake and inference of the corresponding
 configurations.

Result:

Users are able to disable peerHost and peerPort inference and can now
use an empty peerHost when they so desire. The SNI hostname inference
from the provided address can also be disabled in cases when the
address used for connection should not be passed in the SNI extension.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API PR with API changes (New or Deprecated)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants