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

HBASE-27342 Use Hadoop Credentials API to retrieve passwords of TLS key/trust stores (#4747) #4751

Merged
merged 1 commit into from
Sep 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions bin/hbase
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ show_usage() {
echo " cellcounter Run CellCounter tool"
echo " pre-upgrade Run Pre-Upgrade validator tool"
echo " hbtop Run HBTop tool"
echo " credential Run the Hadoop Credential Shell"
echo " CLASSNAME Run the class named CLASSNAME"
}

Expand Down Expand Up @@ -734,6 +735,8 @@ elif [ "$COMMAND" = "hbtop" ] ; then
HBASE_HBTOP_OPTS="${HBASE_HBTOP_OPTS} -Dlog4j2.configurationFile=file:${HBASE_HOME}/conf/log4j2-hbtop.properties"
fi
HBASE_OPTS="${HBASE_OPTS} ${HBASE_HBTOP_OPTS}"
elif [ "$COMMAND" = "credential" ] ; then
CLASS='org.apache.hadoop.security.alias.CredentialShell'
else
CLASS=$COMMAND
if [[ "$CLASS" =~ .*IntegrationTest.* ]] ; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.io.IOException;
import java.net.SocketAddress;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseInterfaceAudience;
import org.apache.hadoop.hbase.HConstants;
Expand Down Expand Up @@ -89,7 +88,7 @@ protected void closeInternal() {
}
}

SslContext getSslContext() throws X509Exception, SSLException {
SslContext getSslContext() throws X509Exception, IOException {
SslContext result = sslContextForClient.get();
if (result == null) {
result = X509Util.createSslContextForClient(conf);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/
package org.apache.hadoop.hbase.io.crypto.tls;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -32,7 +31,6 @@
import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
Expand Down Expand Up @@ -65,6 +63,7 @@
public final class X509Util {

private static final Logger LOG = LoggerFactory.getLogger(X509Util.class);
private static final char[] EMPTY_CHAR_ARRAY = new char[0];

// Config
static final String CONFIG_PREFIX = "hbase.rpc.tls.";
Expand Down Expand Up @@ -140,12 +139,12 @@ static String[] getDefaultCipherSuitesForJavaVersion(String javaVersion) {
}

public static SslContext createSslContextForClient(Configuration config)
throws X509Exception, SSLException {
throws X509Exception, IOException {

SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();

String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, "");
String keyStorePassword = config.get(TLS_CONFIG_KEYSTORE_PASSWORD, "");
char[] keyStorePassword = config.getPassword(TLS_CONFIG_KEYSTORE_PASSWORD);
String keyStoreType = config.get(TLS_CONFIG_KEYSTORE_TYPE, "");

if (keyStoreLocation.isEmpty()) {
Expand All @@ -156,7 +155,7 @@ public static SslContext createSslContextForClient(Configuration config)
}

String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, "");
String trustStorePassword = config.get(TLS_CONFIG_TRUSTSTORE_PASSWORD, "");
char[] trustStorePassword = config.getPassword(TLS_CONFIG_TRUSTSTORE_PASSWORD);
String trustStoreType = config.get(TLS_CONFIG_TRUSTSTORE_TYPE, "");

boolean sslCrlEnabled = config.getBoolean(TLS_CONFIG_CLR, false);
Expand All @@ -177,9 +176,9 @@ public static SslContext createSslContextForClient(Configuration config)
}

public static SslContext createSslContextForServer(Configuration config)
throws X509Exception, SSLException {
throws X509Exception, IOException {
String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, "");
String keyStorePassword = config.get(TLS_CONFIG_KEYSTORE_PASSWORD, "");
char[] keyStorePassword = config.getPassword(TLS_CONFIG_KEYSTORE_PASSWORD);
String keyStoreType = config.get(TLS_CONFIG_KEYSTORE_TYPE, "");

if (keyStoreLocation.isEmpty()) {
Expand All @@ -193,7 +192,7 @@ public static SslContext createSslContextForServer(Configuration config)
.forServer(createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType));

String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, "");
String trustStorePassword = config.get(TLS_CONFIG_TRUSTSTORE_PASSWORD, "");
char[] trustStorePassword = config.getPassword(TLS_CONFIG_TRUSTSTORE_PASSWORD);
String trustStoreType = config.get(TLS_CONFIG_TRUSTSTORE_TYPE, "");

boolean sslCrlEnabled = config.getBoolean(TLS_CONFIG_CLR, false);
Expand Down Expand Up @@ -225,27 +224,25 @@ public static SslContext createSslContextForServer(Configuration config)
* @return the key manager.
* @throws KeyManagerException if something goes wrong.
*/
static X509KeyManager createKeyManager(String keyStoreLocation, String keyStorePassword,
static X509KeyManager createKeyManager(String keyStoreLocation, char[] keyStorePassword,
String keyStoreType) throws KeyManagerException {

if (keyStorePassword == null) {
keyStorePassword = "";
}

if (keyStoreType == null) {
keyStoreType = "jks";
}

if (keyStorePassword == null) {
keyStorePassword = EMPTY_CHAR_ARRAY;
}

try {
char[] password = keyStorePassword.toCharArray();
KeyStore ks = KeyStore.getInstance(keyStoreType);
try (InputStream inputStream =
new BufferedInputStream(Files.newInputStream(new File(keyStoreLocation).toPath()))) {
ks.load(inputStream, password);
try (InputStream inputStream = Files.newInputStream(new File(keyStoreLocation).toPath())) {
ks.load(inputStream, keyStorePassword);
}

KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ks, password);
kmf.init(ks, keyStorePassword);

for (KeyManager km : kmf.getKeyManagers()) {
if (km instanceof X509KeyManager) {
Expand All @@ -272,23 +269,21 @@ static X509KeyManager createKeyManager(String keyStoreLocation, String keyStoreP
* @return the trust manager.
* @throws TrustManagerException if something goes wrong.
*/
static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword,
static X509TrustManager createTrustManager(String trustStoreLocation, char[] trustStorePassword,
String trustStoreType, boolean crlEnabled, boolean ocspEnabled) throws TrustManagerException {

if (trustStorePassword == null) {
trustStorePassword = "";
}

if (trustStoreType == null) {
trustStoreType = "jks";
}

if (trustStorePassword == null) {
trustStorePassword = EMPTY_CHAR_ARRAY;
}

try {
char[] password = trustStorePassword.toCharArray();
KeyStore ts = KeyStore.getInstance(trustStoreType);
try (InputStream inputStream =
new BufferedInputStream(Files.newInputStream(new File(trustStoreLocation).toPath()))) {
ts.load(inputStream, password);
try (InputStream inputStream = Files.newInputStream(new File(trustStoreLocation).toPath())) {
ts.load(inputStream, trustStorePassword);
}

PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.junit.Assume.assumeThat;
import static org.mockito.Mockito.mock;

import java.io.File;
Expand All @@ -42,7 +43,6 @@
import org.apache.hadoop.hbase.exceptions.KeyManagerException;
import org.apache.hadoop.hbase.exceptions.SSLContextException;
import org.apache.hadoop.hbase.exceptions.TrustManagerException;
import org.apache.hadoop.hbase.exceptions.X509Exception;
import org.apache.hadoop.hbase.testclassification.MiscTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
Expand Down Expand Up @@ -74,6 +74,7 @@ public class TestX509Util {
HBaseClassTestRule.forClass(TestX509Util.class);

private static final HBaseCommonTestingUtility UTIL = new HBaseCommonTestingUtility();
private static final char[] EMPTY_CHAR_ARRAY = new char[0];

private static X509TestContextProvider PROVIDER;

Expand All @@ -84,7 +85,7 @@ public class TestX509Util {
public X509KeyType certKeyType;

@Parameterized.Parameter(value = 2)
public String keyPassword;
public char[] keyPassword;

@Parameterized.Parameter(value = 3)
public Integer paramIndex;
Expand All @@ -100,7 +101,7 @@ public static Collection<Object[]> data() {
int paramIndex = 0;
for (X509KeyType caKeyType : X509KeyType.values()) {
for (X509KeyType certKeyType : X509KeyType.values()) {
for (String keyPassword : new String[] { "", "pa$$w0rd" }) {
for (char[] keyPassword : new char[][] { "".toCharArray(), "pa$$w0rd".toCharArray() }) {
params.add(new Object[] { caKeyType, certKeyType, keyPassword, paramIndex++ });
}
}
Expand Down Expand Up @@ -172,13 +173,6 @@ public void testCreateSSLContextWithoutKeyStoreLocationClient() throws Exception
X509Util.createSslContextForClient(conf);
}

@Test(expected = X509Exception.class)
public void testCreateSSLContextWithoutKeyStorePassword() throws Exception {
assumeTrue(x509TestContext.isKeyStoreEncrypted());
conf.unset(X509Util.TLS_CONFIG_KEYSTORE_PASSWORD);
X509Util.createSslContextForServer(conf);
}

@Test
public void testCreateSSLContextWithoutTrustStoreLocationClient() throws Exception {
conf.unset(X509Util.TLS_CONFIG_TRUSTSTORE_LOCATION);
Expand Down Expand Up @@ -220,7 +214,7 @@ public void testLoadJKSKeyStore() throws Exception {

@Test
public void testLoadJKSKeyStoreNullPassword() throws Exception {
assumeTrue(x509TestContext.getKeyStorePassword().isEmpty());
assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
// Make sure that empty password and null password are treated the same
X509Util.createKeyManager(
x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null,
Expand All @@ -237,12 +231,12 @@ public void testLoadJKSKeyStoreFileTypeDefaultToJks() throws Exception {
}

@Test
public void testLoadJKSKeyStoreWithWrongPassword() throws Exception {
public void testLoadJKSKeyStoreWithWrongPassword() {
assertThrows(KeyManagerException.class, () -> {
// Attempting to load with the wrong key password should fail
X509Util.createKeyManager(
x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), "wrong password",
KeyStoreFileType.JKS.getPropertyValue());
x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
"wrong password".toCharArray(), KeyStoreFileType.JKS.getPropertyValue());
});
}

Expand All @@ -256,9 +250,7 @@ public void testLoadJKSTrustStore() throws Exception {

@Test
public void testLoadJKSTrustStoreNullPassword() throws Exception {
if (!x509TestContext.getTrustStorePassword().isEmpty()) {
return;
}
assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
// Make sure that empty password and null password are treated the same
X509Util.createTrustManager(
x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null,
Expand All @@ -279,8 +271,8 @@ public void testLoadJKSTrustStoreWithWrongPassword() throws Exception {
assertThrows(TrustManagerException.class, () -> {
// Attempting to load with the wrong key password should fail
X509Util.createTrustManager(
x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), "wrong password",
KeyStoreFileType.JKS.getPropertyValue(), true, true);
x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(),
"wrong password".toCharArray(), KeyStoreFileType.JKS.getPropertyValue(), true, true);
});
}

Expand All @@ -294,9 +286,7 @@ public void testLoadPKCS12KeyStore() throws Exception {

@Test
public void testLoadPKCS12KeyStoreNullPassword() throws Exception {
if (!x509TestContext.getKeyStorePassword().isEmpty()) {
return;
}
assumeThat(x509TestContext.getKeyStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
// Make sure that empty password and null password are treated the same
X509Util.createKeyManager(
x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), null,
Expand All @@ -309,7 +299,7 @@ public void testLoadPKCS12KeyStoreWithWrongPassword() throws Exception {
// Attempting to load with the wrong key password should fail
X509Util.createKeyManager(
x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(),
"wrong password", KeyStoreFileType.PKCS12.getPropertyValue());
"wrong password".toCharArray(), KeyStoreFileType.PKCS12.getPropertyValue());
});
}

Expand All @@ -324,9 +314,7 @@ public void testLoadPKCS12TrustStore() throws Exception {

@Test
public void testLoadPKCS12TrustStoreNullPassword() throws Exception {
if (!x509TestContext.getTrustStorePassword().isEmpty()) {
return;
}
assumeThat(x509TestContext.getTrustStorePassword(), equalTo(EMPTY_CHAR_ARRAY));
// Make sure that empty password and null password are treated the same
X509Util.createTrustManager(
x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), null,
Expand All @@ -339,7 +327,7 @@ public void testLoadPKCS12TrustStoreWithWrongPassword() throws Exception {
// Attempting to load with the wrong key password should fail
X509Util.createTrustManager(
x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(),
"wrong password", KeyStoreFileType.PKCS12.getPropertyValue(), true, true);
"wrong password".toCharArray(), KeyStoreFileType.PKCS12.getPropertyValue(), true, true);
});
}

Expand Down