001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.io.crypto.tls; 019 020import java.io.IOException; 021import java.nio.file.Path; 022import java.nio.file.Paths; 023import java.security.GeneralSecurityException; 024import java.security.KeyStore; 025import java.security.Security; 026import java.security.cert.PKIXBuilderParameters; 027import java.security.cert.X509CertSelector; 028import java.time.Duration; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.List; 032import java.util.Objects; 033import java.util.Set; 034import java.util.concurrent.atomic.AtomicReference; 035import javax.net.ssl.CertPathTrustManagerParameters; 036import javax.net.ssl.KeyManager; 037import javax.net.ssl.KeyManagerFactory; 038import javax.net.ssl.TrustManager; 039import javax.net.ssl.TrustManagerFactory; 040import javax.net.ssl.X509ExtendedTrustManager; 041import javax.net.ssl.X509KeyManager; 042import javax.net.ssl.X509TrustManager; 043import org.apache.hadoop.conf.Configuration; 044import org.apache.hadoop.hbase.exceptions.KeyManagerException; 045import org.apache.hadoop.hbase.exceptions.SSLContextException; 046import org.apache.hadoop.hbase.exceptions.TrustManagerException; 047import org.apache.hadoop.hbase.exceptions.X509Exception; 048import org.apache.hadoop.hbase.io.FileChangeWatcher; 049import org.apache.yetus.audience.InterfaceAudience; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052 053import org.apache.hbase.thirdparty.com.google.common.collect.ObjectArrays; 054import org.apache.hbase.thirdparty.io.netty.handler.ssl.OpenSsl; 055import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslContext; 056import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslContextBuilder; 057import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslProvider; 058 059/** 060 * Utility code for X509 handling Default cipher suites: Performance testing done by Facebook 061 * engineers shows that on Intel x86_64 machines, Java9 performs better with GCM and Java8 performs 062 * better with CBC, so these seem like reasonable defaults. 063 * <p/> 064 * This file has been copied from the Apache ZooKeeper project. 065 * @see <a href= 066 * "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java">Base 067 * revision</a> 068 */ 069@InterfaceAudience.Private 070public final class X509Util { 071 072 private static final Logger LOG = LoggerFactory.getLogger(X509Util.class); 073 private static final char[] EMPTY_CHAR_ARRAY = new char[0]; 074 075 // 076 // Common tls configs across both server and client 077 // 078 static final String CONFIG_PREFIX = "hbase.rpc.tls."; 079 public static final String TLS_CONFIG_PROTOCOL = CONFIG_PREFIX + "protocol"; 080 public static final String TLS_CONFIG_KEYSTORE_LOCATION = CONFIG_PREFIX + "keystore.location"; 081 public static final String TLS_CONFIG_KEYSTORE_TYPE = CONFIG_PREFIX + "keystore.type"; 082 public static final String TLS_CONFIG_KEYSTORE_PASSWORD = CONFIG_PREFIX + "keystore.password"; 083 public static final String TLS_CONFIG_TRUSTSTORE_LOCATION = CONFIG_PREFIX + "truststore.location"; 084 public static final String TLS_CONFIG_TRUSTSTORE_TYPE = CONFIG_PREFIX + "truststore.type"; 085 public static final String TLS_CONFIG_TRUSTSTORE_PASSWORD = CONFIG_PREFIX + "truststore.password"; 086 public static final String TLS_CONFIG_CLR = CONFIG_PREFIX + "clr"; 087 public static final String TLS_CONFIG_OCSP = CONFIG_PREFIX + "ocsp"; 088 public static final String TLS_CONFIG_REVERSE_DNS_LOOKUP_ENABLED = 089 CONFIG_PREFIX + "host-verification.reverse-dns.enabled"; 090 public static final String TLS_ENABLED_PROTOCOLS = CONFIG_PREFIX + "enabledProtocols"; 091 public static final String TLS_CIPHER_SUITES = CONFIG_PREFIX + "ciphersuites"; 092 public static final String TLS_CERT_RELOAD = CONFIG_PREFIX + "certReload"; 093 public static final String TLS_USE_OPENSSL = CONFIG_PREFIX + "useOpenSsl"; 094 public static final String DEFAULT_PROTOCOL = "TLSv1.2"; 095 096 // 097 // Server-side specific configs 098 // 099 public static final String HBASE_SERVER_NETTY_TLS_ENABLED = "hbase.server.netty.tls.enabled"; 100 public static final String HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE = 101 "hbase.server.netty.tls.client.auth.mode"; 102 public static final String HBASE_SERVER_NETTY_TLS_VERIFY_CLIENT_HOSTNAME = 103 "hbase.server.netty.tls.verify.client.hostname"; 104 public static final String HBASE_SERVER_NETTY_TLS_SUPPORTPLAINTEXT = 105 "hbase.server.netty.tls.supportplaintext"; 106 107 /** 108 * Set the SSL wrapSize for netty. This is only a maximum wrap size. Buffers smaller than this 109 * will not be consolidated, but buffers larger than this will be split into multiple wrap 110 * buffers. The netty default of 16k is not great for hbase which tends to return larger payloads 111 * than that, meaning most responses end up getting chunked up. This leads to more memory 112 * contention in netty's PoolArena. See https://github.com/netty/netty/pull/13551 113 */ 114 public static final String HBASE_SERVER_NETTY_TLS_WRAP_SIZE = "hbase.server.netty.tls.wrapSize"; 115 public static final int DEFAULT_HBASE_SERVER_NETTY_TLS_WRAP_SIZE = 1024 * 1024; 116 // 117 // Client-side specific configs 118 // 119 public static final String HBASE_CLIENT_NETTY_TLS_ENABLED = "hbase.client.netty.tls.enabled"; 120 public static final String HBASE_CLIENT_NETTY_TLS_VERIFY_SERVER_HOSTNAME = 121 "hbase.client.netty.tls.verify.server.hostname"; 122 public static final String HBASE_CLIENT_NETTY_TLS_HANDSHAKETIMEOUT = 123 "hbase.client.netty.tls.handshaketimeout"; 124 public static final int DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS = 5000; 125 126 private static String[] getTls13Ciphers() { 127 return new String[] { "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" }; 128 } 129 130 private static String[] getGCMCiphers() { 131 return new String[] { "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 132 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 133 "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" }; 134 } 135 136 private static String[] getCBCCiphers() { 137 return new String[] { "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 138 "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 139 "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 140 "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", 141 "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" }; 142 } 143 144 // On Java 8, prefer CBC ciphers since AES-NI support is lacking and GCM is slower than CBC. 145 private static final String[] DEFAULT_CIPHERS_JAVA8 = 146 ObjectArrays.concat(getCBCCiphers(), getGCMCiphers(), String.class); 147 // On Java 9 and later, prefer GCM ciphers due to improved AES-NI support. 148 // Note that this performance assumption might not hold true for architectures other than x86_64. 149 private static final String[] DEFAULT_CIPHERS_JAVA9 = 150 ObjectArrays.concat(getGCMCiphers(), getCBCCiphers(), String.class); 151 private static final String[] DEFAULT_CIPHERS_JAVA11 = 152 ObjectArrays.concat(ObjectArrays.concat(getTls13Ciphers(), getGCMCiphers(), String.class), 153 getCBCCiphers(), String.class); 154 155 private static final String[] DEFAULT_CIPHERS_OPENSSL = getOpenSslFilteredDefaultCiphers(); 156 157 public static final String HBASE_TLS_FILEPOLL_INTERVAL_MILLIS = 158 CONFIG_PREFIX + "filepoll.interval.millis"; 159 // 1 minute 160 private static final long DEFAULT_FILE_POLL_INTERVAL = Duration.ofSeconds(60).toMillis(); 161 162 /** 163 * Not all of our default ciphers are available in OpenSSL. Takes our default cipher lists and 164 * filters them to only those available in OpenSsl. Prefers TLS 1.3, then GCM, then CBC because 165 * GCM tends to be better and faster, and we don't need to worry about the java8 vs 9 performance 166 * issue if OpenSSL is handling it. 167 */ 168 private static String[] getOpenSslFilteredDefaultCiphers() { 169 if (!OpenSsl.isAvailable()) { 170 return new String[0]; 171 } 172 173 Set<String> openSslSuites = OpenSsl.availableJavaCipherSuites(); 174 List<String> defaultSuites = new ArrayList<>(); 175 Arrays.stream(getTls13Ciphers()).filter(openSslSuites::contains).forEach(defaultSuites::add); 176 Arrays.stream(getGCMCiphers()).filter(openSslSuites::contains).forEach(defaultSuites::add); 177 Arrays.stream(getCBCCiphers()).filter(openSslSuites::contains).forEach(defaultSuites::add); 178 return defaultSuites.toArray(new String[0]); 179 } 180 181 /** 182 * Enum specifying the client auth requirement of server-side TLS sockets created by this 183 * X509Util. 184 * <ul> 185 * <li>NONE - do not request a client certificate.</li> 186 * <li>WANT - request a client certificate, but allow anonymous clients to connect.</li> 187 * <li>NEED - require a client certificate, disconnect anonymous clients.</li> 188 * </ul> 189 * If the config property is not set, the default value is NEED. 190 */ 191 public enum ClientAuth { 192 NONE(org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth.NONE), 193 WANT(org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth.OPTIONAL), 194 NEED(org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth.REQUIRE); 195 196 private final org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth nettyAuth; 197 198 ClientAuth(org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth nettyAuth) { 199 this.nettyAuth = nettyAuth; 200 } 201 202 /** 203 * Converts a property value to a ClientAuth enum. If the input string is empty or null, returns 204 * <code>ClientAuth.NEED</code>. 205 * @param prop the property string. 206 * @return the ClientAuth. 207 * @throws IllegalArgumentException if the property value is not "NONE", "WANT", "NEED", or 208 * empty/null. 209 */ 210 public static ClientAuth fromPropertyValue(String prop) { 211 if (prop == null || prop.length() == 0) { 212 return NEED; 213 } 214 return ClientAuth.valueOf(prop.toUpperCase()); 215 } 216 217 public org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth toNettyClientAuth() { 218 return nettyAuth; 219 } 220 } 221 222 private X509Util() { 223 // disabled 224 } 225 226 static String[] getDefaultCipherSuites(boolean useOpenSsl) { 227 if (useOpenSsl) { 228 return DEFAULT_CIPHERS_OPENSSL; 229 } 230 return getDefaultCipherSuitesForJavaVersion(System.getProperty("java.specification.version")); 231 } 232 233 static String[] getDefaultCipherSuitesForJavaVersion(String javaVersion) { 234 Objects.requireNonNull(javaVersion); 235 236 if (javaVersion.matches("\\d+")) { 237 // Must be Java 9 or later 238 int javaVersionInt = Integer.parseInt(javaVersion); 239 if (javaVersionInt >= 11) { 240 LOG.debug( 241 "Using Java11+ optimized cipher suites for Java version {}, including TLSv1.3 support", 242 javaVersion); 243 return DEFAULT_CIPHERS_JAVA11; 244 } else { 245 LOG.debug("Using Java9+ optimized cipher suites for Java version {}", javaVersion); 246 return DEFAULT_CIPHERS_JAVA9; 247 } 248 } else if (javaVersion.startsWith("1.")) { 249 // Must be Java 1.8 or earlier 250 LOG.debug("Using Java8 optimized cipher suites for Java version {}", javaVersion); 251 return DEFAULT_CIPHERS_JAVA8; 252 } else { 253 LOG.debug("Could not parse java version {}, using Java8 optimized cipher suites", 254 javaVersion); 255 return DEFAULT_CIPHERS_JAVA8; 256 } 257 } 258 259 public static SslContext createSslContextForClient(Configuration config) 260 throws X509Exception, IOException { 261 262 SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); 263 264 boolean useOpenSsl = configureOpenSslIfAvailable(sslContextBuilder, config); 265 String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, ""); 266 char[] keyStorePassword = config.getPassword(TLS_CONFIG_KEYSTORE_PASSWORD); 267 String keyStoreType = config.get(TLS_CONFIG_KEYSTORE_TYPE, ""); 268 269 if (keyStoreLocation.isEmpty()) { 270 LOG.warn(TLS_CONFIG_KEYSTORE_LOCATION + " not specified"); 271 } else { 272 sslContextBuilder 273 .keyManager(createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType)); 274 } 275 276 String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, ""); 277 char[] trustStorePassword = config.getPassword(TLS_CONFIG_TRUSTSTORE_PASSWORD); 278 String trustStoreType = config.get(TLS_CONFIG_TRUSTSTORE_TYPE, ""); 279 280 boolean sslCrlEnabled = config.getBoolean(TLS_CONFIG_CLR, false); 281 boolean sslOcspEnabled = config.getBoolean(TLS_CONFIG_OCSP, false); 282 283 boolean verifyServerHostname = 284 config.getBoolean(HBASE_CLIENT_NETTY_TLS_VERIFY_SERVER_HOSTNAME, true); 285 boolean allowReverseDnsLookup = config.getBoolean(TLS_CONFIG_REVERSE_DNS_LOOKUP_ENABLED, true); 286 287 if (trustStoreLocation.isEmpty()) { 288 LOG.warn(TLS_CONFIG_TRUSTSTORE_LOCATION + " not specified"); 289 } else { 290 sslContextBuilder 291 .trustManager(createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, 292 sslCrlEnabled, sslOcspEnabled, verifyServerHostname, allowReverseDnsLookup)); 293 } 294 295 sslContextBuilder.enableOcsp(sslOcspEnabled); 296 sslContextBuilder.protocols(getEnabledProtocols(config)); 297 sslContextBuilder.ciphers(Arrays.asList(getCipherSuites(config, useOpenSsl))); 298 299 return sslContextBuilder.build(); 300 } 301 302 /** 303 * Adds SslProvider.OPENSSL if OpenSsl is available and enabled. In order to make it available, 304 * one must ensure that a properly shaded netty-tcnative is on the classpath. Properly shaded 305 * means relocated to be prefixed with "org.apache.hbase.thirdparty" like the rest of the netty 306 * classes. We make available org.apache.hbase:hbase-openssl as a convenience module which one can 307 * use to pull in a shaded netty-tcnative statically linked against boringssl. 308 */ 309 private static boolean configureOpenSslIfAvailable(SslContextBuilder sslContextBuilder, 310 Configuration conf) { 311 if (OpenSsl.isAvailable() && conf.getBoolean(TLS_USE_OPENSSL, true)) { 312 LOG.debug("Using netty-tcnative to accelerate SSL handling"); 313 sslContextBuilder.sslProvider(SslProvider.OPENSSL); 314 return true; 315 } else { 316 if (LOG.isDebugEnabled()) { 317 LOG.debug("Using default JDK SSL provider because netty-tcnative is not {}", 318 OpenSsl.isAvailable() ? "enabled" : "available"); 319 } 320 sslContextBuilder.sslProvider(SslProvider.JDK); 321 return false; 322 } 323 } 324 325 public static SslContext createSslContextForServer(Configuration config) 326 throws X509Exception, IOException { 327 String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, ""); 328 char[] keyStorePassword = config.getPassword(TLS_CONFIG_KEYSTORE_PASSWORD); 329 String keyStoreType = config.get(TLS_CONFIG_KEYSTORE_TYPE, ""); 330 331 if (keyStoreLocation.isEmpty()) { 332 throw new SSLContextException( 333 "Keystore is required for SSL server: " + TLS_CONFIG_KEYSTORE_LOCATION); 334 } 335 336 SslContextBuilder sslContextBuilder; 337 sslContextBuilder = SslContextBuilder 338 .forServer(createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType)); 339 340 boolean useOpenSsl = configureOpenSslIfAvailable(sslContextBuilder, config); 341 String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, ""); 342 char[] trustStorePassword = config.getPassword(TLS_CONFIG_TRUSTSTORE_PASSWORD); 343 String trustStoreType = config.get(TLS_CONFIG_TRUSTSTORE_TYPE, ""); 344 345 boolean sslCrlEnabled = config.getBoolean(TLS_CONFIG_CLR, false); 346 boolean sslOcspEnabled = config.getBoolean(TLS_CONFIG_OCSP, false); 347 348 ClientAuth clientAuth = 349 ClientAuth.fromPropertyValue(config.get(HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE)); 350 boolean verifyClientHostname = 351 config.getBoolean(HBASE_SERVER_NETTY_TLS_VERIFY_CLIENT_HOSTNAME, true); 352 boolean allowReverseDnsLookup = config.getBoolean(TLS_CONFIG_REVERSE_DNS_LOOKUP_ENABLED, true); 353 354 if (trustStoreLocation.isEmpty()) { 355 LOG.warn(TLS_CONFIG_TRUSTSTORE_LOCATION + " not specified"); 356 } else { 357 sslContextBuilder 358 .trustManager(createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, 359 sslCrlEnabled, sslOcspEnabled, verifyClientHostname, allowReverseDnsLookup)); 360 } 361 362 sslContextBuilder.enableOcsp(sslOcspEnabled); 363 sslContextBuilder.protocols(getEnabledProtocols(config)); 364 sslContextBuilder.ciphers(Arrays.asList(getCipherSuites(config, useOpenSsl))); 365 sslContextBuilder.clientAuth(clientAuth.toNettyClientAuth()); 366 367 return sslContextBuilder.build(); 368 } 369 370 /** 371 * Creates a key manager by loading the key store from the given file of the given type, 372 * optionally decrypting it using the given password. 373 * @param keyStoreLocation the location of the key store file. 374 * @param keyStorePassword optional password to decrypt the key store. If empty, assumes the key 375 * store is not encrypted. 376 * @param keyStoreType must be JKS, PEM, PKCS12, BCFKS or null. If null, attempts to 377 * autodetect the key store type from the file extension (e.g. .jks / 378 * .pem). 379 * @return the key manager. 380 * @throws KeyManagerException if something goes wrong. 381 */ 382 static X509KeyManager createKeyManager(String keyStoreLocation, char[] keyStorePassword, 383 String keyStoreType) throws KeyManagerException { 384 385 if (keyStorePassword == null) { 386 keyStorePassword = EMPTY_CHAR_ARRAY; 387 } 388 389 try { 390 KeyStoreFileType storeFileType = 391 KeyStoreFileType.fromPropertyValueOrFileName(keyStoreType, keyStoreLocation); 392 KeyStore ks = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(storeFileType) 393 .setKeyStorePath(keyStoreLocation).setKeyStorePassword(keyStorePassword).build() 394 .loadKeyStore(); 395 396 KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); 397 kmf.init(ks, keyStorePassword); 398 399 for (KeyManager km : kmf.getKeyManagers()) { 400 if (km instanceof X509KeyManager) { 401 return (X509KeyManager) km; 402 } 403 } 404 throw new KeyManagerException("Couldn't find X509KeyManager"); 405 } catch (IOException | GeneralSecurityException | IllegalArgumentException e) { 406 throw new KeyManagerException(e); 407 } 408 } 409 410 /** 411 * Creates a trust manager by loading the trust store from the given file of the given type, 412 * optionally decrypting it using the given password. 413 * @param trustStoreLocation the location of the trust store file. 414 * @param trustStorePassword optional password to decrypt the trust store (only applies to JKS 415 * trust stores). If empty, assumes the trust store is not encrypted. 416 * @param trustStoreType must be JKS, PEM, PKCS12, BCFKS or null. If null, attempts to 417 * autodetect the trust store type from the file extension (e.g. .jks 418 * / .pem). 419 * @param crlEnabled enable CRL (certificate revocation list) checks. 420 * @param ocspEnabled enable OCSP (online certificate status protocol) checks. 421 * @param verifyHostName if true, ssl peer hostname must match name in certificate 422 * @param allowReverseDnsLookup if true, allow falling back to reverse dns lookup in verifying 423 * hostname 424 * @return the trust manager. 425 * @throws TrustManagerException if something goes wrong. 426 */ 427 static X509TrustManager createTrustManager(String trustStoreLocation, char[] trustStorePassword, 428 String trustStoreType, boolean crlEnabled, boolean ocspEnabled, boolean verifyHostName, 429 boolean allowReverseDnsLookup) throws TrustManagerException { 430 431 if (trustStorePassword == null) { 432 trustStorePassword = EMPTY_CHAR_ARRAY; 433 } 434 435 try { 436 KeyStoreFileType storeFileType = 437 KeyStoreFileType.fromPropertyValueOrFileName(trustStoreType, trustStoreLocation); 438 KeyStore ts = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(storeFileType) 439 .setTrustStorePath(trustStoreLocation).setTrustStorePassword(trustStorePassword).build() 440 .loadTrustStore(); 441 442 PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); 443 if (crlEnabled || ocspEnabled) { 444 pbParams.setRevocationEnabled(true); 445 System.setProperty("com.sun.net.ssl.checkRevocation", "true"); 446 if (crlEnabled) { 447 System.setProperty("com.sun.security.enableCRLDP", "true"); 448 } 449 if (ocspEnabled) { 450 Security.setProperty("ocsp.enable", "true"); 451 } 452 } else { 453 pbParams.setRevocationEnabled(false); 454 } 455 456 // Revocation checking is only supported with the PKIX algorithm 457 TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); 458 tmf.init(new CertPathTrustManagerParameters(pbParams)); 459 460 for (final TrustManager tm : tmf.getTrustManagers()) { 461 if (tm instanceof X509ExtendedTrustManager) { 462 return new HBaseTrustManager((X509ExtendedTrustManager) tm, verifyHostName, 463 allowReverseDnsLookup); 464 } 465 } 466 throw new TrustManagerException("Couldn't find X509TrustManager"); 467 } catch (IOException | GeneralSecurityException | IllegalArgumentException e) { 468 throw new TrustManagerException(e); 469 } 470 } 471 472 private static String[] getEnabledProtocols(Configuration config) { 473 String enabledProtocolsInput = config.get(TLS_ENABLED_PROTOCOLS); 474 if (enabledProtocolsInput == null) { 475 return new String[] { config.get(TLS_CONFIG_PROTOCOL, DEFAULT_PROTOCOL) }; 476 } 477 return enabledProtocolsInput.split(","); 478 } 479 480 private static String[] getCipherSuites(Configuration config, boolean useOpenSsl) { 481 String cipherSuitesInput = config.get(TLS_CIPHER_SUITES); 482 if (cipherSuitesInput == null) { 483 return getDefaultCipherSuites(useOpenSsl); 484 } else { 485 return cipherSuitesInput.split(","); 486 } 487 } 488 489 /** 490 * Enable certificate file reloading by creating FileWatchers for keystore and truststore. 491 * AtomicReferences will be set with the new instances. resetContext - if not null - will be 492 * called when the file has been modified. 493 * @param keystoreWatcher Reference to keystoreFileWatcher. 494 * @param trustStoreWatcher Reference to truststoreFileWatcher. 495 * @param resetContext Callback for file changes. 496 */ 497 public static void enableCertFileReloading(Configuration config, 498 AtomicReference<FileChangeWatcher> keystoreWatcher, 499 AtomicReference<FileChangeWatcher> trustStoreWatcher, Runnable resetContext) 500 throws IOException { 501 String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, ""); 502 keystoreWatcher.set(newFileChangeWatcher(config, keyStoreLocation, resetContext)); 503 String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, ""); 504 // we are using the same callback for both. there's no reason to kick off two 505 // threads if keystore/truststore are both at the same location 506 if (!keyStoreLocation.equals(trustStoreLocation)) { 507 trustStoreWatcher.set(newFileChangeWatcher(config, trustStoreLocation, resetContext)); 508 } 509 } 510 511 private static FileChangeWatcher newFileChangeWatcher(Configuration config, String fileLocation, 512 Runnable resetContext) throws IOException { 513 if (fileLocation == null || fileLocation.isEmpty() || resetContext == null) { 514 return null; 515 } 516 final Path filePath = Paths.get(fileLocation).toAbsolutePath(); 517 FileChangeWatcher fileChangeWatcher = 518 new FileChangeWatcher(filePath, Objects.toString(filePath.getFileName()), 519 Duration 520 .ofMillis(config.getLong(HBASE_TLS_FILEPOLL_INTERVAL_MILLIS, DEFAULT_FILE_POLL_INTERVAL)), 521 watchEventFilePath -> handleWatchEvent(watchEventFilePath, resetContext)); 522 fileChangeWatcher.start(); 523 return fileChangeWatcher; 524 } 525 526 /** 527 * Handler for watch events that let us know a file we may care about has changed on disk. 528 */ 529 private static void handleWatchEvent(Path filePath, Runnable resetContext) { 530 LOG.info("Attempting to reset default SSL context after receiving watch event on file {}", 531 filePath); 532 resetContext.run(); 533 } 534}