This is an automated email from the ASF dual-hosted git repository. dmitriusan pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push: new 2bfbef2 AMBARI-24871. Create an Ambari-server class that is called as an entr… (#2589) 2bfbef2 is described below commit 2bfbef2cab3cac2c4720dca0942fed8d3efd9bc9 Author: Lisnichenko Dmitro <dmitriu...@apache.org> AuthorDate: Thu Nov 15 19:36:30 2018 +0200 AMBARI-24871. Create an Ambari-server class that is called as an entr… (#2589) * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data (dlysnichenko) * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data. Fix spelling (dlysnichenko) * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data. Address review comments (dlysnichenko) * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data. Address review comments (dlysnichenko) * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data. Fix DI and tests * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data. Revert some cleanup * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data. Add test (dlysnichenko) * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data - fix tests (dlyscnichenko) * AMBARI-24871. Create an Ambari-server class that is called as an entry point to encrypt service configuration sensitive data - fix rat check (dlyscnichenko) --- .../configuration/ComponentSSLConfiguration.java | 2 +- .../ambari/server/configuration/Configuration.java | 6 +- .../internal/AmbariServerConfigurationHandler.java | 3 +- .../security/encryption/AESEncryptionService.java | 8 +- .../security/encryption/CredentialProvider.java | 19 +-- .../encryption/CredentialStoreServiceImpl.java | 10 +- .../security/encryption/MasterKeyServiceImpl.java | 41 ++++-- .../encryption/SensitiveDataEncryption.java | 142 ++++++++++++++++++ .../apache/ambari/server/utils/PasswordUtils.java | 17 ++- .../server/configuration/ConfigurationTest.java | 6 +- ...ComponentConfigurationResourceProviderTest.java | 5 +- .../encryption/CredentialProviderTest.java | 27 ++-- .../security/encryption/EncryptionServiceTest.java | 42 +++++- .../security/encryption/MasterKeyServiceTest.java | 9 +- .../encryption/SensitiveDataEncryptionTest.java | 161 +++++++++++++++++++++ .../ambari/server/utils/PasswordUtilsTest.java | 8 +- 16 files changed, 422 insertions(+), 84 deletions(-) diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/ComponentSSLConfiguration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/ComponentSSLConfiguration.java index a9d485f..81c64c7 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/ComponentSSLConfiguration.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/ComponentSSLConfiguration.java @@ -118,7 +118,7 @@ public class ComponentSSLConfiguration { private String getPassword(Configuration configuration) { String rawPassword = configuration.getProperty(Configuration.SSL_TRUSTSTORE_PASSWORD.getKey()); - String password = PasswordUtils.getInstance().readPasswordFromStore(rawPassword, configuration.getMasterKeyLocation(), configuration.isMasterKeyPersisted(), configuration.getMasterKeyStoreLocation()); + String password = PasswordUtils.getInstance().readPasswordFromStore(rawPassword, configuration); return password == null ? rawPassword : password; } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java index 63c1777..18ba439 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java @@ -2937,7 +2937,7 @@ public class Configuration { System.setProperty(JAVAX_SSL_TRUSTSTORE, getProperty(SSL_TRUSTSTORE_PATH)); } if (getProperty(SSL_TRUSTSTORE_PASSWORD) != null) { - String ts_password = PasswordUtils.getInstance().readPasswordFromStore(getProperty(SSL_TRUSTSTORE_PASSWORD), getMasterKeyLocation(), isMasterKeyPersisted(), getMasterKeyStoreLocation()); + String ts_password = PasswordUtils.getInstance().readPasswordFromStore(getProperty(SSL_TRUSTSTORE_PASSWORD), this); if (ts_password != null) { System.setProperty(JAVAX_SSL_TRUSTSTORE_PASSWORD, ts_password); } else { @@ -3920,7 +3920,7 @@ public class Configuration { String dbpasswd = null; boolean isPasswordAlias = false; if (CredentialProvider.isAliasString(passwdProp)) { - dbpasswd = PasswordUtils.getInstance().readPasswordFromStore(passwdProp, getMasterKeyLocation(), isMasterKeyPersisted(), getMasterKeyStoreLocation()); + dbpasswd = PasswordUtils.getInstance().readPasswordFromStore(passwdProp, this); isPasswordAlias =true; } @@ -5794,7 +5794,7 @@ public class Configuration { JETTY_THREAD_POOL("Jetty API & Agent Thread Pools"); /** - * A decription of the grouping. + * A description of the grouping. */ private String m_description; diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java index e8b68ee..8bfd348 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerConfigurationHandler.java @@ -204,8 +204,7 @@ public class AmbariServerConfigurationHandler extends RootServiceComponentConfig private CredentialProvider getCredentialProvider() throws AmbariException { if (credentialProvider == null) { - credentialProvider = new CredentialProvider(null, ambariConfiguration.getMasterKeyLocation(), - ambariConfiguration.isMasterKeyPersisted(), ambariConfiguration.getMasterKeyStoreLocation()); + credentialProvider = new CredentialProvider(null, ambariConfiguration); } return credentialProvider; } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java index 25b3fc7..c92a752 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java @@ -20,6 +20,9 @@ package org.apache.ambari.server.security.encryption; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; +import javax.inject.Inject; + +import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.utils.TextEncoding; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; @@ -38,6 +41,9 @@ public class AESEncryptionService implements EncryptionService { private MasterKeyService environmentMasterKeyService; + @Inject + private Configuration configuration; + @Override public String encrypt(String toBeEncrypted) throws Exception { return encrypt(toBeEncrypted, TextEncoding.BASE_64); @@ -76,7 +82,7 @@ public class AESEncryptionService implements EncryptionService { private void initEnvironmentMasterKeyService() { if (environmentMasterKeyService == null) { - environmentMasterKeyService = new MasterKeyServiceImpl(); + environmentMasterKeyService = new MasterKeyServiceImpl(configuration); if (!environmentMasterKeyService.isMasterKeyInitialized()) { throw new SecurityException("You are trying to use a persisted master key but its initialization has been failed!"); } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java index ce0e843..7d7bb68 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java @@ -44,25 +44,17 @@ public class CredentialProvider { private CredentialStore keystoreService; private static final Logger LOG = LoggerFactory.getLogger(CredentialProvider.class); - public CredentialProvider(String masterKey, File masterKeyLocation, - boolean isMasterKeyPersisted, File masterKeyStoreLocation) throws AmbariException { + public CredentialProvider(String masterKey, Configuration configuration) throws AmbariException { MasterKeyService masterKeyService; if (masterKey != null) { masterKeyService = new MasterKeyServiceImpl(masterKey); } else { - if (isMasterKeyPersisted) { - if (masterKeyLocation == null) { - throw new IllegalArgumentException("The master key file location must be specified if the master key is persisted"); - } - masterKeyService = new MasterKeyServiceImpl(masterKeyLocation); - } else { - masterKeyService = new MasterKeyServiceImpl(); - } + masterKeyService = new MasterKeyServiceImpl(configuration); } if (!masterKeyService.isMasterKeyInitialized()) { throw new AmbariException("Master key initialization failed."); } - this.keystoreService = new FileBasedCredentialStore(masterKeyStoreLocation); + this.keystoreService = new FileBasedCredentialStore(configuration.getMasterKeyStoreLocation()); this.keystoreService.setMasterKeyService(masterKeyService); } @@ -145,10 +137,7 @@ public class CredentialProvider { LOG.debug("Master key provided as an argument."); } try { - credentialProvider = new CredentialProvider(masterKey, - configuration.getMasterKeyLocation(), - configuration.isMasterKeyPersisted(), - configuration.getMasterKeyStoreLocation()); + credentialProvider = new CredentialProvider(masterKey, configuration); } catch (Exception ex) { ex.printStackTrace(); System.exit(1); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java index c36d21f..9fb2fe5 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java @@ -51,8 +51,6 @@ public class CredentialStoreServiceImpl implements CredentialStoreService { this.securePasswordHelper = securePasswordHelper; if (configuration != null) { - File masterKeyLocation = configuration.getMasterKeyLocation(); - try { initializeTemporaryCredentialStore(configuration.getTemporaryKeyStoreRetentionMinutes(), TimeUnit.MINUTES, @@ -63,15 +61,9 @@ public class CredentialStoreServiceImpl implements CredentialStoreService { LOG.error("Failed to initialize the temporary credential store. Storage of temporary credentials will fail.", e); } - // If the MasterKeyService is initialized, assume that we should be initializing the persistent // CredentialStore; else do not initialize it. - MasterKeyService masterKeyService = null; - if(masterKeyLocation.exists()) { - masterKeyService = new MasterKeyServiceImpl(masterKeyLocation); - } else { - masterKeyService = new MasterKeyServiceImpl(); - } + MasterKeyService masterKeyService = new MasterKeyServiceImpl(configuration); if (masterKeyService.isMasterKeyInitialized()) { try { initializePersistedCredentialStore(configuration.getMasterKeyStoreLocation(), masterKeyService); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/MasterKeyServiceImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/MasterKeyServiceImpl.java index 8a8dd58..e0568f9 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/MasterKeyServiceImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/MasterKeyServiceImpl.java @@ -51,6 +51,10 @@ public class MasterKeyServiceImpl implements MasterKeyService { * @param masterKeyFile the location of the master key file */ public MasterKeyServiceImpl(File masterKeyFile) { + initFromFile(masterKeyFile); + } + + private void initFromFile(File masterKeyFile) { if (masterKeyFile == null) { throw new IllegalArgumentException("Master Key location not provided."); } @@ -84,12 +88,22 @@ public class MasterKeyServiceImpl implements MasterKeyService { } /** - * Constructs a new MasterKeyServiceImpl using the master key found in the environment. + * default constructor + */ + public MasterKeyServiceImpl(){} + + /** + * Constructs a new MasterKeyServiceImpl using prefered source according config. + * masterKey > masterKeyLocation > environment */ - public MasterKeyServiceImpl() { - String key = readMasterKey(); - if (key != null) { - master = key.toCharArray(); + public MasterKeyServiceImpl(Configuration configuration) { + if (configuration!= null && configuration.isMasterKeyPersisted()) { + if (configuration.getMasterKeyLocation() == null) { + throw new IllegalArgumentException("The master key file location must be specified if the master key is persisted"); + } + initFromFile(configuration.getMasterKeyLocation()); + } else { + initializeFromEnv(); } } @@ -113,7 +127,7 @@ public class MasterKeyServiceImpl implements MasterKeyService { masterKeyLocation = args[1]; } if (args.length > 2 && !args[2].isEmpty()) { - persistMasterKey = args[2].toLowerCase().equals("true"); + persistMasterKey = args[2].equalsIgnoreCase("true"); } } @@ -196,7 +210,7 @@ public class MasterKeyServiceImpl implements MasterKeyService { * @return true if the file is identitified as "master key" file; otherwise false */ private static boolean isMasterKeyFile(File file) { - try (FileReader reader = new FileReader(file);) { + try (FileReader reader = new FileReader(file)) { char[] buffer = new char[MASTER_PERSISTENCE_TAG_PREFIX.length()]; return (reader.read(buffer) == buffer.length) && Arrays.equals(buffer, MASTER_PERSISTENCE_TAG_PREFIX.toCharArray()); } catch (Exception e) { @@ -243,8 +257,8 @@ public class MasterKeyServiceImpl implements MasterKeyService { } } - private String readMasterKey() { - String key = null; + private void initializeFromEnv() { + String key; Map<String, String> envVariables = System.getenv(); if (envVariables != null && !envVariables.isEmpty()) { key = envVariables.get(Configuration.MASTER_KEY_ENV_PROP); @@ -255,19 +269,18 @@ public class MasterKeyServiceImpl implements MasterKeyService { if (keyFile.exists()) { try { initializeFromFile(keyFile); - if (master != null) { - key = new String(master); - } - FileUtils.deleteQuietly(keyFile); } catch (Exception e) { LOG.error("Cannot read master key from file: " + keyPath); e.printStackTrace(); } } + } else { + LOG.error("Cannot read master key property {1} or master key file property {3} from environment"); } + } else { + master = key.toCharArray(); } } - return key; } private void initializeFromFile(File masterFile) throws Exception { diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/SensitiveDataEncryption.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/SensitiveDataEncryption.java new file mode 100644 index 0000000..1d4bdc9 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/SensitiveDataEncryption.java @@ -0,0 +1,142 @@ +/* + * 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.ambari.server.security.encryption; + +import java.util.Collection; +import java.util.Map; + +import org.apache.ambari.server.audit.AuditLoggerModule; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.ControllerModule; +import org.apache.ambari.server.ldap.LdapModule; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.Config; +import org.apache.ambari.server.utils.EventBusSynchronizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.persist.PersistService; + +/** + * Ambari-server class that is called as an entry point to encrypt service configuration sensitive data + */ +public class SensitiveDataEncryption { + private static final Logger LOG = LoggerFactory.getLogger + (SensitiveDataEncryption.class); + + private final PersistService persistService; + private final Injector injector; + + + @Inject + public SensitiveDataEncryption(Injector injector, + PersistService persistService) { + this.injector = injector; + this.persistService = persistService; + } + + /** + * Extension of main controller module + */ + private static class EncryptionHelperControllerModule extends ControllerModule { + + public EncryptionHelperControllerModule() throws Exception { + } + + @Override + protected void configure() { + super.configure(); + EventBusSynchronizer.synchronizeAmbariEventPublisher(binder()); + } + } + + /** + * Extension of audit logger module + */ + private static class EncryptionHelperAuditModule extends AuditLoggerModule { + + @Override + protected void configure() { + super.configure(); + } + + } + + public void startPersistenceService() { + persistService.start(); + } + + public void stopPersistenceService() { + persistService.stop(); + } + + /** + * Iterates thought all configs and encryption/decryption sensitive properties according to action + * using the default configured masterkey + * @param args "encryption" or "decryption" action expected + */ + public static void main(String[] args) { + if (args.length < 1 || (!"encryption".equals(args[0]) && !"decryption".equals(args[0] ))){ + LOG.error("The action parameter (\"encryption\" or \"decryption\") is required"); + System.exit(-1); + } + boolean encrypt = "encryption".equals(args[0]); + SensitiveDataEncryption sensitiveDataEncryption = null; + try { + Injector injector = Guice.createInjector(new EncryptionHelperControllerModule(), new EncryptionHelperAuditModule(), new LdapModule()); + sensitiveDataEncryption = injector.getInstance(SensitiveDataEncryption.class); + sensitiveDataEncryption.startPersistenceService(); + sensitiveDataEncryption.doEncryption(encrypt); + } catch (Throwable e) { + LOG.error("Exception occurred during config encryption/decryption:", e); + } finally { + if (sensitiveDataEncryption != null) { + sensitiveDataEncryption.stopPersistenceService(); + } + } + } + + /** + * @param encrypt selects mode: true=encrypt, false=decrypt + */ + public void doEncryption(boolean encrypt) { + AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class); + Encryptor<Config> configEncryptor = injector.getInstance(ConfigPropertiesEncryptor.class); + Clusters clusters = ambariManagementController.getClusters(); + if (clusters != null) { + Map<String, Cluster> clusterMap = clusters.getClusters(); + if (clusterMap != null && !clusterMap.isEmpty()) { + for (final Cluster cluster : clusterMap.values()) { + Collection<Config> configs = cluster.getAllConfigs(); + for (Config config : configs) { + if (encrypt) { + configEncryptor.encryptSensitiveData(config); + } else { + configEncryptor.decryptSensitiveData(config); + } + config.save(); + } + } + } + } + } +} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/utils/PasswordUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/utils/PasswordUtils.java index dafc47c..5c21503 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/utils/PasswordUtils.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/utils/PasswordUtils.java @@ -125,7 +125,7 @@ public class PasswordUtils { } private String readPasswordFromStore(String aliasStr) { - return readPasswordFromStore(aliasStr, configuration.getMasterKeyLocation(), configuration.isMasterKeyPersisted(), configuration.getMasterKeyStoreLocation()); + return readPasswordFromStore(aliasStr, configuration); } /** @@ -133,19 +133,20 @@ public class PasswordUtils { * * @param aliasStr * the Credential Store alias you want to read the password for - * @param masterKeyLocation + * @param configuration with configured master key: + * masterKeyLocation * the master key location file - * @param isMasterKeyPersisted + * isMasterKeyPersisted * a flag indicating whether the master key is persisted - * @param masterKeyStoreLocation + * masterKeyStoreLocation * the master key-store location file * @return the password of the given alias if it is not <code>blank</code> and * there is password stored for this alias in Credential Store; * <code>null</code> otherwise */ - public String readPasswordFromStore(String aliasStr, File masterKeyLocation, boolean isMasterKeyPersisted, File masterKeyStoreLocation) { + public String readPasswordFromStore(String aliasStr, Configuration configuration) { String password = null; - loadCredentialProvider(masterKeyLocation, isMasterKeyPersisted, masterKeyStoreLocation); + loadCredentialProvider(configuration); if (credentialProvider != null) { char[] result = null; try { @@ -166,11 +167,11 @@ public class PasswordUtils { return password; } - private void loadCredentialProvider(File masterKeyLocation, boolean isMasterKeyPersisted, File masterKeyStoreLocation) { + private void loadCredentialProvider(Configuration configuration) { if (credentialProvider == null) { try { LOCK.lock(); - credentialProvider = new CredentialProvider(null, masterKeyLocation, isMasterKeyPersisted, masterKeyStoreLocation); + credentialProvider = new CredentialProvider(null, configuration); } catch (Exception e) { LOG.info("Credential provider creation failed", e); credentialProvider = null; diff --git a/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java b/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java index b8d95c7..52ef289 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java @@ -231,7 +231,7 @@ public class ConfigurationTest { String encrypted = "fake-encrypted-password"; ambariProperties.setProperty(Configuration.SSL_TRUSTSTORE_PASSWORD.getKey(), unencrypted); Configuration conf = spy(new Configuration(ambariProperties)); - PowerMock.stub(PowerMock.method(PasswordUtils.class, "readPasswordFromStore", String.class, File.class, boolean.class, File.class)).toReturn(null); + PowerMock.stub(PowerMock.method(PasswordUtils.class, "readPasswordFromStore", String.class, Configuration.class)).toReturn(null); conf.loadSSLParams(); Assert.assertEquals(System.getProperty(Configuration.JAVAX_SSL_TRUSTSTORE_PASSWORD, "unknown"), unencrypted); } @@ -243,7 +243,7 @@ public class ConfigurationTest { String encrypted = "fake-encrypted-password"; ambariProperties.setProperty(Configuration.SSL_TRUSTSTORE_PASSWORD.getKey(), unencrypted); Configuration conf = spy(new Configuration(ambariProperties)); - PowerMock.stub(PowerMock.method(PasswordUtils.class, "readPasswordFromStore", String.class, File.class, boolean.class, File.class)).toReturn(encrypted); + PowerMock.stub(PowerMock.method(PasswordUtils.class, "readPasswordFromStore", String.class, Configuration.class)).toReturn(encrypted); conf.loadSSLParams(); Assert.assertEquals(System.getProperty(Configuration.JAVAX_SSL_TRUSTSTORE_PASSWORD, "unknown"), encrypted); } @@ -289,7 +289,7 @@ public class ConfigurationTest { passwordFile); Configuration conf = new Configuration(ambariProperties); - PowerMock.stub(PowerMock.method(PasswordUtils.class,"readPasswordFromStore", String.class, File.class, boolean.class, File.class)).toReturn(null); + PowerMock.stub(PowerMock.method(PasswordUtils.class,"readPasswordFromStore", String.class, Configuration.class)).toReturn(null); Assert.assertEquals("ambaritest", conf.getDatabasePassword()); } diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java index 0576528..f84fcf6 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentConfigurationResourceProviderTest.java @@ -527,11 +527,8 @@ public class RootServiceComponentConfigurationResourceProviderTest extends EasyM File masterKeyLocation = createNiceMock(File.class); File masterKeyStoreLocation = createNiceMock(File.class); - expect(configuration.getMasterKeyLocation()).andReturn(masterKeyLocation).once(); - expect(configuration.isMasterKeyPersisted()).andReturn(false).once(); - expect(configuration.getMasterKeyStoreLocation()).andReturn(masterKeyStoreLocation).once(); CredentialProvider credentialProvider = PowerMock.createMock(CredentialProvider.class); - PowerMock.expectNew(CredentialProvider.class, null, null, masterKeyLocation, false, masterKeyStoreLocation).andReturn(credentialProvider); + PowerMock.expectNew(CredentialProvider.class, null, null, configuration).andReturn(credentialProvider); credentialProvider.addAliasToCredentialStore("currentPasswd", "newPasswd"); PowerMock.expectLastCall().once(); PowerMock.replay(credentialProvider, CredentialProvider.class); diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/CredentialProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/CredentialProviderTest.java index cded559..2207ac3 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/CredentialProviderTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/CredentialProviderTest.java @@ -19,6 +19,7 @@ package org.apache.ambari.server.security.encryption; import java.io.File; import java.io.IOException; +import java.util.Properties; import org.apache.ambari.server.configuration.Configuration; import org.junit.After; @@ -52,21 +53,20 @@ public class CredentialProviderTest { CredentialProvider cr; File msFile = tmpFolder.newFile(Configuration.MASTER_KEY_FILENAME_DEFAULT); File mksFile = tmpFolder.newFile(Configuration.MASTER_KEYSTORE_FILENAME_DEFAULT); - try { - new CredentialProvider(null, null, true, null); - Assert.fail("Expected an exception"); - } catch (Throwable t) { - Assert.assertTrue(t instanceof IllegalArgumentException); - } - // Without master key persisted - cr = new CredentialProvider("blahblah!", msFile, false, mksFile); + Configuration configuration = new Configuration(new Properties()); + configuration.setProperty(Configuration.MASTER_KEY_LOCATION, msFile.getParent()); + configuration.setProperty(Configuration.MASTER_KEYSTORE_LOCATION, mksFile.getParent()); + + // With master key persisted + createMasterKey(); + cr = new CredentialProvider(null, configuration); Assert.assertNotNull(cr); Assert.assertNotNull(cr.getKeystoreService()); - // With master key persisted msFile.delete(); mksFile.delete(); - createMasterKey(); - cr = new CredentialProvider(null, msFile, true, mksFile); + // Without master key persisted + + cr = new CredentialProvider("blahblah!", configuration); Assert.assertNotNull(cr); Assert.assertNotNull(cr.getKeystoreService()); } @@ -91,10 +91,13 @@ public class CredentialProviderTest { public void testCredentialStore() throws Exception { File msFile = tmpFolder.newFile(Configuration.MASTER_KEY_FILENAME_DEFAULT); File mksFile = tmpFolder.newFile(Configuration.MASTER_KEYSTORE_FILENAME_DEFAULT); + Configuration configuration = new Configuration(new Properties()); + configuration.setProperty(Configuration.MASTER_KEY_LOCATION, msFile.getParent()); + configuration.setProperty(Configuration.MASTER_KEYSTORE_LOCATION, mksFile.getParent()); // With master key persisted createMasterKey(); - CredentialProvider cr = new CredentialProvider(null, msFile, true, mksFile); + CredentialProvider cr = new CredentialProvider(null, configuration); Assert.assertNotNull(cr); Assert.assertNotNull(cr.getKeystoreService()); diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/EncryptionServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/EncryptionServiceTest.java index 49bd871..ee0ba01 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/EncryptionServiceTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/EncryptionServiceTest.java @@ -26,9 +26,12 @@ import static org.powermock.api.easymock.PowerMock.verifyAll; import java.io.File; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.state.stack.OsFamily; import org.apache.ambari.server.utils.TextEncoding; +import org.easymock.EasyMock; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -37,6 +40,10 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; + import junit.framework.Assert; @RunWith(PowerMockRunner.class) @@ -47,7 +54,6 @@ public class EncryptionServiceTest { @Rule private final TemporaryFolder tmpFolder = new TemporaryFolder(); - private final EncryptionService encryptionService = new AESEncryptionService(); @Test public void testEncryptAndDecryptUsingCustomKeyWithBase64Encoding() throws Exception { @@ -62,13 +68,43 @@ public class EncryptionServiceTest { public void testEncryptAndDecryptUsingCustomKey(TextEncoding textEncoding) throws Exception { final String key = "mySuperS3cr3tMast3rKey!"; final String toBeEncrypted = "mySuperS3cr3tP4ssW0rD!"; + EncryptionService encryptionService = new AESEncryptionService(); final String encrypted = encryptionService.encrypt(toBeEncrypted, key, textEncoding); final String decrypted = encryptionService.decrypt(encrypted, key, textEncoding); assertEquals(toBeEncrypted, decrypted); } @Test - public void testEncryptAndDecryptUsingPersistedMasterKey() throws Exception { + public void testEncryptAndDecryptUsingPersistedMasterKey()throws Exception { + final String fileDir = tmpFolder.newFolder("keys").getAbsolutePath(); + final File masterKeyFile = new File(fileDir, "master"); + final String masterKey = "mySuperS3cr3tMast3rKey!"; + final MasterKeyServiceImpl ms = new MasterKeyServiceImpl("dummyKey"); + Assert.assertTrue(ms.initializeMasterKeyFile(masterKeyFile, masterKey)); + + final String toBeEncrypted = "mySuperS3cr3tP4ssW0rD!"; + + Configuration configuration = new Configuration(new Properties()); + configuration.setProperty(Configuration.MASTER_KEY_LOCATION, masterKeyFile.getParent()); + Injector injector = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + bind(Configuration.class).toInstance(configuration); + bind(OsFamily.class).toInstance(EasyMock.createMock(OsFamily.class)); + } + }); + EncryptionService encryptionService = new AESEncryptionService(); + injector.injectMembers(encryptionService); + + final String encrypted = encryptionService.encrypt(toBeEncrypted); + final String decrypted = encryptionService.decrypt(encrypted); + verifyAll(); + assertEquals(toBeEncrypted, decrypted); + } + + @Test + public void testEncryptAndDecryptUsingEnvDefinedMasterKey() throws Exception { final String fileDir = tmpFolder.newFolder("keys").getAbsolutePath(); final File masterKeyFile = new File(fileDir, "master"); final String masterKey = "mySuperS3cr3tMast3rKey!"; @@ -78,6 +114,7 @@ public class EncryptionServiceTest { final String toBeEncrypted = "mySuperS3cr3tP4ssW0rD!"; setupEnvironmentVariableExpectations(masterKeyFile); + EncryptionService encryptionService = new AESEncryptionService(); final String encrypted = encryptionService.encrypt(toBeEncrypted); final String decrypted = encryptionService.decrypt(encrypted); verifyAll(); @@ -87,6 +124,7 @@ public class EncryptionServiceTest { @Test(expected = SecurityException.class) public void shouldThrowSecurityExceptionInCaseOfEncryptingWithNonExistingPersistedMasterKey() throws Exception { final String toBeEncrypted = "mySuperS3cr3tP4ssW0rD!"; + EncryptionService encryptionService = new AESEncryptionService(); encryptionService.encrypt(toBeEncrypted); } diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/MasterKeyServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/MasterKeyServiceTest.java index 032bb23..6d61950 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/MasterKeyServiceTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/MasterKeyServiceTest.java @@ -28,6 +28,7 @@ import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermission; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import java.util.Set; import org.apache.ambari.server.configuration.Configuration; @@ -101,7 +102,8 @@ public class MasterKeyServiceTest extends TestCase { mockStatic(System.class); expect(System.getenv()).andReturn(mapRet); replayAll(); - MasterKeyService ms = new MasterKeyServiceImpl(); + Configuration configuration = new Configuration(new Properties()); + MasterKeyService ms = new MasterKeyServiceImpl(configuration); verifyAll(); Assert.assertTrue(ms.isMasterKeyInitialized()); Assert.assertNotNull(ms.getMasterSecret()); @@ -125,12 +127,13 @@ public class MasterKeyServiceTest extends TestCase { mockStatic(System.class); expect(System.getenv()).andReturn(mapRet); replayAll(); - ms = new MasterKeyServiceImpl(); + Configuration configuration = new Configuration(new Properties()); + ms = new MasterKeyServiceImpl(configuration); verifyAll(); Assert.assertTrue(ms.isMasterKeyInitialized()); Assert.assertNotNull(ms.getMasterSecret()); Assert.assertEquals("ThisisSomePassPhrase", new String(ms.getMasterSecret())); - Assert.assertFalse(masterKeyFile.exists()); + Assert.assertTrue(masterKeyFile.exists()); } @Override diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/SensitiveDataEncryptionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/SensitiveDataEncryptionTest.java new file mode 100644 index 0000000..842b570 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/encryption/SensitiveDataEncryptionTest.java @@ -0,0 +1,161 @@ +/* + * 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.ambari.server.security.encryption; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; + +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.orm.DBAccessor; +import org.apache.ambari.server.stack.StackManagerFactory; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Clusters; +import org.apache.ambari.server.state.Config; +import org.apache.ambari.server.state.stack.OsFamily; +import org.easymock.EasyMockSupport; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.persist.PersistService; + +public class SensitiveDataEncryptionTest { + + + @Rule + public TemporaryFolder tmpFolder = new TemporaryFolder(); + + @Before + public void setUp() throws Exception { + tmpFolder.create(); + } + + + @Test + public void testSensitiveDataEncryption() throws Exception { + EasyMockSupport easyMockSupport = new EasyMockSupport(); + + final DBAccessor mockDBDbAccessor = easyMockSupport.createNiceMock(DBAccessor.class); + + final StackManagerFactory mockStackManagerFactory = easyMockSupport.createNiceMock(StackManagerFactory.class); + final EntityManager mockEntityManager = easyMockSupport.createNiceMock(EntityManager.class); + final Clusters mockClusters = easyMockSupport.createNiceMock(Clusters.class); + final AmbariManagementController mockAmbariManagementController = easyMockSupport.createNiceMock(AmbariManagementController.class); + final OsFamily mockOSFamily = easyMockSupport.createNiceMock(OsFamily.class); + final ConfigPropertiesEncryptor mockConfigPropertiesEncryptor = easyMockSupport.createNiceMock(ConfigPropertiesEncryptor.class); + final Injector mockInjector = createInjector(mockDBDbAccessor, mockStackManagerFactory, + mockEntityManager, mockClusters, mockAmbariManagementController, mockOSFamily, mockConfigPropertiesEncryptor); + + Map<String, Cluster> clusters = new HashMap<>(); + Cluster cluster = easyMockSupport.createStrictMock(Cluster.class); + clusters.put("c1", cluster); + expect(mockAmbariManagementController.getClusters()).andReturn(mockClusters).once(); + expect(mockClusters.getClusters()).andReturn(clusters).once(); + + List<Config> configs = new ArrayList<Config>(); + Config config = easyMockSupport.createStrictMock(Config.class); + configs.add(config); + expect(cluster.getAllConfigs()).andReturn(configs).once(); + + mockConfigPropertiesEncryptor.encryptSensitiveData(config); + + config.save(); + expectLastCall(); + + final PersistService mockPersistService = easyMockSupport.createNiceMock(PersistService.class); + SensitiveDataEncryption sensitiveDataEncryption = new SensitiveDataEncryption(mockInjector, mockPersistService); + + easyMockSupport.replayAll(); + + sensitiveDataEncryption.doEncryption(true); + + easyMockSupport.verifyAll(); + } + + + @Test + public void testSensitiveDataDecryption() throws Exception { + EasyMockSupport easyMockSupport = new EasyMockSupport(); + + final DBAccessor mockDBDbAccessor = easyMockSupport.createNiceMock(DBAccessor.class); + + final StackManagerFactory mockStackManagerFactory = easyMockSupport.createNiceMock(StackManagerFactory.class); + final EntityManager mockEntityManager = easyMockSupport.createNiceMock(EntityManager.class); + final Clusters mockClusters = easyMockSupport.createNiceMock(Clusters.class); + final AmbariManagementController mockAmbariManagementController = easyMockSupport.createNiceMock(AmbariManagementController.class); + final OsFamily mockOSFamily = easyMockSupport.createNiceMock(OsFamily.class); + final ConfigPropertiesEncryptor mockConfigPropertiesEncryptor = easyMockSupport.createNiceMock(ConfigPropertiesEncryptor.class); + final Injector mockInjector = createInjector(mockDBDbAccessor, mockStackManagerFactory, + mockEntityManager, mockClusters, mockAmbariManagementController, mockOSFamily, mockConfigPropertiesEncryptor); + + Map<String, Cluster> clusters = new HashMap<>(); + Cluster cluster = easyMockSupport.createStrictMock(Cluster.class); + clusters.put("c1", cluster); + expect(mockAmbariManagementController.getClusters()).andReturn(mockClusters).once(); + expect(mockClusters.getClusters()).andReturn(clusters).once(); + + List<Config> configs = new ArrayList<Config>(); + Config config = easyMockSupport.createStrictMock(Config.class); + configs.add(config); + expect(cluster.getAllConfigs()).andReturn(configs).once(); + + mockConfigPropertiesEncryptor.decryptSensitiveData(config); + + config.save(); + expectLastCall(); + + final PersistService mockPersistService = easyMockSupport.createNiceMock(PersistService.class); + SensitiveDataEncryption sensitiveDataEncryption = new SensitiveDataEncryption(mockInjector, mockPersistService); + + easyMockSupport.replayAll(); + + sensitiveDataEncryption.doEncryption(false); + + easyMockSupport.verifyAll(); + } + + private Injector createInjector(DBAccessor mockDBDbAccessor, StackManagerFactory mockStackManagerFactory, + EntityManager mockEntityManager, Clusters mockClusters, + AmbariManagementController mockAmbariManagementController, OsFamily mockOSFamily, + ConfigPropertiesEncryptor mockConfigPropertiesEncryptor) { + return Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(StackManagerFactory.class).toInstance(mockStackManagerFactory); + bind(EntityManager.class).toInstance(mockEntityManager); + bind(DBAccessor.class).toInstance(mockDBDbAccessor); + bind(Clusters.class).toInstance(mockClusters); + bind(OsFamily.class).toInstance(mockOSFamily); + bind(AmbariManagementController.class).toInstance(mockAmbariManagementController); + bind(ConfigPropertiesEncryptor.class).toInstance(mockConfigPropertiesEncryptor); + } + }); + } + +} diff --git a/ambari-server/src/test/java/org/apache/ambari/server/utils/PasswordUtilsTest.java b/ambari-server/src/test/java/org/apache/ambari/server/utils/PasswordUtilsTest.java index b18ebd3..bee4d04 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/utils/PasswordUtilsTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/utils/PasswordUtilsTest.java @@ -18,7 +18,6 @@ package org.apache.ambari.server.utils; -import static org.easymock.EasyMock.expect; import static org.junit.Assert.assertEquals; import java.io.File; @@ -105,12 +104,7 @@ public class PasswordUtilsTest extends EasyMockSupport { } private void setupBasicCredentialProviderExpectations(CredentialProvider credentialProvider) throws Exception { - final File masterKeyLocation = createNiceMock(File.class); - final File masterKeyStoreLocation = createNiceMock(File.class); - expect(configuration.getMasterKeyLocation()).andReturn(masterKeyLocation).once(); - expect(configuration.isMasterKeyPersisted()).andReturn(false).once(); - expect(configuration.getMasterKeyStoreLocation()).andReturn(masterKeyStoreLocation).once(); - PowerMock.expectNew(CredentialProvider.class, null, (String) null, masterKeyLocation, false, masterKeyStoreLocation).andReturn(credentialProvider); + PowerMock.expectNew(CredentialProvider.class, null, null, configuration).andReturn(credentialProvider); } private Injector createInjector() {