Our client uses tls connection not the mtls. As we use jwt authorisation for the public front end. So our server config looks like below
frontend:
server:
requireClientAuth: true
certFile: ./certificates/internode/cluster-internode.pem
keyFile: ./certificates/internode/cluster-internode.key
clientCaFiles:
- ./certificates/internode/server-intermediate-ca.pem
hostOverrides:
temporal:
requireClientAuth: false
certFile: ./certificates/frontend/cluster-internode.pem
keyFile: ./certificates/frontend/cluster-internode.key
clientCaFiles:
- ./certificates/frontend/server-intermediate-ca.pem
public endpoint should route to temporal i,e temporal:7233. It works fine with tctl .
tctl --address temporal:7233 --tls_ca_path ca.pem --headers_provider_plugin /usr/local/bin/tctl-authorization-plugin namespace list
But we cannot make it work with java client as it routes to the default server config. As it doesn’t find the server name . We see the logs in temporal server as below “server-name” is empty and it doesn’t recognise the host override.
{"level":"warn","ts":"2022-01-13T11:41:41.008Z","msg":"cannot find a per-host provider for attempted incoming TLS connection. returning default TLS configuration","server-name":"","address":"127.0.0.1:57946","logging-call-at":"localStoreTlsProvider.go:277"}
sslContext = SslContextBuilder
.forClient().trustManager(this.getClass().getClassLoader().getResourceAsStream(prefix + "ca.pem"))
.applicationProtocolConfig(
new ApplicationProtocolConfig(
ApplicationProtocolConfig.Protocol.ALPN,
ApplicationProtocolConfig.SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
ApplicationProtocolConfig.SelectedListenerFailureBehavior
.CHOOSE_MY_LAST_PROTOCOL, "h2"))
.clientAuth(ClientAuth.NONE).build();
WorkflowServiceStubsOptions serviceStubOptions =
WorkflowServiceStubsOptions.newBuilder()
.addGrpcMetadataProvider(new AuthorizationGrpcMetadataProvider(tokenSupplier))
.setTarget(config.getTemporalHostPort())
.setSslContext(sslContext)
.build();
We have narrowed it down to the the Netty/GRPC library behaviour where if the endpoint is supplied as temporal
SNI is not getting added where if endpoint is temporal.local
then SNI works just fine and the client TLS Hello contains the server name. It does look like the dot in the dns name makes a difference.
I think it has something to do with this:
src.readableBytes(), true, Base64Dialect.STANDARD, allocator);
src.readerIndex(src.writerIndex());
return dst;
}
/**
* Validate that the given hostname can be used in SNI extension.
*/
static boolean isValidHostNameForSNI(String hostname) {
return hostname != null &&
hostname.indexOf('.') > 0 &&
!hostname.endsWith(".") && !hostname.startsWith("/") &&
!NetUtil.isValidIpV4Address(hostname) &&
!NetUtil.isValidIpV6Address(hostname);
}
/**
* Returns {@code true} if the the given cipher (in openssl format) is for TLSv1.3, {@code false} otherwise.
*/
static boolean isTLSv13Cipher(String cipher) {
// See https://tools.ietf.org/html/rfc8446#appendix-B.4
Netty checks if a host name has a dot in it and if it doesn’t - it discards it as a valid SNI.
2 Likes
See
opened 10:14PM - 14 Jan 22 UTC
closed 12:11AM - 18 Jan 22 UTC
### Expected behavior
If we establish a secured client connection to `tempora… l.local:9090`, `temporal.local` hostname is used in TLS SNI Extension.
If we establish a secured client connection to `temporal:9090`, `temporal` hostname is used in TLS SNI Extension.
`temporal` is a valid SNI hostname as much as `temporal.local`. `new javax.net.ssl.SNIHostName(hostname)` doesn't throw for both of them.
### Actual behavior
If we establish a secured client connection to `temporal.local:9090`, `temporal.local` is coming as an SNI.
If we establish a secured client connection to `temporal:9090`, `temporal` is DISCARDED and NOT coming as an SNI.
### Root cause
Netty enforces that the SNI hostname has to have a dot.
https://github.com/netty/netty/blob/b6021330a7db9bf2a1a5e7fe9bb4b0584bb50ecd/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslEngine.java#L344
```java
// Use SNI if peerHost was specified and a valid hostname
// See https://github.com/netty/netty/issues/4746
if (clientMode && SslUtils.isValidHostNameForSNI(peerHost)) {
// If on java8 and later we should do some extra validation to ensure we can construct the
// SNIHostName later again.
if (PlatformDependent.javaVersion() >= 8) {
if (Java8SslUtils.isValidHostNameForSNI(peerHost)) {
SSL.setTlsExtHostName(ssl, peerHost);
sniHostNames = Collections.singletonList(peerHost);
}
} else {
SSL.setTlsExtHostName(ssl, peerHost);
sniHostNames = Collections.singletonList(peerHost);
}
}
```
https://github.com/netty/netty/blob/b6021330a7db9bf2a1a5e7fe9bb4b0584bb50ecd/handler/src/main/java/io/netty/handler/ssl/SslUtils.java#L482
```java
static boolean isValidHostNameForSNI(String hostname) {
return hostname != null &&
hostname.indexOf('.') > 0 &&
!hostname.endsWith(".") && !hostname.startsWith("/") &&
!NetUtil.isValidIpV4Address(hostname) &&
!NetUtil.isValidIpV6Address(hostname);
}
```
### Netty version
4.1.63.Final, master(765e56a93ae15d682a0611fbb3886eb7df738fc9)
https://gamlor.info/posts-output/2019-09-05-java-client-sni/en/
It looks like you have to work with “temporal.local” and just “temporal” can not be used as a valid SNI name according to the spec.