|
Glassfish example source code file (SSHLauncher.java)
The Glassfish SSHLauncher.java source code/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package org.glassfish.cluster.ssh.launcher; import com.trilead.ssh2.ChannelCondition; import com.trilead.ssh2.Session; import java.io.*; import com.sun.enterprise.util.StringUtils; import com.sun.enterprise.util.OS; import com.sun.enterprise.util.io.FileUtils; import com.sun.enterprise.universal.process.ProcessManager; import com.sun.enterprise.universal.process.ProcessManagerException; import com.sun.enterprise.universal.process.ProcessUtils; import com.trilead.ssh2.Connection; import com.trilead.ssh2.KnownHosts; import com.trilead.ssh2.SCPClient; import org.glassfish.internal.api.RelativePathResolver; import org.glassfish.cluster.ssh.util.HostVerifier; import org.glassfish.cluster.ssh.util.SSHUtil; import org.jvnet.hk2.annotations.Inject; import org.jvnet.hk2.annotations.Service; import org.jvnet.hk2.annotations.Scoped; import org.jvnet.hk2.component.Habitat; import org.jvnet.hk2.component.PerLookup; import com.sun.enterprise.config.serverbeans.SshConnector; import com.sun.enterprise.config.serverbeans.SshAuth; import com.sun.enterprise.config.serverbeans.Node; import org.glassfish.cluster.ssh.sftp.SFTPClient; import java.io.OutputStream; import java.io.File; import java.io.IOException; import java.io.FileNotFoundException; import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; /** * @author Rajiv Mordani */ @Service(name="SSHLauncher") @Scoped(PerLookup.class) public class SSHLauncher { private static final String SSH_DIR = ".ssh" + File.separator; private static final String AUTH_KEY_FILE = "authorized_keys"; private static final int DEFAULT_TIMEOUT_MSEC = 120000; // 2 minutes private static final String SSH_KEYGEN = "ssh-keygen"; private static final char LINE_SEP = System.getProperty("line.separator").charAt(0); /** * Database of known hosts. */ private static KnownHosts knownHostsDatabase = new KnownHosts(); /** * The host name which to connect to via ssh */ private String host; /** * The port on which the ssh daemon is running */ private int port; /** * The user name to use for authenticating with the ssh daemon */ private String userName; /** * The name of private key file. */ private String keyFile; /** * The connection object that represents the connection to the host * via ssh */ private Connection connection; private String authType; private String keyPassPhrase; private File knownHosts; private Logger logger; private String password; // Password before it has been expanded. Used for debugging. private String rawPassword = null; private String rawKeyPassPhrase = null; @Inject private Habitat habitat; public void init(Node node, Logger logger) { this.logger = logger; int port; String host; SshConnector connector = node.getSshConnector(); host = connector.getSshHost(); if (SSHUtil.checkString(host) != null) { this.host = host; } else { this.host = node.getNodeHost(); } if (logger.isLoggable(Level.FINE)) { logger.fine("Connecting to host " + host); } //XXX Why do we need this again? This is already done above and set to host String sshHost = connector.getSshHost(); if (sshHost != null) this.host = sshHost; SshAuth sshAuth = connector.getSshAuth(); String userName = null; if (sshAuth != null) { userName = sshAuth.getUserName(); this.keyFile = sshAuth.getKeyfile(); this.rawPassword = sshAuth.getPassword(); this.rawKeyPassPhrase = sshAuth.getKeyPassphrase(); } try { port = Integer.parseInt(connector.getSshPort()); } catch(NumberFormatException nfe) { port = 22; } init(userName, this.host, port, this.rawPassword, keyFile, this.rawKeyPassPhrase, logger); } public void init(String userName, String host, int port, String password, String keyFile, String keyPassPhrase, Logger logger) { this.port = port == 0 ? 22 : port; this.host = host; this.keyFile = (keyFile == null) ? SSHUtil.getExistingKeyFile(): keyFile; this.logger = logger; this.userName = SSHUtil.checkString(userName) == null ? System.getProperty("user.name") : userName; this.rawPassword = password; this.password = expandPasswordAlias(password); this.rawKeyPassPhrase = keyPassPhrase; this.keyPassPhrase = expandPasswordAlias(keyPassPhrase); if (knownHosts == null) { File home = new File(System.getProperty("user.home")); knownHosts = new File(home,".ssh/known_hosts"); } if (knownHosts.exists()) { try { knownHostsDatabase.addHostkeys(knownHosts); } catch (IOException e) { e.printStackTrace(); } } if (logger.isLoggable(Level.FINER)) { logger.finer("SSH info is " + toString()); } } /** * Opens the connection to the host and authenticates with public * key. * */ private void openConnection() throws IOException { boolean isAuthenticated = false; String message= ""; connection = new Connection(host, port); connection.connect(new HostVerifier(knownHostsDatabase)); if(SSHUtil.checkString(keyFile) == null && SSHUtil.checkString(password) == null) { message += "No key or password specified - trying default keys \n"; if (logger.isLoggable(Level.FINE)) { logger.fine("keyfile and password are null. Will try to authenticate with default key file if available"); } // check the default key locations if no authentication // method is explicitly configured. File home = new File(System.getProperty("user.home")); for (String keyName : Arrays.asList("id_rsa","id_dsa", "identity")) { message += "Tried to authenticate using " + keyName + "\n"; File key = new File(home,".ssh/"+keyName); if (key.exists()) { isAuthenticated = connection.authenticateWithPublicKey(userName, key, null); } if (isAuthenticated) { if (logger.isLoggable(Level.FINE)) { logger.fine("Authentication successful using key " + keyName); } message = null; break; } } } if (!isAuthenticated && SSHUtil.checkString(password) != null) { if (logger.isLoggable(Level.FINE)) { logger.fine("Authenticating with password " + getPrintablePassword(password)); } try { isAuthenticated = connection.authenticateWithPassword(userName, password); } catch (IOException iex) { if (logger.isLoggable(Level.FINE)) { logger.fine("Tried authenticating with password " + password); } } } if (!isAuthenticated && SSHUtil.checkString(keyFile) != null) { if (logger.isLoggable(Level.FINER)) { logger.finer("Specified key file is " + keyFile); } File key = new File(keyFile); if (key.exists()) { if (logger.isLoggable(Level.FINER)) { logger.finer("Specified key file exists at " + key); } try { isAuthenticated = connection.authenticateWithPublicKey( userName, key, keyPassPhrase); } catch (IOException ex){ if (logger.isLoggable(Level.FINE)) { logger.fine("Tried authenticating with specified key at " + key); } } } } if (!isAuthenticated && !connection.isAuthenticationComplete()) { connection.close(); connection = null; if (logger.isLoggable(Level.FINE)) { logger.fine("Could not authenticate"); } throw new IOException("Could not authenticate. " + message); } message = null; SSHUtil.register(connection); } /** * Executes a command on the remote system via ssh, optionally sending * lines of data to the remote process's System.in. * * @param command the command to execute in the form of an argv style list * @param os stream to receive the output from the command * @param stdinLines optional data to be sent to the process's System.in * stream; null if no input should be sent * @return * @throws IOException * @throws InterruptedException */ public int runCommand(List<String> command, OutputStream os, List<String> stdinLines) throws IOException, InterruptedException { return runCommand(commandListToQuotedString(command), os, stdinLines); } public int runCommand(List<String> command, OutputStream os) throws IOException, InterruptedException { return runCommand(command, os, null); } /** * WARNING! This method does not handle paths with spaces in them. * To use this method you must make sure all paths in the command string * are quoted correctly. Otherwise use the methods that take command as * a list instead. */ public int runCommand(String command, OutputStream os) throws IOException, InterruptedException { return runCommand(command, os, null); } /** * Executes a command on the remote system via ssh, optionally sending * lines of data to the remote process's System.in. * * WARNING! This method does not handle paths with spaces in them. * To use this method you must make sure all paths in the command string * are quoted correctly. Otherwise use the methods that take command as * a list instead. * * @param command the command to execute * @param os stream to receive the output from the command * @param stdinLines optional data to be sent to the process's System.in stream; null if no input should be sent * @return * @throws IOException * @throws InterruptedException */ public int runCommand(String command, OutputStream os, List<String> stdinLines) throws IOException, InterruptedException { command = SFTPClient.normalizePath(command); if (logger.isLoggable(Level.FINER)) { logger.finer("Running command " + command + " on host: " + this.host); } openConnection(); final Session sess = connection.openSession(); int status = exec(sess, command, os, listInputStream(stdinLines)); // XXX: Should we close connection after each command or cache it // and re-use it? SSHUtil.unregister(connection); connection = null; return status; } private int exec(final Session session, final String command, final OutputStream os, final InputStream is) throws IOException, InterruptedException { try { session.execCommand(command); PumpThread t1 = new PumpThread(session.getStdout(), os); t1.start(); PumpThread t2 = new PumpThread(session.getStderr(), os); t2.start(); final OutputStream stdin = session.getStdin(); if (is != null) { final PumpThread inputPump = new PumpThread(is, stdin); inputPump.run(); } stdin.close(); t1.join(); t2.join(); // wait for some time since the delivery of the exit status often gets delayed session.waitForCondition(ChannelCondition.EXIT_STATUS,3000); Integer r = session.getExitStatus(); if(r!=null) return r.intValue(); return -1; } finally { session.close(); } } private InputStream listInputStream(final List<String> stdinLines) throws IOException { if (stdinLines == null) { return null; } final ByteArrayOutputStream baos = new ByteArrayOutputStream(); for (String line : stdinLines) { baos.write(line.getBytes()); baos.write(LINE_SEP); } return new ByteArrayInputStream(baos.toByteArray()); } /** * Pumps {@link InputStream} to {@link OutputStream}. * * @author Kohsuke Kawaguchi */ private static final class PumpThread extends Thread { private final InputStream in; private final OutputStream out; public PumpThread(InputStream in, OutputStream out) { super("pump thread"); this.in = in; this.out = out; } public void run() { byte[] buf = new byte[1024]; try { while(true) { int len = in.read(buf); if(len<0) { in.close(); return; } out.write(buf,0,len); } } catch (IOException e) { e.printStackTrace(); } } } public void pingConnection() throws IOException, InterruptedException { logger.fine("Pinging connection for host: " + this.host); openConnection(); SSHUtil.unregister(connection); connection = null; } /* validate user provided ars * check connecton to host * check that the install dir is correct * landmarkPath must be relative to the installdir */ public void validate(String host, int port, String userName, String password, String keyFile, String keyPassPhrase, String installDir, String landmarkPath, Logger logger) throws IOException { boolean validInstallDir = false; init(userName, host, port, password, keyFile, keyPassPhrase, logger); openConnection(); logger.fine("Connection settings valid"); String testPath = installDir; if (StringUtils.ok(testPath)) { // Validate if installDir exists SFTPClient sftpClient = new SFTPClient(connection); if (sftpClient.exists(testPath)) { // installDir exists. Now check for landmark if provided if (StringUtils.ok(landmarkPath)) { testPath = installDir + "/" + landmarkPath; } validInstallDir = sftpClient.exists(testPath); } else { validInstallDir = false; } SSHUtil.unregister(connection); connection = null; if (!validInstallDir) { String msg = "Invalid install directory: could not find " + testPath + " on " + host; logger.warning(msg); throw new FileNotFoundException(msg); } logger.fine("Node home validated"); } } public void validate(String host, int port, String userName, String password, String keyFile, String keyPassPhrase, String installDir, Logger logger) throws IOException { // Validate with no landmark file validate(host, port, userName, password, keyFile, keyPassPhrase, installDir, null, logger); } public SFTPClient getSFTPClient() throws IOException { openConnection(); SFTPClient sftpClient = new SFTPClient(connection); return sftpClient; } public SCPClient getSCPClient() throws IOException { openConnection(); return new SCPClient(connection); } public String expandPasswordAlias(String alias) { String expandedPassword = null; if (alias == null) { return null; } try { expandedPassword = RelativePathResolver.getRealPasswordFromAlias(alias); } catch (Exception e) { logger.warning(StringUtils.cat(": ", alias, e.getMessage())); return null; } return expandedPassword; } public boolean isPasswordAlias(String alias) { // Check if the passed string is specified using the alias syntax String aliasName = RelativePathResolver.getAlias(alias); return (aliasName != null); } /** * Return a version of the password that is printable. * @param p password string * @return printable version of password */ private String getPrintablePassword(String p) { // We only display the password if it is an alias, else // we display "<concealed>". String printable = "null"; if (p != null) { if (isPasswordAlias(p)) { printable = p; } else { printable = "<concealed>"; } } return printable; } public void setupKey(String node, String pubKeyFile, boolean generateKey, String passwd) throws IOException, InterruptedException { boolean connected = false; File key = new File(keyFile); if(logger.isLoggable(Level.FINER)) logger.finer("Key = " + keyFile); if (key.exists()) { if (checkConnection()) { throw new IOException("SSH public key authentication is already configured for " + userName + "@" + node); } } else { if (generateKey) { if(!generateKeyPair()) { throw new IOException("SSH key pair generation failed. Please generate key manually."); } } else { throw new IOException("SSH key pair not present. Please generate a key pair manually or specify an existing one and re-run the command."); } } //password is must for key distribution if (passwd == null) { throw new IOException("SSH password is required for distributing the public key. You can specify the SSH password in a password file and pass it through --passwordfile option."); } try { connection = new Connection(node, port); connection.connect(); connected = connection.authenticateWithPassword(userName, passwd); } catch (Exception ex) { //logger.printExceptionStackTrace(ex); throw new IOException("SSH password authentication failed for user " + userName + " on host " + node); } if(!connected) { throw new IOException("SSH password authentication failed for user " + userName + " on host " + node); } //initiate scp client SCPClient scp = new SCPClient(connection); SFTPClient sftp = new SFTPClient(connection); if (key.exists()) { //fixes .ssh file mode setupSSHDir(); if (pubKeyFile == null) { pubKeyFile = keyFile + ".pub"; } File pubKey = new File(pubKeyFile); if(!pubKey.exists()) { throw new IOException("Public key file " + pubKeyFile + " does not exist."); } try { if(!sftp.exists(SSH_DIR)) { if(logger.isLoggable(Level.FINER)) { logger.fine(SSH_DIR + " does not exist"); } sftp.mkdirs(".ssh", 0700); } } catch (Exception e) { if(logger.isLoggable(Level.FINER)) { e.printStackTrace(); } throw new IOException("Error while creating .ssh directory on remote host:" + e.getMessage()); } //copy over the public key to remote host scp.put(pubKey.getAbsolutePath(), ".ssh"); //append the public key file contents to authorized_keys file on remote host String mergeCommand = "cd .ssh; cat " + pubKey.getName() + " >> " + AUTH_KEY_FILE; if(logger.isLoggable(Level.FINER)) { logger.finer("mergeCommand = " + mergeCommand); } if(connection.exec(mergeCommand, new ByteArrayOutputStream())!=0) { throw new IOException("Failed to propogate the public key " + pubKeyFile + " to " + host); } logger.info("Copied keyfile " + pubKeyFile + " to " + userName + "@" + host); //remove the public key file on remote host //for some reason sftp.rm() hangs for MKS ssh if(connection.exec("rm .ssh/" + pubKey.getName(), new ByteArrayOutputStream())!=0) { logger.warning("WARNING: Failed to remove the public key file " + pubKey.getName() + " on remote host " + host); } if(logger.isLoggable(Level.FINER)) { logger.finer("Removed the key file on remote host"); } connection.close(); } } public static byte[] toByteArray( InputStream input ) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buf = new byte[4096]; int len; while ((len = input.read(buf)) >= 0) { output.write(buf, 0, len); } byte[] o = output.toByteArray(); output.close(); return o; } public boolean checkConnection() throws IOException { boolean status = false; Connection c = null; c = new Connection(host, port); c.connect(); File f = new File(keyFile); if(logger.isLoggable(Level.FINER)) { logger.finer("Checking connection..."); } status = c.authenticateWithPublicKey(userName, f, rawKeyPassPhrase); if (status) { logger.info("Successfully connected to " + userName + "@" + host + " using keyfile " + keyFile); } c.close(); return status; } public boolean checkPasswordAuth() { boolean status = false; Connection c = null; try { c = new Connection(host, port); c.connect(); if(logger.isLoggable(Level.FINER)) { logger.finer("Checking connection..."); } status = c.authenticateWithPassword(userName, password); if (status) { logger.finer("Successfully connected to " + userName + "@" + host + " using password authentication"); } } catch(IOException ioe) { //logger.printExceptionStackTrace(ioe); if (logger.isLoggable(Level.FINER)) { ioe.printStackTrace(); } } c.close(); return status; } /** * Invoke ssh-keygen using ProcessManager API */ private boolean generateKeyPair() throws IOException { String keygenCmd = findSSHKeygen(); if(logger.isLoggable(Level.FINER)) { logger.finer("Using " + keygenCmd + " to generate key pair"); } setupSSHDir(); StringBuffer k = new StringBuffer(); List<String> cmdLine = new ArrayList Other Glassfish examples (source code examples)Here is a short list of links related to this Glassfish SSHLauncher.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.