petersomogyi commented on code in PR #8681:
URL: https://github.com/apache/hbase/pull/8681#discussion_r4094086064
##########
hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestX509Util.java:
##########
@@ -375,4 +376,142 @@ public void testLoadPKCS12TrustStoreWithWrongPassword() {
});
}
+ //
---------------------------------------------------------------------------
+ // Role-scoped configuration resolution (single-EKU certificate support)
+ //
---------------------------------------------------------------------------
+
+ @TestTemplate
+ public void testResolveConfigPrefersRoleScopedOverLegacy() {
+ conf.set("test.role", "role-value");
+ conf.set("test.legacy", "legacy-value");
+ assertEquals("role-value",
+ X509Util.resolveConfig(conf, "test.role", "test.legacy",
"default-value"));
+ }
+
+ @TestTemplate
+ public void testResolveConfigFallsBackToLegacyWhenRoleUnset() {
+ conf.unset("test.role");
+ conf.set("test.legacy", "legacy-value");
+ assertEquals("legacy-value",
+ X509Util.resolveConfig(conf, "test.role", "test.legacy",
"default-value"));
+ }
+
+ @TestTemplate
+ public void testResolveConfigReturnsDefaultWhenBothUnset() {
+ conf.unset("test.role");
+ conf.unset("test.legacy");
+ assertEquals("default-value",
+ X509Util.resolveConfig(conf, "test.role", "test.legacy",
"default-value"));
+ }
+
+ @TestTemplate
+ public void testResolvePasswordPrefersRoleScopedOverLegacy() throws
Exception {
+ conf.set("test.role.password", "role-pw");
+ conf.set("test.legacy.password", "legacy-pw");
+ assertArrayEquals("role-pw".toCharArray(),
+ X509Util.resolvePassword(conf, "test.role.password",
"test.legacy.password"));
+ }
+
+ @TestTemplate
+ public void testResolvePasswordFallsBackToLegacyWhenRoleUnset() throws
Exception {
+ conf.unset("test.role.password");
+ conf.set("test.legacy.password", "legacy-pw");
+ assertArrayEquals("legacy-pw".toCharArray(),
+ X509Util.resolvePassword(conf, "test.role.password",
"test.legacy.password"));
+ }
+
+ @TestTemplate
+ public void testResolvePasswordReturnsNullWhenBothUnset() throws Exception {
+ conf.unset("test.role.password");
+ conf.unset("test.legacy.password");
+ assertNull(X509Util.resolvePassword(conf, "test.role.password",
"test.legacy.password"));
+ }
+
+ @TestTemplate
+ public void testCreateSSLContextForClientUsesRoleScopedKeystoreWhenSet()
throws Exception {
+ // Move the legacy keystore values to the client-scoped keys and clear the
legacy keys, so
+ // that a successful context build proves the client-scoped keys were
consulted.
+ String location = conf.get(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
+ String password = conf.get(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD);
+ String type = conf.get(X509Util.TLS_CONFIG_KEYSTORE_TYPE);
+ conf.set(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_LOCATION, location);
+ conf.set(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_PASSWORD, password);
+ conf.set(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_TYPE, type);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_TYPE);
+
+ SslContext sslContext = X509Util.createSslContextForClient(conf);
+ ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
+ // Handshake would fail if the key manager weren't wired; smoke-test that
engine creation works.
+ assertTrue(
+
sslContext.newEngine(byteBufAllocatorMock).getSSLParameters().getProtocols().length
> 0);
+ }
+
+ @TestTemplate
+ public void testCreateSSLContextForClientFallsBackToLegacyKeystore() throws
Exception {
+ // The base setUp() only sets the legacy TLS_CONFIG_KEYSTORE_* /
TLS_CONFIG_TRUSTSTORE_* keys.
+ // The role-scoped keys are intentionally unset; the context must still
build using the legacy
+ // values (backward-compat regression guard).
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_LOCATION);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_PASSWORD);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_TYPE);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_TRUSTSTORE_LOCATION);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_TRUSTSTORE_PASSWORD);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_TRUSTSTORE_TYPE);
+
+ SslContext sslContext = X509Util.createSslContextForClient(conf);
+ ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
+ assertTrue(
+
sslContext.newEngine(byteBufAllocatorMock).getSSLParameters().getProtocols().length
> 0);
+ }
+
+ @TestTemplate
+ public void testCreateSSLContextForServerUsesRoleScopedKeystoreWhenSet()
throws Exception {
+ String location = conf.get(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
+ String password = conf.get(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD);
+ String type = conf.get(X509Util.TLS_CONFIG_KEYSTORE_TYPE);
+ conf.set(X509Util.TLS_CONFIG_SERVER_KEYSTORE_LOCATION, location);
+ conf.set(X509Util.TLS_CONFIG_SERVER_KEYSTORE_PASSWORD, password);
+ conf.set(X509Util.TLS_CONFIG_SERVER_KEYSTORE_TYPE, type);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_TYPE);
+
+ SslContext sslContext = X509Util.createSslContextForServer(conf);
+ ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
+ assertTrue(
+
sslContext.newEngine(byteBufAllocatorMock).getSSLParameters().getProtocols().length
> 0);
+ }
+
+ @TestTemplate
+ public void testCreateSSLContextForServerFallsBackToLegacyKeystore() throws
Exception {
Review Comment:
Same comment as testCreateSSLContextForClientFallsBackToLegacyKeystore
##########
hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftServer.java:
##########
@@ -413,16 +423,58 @@ protected void setupHTTPServer() throws IOException {
httpsConfig.addCustomizer(new SecureRequestCustomizer());
SslContextFactory.Server sslCtxFactory = new SslContextFactory.Server();
- String keystore = conf.get(THRIFT_SSL_KEYSTORE_STORE_KEY);
+ // Prefer the role-scoped hbase.thrift.ssl.server.* keys, falling back
to the historical
+ // unscoped hbase.thrift.ssl.* keys for backward compatibility with
existing deployments.
+ String keystore = X509Util.resolveConfig(conf,
THRIFT_SSL_SERVER_KEYSTORE_STORE_KEY,
+ THRIFT_SSL_KEYSTORE_STORE_KEY, null);
String password =
- HBaseConfiguration.getPassword(conf, THRIFT_SSL_KEYSTORE_PASSWORD_KEY,
null);
+ HBaseConfiguration.getPassword(conf,
THRIFT_SSL_SERVER_KEYSTORE_PASSWORD_KEY,
+ HBaseConfiguration.getPassword(conf,
THRIFT_SSL_KEYSTORE_PASSWORD_KEY, null));
String keyPassword =
- HBaseConfiguration.getPassword(conf,
THRIFT_SSL_KEYSTORE_KEYPASSWORD_KEY, password);
+ HBaseConfiguration.getPassword(conf,
THRIFT_SSL_SERVER_KEYSTORE_KEYPASSWORD_KEY,
+ HBaseConfiguration.getPassword(conf,
THRIFT_SSL_KEYSTORE_KEYPASSWORD_KEY, password));
sslCtxFactory.setKeyStorePath(keystore);
sslCtxFactory.setKeyStorePassword(password);
sslCtxFactory.setKeyManagerPassword(keyPassword);
sslCtxFactory
- .setKeyStoreType(conf.get(THRIFT_SSL_KEYSTORE_TYPE_KEY,
THRIFT_SSL_KEYSTORE_TYPE_DEFAULT));
+ .setKeyStoreType(X509Util.resolveConfig(conf,
THRIFT_SSL_SERVER_KEYSTORE_TYPE_KEY,
+ THRIFT_SSL_KEYSTORE_TYPE_KEY, THRIFT_SSL_KEYSTORE_TYPE_DEFAULT));
+
+ // Truststore is entirely new for Thrift — no legacy fallback because
there is no historical
+ // hbase.thrift.ssl.truststore.* configuration. When left unset, no
truststore is configured
+ // on the connector and any hbase.thrift.ssl.server.client.auth.mode =
WANT/NEED setting
+ // will fail the handshake for lack of a peer-cert trust root.
Review Comment:
Opus 5.5 called out this: Jetty falls back to the JVM cacerts, so any
publicly-CA-signed client cert is accepted and it will not cause a handshake
fail. It should fail at startup if configs are missing or WARN about it to let
the operator aware of the configuration issue.
##########
hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestInfoServerTLSConfig.java:
##########
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.http;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.testclassification.MiscTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link InfoServer}'s TLS-config resolution: the 3-tier
fallback chain
+ * ({@code hbase.ui.ssl.server.*} → {@code hbase.ui.ssl.*} → {@code
ssl.server.*}) that underpins
+ * single-EKU certificate support on the UI surface, and the client-auth-mode
key.
+ */
+@Tag(MiscTests.TAG)
+@Tag(SmallTests.TAG)
+public class TestInfoServerTLSConfig {
+
+ private static final String POSTFIX = "keystore.location";
+ private static final String ROLE_SCOPED_KEY = "hbase.ui.ssl.server." +
POSTFIX;
+ private static final String HBASE_KEY = "hbase.ui.ssl." + POSTFIX;
+ private static final String HADOOP_KEY = "ssl.server." + POSTFIX;
+
+ @Test
+ public void testRoleScopedTakesPrecedenceOverHBasePrefixed() {
+ Configuration c = new Configuration(false);
+ c.set(ROLE_SCOPED_KEY, "role-scoped-value");
+ c.set(HBASE_KEY, "hbase-prefixed-value");
+ c.set(HADOOP_KEY, "hadoop-prefixed-value");
+ assertEquals("role-scoped-value", InfoServer.getTLSProperty(c, POSTFIX));
+ }
+
+ @Test
+ public void testHBasePrefixedTakesPrecedenceOverHadoopPrefixed() {
+ Configuration c = new Configuration(false);
+ // No role-scoped key set — hbase-prefixed key must win.
+ c.set(HBASE_KEY, "hbase-prefixed-value");
+ c.set(HADOOP_KEY, "hadoop-prefixed-value");
+ assertEquals("hbase-prefixed-value", InfoServer.getTLSProperty(c,
POSTFIX));
+ }
+
+ @Test
+ public void testFallsBackToHadoopPrefixedWhenNoOthersSet() {
+ Configuration c = new Configuration(false);
+ c.set(HADOOP_KEY, "hadoop-prefixed-value");
+ assertEquals("hadoop-prefixed-value", InfoServer.getTLSProperty(c,
POSTFIX));
+ }
+
+ @Test
+ public void testReturnsDefaultWhenNoneSet() {
+ Configuration c = new Configuration(false);
+ assertEquals("jks", InfoServer.getTLSProperty(c, POSTFIX, "jks"));
+ assertNull(InfoServer.getTLSProperty(c, POSTFIX));
+ }
+
+ @Test
+ public void testGetTLSPasswordHonorsThreeTierFallback() throws Exception {
+ Configuration c = new Configuration(false);
+ // Configuration.getPassword returns null when unset; verifying the
fallback picks each tier.
+ c.set(ROLE_SCOPED_KEY.replace("keystore.location", "keystore.password"),
"role-pw");
+ c.set(HBASE_KEY.replace("keystore.location", "keystore.password"),
"hbase-pw");
+ c.set(HADOOP_KEY.replace("keystore.location", "keystore.password"),
"hadoop-pw");
+ assertEquals("role-pw", InfoServer.getTLSPassword(c, "keystore.password"));
+
+ c.unset(ROLE_SCOPED_KEY.replace("keystore.location", "keystore.password"));
+ assertEquals("hbase-pw", InfoServer.getTLSPassword(c,
"keystore.password"));
+
+ c.unset(HBASE_KEY.replace("keystore.location", "keystore.password"));
+ assertEquals("hadoop-pw", InfoServer.getTLSPassword(c,
"keystore.password"));
+
+ c.unset(HADOOP_KEY.replace("keystore.location", "keystore.password"));
+ assertNull(InfoServer.getTLSPassword(c, "keystore.password"));
+ }
+
+ @Test
+ public void testClientAuthModeKeyIsRoleScoped() {
+ // Guard against a "double server.server." regression: the
client-auth-mode config key must
+ // resolve to the single role-scoped key, not to a nested/prefixed form.
+ assertEquals("hbase.ui.ssl.server.client.auth.mode",
InfoServer.HBASE_UI_SSL_CLIENT_AUTH_MODE);
+ }
Review Comment:
What is the point of this test?
##########
hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java:
##########
@@ -175,20 +288,27 @@ public static SslContext
createSslContextForClient(Configuration config)
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
configureOpenSslIfAvailable(sslContextBuilder, config);
- String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, "");
- char[] keyStorePassword = config.getPassword(TLS_CONFIG_KEYSTORE_PASSWORD);
- String keyStoreType = config.get(TLS_CONFIG_KEYSTORE_TYPE, "");
+ String keyStoreLocation =
+ resolveConfig(config, TLS_CONFIG_CLIENT_KEYSTORE_LOCATION,
TLS_CONFIG_KEYSTORE_LOCATION, "");
+ char[] keyStorePassword =
+ resolvePassword(config, TLS_CONFIG_CLIENT_KEYSTORE_PASSWORD,
TLS_CONFIG_KEYSTORE_PASSWORD);
+ String keyStoreType =
+ resolveConfig(config, TLS_CONFIG_CLIENT_KEYSTORE_TYPE,
TLS_CONFIG_KEYSTORE_TYPE, "");
if (keyStoreLocation.isEmpty()) {
- LOG.warn(TLS_CONFIG_KEYSTORE_LOCATION + " not specified");
+ LOG.warn("Neither {} nor {} specified",
TLS_CONFIG_CLIENT_KEYSTORE_LOCATION,
+ TLS_CONFIG_KEYSTORE_LOCATION);
} else {
sslContextBuilder
.keyManager(createKeyManager(keyStoreLocation, keyStorePassword,
keyStoreType));
}
- String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, "");
- char[] trustStorePassword =
config.getPassword(TLS_CONFIG_TRUSTSTORE_PASSWORD);
- String trustStoreType = config.get(TLS_CONFIG_TRUSTSTORE_TYPE, "");
+ String trustStoreLocation = resolveConfig(config,
TLS_CONFIG_CLIENT_TRUSTSTORE_LOCATION,
+ TLS_CONFIG_TRUSTSTORE_LOCATION, "");
+ char[] trustStorePassword = resolvePassword(config,
TLS_CONFIG_CLIENT_TRUSTSTORE_PASSWORD,
+ TLS_CONFIG_TRUSTSTORE_PASSWORD);
+ String trustStoreType =
+ resolveConfig(config, TLS_CONFIG_CLIENT_TRUSTSTORE_TYPE,
TLS_CONFIG_TRUSTSTORE_TYPE, "");
Review Comment:
The fallback happens per config key. Setting only
`hbase.rpc.tls.client.keystore.location` means that legacy password and type
will be matched with it causing an error. These should not be combined.
##########
hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftServer.java:
##########
@@ -413,16 +423,58 @@ protected void setupHTTPServer() throws IOException {
httpsConfig.addCustomizer(new SecureRequestCustomizer());
SslContextFactory.Server sslCtxFactory = new SslContextFactory.Server();
- String keystore = conf.get(THRIFT_SSL_KEYSTORE_STORE_KEY);
+ // Prefer the role-scoped hbase.thrift.ssl.server.* keys, falling back
to the historical
+ // unscoped hbase.thrift.ssl.* keys for backward compatibility with
existing deployments.
+ String keystore = X509Util.resolveConfig(conf,
THRIFT_SSL_SERVER_KEYSTORE_STORE_KEY,
+ THRIFT_SSL_KEYSTORE_STORE_KEY, null);
String password =
- HBaseConfiguration.getPassword(conf, THRIFT_SSL_KEYSTORE_PASSWORD_KEY,
null);
+ HBaseConfiguration.getPassword(conf,
THRIFT_SSL_SERVER_KEYSTORE_PASSWORD_KEY,
+ HBaseConfiguration.getPassword(conf,
THRIFT_SSL_KEYSTORE_PASSWORD_KEY, null));
String keyPassword =
- HBaseConfiguration.getPassword(conf,
THRIFT_SSL_KEYSTORE_KEYPASSWORD_KEY, password);
+ HBaseConfiguration.getPassword(conf,
THRIFT_SSL_SERVER_KEYSTORE_KEYPASSWORD_KEY,
+ HBaseConfiguration.getPassword(conf,
THRIFT_SSL_KEYSTORE_KEYPASSWORD_KEY, password));
sslCtxFactory.setKeyStorePath(keystore);
sslCtxFactory.setKeyStorePassword(password);
sslCtxFactory.setKeyManagerPassword(keyPassword);
sslCtxFactory
- .setKeyStoreType(conf.get(THRIFT_SSL_KEYSTORE_TYPE_KEY,
THRIFT_SSL_KEYSTORE_TYPE_DEFAULT));
+ .setKeyStoreType(X509Util.resolveConfig(conf,
THRIFT_SSL_SERVER_KEYSTORE_TYPE_KEY,
+ THRIFT_SSL_KEYSTORE_TYPE_KEY, THRIFT_SSL_KEYSTORE_TYPE_DEFAULT));
+
+ // Truststore is entirely new for Thrift — no legacy fallback because
there is no historical
+ // hbase.thrift.ssl.truststore.* configuration. When left unset, no
truststore is configured
+ // on the connector and any hbase.thrift.ssl.server.client.auth.mode =
WANT/NEED setting
+ // will fail the handshake for lack of a peer-cert trust root.
Review Comment:
Same problem is present in REST and InfoServer.
##########
hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/TestX509Util.java:
##########
@@ -375,4 +376,142 @@ public void testLoadPKCS12TrustStoreWithWrongPassword() {
});
}
+ //
---------------------------------------------------------------------------
+ // Role-scoped configuration resolution (single-EKU certificate support)
+ //
---------------------------------------------------------------------------
+
+ @TestTemplate
+ public void testResolveConfigPrefersRoleScopedOverLegacy() {
+ conf.set("test.role", "role-value");
+ conf.set("test.legacy", "legacy-value");
+ assertEquals("role-value",
+ X509Util.resolveConfig(conf, "test.role", "test.legacy",
"default-value"));
+ }
+
+ @TestTemplate
+ public void testResolveConfigFallsBackToLegacyWhenRoleUnset() {
+ conf.unset("test.role");
+ conf.set("test.legacy", "legacy-value");
+ assertEquals("legacy-value",
+ X509Util.resolveConfig(conf, "test.role", "test.legacy",
"default-value"));
+ }
+
+ @TestTemplate
+ public void testResolveConfigReturnsDefaultWhenBothUnset() {
+ conf.unset("test.role");
+ conf.unset("test.legacy");
+ assertEquals("default-value",
+ X509Util.resolveConfig(conf, "test.role", "test.legacy",
"default-value"));
+ }
+
+ @TestTemplate
+ public void testResolvePasswordPrefersRoleScopedOverLegacy() throws
Exception {
+ conf.set("test.role.password", "role-pw");
+ conf.set("test.legacy.password", "legacy-pw");
+ assertArrayEquals("role-pw".toCharArray(),
+ X509Util.resolvePassword(conf, "test.role.password",
"test.legacy.password"));
+ }
+
+ @TestTemplate
+ public void testResolvePasswordFallsBackToLegacyWhenRoleUnset() throws
Exception {
+ conf.unset("test.role.password");
+ conf.set("test.legacy.password", "legacy-pw");
+ assertArrayEquals("legacy-pw".toCharArray(),
+ X509Util.resolvePassword(conf, "test.role.password",
"test.legacy.password"));
+ }
+
+ @TestTemplate
+ public void testResolvePasswordReturnsNullWhenBothUnset() throws Exception {
+ conf.unset("test.role.password");
+ conf.unset("test.legacy.password");
+ assertNull(X509Util.resolvePassword(conf, "test.role.password",
"test.legacy.password"));
+ }
+
+ @TestTemplate
+ public void testCreateSSLContextForClientUsesRoleScopedKeystoreWhenSet()
throws Exception {
+ // Move the legacy keystore values to the client-scoped keys and clear the
legacy keys, so
+ // that a successful context build proves the client-scoped keys were
consulted.
+ String location = conf.get(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
+ String password = conf.get(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD);
+ String type = conf.get(X509Util.TLS_CONFIG_KEYSTORE_TYPE);
+ conf.set(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_LOCATION, location);
+ conf.set(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_PASSWORD, password);
+ conf.set(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_TYPE, type);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_LOCATION);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD);
+ conf.unset(X509Util.TLS_CONFIG_KEYSTORE_TYPE);
+
+ SslContext sslContext = X509Util.createSslContextForClient(conf);
+ ByteBufAllocator byteBufAllocatorMock = mock(ByteBufAllocator.class);
+ // Handshake would fail if the key manager weren't wired; smoke-test that
engine creation works.
+ assertTrue(
+
sslContext.newEngine(byteBufAllocatorMock).getSSLParameters().getProtocols().length
> 0);
+ }
+
+ @TestTemplate
+ public void testCreateSSLContextForClientFallsBackToLegacyKeystore() throws
Exception {
+ // The base setUp() only sets the legacy TLS_CONFIG_KEYSTORE_* /
TLS_CONFIG_TRUSTSTORE_* keys.
+ // The role-scoped keys are intentionally unset; the context must still
build using the legacy
+ // values (backward-compat regression guard).
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_LOCATION);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_PASSWORD);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_KEYSTORE_TYPE);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_TRUSTSTORE_LOCATION);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_TRUSTSTORE_PASSWORD);
+ conf.unset(X509Util.TLS_CONFIG_CLIENT_TRUSTSTORE_TYPE);
Review Comment:
Isn't this a no-op?
Apart from that this test is the same as
`testCreateSSLContextWithoutCustomProtocol` but that also validates the TLS
protocols, not just if it is not empty.
##########
hbase-http/src/main/java/org/apache/hadoop/hbase/http/InfoServer.java:
##########
@@ -83,6 +94,16 @@ public InfoServer(String name, String bindAddress, int port,
boolean findPort,
.setExcludeProtocols(getTLSProperty(c, "exclude.protocols"))
.setIncludeCiphers(getTLSProperty(c, "include.cipher.list"))
.setExcludeCiphers(getTLSProperty(c, "exclude.cipher.list"));
+
+ // Activate mutual TLS if configured. Default is NONE, which preserves
today's behavior of
+ // never requesting a client certificate on the UI connector (leaving
any configured
+ // truststore inert for peer verification). Set
hbase.ui.ssl.server.client.auth.mode to
+ // WANT or NEED to opt in. The client.auth.mode key is looked up
directly on
+ // HBASE_WEB_TLS_SERVER_CONFIG_PREFIX; there is no legacy or
Hadoop-prefixed fallback.
+ X509Util.ClientAuth clientAuth = X509Util.ClientAuth
+ .fromPropertyValue(c.get(HBASE_UI_SSL_CLIENT_AUTH_MODE,
X509Util.ClientAuth.NONE.name()));
Review Comment:
It might be an edge case but when the config is in `hbase-site.xml` with
empty value then it will use `NEED` not `NONE` when the config is not there at
all.
```
/**
* Converts a property value to a ClientAuth enum. If the input string
is empty or null, returns
* <code>ClientAuth.NEED</code>.
* @param prop the property string.
* @return the ClientAuth.
* @throws IllegalArgumentException if the property value is not "NONE",
"WANT", "NEED", or
* empty/null.
*/
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]