diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index f0fbdcd696..d25d8c6b7a 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -531,6 +531,8 @@ Release 2.5.0 - UNRELEASED HADOOP-10630. Possible race condition in RetryInvocationHandler. (jing9) + HADOOP-10658. SSLFactory expects truststores being configured. (tucu via atm) + Release 2.4.1 - UNRELEASED INCOMPATIBLE CHANGES diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/FileBasedKeyStoresFactory.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/FileBasedKeyStoresFactory.java index 52e54addaa..ea22a881e3 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/FileBasedKeyStoresFactory.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/ssl/FileBasedKeyStoresFactory.java @@ -188,33 +188,33 @@ public void init(SSLFactory.Mode mode) String locationProperty = resolvePropertyName(mode, SSL_TRUSTSTORE_LOCATION_TPL_KEY); String truststoreLocation = conf.get(locationProperty, ""); - if (truststoreLocation.isEmpty()) { - throw new GeneralSecurityException("The property '" + locationProperty + - "' has not been set in the ssl configuration file."); + if (!truststoreLocation.isEmpty()) { + String passwordProperty = resolvePropertyName(mode, + SSL_TRUSTSTORE_PASSWORD_TPL_KEY); + String truststorePassword = conf.get(passwordProperty, ""); + if (truststorePassword.isEmpty()) { + throw new GeneralSecurityException("The property '" + passwordProperty + + "' has not been set in the ssl configuration file."); + } + long truststoreReloadInterval = + conf.getLong( + resolvePropertyName(mode, SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY), + DEFAULT_SSL_TRUSTSTORE_RELOAD_INTERVAL); + + LOG.debug(mode.toString() + " TrustStore: " + truststoreLocation); + + trustManager = new ReloadingX509TrustManager(truststoreType, + truststoreLocation, + truststorePassword, + truststoreReloadInterval); + trustManager.init(); + LOG.debug(mode.toString() + " Loaded TrustStore: " + truststoreLocation); + trustManagers = new TrustManager[]{trustManager}; + } else { + LOG.warn("The property '" + locationProperty + "' has not been set, " + + "no TrustStore will be loaded"); + trustManagers = null; } - - String passwordProperty = resolvePropertyName(mode, - SSL_TRUSTSTORE_PASSWORD_TPL_KEY); - String truststorePassword = conf.get(passwordProperty, ""); - if (truststorePassword.isEmpty()) { - throw new GeneralSecurityException("The property '" + passwordProperty + - "' has not been set in the ssl configuration file."); - } - long truststoreReloadInterval = - conf.getLong( - resolvePropertyName(mode, SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY), - DEFAULT_SSL_TRUSTSTORE_RELOAD_INTERVAL); - - LOG.debug(mode.toString() + " TrustStore: " + truststoreLocation); - - trustManager = new ReloadingX509TrustManager(truststoreType, - truststoreLocation, - truststorePassword, - truststoreReloadInterval); - trustManager.init(); - LOG.debug(mode.toString() + " Loaded TrustStore: " + truststoreLocation); - - trustManagers = new TrustManager[]{trustManager}; } /** diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java index 937b437a3d..a07faebe03 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/KeyStoreTestUtil.java @@ -76,8 +76,8 @@ public static String getClasspathDir(Class klass) throws Exception { * @throws GeneralSecurityException thrown if an Security error ocurred. */ public static X509Certificate generateCertificate(String dn, KeyPair pair, - int days, String algorithm) - throws GeneralSecurityException, IOException { + int days, String algorithm) + throws GeneralSecurityException, IOException { PrivateKey privkey = pair.getPrivate(); X509CertInfo info = new X509CertInfo(); Date from = new Date(); @@ -92,7 +92,7 @@ public static X509Certificate generateCertificate(String dn, KeyPair pair, info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); info - .set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); + .set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); @@ -103,30 +103,30 @@ public static X509Certificate generateCertificate(String dn, KeyPair pair, // Update the algorith, and resign. algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG); info - .set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, - algo); + .set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, + algo); cert = new X509CertImpl(info); cert.sign(privkey, algorithm); return cert; } public static KeyPair generateKeyPair(String algorithm) - throws NoSuchAlgorithmException { + throws NoSuchAlgorithmException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm); keyGen.initialize(1024); return keyGen.genKeyPair(); } private static KeyStore createEmptyKeyStore() - throws GeneralSecurityException, IOException { + throws GeneralSecurityException, IOException { KeyStore ks = KeyStore.getInstance("JKS"); ks.load(null, null); // initialize return ks; } private static void saveKeyStore(KeyStore ks, String filename, - String password) - throws GeneralSecurityException, IOException { + String password) + throws GeneralSecurityException, IOException { FileOutputStream out = new FileOutputStream(filename); try { ks.store(out, password.toCharArray()); @@ -136,18 +136,18 @@ private static void saveKeyStore(KeyStore ks, String filename, } public static void createKeyStore(String filename, - String password, String alias, - Key privateKey, Certificate cert) - throws GeneralSecurityException, IOException { + String password, String alias, + Key privateKey, Certificate cert) + throws GeneralSecurityException, IOException { KeyStore ks = createEmptyKeyStore(); ks.setKeyEntry(alias, privateKey, password.toCharArray(), - new Certificate[]{cert}); + new Certificate[]{cert}); saveKeyStore(ks, filename, password); } /** * Creates a keystore with a single key and saves it to a file. - * + * * @param filename String file to save * @param password String store password to set on keystore * @param keyPassword String key password to set on key @@ -158,27 +158,27 @@ public static void createKeyStore(String filename, * @throws IOException if there is an I/O error saving the file */ public static void createKeyStore(String filename, - String password, String keyPassword, String alias, - Key privateKey, Certificate cert) - throws GeneralSecurityException, IOException { + String password, String keyPassword, String alias, + Key privateKey, Certificate cert) + throws GeneralSecurityException, IOException { KeyStore ks = createEmptyKeyStore(); ks.setKeyEntry(alias, privateKey, keyPassword.toCharArray(), - new Certificate[]{cert}); + new Certificate[]{cert}); saveKeyStore(ks, filename, password); } public static void createTrustStore(String filename, - String password, String alias, - Certificate cert) - throws GeneralSecurityException, IOException { + String password, String alias, + Certificate cert) + throws GeneralSecurityException, IOException { KeyStore ks = createEmptyKeyStore(); ks.setCertificateEntry(alias, cert); saveKeyStore(ks, filename, password); } public static void createTrustStore( - String filename, String password, Map certs) - throws GeneralSecurityException, IOException { + String filename, String password, Map certs) + throws GeneralSecurityException, IOException { KeyStore ks = createEmptyKeyStore(); for (Map.Entry cert : certs.entrySet()) { ks.setCertificateEntry(cert.getKey(), cert.getValue()); @@ -187,7 +187,7 @@ public static void createTrustStore( } public static void cleanupSSLConfig(String keystoresDir, String sslConfDir) - throws Exception { + throws Exception { File f = new File(keystoresDir + "/clientKS.jks"); f.delete(); f = new File(keystoresDir + "/serverKS.jks"); @@ -196,7 +196,7 @@ public static void cleanupSSLConfig(String keystoresDir, String sslConfDir) f.delete(); f = new File(sslConfDir + "/ssl-client.xml"); f.delete(); - f = new File(sslConfDir + "/ssl-server.xml"); + f = new File(sslConfDir + "/ssl-server.xml"); f.delete(); } @@ -205,22 +205,42 @@ public static void cleanupSSLConfig(String keystoresDir, String sslConfDir) * SSLFactory. This includes keys, certs, keystores, truststores, the server * SSL configuration file, the client SSL configuration file, and the master * configuration file read by the SSLFactory. - * + * * @param keystoresDir String directory to save keystores * @param sslConfDir String directory to save SSL configuration files * @param conf Configuration master configuration to be used by an SSLFactory, - * which will be mutated by this method + * which will be mutated by this method * @param useClientCert boolean true to make the client present a cert in the - * SSL handshake + * SSL handshake */ public static void setupSSLConfig(String keystoresDir, String sslConfDir, - Configuration conf, boolean useClientCert) + Configuration conf, boolean useClientCert) throws Exception { + setupSSLConfig(keystoresDir, sslConfDir, conf, useClientCert, true); + } + + /** + * Performs complete setup of SSL configuration in preparation for testing an + * SSLFactory. This includes keys, certs, keystores, truststores, the server + * SSL configuration file, the client SSL configuration file, and the master + * configuration file read by the SSLFactory. + * + * @param keystoresDir String directory to save keystores + * @param sslConfDir String directory to save SSL configuration files + * @param conf Configuration master configuration to be used by an SSLFactory, + * which will be mutated by this method + * @param useClientCert boolean true to make the client present a cert in the + * SSL handshake + * @param trustStore boolean true to create truststore, false not to create it + */ + public static void setupSSLConfig(String keystoresDir, String sslConfDir, + Configuration conf, boolean useClientCert, + boolean trustStore) throws Exception { String clientKS = keystoresDir + "/clientKS.jks"; String clientPassword = "clientP"; String serverKS = keystoresDir + "/serverKS.jks"; String serverPassword = "serverP"; - String trustKS = keystoresDir + "/trustKS.jks"; + String trustKS = null; String trustPassword = "trustP"; File sslClientConfFile = new File(sslConfDir + "/ssl-client.xml"); @@ -246,7 +266,10 @@ public static void setupSSLConfig(String keystoresDir, String sslConfDir, sKP.getPrivate(), sCert); certs.put("server", sCert); - KeyStoreTestUtil.createTrustStore(trustKS, trustPassword, certs); + if (trustStore) { + trustKS = keystoresDir + "/trustKS.jks"; + KeyStoreTestUtil.createTrustStore(trustKS, trustPassword, certs); + } Configuration clientSSLConf = createClientSSLConfig(clientKS, clientPassword, clientPassword, trustKS); diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/TestSSLFactory.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/TestSSLFactory.java index 20585b15d0..d719170d55 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/TestSSLFactory.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/ssl/TestSSLFactory.java @@ -50,11 +50,12 @@ public static void setUp() throws Exception { base.mkdirs(); } - private Configuration createConfiguration(boolean clientCert) + private Configuration createConfiguration(boolean clientCert, + boolean trustStore) throws Exception { Configuration conf = new Configuration(); KeyStoreTestUtil.setupSSLConfig(KEYSTORES_DIR, sslConfsDir, conf, - clientCert); + clientCert, trustStore); return conf; } @@ -67,7 +68,7 @@ public void cleanUp() throws Exception { @Test(expected = IllegalStateException.class) public void clientMode() throws Exception { - Configuration conf = createConfiguration(false); + Configuration conf = createConfiguration(false, true); SSLFactory sslFactory = new SSLFactory(SSLFactory.Mode.CLIENT, conf); try { sslFactory.init(); @@ -80,7 +81,7 @@ public void clientMode() throws Exception { } private void serverMode(boolean clientCert, boolean socket) throws Exception { - Configuration conf = createConfiguration(clientCert); + Configuration conf = createConfiguration(clientCert, true); SSLFactory sslFactory = new SSLFactory(SSLFactory.Mode.SERVER, conf); try { sslFactory.init(); @@ -119,7 +120,7 @@ public void serverModeWithClientCertsVerifier() throws Exception { @Test public void validHostnameVerifier() throws Exception { - Configuration conf = createConfiguration(false); + Configuration conf = createConfiguration(false, true); conf.unset(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY); SSLFactory sslFactory = new SSLFactory(SSLFactory.Mode.CLIENT, conf); @@ -157,7 +158,7 @@ public void validHostnameVerifier() throws Exception { @Test(expected = GeneralSecurityException.class) public void invalidHostnameVerifier() throws Exception { - Configuration conf = createConfiguration(false); + Configuration conf = createConfiguration(false, true); conf.set(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY, "foo"); SSLFactory sslFactory = new SSLFactory(SSLFactory.Mode.CLIENT, conf); try { @@ -169,7 +170,7 @@ public void invalidHostnameVerifier() throws Exception { @Test public void testConnectionConfigurator() throws Exception { - Configuration conf = createConfiguration(false); + Configuration conf = createConfiguration(false, true); conf.set(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY, "STRICT_IE6"); SSLFactory sslFactory = new SSLFactory(SSLFactory.Mode.CLIENT, conf); try { @@ -275,7 +276,7 @@ private void checkSSLFactoryInitWithPasswords(SSLFactory.Mode mode, @Test public void testNoClientCertsInitialization() throws Exception { - Configuration conf = createConfiguration(false); + Configuration conf = createConfiguration(false, true); conf.unset(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY); SSLFactory sslFactory = new SSLFactory(SSLFactory.Mode.CLIENT, conf); try { @@ -285,4 +286,16 @@ public void testNoClientCertsInitialization() throws Exception { } } + @Test + public void testNoTrustStore() throws Exception { + Configuration conf = createConfiguration(false, false); + conf.unset(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY); + SSLFactory sslFactory = new SSLFactory(SSLFactory.Mode.SERVER, conf); + try { + sslFactory.init(); + } finally { + sslFactory.destroy(); + } + } + }