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 private static final Duration FILE_POLL_INTERVAL = Duration.ofMinutes(1); 158 159 /** 160 * Not all of our default ciphers are available in OpenSSL. Takes our default cipher lists and 161 * filters them to only those available in OpenSsl. Prefers TLS 1.3, then GCM, then CBC because 162 * GCM tends to be better and faster, and we don't need to worry about the java8 vs 9 performance 163 * issue if OpenSSL is handling it. 164 */ 165 private static String[] getOpenSslFilteredDefaultCiphers() { 166 if (!OpenSsl.isAvailable()) { 167 return new String[0]; 168 } 169 170 Set<String> openSslSuites = OpenSsl.availableJavaCipherSuites(); 171 List<String> defaultSuites = new ArrayList<>(); 172 Arrays.stream(getTls13Ciphers()).filter(openSslSuites::contains).forEach(defaultSuites::add); 173 Arrays.stream(getGCMCiphers()).filter(openSslSuites::contains).forEach(defaultSuites::add); 174 Arrays.stream(getCBCCiphers()).filter(openSslSuites::contains).forEach(defaultSuites::add); 175 return defaultSuites.toArray(new String[0]); 176 } 177 178 /** 179 * Enum specifying the client auth requirement of server-side TLS sockets created by this 180 * X509Util. 181 * <ul> 182 * <li>NONE - do not request a client certificate.</li> 183 * <li>WANT - request a client certificate, but allow anonymous clients to connect.</li> 184 * <li>NEED - require a client certificate, disconnect anonymous clients.</li> 185 * </ul> 186 * If the config property is not set, the default value is NEED. 187 */ 188 public enum ClientAuth { 189 NONE(org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth.NONE), 190 WANT(org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth.OPTIONAL), 191 NEED(org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth.REQUIRE); 192 193 private final org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth nettyAuth; 194 195 ClientAuth(org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth nettyAuth) { 196 this.nettyAuth = nettyAuth; 197 } 198 199 /** 200 * Converts a property value to a ClientAuth enum. If the input string is empty or null, returns 201 * <code>ClientAuth.NEED</code>. 202 * @param prop the property string. 203 * @return the ClientAuth. 204 * @throws IllegalArgumentException if the property value is not "NONE", "WANT", "NEED", or 205 * empty/null. 206 */ 207 public static ClientAuth fromPropertyValue(String prop) { 208 if (prop == null || prop.length() == 0) { 209 return NEED; 210 } 211 return ClientAuth.valueOf(prop.toUpperCase()); 212 } 213 214 public org.apache.hbase.thirdparty.io.netty.handler.ssl.ClientAuth toNettyClientAuth() { 215 return nettyAuth; 216 } 217 } 218 219 private X509Util() { 220 // disabled 221 } 222 223 static String[] getDefaultCipherSuites(boolean useOpenSsl) { 224 if (useOpenSsl) { 225 return DEFAULT_CIPHERS_OPENSSL; 226 } 227 return getDefaultCipherSuitesForJavaVersion(System.getProperty("java.specification.version")); 228 } 229 230 static String[] getDefaultCipherSuitesForJavaVersion(String javaVersion) { 231 Objects.requireNonNull(javaVersion); 232 233 if (javaVersion.matches("\\d+")) { 234 // Must be Java 9 or later 235 int javaVersionInt = Integer.parseInt(javaVersion); 236 if (javaVersionInt >= 11) { 237 LOG.debug( 238 "Using Java11+ optimized cipher suites for Java version {}, including TLSv1.3 support", 239 javaVersion); 240 return DEFAULT_CIPHERS_JAVA11; 241 } else { 242 LOG.debug("Using Java9+ optimized cipher suites for Java version {}", javaVersion); 243 return DEFAULT_CIPHERS_JAVA9; 244 } 245 } else if (javaVersion.startsWith("1.")) { 246 // Must be Java 1.8 or earlier 247 LOG.debug("Using Java8 optimized cipher suites for Java version {}", javaVersion); 248 return DEFAULT_CIPHERS_JAVA8; 249 } else { 250 LOG.debug("Could not parse java version {}, using Java8 optimized cipher suites", 251 javaVersion); 252 return DEFAULT_CIPHERS_JAVA8; 253 } 254 } 255 256 public static SslContext createSslContextForClient(Configuration config) 257 throws X509Exception, IOException { 258 259 SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); 260 261 boolean useOpenSsl = configureOpenSslIfAvailable(sslContextBuilder, config); 262 String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, ""); 263 char[] keyStorePassword = config.getPassword(TLS_CONFIG_KEYSTORE_PASSWORD); 264 String keyStoreType = config.get(TLS_CONFIG_KEYSTORE_TYPE, ""); 265 266 if (keyStoreLocation.isEmpty()) { 267 LOG.warn(TLS_CONFIG_KEYSTORE_LOCATION + " not specified"); 268 } else { 269 sslContextBuilder 270 .keyManager(createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType)); 271 } 272 273 String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, ""); 274 char[] trustStorePassword = config.getPassword(TLS_CONFIG_TRUSTSTORE_PASSWORD); 275 String trustStoreType = config.get(TLS_CONFIG_TRUSTSTORE_TYPE, ""); 276 277 boolean sslCrlEnabled = config.getBoolean(TLS_CONFIG_CLR, false); 278 boolean sslOcspEnabled = config.getBoolean(TLS_CONFIG_OCSP, false); 279 280 boolean verifyServerHostname = 281 config.getBoolean(HBASE_CLIENT_NETTY_TLS_VERIFY_SERVER_HOSTNAME, true); 282 boolean allowReverseDnsLookup = config.getBoolean(TLS_CONFIG_REVERSE_DNS_LOOKUP_ENABLED, true); 283 284 if (trustStoreLocation.isEmpty()) { 285 LOG.warn(TLS_CONFIG_TRUSTSTORE_LOCATION + " not specified"); 286 } else { 287 sslContextBuilder 288 .trustManager(createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, 289 sslCrlEnabled, sslOcspEnabled, verifyServerHostname, allowReverseDnsLookup)); 290 } 291 292 sslContextBuilder.enableOcsp(sslOcspEnabled); 293 sslContextBuilder.protocols(getEnabledProtocols(config)); 294 sslContextBuilder.ciphers(Arrays.asList(getCipherSuites(config, useOpenSsl))); 295 296 return sslContextBuilder.build(); 297 } 298 299 /** 300 * Adds SslProvider.OPENSSL if OpenSsl is available and enabled. In order to make it available, 301 * one must ensure that a properly shaded netty-tcnative is on the classpath. Properly shaded 302 * means relocated to be prefixed with "org.apache.hbase.thirdparty" like the rest of the netty 303 * classes. We make available org.apache.hbase:hbase-openssl as a convenience module which one can 304 * use to pull in a shaded netty-tcnative statically linked against boringssl. 305 */ 306 private static boolean configureOpenSslIfAvailable(SslContextBuilder sslContextBuilder, 307 Configuration conf) { 308 if (OpenSsl.isAvailable() && conf.getBoolean(TLS_USE_OPENSSL, true)) { 309 LOG.debug("Using netty-tcnative to accelerate SSL handling"); 310 sslContextBuilder.sslProvider(SslProvider.OPENSSL); 311 return true; 312 } else { 313 if (LOG.isDebugEnabled()) { 314 LOG.debug("Using default JDK SSL provider because netty-tcnative is not {}", 315 OpenSsl.isAvailable() ? "enabled" : "available"); 316 } 317 sslContextBuilder.sslProvider(SslProvider.JDK); 318 return false; 319 } 320 } 321 322 public static SslContext createSslContextForServer(Configuration config) 323 throws X509Exception, IOException { 324 String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, ""); 325 char[] keyStorePassword = config.getPassword(TLS_CONFIG_KEYSTORE_PASSWORD); 326 String keyStoreType = config.get(TLS_CONFIG_KEYSTORE_TYPE, ""); 327 328 if (keyStoreLocation.isEmpty()) { 329 throw new SSLContextException( 330 "Keystore is required for SSL server: " + TLS_CONFIG_KEYSTORE_LOCATION); 331 } 332 333 SslContextBuilder sslContextBuilder; 334 sslContextBuilder = SslContextBuilder 335 .forServer(createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType)); 336 337 boolean useOpenSsl = configureOpenSslIfAvailable(sslContextBuilder, config); 338 String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, ""); 339 char[] trustStorePassword = config.getPassword(TLS_CONFIG_TRUSTSTORE_PASSWORD); 340 String trustStoreType = config.get(TLS_CONFIG_TRUSTSTORE_TYPE, ""); 341 342 boolean sslCrlEnabled = config.getBoolean(TLS_CONFIG_CLR, false); 343 boolean sslOcspEnabled = config.getBoolean(TLS_CONFIG_OCSP, false); 344 345 ClientAuth clientAuth = 346 ClientAuth.fromPropertyValue(config.get(HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE)); 347 boolean verifyClientHostname = 348 config.getBoolean(HBASE_SERVER_NETTY_TLS_VERIFY_CLIENT_HOSTNAME, true); 349 boolean allowReverseDnsLookup = config.getBoolean(TLS_CONFIG_REVERSE_DNS_LOOKUP_ENABLED, true); 350 351 if (trustStoreLocation.isEmpty()) { 352 LOG.warn(TLS_CONFIG_TRUSTSTORE_LOCATION + " not specified"); 353 } else { 354 sslContextBuilder 355 .trustManager(createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, 356 sslCrlEnabled, sslOcspEnabled, verifyClientHostname, allowReverseDnsLookup)); 357 } 358 359 sslContextBuilder.enableOcsp(sslOcspEnabled); 360 sslContextBuilder.protocols(getEnabledProtocols(config)); 361 sslContextBuilder.ciphers(Arrays.asList(getCipherSuites(config, useOpenSsl))); 362 sslContextBuilder.clientAuth(clientAuth.toNettyClientAuth()); 363 364 return sslContextBuilder.build(); 365 } 366 367 /** 368 * Creates a key manager by loading the key store from the given file of the given type, 369 * optionally decrypting it using the given password. 370 * @param keyStoreLocation the location of the key store file. 371 * @param keyStorePassword optional password to decrypt the key store. If empty, assumes the key 372 * store is not encrypted. 373 * @param keyStoreType must be JKS, PEM, PKCS12, BCFKS or null. If null, attempts to 374 * autodetect the key store type from the file extension (e.g. .jks / 375 * .pem). 376 * @return the key manager. 377 * @throws KeyManagerException if something goes wrong. 378 */ 379 static X509KeyManager createKeyManager(String keyStoreLocation, char[] keyStorePassword, 380 String keyStoreType) throws KeyManagerException { 381 382 if (keyStorePassword == null) { 383 keyStorePassword = EMPTY_CHAR_ARRAY; 384 } 385 386 try { 387 KeyStoreFileType storeFileType = 388 KeyStoreFileType.fromPropertyValueOrFileName(keyStoreType, keyStoreLocation); 389 KeyStore ks = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(storeFileType) 390 .setKeyStorePath(keyStoreLocation).setKeyStorePassword(keyStorePassword).build() 391 .loadKeyStore(); 392 393 KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); 394 kmf.init(ks, keyStorePassword); 395 396 for (KeyManager km : kmf.getKeyManagers()) { 397 if (km instanceof X509KeyManager) { 398 return (X509KeyManager) km; 399 } 400 } 401 throw new KeyManagerException("Couldn't find X509KeyManager"); 402 } catch (IOException | GeneralSecurityException | IllegalArgumentException e) { 403 throw new KeyManagerException(e); 404 } 405 } 406 407 /** 408 * Creates a trust manager by loading the trust store from the given file of the given type, 409 * optionally decrypting it using the given password. 410 * @param trustStoreLocation the location of the trust store file. 411 * @param trustStorePassword optional password to decrypt the trust store (only applies to JKS 412 * trust stores). If empty, assumes the trust store is not encrypted. 413 * @param trustStoreType must be JKS, PEM, PKCS12, BCFKS or null. If null, attempts to 414 * autodetect the trust store type from the file extension (e.g. .jks 415 * / .pem). 416 * @param crlEnabled enable CRL (certificate revocation list) checks. 417 * @param ocspEnabled enable OCSP (online certificate status protocol) checks. 418 * @param verifyHostName if true, ssl peer hostname must match name in certificate 419 * @param allowReverseDnsLookup if true, allow falling back to reverse dns lookup in verifying 420 * hostname 421 * @return the trust manager. 422 * @throws TrustManagerException if something goes wrong. 423 */ 424 static X509TrustManager createTrustManager(String trustStoreLocation, char[] trustStorePassword, 425 String trustStoreType, boolean crlEnabled, boolean ocspEnabled, boolean verifyHostName, 426 boolean allowReverseDnsLookup) throws TrustManagerException { 427 428 if (trustStorePassword == null) { 429 trustStorePassword = EMPTY_CHAR_ARRAY; 430 } 431 432 try { 433 KeyStoreFileType storeFileType = 434 KeyStoreFileType.fromPropertyValueOrFileName(trustStoreType, trustStoreLocation); 435 KeyStore ts = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(storeFileType) 436 .setTrustStorePath(trustStoreLocation).setTrustStorePassword(trustStorePassword).build() 437 .loadTrustStore(); 438 439 PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); 440 if (crlEnabled || ocspEnabled) { 441 pbParams.setRevocationEnabled(true); 442 System.setProperty("com.sun.net.ssl.checkRevocation", "true"); 443 if (crlEnabled) { 444 System.setProperty("com.sun.security.enableCRLDP", "true"); 445 } 446 if (ocspEnabled) { 447 Security.setProperty("ocsp.enable", "true"); 448 } 449 } else { 450 pbParams.setRevocationEnabled(false); 451 } 452 453 // Revocation checking is only supported with the PKIX algorithm 454 TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); 455 tmf.init(new CertPathTrustManagerParameters(pbParams)); 456 457 for (final TrustManager tm : tmf.getTrustManagers()) { 458 if (tm instanceof X509ExtendedTrustManager) { 459 return new HBaseTrustManager((X509ExtendedTrustManager) tm, verifyHostName, 460 allowReverseDnsLookup); 461 } 462 } 463 throw new TrustManagerException("Couldn't find X509TrustManager"); 464 } catch (IOException | GeneralSecurityException | IllegalArgumentException e) { 465 throw new TrustManagerException(e); 466 } 467 } 468 469 private static String[] getEnabledProtocols(Configuration config) { 470 String enabledProtocolsInput = config.get(TLS_ENABLED_PROTOCOLS); 471 if (enabledProtocolsInput == null) { 472 return new String[] { config.get(TLS_CONFIG_PROTOCOL, DEFAULT_PROTOCOL) }; 473 } 474 return enabledProtocolsInput.split(","); 475 } 476 477 private static String[] getCipherSuites(Configuration config, boolean useOpenSsl) { 478 String cipherSuitesInput = config.get(TLS_CIPHER_SUITES); 479 if (cipherSuitesInput == null) { 480 return getDefaultCipherSuites(useOpenSsl); 481 } else { 482 return cipherSuitesInput.split(","); 483 } 484 } 485 486 /** 487 * Enable certificate file reloading by creating FileWatchers for keystore and truststore. 488 * AtomicReferences will be set with the new instances. resetContext - if not null - will be 489 * called when the file has been modified. 490 * @param keystoreWatcher Reference to keystoreFileWatcher. 491 * @param trustStoreWatcher Reference to truststoreFileWatcher. 492 * @param resetContext Callback for file changes. 493 */ 494 public static void enableCertFileReloading(Configuration config, 495 AtomicReference<FileChangeWatcher> keystoreWatcher, 496 AtomicReference<FileChangeWatcher> trustStoreWatcher, Runnable resetContext) 497 throws IOException { 498 String keyStoreLocation = config.get(TLS_CONFIG_KEYSTORE_LOCATION, ""); 499 keystoreWatcher.set(newFileChangeWatcher(keyStoreLocation, resetContext)); 500 String trustStoreLocation = config.get(TLS_CONFIG_TRUSTSTORE_LOCATION, ""); 501 // we are using the same callback for both. there's no reason to kick off two 502 // threads if keystore/truststore are both at the same location 503 if (!keyStoreLocation.equals(trustStoreLocation)) { 504 trustStoreWatcher.set(newFileChangeWatcher(trustStoreLocation, resetContext)); 505 } 506 } 507 508 private static FileChangeWatcher newFileChangeWatcher(String fileLocation, Runnable resetContext) 509 throws IOException { 510 if (fileLocation == null || fileLocation.isEmpty() || resetContext == null) { 511 return null; 512 } 513 final Path filePath = Paths.get(fileLocation).toAbsolutePath(); 514 FileChangeWatcher fileChangeWatcher = 515 new FileChangeWatcher(filePath, Objects.toString(filePath.getFileName()), FILE_POLL_INTERVAL, 516 watchEventFilePath -> handleWatchEvent(watchEventFilePath, resetContext)); 517 fileChangeWatcher.start(); 518 return fileChangeWatcher; 519 } 520 521 /** 522 * Handler for watch events that let us know a file we may care about has changed on disk. 523 */ 524 private static void handleWatchEvent(Path filePath, Runnable resetContext) { 525 LOG.info("Attempting to reset default SSL context after receiving watch event on file {}", 526 filePath); 527 resetContext.run(); 528 } 529}