/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.systemservices.impl.security;
import java.util.Calendar;
import java.util.Map;
import com.emc.storageos.services.util.PlatformUtils;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.emc.storageos.coordinator.client.model.PropertyInfoExt;
import com.emc.storageos.model.property.PropertiesMetadata;
import com.emc.storageos.model.property.PropertyInfoRestRep;
import com.emc.storageos.model.property.PropertyMetadata;
import com.emc.storageos.security.keystore.impl.CoordinatorConfigStoringHelper;
import com.emc.storageos.security.ssh.PEMUtil;
import com.emc.storageos.security.ssh.SSHKeyPair;
import com.emc.storageos.security.ssh.SSHKeyPairGenerator;
import com.emc.storageos.security.ssh.SSHParam;
import com.emc.storageos.systemservices.impl.property.PropertyManager;
import com.emc.storageos.systemservices.impl.upgrade.CoordinatorClientExt;
import com.emc.storageos.systemservices.impl.validate.PropertiesConfigurationValidator;
/**
* This class generates ssh host and user keys and ssh config files (ssh_known_hosts or authorizedkeys2)
* and store them into system properties
*/
public class SshConfigurator {
private static final Logger log = LoggerFactory.getLogger(SshConfigurator.class);
private static final String SSH_LOCK = "sshLock";
private static final String[] SSH_USERS = { "root", "svcuser", "storageos" };
// System property keys
private static final String SSH_CONFIG_VERSION = "ssh_config_version";
// All generated ssh staff will be stored and be dumped to system properties together
private PropertyInfoExt sshProps;
@Autowired
private PropertiesConfigurationValidator validator;
private CoordinatorClientExt coordinator;
@Autowired
private PropertyManager propertyManager;
private InterProcessLock sshLock;
public void run() throws Exception {
log.info("Checking if need to sync SSH configuration ...");
if (!PlatformUtils.isAppliance()) {
log.info("This is not a ViPR appliance so skip ssh configuration.");
return;
}
sshProps = coordinator.getTargetInfo(PropertyInfoExt.class);
CoordinatorConfigStoringHelper coordinatorHelper =
new CoordinatorConfigStoringHelper(coordinator.getCoordinatorClient());
// This lock and sshKeyGenRequired together to ensure only one node regenerates keys.
sshLock = coordinatorHelper.acquireLock(SSH_LOCK);
try {
if (!sshKeyGenRequired()) {
log.info("Real SSH keys are already in place. No need to regenerate.");
return;
}
// Go here if regeneration required
doRun();
log.info("SSH configuration is synced");
} finally {
coordinatorHelper.releaseLock(sshLock);
}
}
/**
* return true if current version equal to 0 (default version)
*
* @return
*/
private boolean sshKeyGenRequired() throws Exception {
String currentVersion = getCurrentVersion();
if (currentVersion == null) {
return true;
}
return getCurrentVersion().equals(getDefaultVersion());
}
private String getCurrentVersion() throws Exception {
Map<String, String> curProps = coordinator.getTargetInfo(PropertyInfoExt.class).getProperties();
return curProps.get(SSH_CONFIG_VERSION);
}
private String getDefaultVersion() {
Map<String, PropertyMetadata> metadata = PropertiesMetadata.getGlobalMetadata();
PropertyMetadata prop = metadata.get(SSH_CONFIG_VERSION);
return prop.getDefaultValue();
}
private void doRun() throws Exception {
log.info("Configuring SSH ...");
// generate host keys /etc/ssh/ssh_host_*_key and save them to system properties
genSshHostKeys();
// generate user keys <user home>/.ssh/id_* and save them to system properties
// now the users are root, storageos, svcuser
for (String user : SSH_USERS) {
genSshUserKeys(user);
}
saveToSystemProperties();
log.info("Configured SSH successfully.");
}
private void genSshUserKeys(String user) throws Exception {
// For rsa
SSHKeyPair rsaKeyPair = SSHKeyPairGenerator.getInstance(SSHParam.KeyAlgo.RSA).generate();
String rsaPrivKey = PEMUtil.encodePrivateKey(rsaKeyPair.getPrivateKey());
saveToResultSet(userIdPropName(user, SSHParam.KeyAlgo.RSA), rsaPrivKey);
// For dsa
SSHKeyPair dsaKeyPair = SSHKeyPairGenerator.getInstance(SSHParam.KeyAlgo.DSA).generate();
String dsaPrivKey = PEMUtil.encodePrivateKey(dsaKeyPair.getPrivateKey());
saveToResultSet(userIdPropName(user, SSHParam.KeyAlgo.DSA), dsaPrivKey);
// For ec keys. Note the ECDSA is not supported by JDK.
try {
SSHKeyPair ecKeyPair = SSHKeyPairGenerator.getInstance(SSHParam.KeyAlgo.ECDSA).generate();
String ecPrivKey = PEMUtil.encodePrivateKey(ecKeyPair.getPrivateKey());
saveToResultSet(userIdPropName(user, SSHParam.KeyAlgo.ECDSA), ecPrivKey);
} catch (com.emc.storageos.security.exceptions.SecurityException e) {
log.info("ECDSA keys are not supported. Skipping the key generation.");
}
log.info("SSH user keys are generated successfully.");
}
private void genSshHostKeys() throws Exception {
// For rsa
SSHKeyPair rsaKeyPair = SSHKeyPairGenerator.getInstance(SSHParam.KeyAlgo.RSA).generate();
String rsaPrivKey = PEMUtil.encodePrivateKey(rsaKeyPair.getPrivateKey());
saveToResultSet(hostPropName(SSHParam.KeyAlgo.RSA), rsaPrivKey);
// For dsa
SSHKeyPair dsaKeyPair = SSHKeyPairGenerator.getInstance(SSHParam.KeyAlgo.DSA).generate();
String dsaPrivKey = PEMUtil.encodePrivateKey(dsaKeyPair.getPrivateKey());
saveToResultSet(hostPropName(SSHParam.KeyAlgo.DSA), dsaPrivKey);
// For ec keys
try {
SSHKeyPair ecKeyPair = SSHKeyPairGenerator.getInstance(SSHParam.KeyAlgo.ECDSA).generate();
String ecPrivKey = PEMUtil.encodePrivateKey(ecKeyPair.getPrivateKey());
saveToResultSet(hostPropName(SSHParam.KeyAlgo.ECDSA), ecPrivKey);
} catch (com.emc.storageos.security.exceptions.SecurityException e) {
log.info("ECDSA is not supported. Skipping the key generation.");
}
log.info("SSH host keys are generated successfully.");
}
private void saveToResultSet(String key, String val) {
sshProps.addProperty(key, validator.getValidPropValue(key, val, false));
}
private void saveToSystemProperties() throws Exception {
// Before saving, update version
String sshVersion = Long.toString(Calendar.getInstance().getTimeInMillis());
sshProps.addProperty(SSH_CONFIG_VERSION, sshVersion);
// Update config version as well
sshProps.addProperty(PropertyInfoRestRep.CONFIG_VERSION, sshVersion);
// Set to false to bypass the stability check of system. There should no contention with UpgradeManager
coordinator.setTargetInfo(sshProps, false);
log.info("All ssh configurations are saved to system properties successfully");
}
public static String hostPropName(SSHParam.KeyAlgo algo) {
return String.format("ssh_host_%s_key", algo.name().toLowerCase());
}
public static String userIdPropName(String user, SSHParam.KeyAlgo algo) {
return String.format("%s_id_%s", user, algo.name().toLowerCase());
}
public void setCoordinator(CoordinatorClientExt coordinator) {
this.coordinator = coordinator;
}
}