Updated DB encryption for ScaleIO credentials and Added lock while spooling managed storage template (#103)

* Encrypt the ScaleIO storage pool credentials in the DB

* Added sync lock while spooling managed storage template
This commit is contained in:
sureshanaparti 2020-10-21 17:50:33 +05:30 committed by GitHub
parent a61ba8c755
commit 7d5a4cde7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 51 deletions

View File

@ -53,6 +53,7 @@ import com.cloud.storage.dao.VolumeDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
@ -477,8 +478,10 @@ public class ScaleIOVMSnapshotStrategy extends ManagerBase implements VMSnapshot
private ScaleIOGatewayClient getScaleIOClient(final Long storagePoolId) throws Exception {
final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(storagePoolId);
final String url = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT).getValue();
final String username = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME).getValue();
final String password = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD).getValue();
final String encryptedUsername = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME).getValue();
final String username = DBEncryptionUtil.decrypt(encryptedUsername);
final String encryptedPassword = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD).getValue();
final String password = DBEncryptionUtil.decrypt(encryptedPassword);
return ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
}
}

View File

@ -837,6 +837,9 @@ public class VolumeServiceImpl implements VolumeService {
if (templatePoolRef == null) {
throw new CloudRuntimeException("Failed to find template " + srcTemplateInfo.getUniqueName() + " in storage pool " + destPrimaryDataStore.getId());
} else if (templatePoolRef.getState() == ObjectInDataStoreStateMachine.State.Ready) {
// Template already exists
return templateOnPrimary;
}
// At this point, we have an entry in the DB that points to our cached template.
@ -852,13 +855,6 @@ public class VolumeServiceImpl implements VolumeService {
throw new CloudRuntimeException("Unable to acquire lock on VMTemplateStoragePool: " + templatePoolRefId);
}
// Template already exists
if (templatePoolRef.getState() == ObjectInDataStoreStateMachine.State.Ready) {
_tmpltPoolDao.releaseFromLockTable(templatePoolRefId);
return templateOnPrimary;
}
try {
// create a cache volume on the back-end
@ -908,22 +904,20 @@ public class VolumeServiceImpl implements VolumeService {
int storagePoolMaxWaitSeconds = NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()), 3600);
long templatePoolRefId = templatePoolRef.getId();
templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolRefId, storagePoolMaxWaitSeconds);
if (templatePoolRef == null) {
throw new CloudRuntimeException("Unable to acquire lock on VMTemplateStoragePool: " + templatePoolRefId);
}
if (templatePoolRef.getDownloadState() == Status.DOWNLOADED) {
// There can be cases where we acquired the lock, but the template
// was already copied by a previous thread. Just return in that case.
s_logger.debug("Template already downloaded, nothing to do");
return;
}
try {
templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolRefId, storagePoolMaxWaitSeconds);
if (templatePoolRef == null) {
throw new CloudRuntimeException("Unable to acquire lock on VMTemplateStoragePool: " + templatePoolRefId);
}
if (templatePoolRef.getDownloadState() == Status.DOWNLOADED) {
// There can be cases where we acquired the lock, but the template
// was already copied by a previous thread. Just return in that case.
s_logger.debug("Template already downloaded, nothing to do");
return;
}
// copy the template from sec storage to the created volume
CreateBaseImageContext<CreateCmdResult> copyContext = new CreateBaseImageContext<>(null, null, destPrimaryDataStore, srcTemplateInfo, copyTemplateFuture, templateOnPrimary,
templatePoolRefId);
@ -1236,16 +1230,7 @@ public class VolumeServiceImpl implements VolumeService {
throw new CloudRuntimeException("Destination host should not be null.");
}
PrimaryDataStore destPrimaryDataStore = dataStoreMgr.getPrimaryDataStore(destDataStoreId);
// Check if template exists on the storage pool. If not, downland and copy to managed storage pool
VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(destDataStoreId, srcTemplateId);
if (templatePoolRef != null && templatePoolRef.getDownloadState() == Status.DOWNLOADED) {
return tmplFactory.getTemplate(srcTemplateId, destPrimaryDataStore);
}
TemplateInfo srcTemplateInfo = tmplFactory.getTemplate(srcTemplateId);
if (srcTemplateInfo == null) {
throw new CloudRuntimeException("Failed to get info of template: " + srcTemplateId);
}
@ -1254,8 +1239,29 @@ public class VolumeServiceImpl implements VolumeService {
throw new CloudRuntimeException("Unsupported format: " + Storage.ImageFormat.ISO.toString() + " for managed storage template");
}
GlobalLock lock = null;
TemplateInfo templateOnPrimary = null;
try {
String templateIdManagedPoolIdLockString = "templateId:" + srcTemplateId + "managedPoolId:" + destDataStoreId;
lock = GlobalLock.getInternLock(templateIdManagedPoolIdLockString);
if (lock == null) {
throw new CloudRuntimeException("Unable to create managed storage template, couldn't get global lock on " + templateIdManagedPoolIdLockString);
}
int storagePoolMaxWaitSeconds = NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()), 3600);
if (!lock.lock(storagePoolMaxWaitSeconds)) {
s_logger.debug("Unable to create managed storage template, couldn't lock on " + templateIdManagedPoolIdLockString);
throw new CloudRuntimeException("Unable to create managed storage template, couldn't lock on " + templateIdManagedPoolIdLockString);
}
PrimaryDataStore destPrimaryDataStore = dataStoreMgr.getPrimaryDataStore(destDataStoreId);
// Check if template exists on the storage pool. If not, downland and copy to managed storage pool
VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(destDataStoreId, srcTemplateId);
if (templatePoolRef != null && templatePoolRef.getDownloadState() == Status.DOWNLOADED) {
return tmplFactory.getTemplate(srcTemplateId, destPrimaryDataStore);
}
templateOnPrimary = createManagedTemplateVolume(srcTemplateInfo, destPrimaryDataStore);
if (templateOnPrimary == null) {
throw new CloudRuntimeException("Failed to create template " + srcTemplateInfo.getUniqueName() + " on primary storage: " + destDataStoreId);
@ -1311,6 +1317,11 @@ public class VolumeServiceImpl implements VolumeService {
}
throw new CloudRuntimeException(e.getMessage());
} finally {
if (lock != null) {
lock.unlock();
lock.releaseRef();
}
}
}

View File

@ -73,6 +73,7 @@ import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.utils.Pair;
import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachineManager;
import com.google.common.base.Preconditions;
@ -105,8 +106,10 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
private ScaleIOGatewayClient getScaleIOClient(final Long storagePoolId) throws Exception {
final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(storagePoolId);
final String url = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_ENDPOINT).getValue();
final String username = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME).getValue();
final String password = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD).getValue();
final String encryptedUsername = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_USERNAME).getValue();
final String username = DBEncryptionUtil.decrypt(encryptedUsername);
final String encryptedPassword = storagePoolDetailsDao.findDetail(storagePoolId, ScaleIOGatewayClient.GATEWAY_API_PASSWORD).getValue();
final String password = DBEncryptionUtil.decrypt(encryptedPassword);
return ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
}

View File

@ -69,6 +69,7 @@ import com.cloud.storage.VMTemplateStorageResourceAssoc;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.template.TemplateManager;
import com.cloud.utils.UriUtils;
import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.exception.CloudRuntimeException;
public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle {
@ -100,7 +101,8 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCyc
private org.apache.cloudstack.storage.datastore.api.StoragePool findStoragePool(String url, String username, String password, String storagePoolName) {
try {
ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, 60);
final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.value();
ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
List<org.apache.cloudstack.storage.datastore.api.StoragePool> storagePools = client.listStoragePools();
for (org.apache.cloudstack.storage.datastore.api.StoragePool pool : storagePools) {
if (pool.getName().equals(storagePoolName)) {
@ -212,8 +214,8 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCyc
}
details.put(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT, gatewayApiURL);
details.put(ScaleIOGatewayClient.GATEWAY_API_USERNAME, gatewayUsername);
details.put(ScaleIOGatewayClient.GATEWAY_API_PASSWORD, gatewayPassword);
details.put(ScaleIOGatewayClient.GATEWAY_API_USERNAME, DBEncryptionUtil.encrypt(gatewayUsername));
details.put(ScaleIOGatewayClient.GATEWAY_API_PASSWORD, DBEncryptionUtil.encrypt(gatewayPassword));
details.put(ScaleIOGatewayClient.STORAGE_POOL_NAME, storagePoolName);
details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, scaleIOPool.getSystemId());
parameters.setDetails(details);
@ -231,10 +233,13 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCyc
List<String> connectedSdcIps = null;
try {
Map <String, String> dataStoreDetails = primaryDataStoreDao.getDetails(dataStore.getId());
String url = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
String username = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
String password = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, 60);
final String url = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
final String encryptedUsername = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
final String username = DBEncryptionUtil.decrypt(encryptedUsername);
final String encryptedPassword = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
final String password = DBEncryptionUtil.decrypt(encryptedPassword);
final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.value();
ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
connectedSdcIps = client.listConnectedSdcIps();
} catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) {
LOGGER.error("Failed to create storage pool", e);
@ -293,9 +298,12 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCyc
try {
Map <String, String> dataStoreDetails = primaryDataStoreDao.getDetails(dataStore.getId());
String url = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
String username = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
String password = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, 60);
String encryptedUsername = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
final String username = DBEncryptionUtil.decrypt(encryptedUsername);
String encryptedPassword = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
final String password = DBEncryptionUtil.decrypt(encryptedPassword);
final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.value();
ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
connectedSdcIps = client.listConnectedSdcIps();
} catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) {
LOGGER.error("Failed to create storage pool", e);

View File

@ -43,6 +43,7 @@ import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.exception.CloudRuntimeException;
public class ScaleIOHostListener implements HypervisorHostListener {
@ -90,9 +91,11 @@ public class ScaleIOHostListener implements HypervisorHostListener {
private boolean isHostSdcConnected(String hostIpAddress, long poolId) {
try {
Map<String, String> dataStoreDetails = _primaryDataStoreDao.getDetails(poolId);
String url = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
String username = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
String password = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
final String url = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT);
final String encryptedUsername = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_USERNAME);
final String username = DBEncryptionUtil.decrypt(encryptedUsername);
final String encryptedPassword = dataStoreDetails.get(ScaleIOGatewayClient.GATEWAY_API_PASSWORD);
final String password = DBEncryptionUtil.decrypt(encryptedPassword);
final int clientTimeout = StorageManager.STORAGE_POOL_CLIENT_TIMEOUT.valueIn(poolId);
ScaleIOGatewayClient client = ScaleIOGatewayClient.getClient(url, username, password, false, clientTimeout);
return client.isSdcConnected(hostIpAddress);

View File

@ -80,6 +80,7 @@ import com.cloud.storage.StoragePoolHostVO;
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.template.TemplateManager;
import com.cloud.utils.crypt.DBEncryptionUtil;
import com.cloud.utils.exception.CloudRuntimeException;
@PrepareForTest(ScaleIOGatewayClient.class)
@ -136,13 +137,17 @@ public class ScaleIOPrimaryDataStoreLifeCycleTest {
Map <String, String> mockDataStoreDetails = new HashMap<>();
mockDataStoreDetails.put(ScaleIOGatewayClient.GATEWAY_API_ENDPOINT, "https://192.168.1.19/api");
mockDataStoreDetails.put(ScaleIOGatewayClient.GATEWAY_API_USERNAME, "root");
mockDataStoreDetails.put(ScaleIOGatewayClient.GATEWAY_API_PASSWORD, "Password@123");
String encryptedUsername = DBEncryptionUtil.encrypt("root");
mockDataStoreDetails.put(ScaleIOGatewayClient.GATEWAY_API_USERNAME, encryptedUsername);
String encryptedPassword = DBEncryptionUtil.encrypt("Password@123");
mockDataStoreDetails.put(ScaleIOGatewayClient.GATEWAY_API_PASSWORD, encryptedPassword);
when(primaryDataStoreDao.getDetails(1L)).thenReturn(mockDataStoreDetails);
PowerMockito.mockStatic(ScaleIOGatewayClient.class);
ScaleIOGatewayClientImpl client = mock(ScaleIOGatewayClientImpl.class);
when(ScaleIOGatewayClient.getClient("https://192.168.1.19/api", "root", "Password@123", false, 60)).thenReturn(client);
String username = DBEncryptionUtil.decrypt(encryptedUsername);
String password = DBEncryptionUtil.decrypt(encryptedPassword);
when(ScaleIOGatewayClient.getClient("https://192.168.1.19/api", username, password, false, 60)).thenReturn(client);
List<String> connectedSdcIps = new ArrayList<>();
connectedSdcIps.add("192.168.1.1");