All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/**
* 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.security.ssl;

import org.apache.hadoop.thirdparty.com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.text.MessageFormat;
import java.util.Timer;

/**
 * {@link KeyStoresFactory} implementation that reads the certificates from
 * keystore files.
 * 

* If either the truststore or the keystore certificates file changes, it * would be refreshed under the corresponding wrapper implementation - * {@link ReloadingX509KeystoreManager} or {@link ReloadingX509TrustManager}. *

*/ @InterfaceAudience.Private @InterfaceStability.Evolving public class FileBasedKeyStoresFactory implements KeyStoresFactory { private static final Logger LOG = LoggerFactory.getLogger(FileBasedKeyStoresFactory.class); /** * The name of the timer thread monitoring file changes. */ public static final String SSL_MONITORING_THREAD_NAME = "SSL Certificates Store Monitor"; /** * The refresh interval used to check if either of the truststore or keystore * certificate file has changed. */ public static final String SSL_STORES_RELOAD_INTERVAL_TPL_KEY = "ssl.{0}.stores.reload.interval"; public static final String SSL_KEYSTORE_LOCATION_TPL_KEY = "ssl.{0}.keystore.location"; public static final String SSL_KEYSTORE_PASSWORD_TPL_KEY = "ssl.{0}.keystore.password"; public static final String SSL_KEYSTORE_KEYPASSWORD_TPL_KEY = "ssl.{0}.keystore.keypassword"; public static final String SSL_KEYSTORE_TYPE_TPL_KEY = "ssl.{0}.keystore.type"; public static final String SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY = "ssl.{0}.truststore.reload.interval"; public static final String SSL_TRUSTSTORE_LOCATION_TPL_KEY = "ssl.{0}.truststore.location"; public static final String SSL_TRUSTSTORE_PASSWORD_TPL_KEY = "ssl.{0}.truststore.password"; public static final String SSL_TRUSTSTORE_TYPE_TPL_KEY = "ssl.{0}.truststore.type"; public static final String SSL_EXCLUDE_CIPHER_LIST = "ssl.{0}.exclude.cipher.list"; /** * Default format of the keystore files. */ public static final String DEFAULT_KEYSTORE_TYPE = "jks"; /** * The default time interval in milliseconds used to check if either * of the truststore or keystore certificates file has changed and needs reloading. */ public static final int DEFAULT_SSL_STORES_RELOAD_INTERVAL = 10000; private Configuration conf; private KeyManager[] keyManagers; private TrustManager[] trustManagers; private ReloadingX509TrustManager trustManager; private Timer fileMonitoringTimer; private void createTrustManagersFromConfiguration(SSLFactory.Mode mode, String truststoreType, String truststoreLocation, long storesReloadInterval) throws IOException, GeneralSecurityException { String passwordProperty = resolvePropertyName(mode, SSL_TRUSTSTORE_PASSWORD_TPL_KEY); String truststorePassword = getPassword(conf, passwordProperty, ""); if (truststorePassword.isEmpty()) { // An empty trust store password is legal; the trust store password // is only required when writing to a trust store. Otherwise it's // an optional integrity check. truststorePassword = null; } // Check if obsolete truststore specific reload interval is present for backward compatible long truststoreReloadInterval = conf.getLong( resolvePropertyName(mode, SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY), storesReloadInterval); if (LOG.isDebugEnabled()) { LOG.debug(mode.toString() + " TrustStore: " + truststoreLocation + ", reloading at " + truststoreReloadInterval + " millis."); } trustManager = new ReloadingX509TrustManager( truststoreType, truststoreLocation, truststorePassword); if (truststoreReloadInterval > 0) { fileMonitoringTimer.schedule( new FileMonitoringTimerTask( Paths.get(truststoreLocation), path -> trustManager.loadFrom(path), exception -> LOG.error(ReloadingX509TrustManager.RELOAD_ERROR_MESSAGE, exception)), truststoreReloadInterval, truststoreReloadInterval); } if (LOG.isDebugEnabled()) { LOG.debug(mode.toString() + " Loaded TrustStore: " + truststoreLocation); } trustManagers = new TrustManager[]{trustManager}; } /** * Implements logic of initializing the KeyManagers with the options * to reload keystores. * @param mode client or server * @param keystoreType The keystore type. * @param storesReloadInterval The interval to check if the keystore certificates * file has changed. */ private void createKeyManagersFromConfiguration(SSLFactory.Mode mode, String keystoreType, long storesReloadInterval) throws GeneralSecurityException, IOException { String locationProperty = resolvePropertyName(mode, SSL_KEYSTORE_LOCATION_TPL_KEY); String keystoreLocation = conf.get(locationProperty, ""); if (keystoreLocation.isEmpty()) { throw new GeneralSecurityException("The property '" + locationProperty + "' has not been set in the ssl configuration file."); } String passwordProperty = resolvePropertyName(mode, SSL_KEYSTORE_PASSWORD_TPL_KEY); String keystorePassword = getPassword(conf, passwordProperty, ""); if (keystorePassword.isEmpty()) { throw new GeneralSecurityException("The property '" + passwordProperty + "' has not been set in the ssl configuration file."); } String keyPasswordProperty = resolvePropertyName(mode, SSL_KEYSTORE_KEYPASSWORD_TPL_KEY); // Key password defaults to the same value as store password for // compatibility with legacy configurations that did not use a separate // configuration property for key password. String keystoreKeyPassword = getPassword( conf, keyPasswordProperty, keystorePassword); if (LOG.isDebugEnabled()) { LOG.debug(mode.toString() + " KeyStore: " + keystoreLocation); } ReloadingX509KeystoreManager keystoreManager = new ReloadingX509KeystoreManager( keystoreType, keystoreLocation, keystorePassword, keystoreKeyPassword); if (storesReloadInterval > 0) { fileMonitoringTimer.schedule( new FileMonitoringTimerTask( Paths.get(keystoreLocation), path -> keystoreManager.loadFrom(path), exception -> LOG.error(ReloadingX509KeystoreManager.RELOAD_ERROR_MESSAGE, exception)), storesReloadInterval, storesReloadInterval); } keyManagers = new KeyManager[] { keystoreManager }; } /** * Resolves a property name to its client/server version if applicable. *

* NOTE: This method is public for testing purposes. * * @param mode client/server mode. * @param template property name template. * @return the resolved property name. */ @VisibleForTesting public static String resolvePropertyName(SSLFactory.Mode mode, String template) { return MessageFormat.format( template, StringUtils.toLowerCase(mode.toString())); } /** * Sets the configuration for the factory. * * @param conf the configuration for the factory. */ @Override public void setConf(Configuration conf) { this.conf = conf; } /** * Returns the configuration of the factory. * * @return the configuration of the factory. */ @Override public Configuration getConf() { return conf; } /** * Initializes the keystores of the factory. * * @param mode if the keystores are to be used in client or server mode. * @throws IOException thrown if the keystores could not be initialized due * to an IO error. * @throws GeneralSecurityException thrown if the keystores could not be * initialized due to a security error. */ @Override public void init(SSLFactory.Mode mode) throws IOException, GeneralSecurityException { boolean requireClientCert = conf.getBoolean(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY, SSLFactory.SSL_REQUIRE_CLIENT_CERT_DEFAULT); long storesReloadInterval = conf.getLong( resolvePropertyName(mode, SSL_STORES_RELOAD_INTERVAL_TPL_KEY), DEFAULT_SSL_STORES_RELOAD_INTERVAL); fileMonitoringTimer = new Timer(SSL_MONITORING_THREAD_NAME, true); // certificate store String keystoreType = conf.get(resolvePropertyName(mode, SSL_KEYSTORE_TYPE_TPL_KEY), DEFAULT_KEYSTORE_TYPE); if (requireClientCert || mode == SSLFactory.Mode.SERVER) { createKeyManagersFromConfiguration(mode, keystoreType, storesReloadInterval); } else { KeyStore keystore = KeyStore.getInstance(keystoreType); keystore.load(null, null); KeyManagerFactory keyMgrFactory = KeyManagerFactory .getInstance(SSLFactory.SSLCERTIFICATE); keyMgrFactory.init(keystore, null); keyManagers = keyMgrFactory.getKeyManagers(); } //trust store String truststoreType = conf.get(resolvePropertyName(mode, SSL_TRUSTSTORE_TYPE_TPL_KEY), DEFAULT_KEYSTORE_TYPE); String locationProperty = resolvePropertyName(mode, SSL_TRUSTSTORE_LOCATION_TPL_KEY); String truststoreLocation = conf.get(locationProperty, ""); if (!truststoreLocation.isEmpty()) { createTrustManagersFromConfiguration(mode, truststoreType, truststoreLocation, storesReloadInterval); } else { if (LOG.isDebugEnabled()) { LOG.debug("The property '" + locationProperty + "' has not been set, " + "no TrustStore will be loaded"); } trustManagers = null; } } String getPassword(Configuration conf, String alias, String defaultPass) { String password = defaultPass; try { char[] passchars = conf.getPassword(alias); if (passchars != null) { password = new String(passchars); } } catch (IOException ioe) { LOG.warn("Exception while trying to get password for alias " + alias + ": " + ioe.getMessage()); } return password; } /** * Releases any resources being used. */ @Override public synchronized void destroy() { if (trustManager != null) { fileMonitoringTimer.cancel(); trustManager = null; keyManagers = null; trustManagers = null; } } /** * Returns the keymanagers for owned certificates. * * @return the keymanagers for owned certificates. */ @Override public KeyManager[] getKeyManagers() { return keyManagers; } /** * Returns the trustmanagers for trusted certificates. * * @return the trustmanagers for trusted certificates. */ @Override public TrustManager[] getTrustManagers() { return trustManagers; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy