All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.cloudslang.content.ssh.services.impl.SSHServiceImpl Maven / Gradle / Ivy

There is a newer version: 0.0.44
Show newest version
/*******************************************************************************
 * (c) Copyright 2017 Hewlett-Packard Development Company, L.P.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License v2.0 which accompany this distribution.
 *
 * The Apache License is available at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *******************************************************************************/
package io.cloudslang.content.ssh.services.impl;

import com.hp.oo.sdk.content.plugin.GlobalSessionObject;
import com.jcraft.jsch.*;
import io.cloudslang.content.ssh.entities.*;
import io.cloudslang.content.ssh.exceptions.SSHException;
import io.cloudslang.content.ssh.exceptions.TimeoutException;
import io.cloudslang.content.ssh.services.SSHService;
import io.cloudslang.content.ssh.utils.CacheUtils;
import io.cloudslang.content.ssh.utils.IdentityKeyUtils;
import io.cloudslang.content.utils.StringUtilities;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Map;

/**
 * @author ioanvranauhp
 *         Date: 10/29/14
 * @author octavian-h
 */
public class SSHServiceImpl implements SSHService {
    private static final String SHELL_CHANNEL = "shell";
    private static final int POLLING_INTERVAL = 10;
    private static final String EXEC_CHANNEL = "exec";
    private static final String KNOWN_HOSTS_ALLOW = "allow";
    private static final String KNOWN_HOSTS_STRICT = "strict";
    private static final String KNOWN_HOSTS_ADD = "add";
    private static final String ALLOWED_CIPHERS = "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc";
    public static final String EXIT_COMMAND = "exit";
    private Session session;
    private Channel execChannel;

    public SSHServiceImpl(Session session, Channel channel) {
        this.session = session;
        this.execChannel = channel;
    }

    /**
     * Open SSH session.
     *
     * @param details                     The connection details.
     * @param identityKey                 The private key file or string.
     * @param knownHostsFile              The known_hosts file and policy.
     * @param connectTimeout              The open SSH session timeout.
     * @param keepContextForExpectCommand Use the same channel for the expect command.
     * @param proxyHTTP                   The proxy settings, parse it as null if no proxy settings required
     * @param allowedCiphers              The list of allowed ciphers. If not empty, it will be used to overwrite the default list.
     */
    public SSHServiceImpl(ConnectionDetails details, IdentityKey identityKey, KnownHostsFile knownHostsFile,
                          int connectTimeout, boolean keepContextForExpectCommand, ProxyHTTP proxyHTTP, String allowedCiphers) throws SSHException {
        JSch jsch = new JSch();
        String finalListOfAllowedCiphers = StringUtilities.isNotBlank(allowedCiphers) ? allowedCiphers : ALLOWED_CIPHERS;
        JSch.setConfig("cipher.s2c", finalListOfAllowedCiphers);
        JSch.setConfig("cipher.c2s", finalListOfAllowedCiphers);
        JSch.setConfig("PreferredAuthentications", "publickey,password,keyboard-interactive");

        try {
            session = jsch.getSession(details.getUsername(), details.getHost(), details.getPort());
        } catch (JSchException e) {
            throw new SSHException(e);
        }

        try {
            String policy = knownHostsFile.getPolicy();
            Path knownHostsFilePath = knownHostsFile.getPath();
            switch (policy.toLowerCase(Locale.ENGLISH)) {
                case KNOWN_HOSTS_ALLOW:
                    session.setConfig("StrictHostKeyChecking", "no");
                    break;
                case KNOWN_HOSTS_STRICT:
                    jsch.setKnownHosts(knownHostsFilePath.toString());
                    session.setConfig("StrictHostKeyChecking", "yes");
                    break;
                case KNOWN_HOSTS_ADD:
                    if (!knownHostsFilePath.isAbsolute()) {
                        throw new SSHException("The known_hosts file path should be absolute.");
                    }
                    if (!Files.exists(knownHostsFilePath)) {
                        Path parent = knownHostsFilePath.getParent();
                        if (parent != null) {
                            Files.createDirectories(parent);
                        }
                        Files.createFile(knownHostsFilePath);
                    }
                    jsch.setKnownHosts(knownHostsFilePath.toString());
                    session.setConfig("StrictHostKeyChecking", "no");
                    break;
                default:
                    throw new SSHException("Unknown known_hosts file policy.");
            }
        } catch (JSchException e) {
            throw new SSHException("The known_hosts file couldn't be set.", e);
        } catch (IOException e) {
            throw new SSHException("The known_hosts file couldn't be created.", e);
        }

        if (identityKey == null) {
            // use the password
            session.setPassword(details.getPassword());
        } else {
            // or use the OpenSSH private key file or string
            IdentityKeyUtils.setIdentity(jsch, identityKey);
        }

        if (proxyHTTP != null) {
            session.setProxy(proxyHTTP);
        }

        try {
            session.connect(connectTimeout);

            if (keepContextForExpectCommand) {
                // create exec channel
                execChannel = session.openChannel(EXEC_CHANNEL);

                // connect to the channel and run the command(s)
                execChannel.connect(connectTimeout);
            }
        } catch (JSchException e) {
            throw new SSHException(e);
        }
    }

    @Override
    public CommandResult runShell(
            final String command,
            final String characterSet,
            boolean usePseudoTerminal,
            int connectTimeout,
            int commandTimeout,
            boolean agentForwarding) {

        try {
            if (!isConnected()) {
                session.connect(connectTimeout);
            }

            final ChannelShell channelShell = (ChannelShell) session.openChannel(SHELL_CHANNEL);
            channelShell.setPty(usePseudoTerminal);
            channelShell.setAgentForwarding(agentForwarding);

            final OutputStream shellIn = channelShell.getOutputStream();
            final InputStream shellOut = channelShell.getInputStream();

            channelShell.connect(connectTimeout);

            final PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(shellIn, characterSet));

            printWriter.println(command);
            printWriter.flush();

            try {
                Thread.sleep(commandTimeout);
            } catch (InterruptedException ignored) {
            }

            printWriter.println(EXIT_COMMAND);
            printWriter.flush();

            final String result = IOUtils.toString(shellOut, characterSet);

            final CommandResult commandResult = new CommandResult();
            commandResult.setStandardOutput(result);

            return commandResult;
        } catch (JSchException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public CommandResult runShellCommand(
            String command,
            String characterSet,
            boolean usePseudoTerminal,
            int connectTimeout,
            int commandTimeout,
            boolean agentForwarding) {
        try {
            if (!isConnected()) {
                session.connect(connectTimeout);
            }
            // create exec channel
            ChannelExec channel = (ChannelExec) session.openChannel(EXEC_CHANNEL);
            channel.setCommand(command.getBytes(characterSet));
            channel.setPty(usePseudoTerminal);
            channel.setAgentForwarding(agentForwarding);
            OutputStream out = new ByteArrayOutputStream();
            channel.setOutputStream(out);
            OutputStream err = new ByteArrayOutputStream();
            channel.setErrStream(err);

            // connect to the channel and run the command(s)
            channel.connect(connectTimeout);

            // wait for response
            long currentTime = System.currentTimeMillis();
            long timeLimit = currentTime + commandTimeout;
            while (!channel.isClosed() && currentTime < timeLimit) {
                try {
                    Thread.sleep(POLLING_INTERVAL);
                } catch (InterruptedException ignore) {
                }
                currentTime = System.currentTimeMillis();
            }
            boolean timedOut = !channel.isClosed();

            // save the response
            CommandResult result = new CommandResult();
            result.setStandardOutput(((ByteArrayOutputStream) out).toString(characterSet));
            if (usePseudoTerminal && channel.getExitStatus() != 0) {
                result.setStandardError(((ByteArrayOutputStream) out).toString(characterSet));
            } else {
                result.setStandardError(((ByteArrayOutputStream) err).toString(characterSet));
            }

            channel.disconnect();
            // The exit status is only available after the channel was closed (more exactly, just before the channel is closed).
            result.setExitCode(channel.getExitStatus());

            if (timedOut) {
                throw new TimeoutException(String.valueOf(result));
            }

            return result;
        } catch (JSchException | UnsupportedEncodingException | TimeoutException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void createLocalTunnel(int localPort, String remoteHost, int remotePort) {
        try {
            session.setPortForwardingL(localPort, remoteHost, remotePort);
        } catch (JSchException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean isConnected() {
        return session.isConnected();
    }

    @Override
    public void close() {
        if (execChannel != null) {
            execChannel.disconnect();
            execChannel = null;
        }
        session.disconnect();
        session = null;
    }

    @Override
    public boolean saveToCache(GlobalSessionObject> sessionParam, String sessionId) {
        return CacheUtils.saveSshSessionAndChannel(session, execChannel, sessionParam, sessionId);

    }

    @Override
    public void removeFromCache(GlobalSessionObject> sessionParam, String sessionId) {
        CacheUtils.removeSshSession(sessionParam, sessionId);
    }

    @Override
    public Session getSSHSession() {
        return session;
    }

    @Override
    public Channel getExecChannel() {
        return execChannel;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy