AIRAVATA-2500 SSHAccountManager: install SSH key and verify
Project: http://git-wip-us.apache.org/repos/asf/airavata/repo Commit: http://git-wip-us.apache.org/repos/asf/airavata/commit/3d161b81 Tree: http://git-wip-us.apache.org/repos/asf/airavata/tree/3d161b81 Diff: http://git-wip-us.apache.org/repos/asf/airavata/diff/3d161b81 Branch: refs/heads/AIRAVATA-2500 Commit: 3d161b8161975be658259739047e36df3bd6ef38 Parents: 2a90139 Author: Marcus Christie <[email protected]> Authored: Mon Aug 14 12:38:53 2017 -0400 Committer: Marcus Christie <[email protected]> Committed: Tue Sep 19 15:07:56 2017 -0400 ---------------------------------------------------------------------- .../accountprovisioning/SSHAccountManager.java | 104 ++++++++++++++----- .../airavata/accountprovisioning/SSHUtil.java | 83 +++++++++++++++ 2 files changed, 162 insertions(+), 25 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/airavata/blob/3d161b81/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHAccountManager.java ---------------------------------------------------------------------- diff --git a/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHAccountManager.java b/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHAccountManager.java index dcb819b..d565fe9 100644 --- a/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHAccountManager.java +++ b/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHAccountManager.java @@ -25,43 +25,48 @@ import org.apache.airavata.common.utils.ServerSettings; import org.apache.airavata.credential.store.client.CredentialStoreClientFactory; import org.apache.airavata.credential.store.cpi.CredentialStoreService; import org.apache.airavata.credential.store.exception.CredentialStoreException; +import org.apache.airavata.model.appcatalog.accountprovisioning.SSHAccountProvisionerConfigParam; +import org.apache.airavata.model.appcatalog.computeresource.ComputeResourceDescription; +import org.apache.airavata.model.appcatalog.gatewayprofile.ComputeResourcePreference; import org.apache.airavata.model.credential.store.PasswordCredential; +import org.apache.airavata.model.credential.store.SSHCredential; import org.apache.airavata.registry.api.RegistryService; import org.apache.airavata.registry.api.client.RegistryServiceClientFactory; import org.apache.airavata.registry.api.exception.RegistryServiceException; import org.apache.thrift.TException; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; public class SSHAccountManager { - // TODO: need private key too to verify - public static void setupSSHAccount(String gatewayId, String hostname, String username, String sshPublicKey) { - - // TODO: finish implementing + // TODO: change return type to one that returns some details of the SSH account setup, for example the scratch location + public static void setupSSHAccount(String gatewayId, String computeResourceId, String username, SSHCredential sshCredential) { // get compute resource preferences for the gateway and hostname + // TODO: close the registry service client transport when done with it + RegistryService.Client registryServiceClient = getRegistryServiceClient(); + ComputeResourcePreference computeResourcePreference = null; + ComputeResourceDescription computeResourceDescription = null; + try { + computeResourcePreference = registryServiceClient.getGatewayComputeResourcePreference(gatewayId, computeResourceId); + computeResourceDescription = registryServiceClient.getComputeResource(computeResourceId); + } catch(TException e) { + throw new RuntimeException(e); + } // get the account provisioner and config values for the preferences - String provisionerName = null; - Map<ConfigParam,String> provisionerConfig = null; - - CredentialStoreService.Client credentialStoreServiceClient = getCredentialStoreClient(); - // Resolve any CRED_STORE_PASSWORD_TOKEN config parameters to passwords - Map<ConfigParam,String> resolvedConfig = new HashMap<>(); - for (Map.Entry<ConfigParam,String> configEntry : provisionerConfig.entrySet() ) { - if (configEntry.getKey().getType() == ConfigParam.ConfigParamType.CRED_STORE_PASSWORD_TOKEN) { - try { - PasswordCredential password = credentialStoreServiceClient.getPasswordCredential(configEntry.getValue(), gatewayId); - resolvedConfig.put(configEntry.getKey(), password.getPassword()); - } catch (TException e) { - throw new RuntimeException("Failed to get password needed to configure " + provisionerName); - } - } else { - resolvedConfig.put(configEntry.getKey(), configEntry.getValue()); - } + if (!computeResourcePreference.isSetSshAccountProvisioner()) { + // TODO: provide better exception? + throw new RuntimeException("Compute resource [" + computeResourceId + "] does not have an SSH Account Provisioner configured for it."); } + String provisionerName = computeResourcePreference.getSshAccountProvisioner(); + Map<ConfigParam,String> provisionerConfig = convertConfigParams(provisionerName, computeResourcePreference.getSshAccountProvisionerConfig()); + + Map<ConfigParam, String> resolvedConfig = resolveProvisionerConfig(gatewayId, provisionerName, provisionerConfig); // instantiate and init the account provisioner SSHAccountProvisioner sshAccountProvisioner = SSHAccountProvisionerFactory.createSSHAccountProvisioner(provisionerName, resolvedConfig); @@ -70,23 +75,72 @@ public class SSHAccountManager { boolean hasAccount = sshAccountProvisioner.hasAccount(username); if (!hasAccount && !sshAccountProvisioner.canCreateAccount()) { - // TODO: throw an exception + // TODO: provide better exception + throw new RuntimeException("User [" + username + "] doesn't have account and [" + provisionerName + "] doesn't support creating account."); } // TODO: first check if SSH key is already installed, or do we care? // Install SSH key + sshAccountProvisioner.installSSHKey(username, sshCredential.getPublicKey()); // Verify can authenticate to host + boolean validated = SSHUtil.validate(username, computeResourceDescription.getHostName(), 22, sshCredential); + if (!validated) { + throw new RuntimeException("Failed to validate installation of key for [" + username + + "] on [" + computeResourceDescription.getHostName() + "] using SSH Account Provisioner [" + + computeResourcePreference.getSshAccountProvisioner() + "]"); + } // create the scratch location on the host + // TODO: create the scratch location String scratchLocation = sshAccountProvisioner.getScratchLocation(username); } - private static RegistryService.Client getRegistryServiceClient() throws RegistryServiceException { + private static Map<ConfigParam, String> resolveProvisionerConfig(String gatewayId, String provisionerName, Map<ConfigParam, String> provisionerConfig) { + CredentialStoreService.Client credentialStoreServiceClient = null; + try { + credentialStoreServiceClient = getCredentialStoreClient(); + // Resolve any CRED_STORE_PASSWORD_TOKEN config parameters to passwords + Map<ConfigParam, String> resolvedConfig = new HashMap<>(); + for (Map.Entry<ConfigParam, String> configEntry : provisionerConfig.entrySet()) { + if (configEntry.getKey().getType() == ConfigParam.ConfigParamType.CRED_STORE_PASSWORD_TOKEN) { + try { + PasswordCredential password = credentialStoreServiceClient.getPasswordCredential(configEntry.getValue(), gatewayId); + resolvedConfig.put(configEntry.getKey(), password.getPassword()); + } catch (TException e) { + throw new RuntimeException("Failed to get password needed to configure " + provisionerName); + } + } else { + resolvedConfig.put(configEntry.getKey(), configEntry.getValue()); + } + } + return resolvedConfig; + } finally { + if (credentialStoreServiceClient != null) { + if (credentialStoreServiceClient.getInputProtocol().getTransport().isOpen()) { + credentialStoreServiceClient.getInputProtocol().getTransport().close(); + } + } + } + } + + private static Map<ConfigParam, String> convertConfigParams(String provisionerName, Map<SSHAccountProvisionerConfigParam, String> thriftConfigParams) { + List<ConfigParam> configParams = SSHAccountProvisionerFactory.getSSHAccountProvisionerConfigParams(provisionerName); + Map<String, ConfigParam> configParamMap = configParams.stream().collect(Collectors.toMap(ConfigParam::getName, Function.identity())); - // TODO: finish implementing - return RegistryServiceClientFactory.createRegistryClient(null, 0); + return thriftConfigParams.entrySet().stream().collect(Collectors.toMap(entry -> configParamMap.get(entry.getKey().getName()), entry -> entry.getValue())); + } + + private static RegistryService.Client getRegistryServiceClient() { + + try { + String registryServerHost = ServerSettings.getRegistryServerHost(); + int registryServerPort = Integer.valueOf(ServerSettings.getRegistryServerPort()); + return RegistryServiceClientFactory.createRegistryClient(registryServerHost, registryServerPort); + } catch (ApplicationSettingsException|RegistryServiceException e) { + throw new RuntimeException("Failed to create registry service client", e); + } } private static CredentialStoreService.Client getCredentialStoreClient() { http://git-wip-us.apache.org/repos/asf/airavata/blob/3d161b81/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHUtil.java ---------------------------------------------------------------------- diff --git a/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHUtil.java b/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHUtil.java new file mode 100644 index 0000000..4b5452a --- /dev/null +++ b/modules/compute-account-provisioning/src/main/java/org/apache/airavata/accountprovisioning/SSHUtil.java @@ -0,0 +1,83 @@ +/* + * 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.airavata.accountprovisioning; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import org.apache.airavata.model.credential.store.SSHCredential; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.UUID; + +/** + * Created by machrist on 2/10/17. + */ +public class SSHUtil { + + public static boolean validate(String username, String hostname, int port, SSHCredential sshCredential) { + + JSch jSch = new JSch(); + Session session = null; + try { + jSch.addIdentity(UUID.randomUUID().toString(), sshCredential.getPrivateKey().getBytes(), sshCredential.getPublicKey().getBytes(), sshCredential.getPassphrase().getBytes()); + session = jSch.getSession(username, hostname, port); + java.util.Properties config = new java.util.Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + session.connect(); + return true; + } catch (JSchException e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + if (session != null && session.isConnected()) { + session.disconnect(); + } + } + } + + public static void main(String[] args) throws JSchException { + + // Test the validate method + String username = System.getProperty("user.name"); + String privateKeyFilepath = System.getProperty("user.home") + "/.ssh/id_rsa"; + String publicKeyFilepath = privateKeyFilepath + ".pub"; + String passphrase = "changeme"; + String hostname = "changeme"; + + Path privateKeyPath = Paths.get(privateKeyFilepath); + Path publicKeyPath = Paths.get(publicKeyFilepath); + + SSHCredential sshCredential = new SSHCredential(); + sshCredential.setPassphrase(passphrase); + try { + sshCredential.setPublicKey(new String(Files.readAllBytes(publicKeyPath), "UTF-8")); + sshCredential.setPrivateKey(new String(Files.readAllBytes(privateKeyPath), "UTF-8")); + } catch (IOException e) { + throw new RuntimeException(e); + } + boolean result = validate(username, hostname, 22, sshCredential); + System.out.println(result); + } +}
