Giter Club home page Giter Club logo

easyssh-proxy's Introduction

easyssh-proxy

GoDoc Lint and Testing codecov Go Report Card Sourcegraph

easyssh-proxy provides a simple implementation of some SSH protocol features in Go.

Feature

This project is forked from easyssh but add some features as the following.

  • Support plain text of user private key.
  • Support key path of user private key.
  • Support Timeout for the TCP connection to establish.
  • Support SSH ProxyCommand.
     +--------+       +----------+      +-----------+
     | Laptop | <-->  | Jumphost | <--> | FooServer |
     +--------+       +----------+      +-----------+

                         OR

     +--------+       +----------+      +-----------+
     | Laptop | <-->  | Firewall | <--> | FooServer |
     +--------+       +----------+      +-----------+
     192.168.1.5       121.1.2.3         10.10.29.68

Usage

You can see ssh, scp, ProxyCommand on examples folder.

ssh

See example/ssh/ssh.go

package main

import (
	"fmt"
	"time"

	"github.com/appleboy/easyssh-proxy"
)

func main() {
	// Create MakeConfig instance with remote username, server address and path to private key.
	ssh := &easyssh.MakeConfig{
		User:   "appleboy",
		Server: "example.com",
		// Optional key or Password without either we try to contact your agent SOCKET
		// Password: "password",
		// Paste your source content of private key
		// Key: `-----BEGIN RSA PRIVATE KEY-----
		// MIIEpAIBAAKCAQEA4e2D/qPN08pzTac+a8ZmlP1ziJOXk45CynMPtva0rtK/RB26
		// 7XC9wlRna4b3Ln8ew3q1ZcBjXwD4ppbTlmwAfQIaZTGJUgQbdsO9YA==
		// -----END RSA PRIVATE KEY-----
		// `,
		KeyPath: "/Users/username/.ssh/id_rsa",
		Port:    "22",
		Timeout: 60 * time.Second,

		// Parse PrivateKey With Passphrase
		Passphrase: "1234",

		// Optional fingerprint SHA256 verification
		// Get Fingerprint: ssh.FingerprintSHA256(key)
		// Fingerprint: "SHA256:mVPwvezndPv/ARoIadVY98vAC0g+P/5633yTC4d/wXE"

		// Enable the use of insecure ciphers and key exchange methods.
		// This enables the use of the the following insecure ciphers and key exchange methods:
		// - aes128-cbc
		// - aes192-cbc
		// - aes256-cbc
		// - 3des-cbc
		// - diffie-hellman-group-exchange-sha256
		// - diffie-hellman-group-exchange-sha1
		// Those algorithms are insecure and may allow plaintext data to be recovered by an attacker.
		// UseInsecureCipher: true,
	}

	// Call Run method with command you want to run on remote server.
	stdout, stderr, done, err := ssh.Run("ls -al", 60*time.Second)
	// Handle errors
	if err != nil {
		panic("Can't run remote command: " + err.Error())
	} else {
		fmt.Println("don is :", done, "stdout is :", stdout, ";   stderr is :", stderr)
	}
}

scp

See example/scp/scp.go

package main

import (
	"fmt"

	"github.com/appleboy/easyssh-proxy"
)

func main() {
	// Create MakeConfig instance with remote username, server address and path to private key.
	ssh := &easyssh.MakeConfig{
		User:     "appleboy",
		Server:   "example.com",
		Password: "123qwe",
		Port:     "22",
	}

	// Call Scp method with file you want to upload to remote server.
	// Please make sure the `tmp` floder exists.
	err := ssh.Scp("/root/source.csv", "/tmp/target.csv")

	// Handle errors
	if err != nil {
		panic("Can't run remote command: " + err.Error())
	} else {
		fmt.Println("success")
	}
}

SSH ProxyCommand

See example/proxy/proxy.go

	ssh := &easyssh.MakeConfig{
		User:    "drone-scp",
		Server:  "localhost",
		Port:    "22",
		KeyPath: "./tests/.ssh/id_rsa",
		Proxy: easyssh.DefaultConfig{
			User:    "drone-scp",
			Server:  "localhost",
			Port:    "22",
			KeyPath: "./tests/.ssh/id_rsa",
		},
	}

SSH Stream Log

See example/stream/stream.go

func main() {
	// Create MakeConfig instance with remote username, server address and path to private key.
	ssh := &easyssh.MakeConfig{
		Server:  "localhost",
		User:    "drone-scp",
		KeyPath: "./tests/.ssh/id_rsa",
		Port:    "22",
		Timeout: 60 * time.Second,
	}

	// Call Run method with command you want to run on remote server.
	stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream("for i in {1..5}; do echo ${i}; sleep 1; done; exit 2;", 60*time.Second)
	// Handle errors
	if err != nil {
		panic("Can't run remote command: " + err.Error())
	} else {
		// read from the output channel until the done signal is passed
		isTimeout := true
	loop:
		for {
			select {
			case isTimeout = <-doneChan:
				break loop
			case outline := <-stdoutChan:
				fmt.Println("out:", outline)
			case errline := <-stderrChan:
				fmt.Println("err:", errline)
			case err = <-errChan:
			}
		}

		// get exit code or command error.
		if err != nil {
			fmt.Println("err: " + err.Error())
		}

		// command time out
		if !isTimeout {
			fmt.Println("Error: command timeout")
		}
	}
}

easyssh-proxy's People

Contributors

amyangfei avatar appleboy avatar astroprofundis avatar dependabot[bot] avatar fatelei avatar favadi avatar freejelliott avatar gnawhleinad avatar july2993 avatar loafoe avatar monsieurv avatar sebastiendorgan avatar sriv1211 avatar wataash avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

easyssh-proxy's Issues

stderr output sent only after all stdout output in Stream

Stream currently waits for all stdout scanning to finish before scanning stderr. This can be quite confusing when using commands that output to both stdout and stderr, as it is not always obvious how the output from stderr lines up in time with the output from stdout.

The expected behaviour would be to see the output from stderr at the time it is output from the command.

I can put up a PR shortly.

Improve host key verification

Currently host key verification is done by checking if the host key fingerprint matches the expected one. This has a flaw in that it assumes that the server host key type will always be the one we are expecting. Servers can have multiple different host keys of different types. The host key selection is done based on the SSH protocol specification, from RFC4253 section 7.1:

      server_host_key_algorithms
         A name-list of the algorithms supported for the server host
         key.  The server lists the algorithms for which it has host
         keys; the client lists the algorithms that it is willing to
         accept.  There MAY be multiple host keys for a host, possibly
         with different algorithms.

         Some host keys may not support both signatures and encryption
         (this can be determined from the algorithm), and thus not all
         host keys are valid for all key exchange methods.

         Algorithm selection depends on whether the chosen key exchange
         algorithm requires a signature or an encryption-capable host
         key.  It MUST be possible to determine this from the public key
         algorithm name.  The first algorithm on the client's name-list
         that satisfies the requirements and is also supported by the
         server MUST be chosen.  If there is no such algorithm, both
         sides MUST disconnect.

This means that the client can choose which host key will be selected based on the sorted list of host key algorithms it sends to the server. This is also described here: https://www.bitvise.com/ssh-server-guide-host-keys

crypto/ssh has a way to set this list in ClientConfig:

	// HostKeyAlgorithms lists the key types that the client will
	// accept from the server as host key, in order of
	// preference. If empty, a reasonable default is used. Any
	// string returned from PublicKey.Type method may be used, or
	// any of the CertAlgoXxxx and KeyAlgoXxxx constants.
	HostKeyAlgorithms []string

easyssh doesn't set this list so it relies on crypto/ssh defaults which may change in the future. This means a crypto/ssh version update may break existing programs that use easyssh with fingerprint verification.

An easy improvement would be to allow specifying this list via MakeConfig. "Any string returned from PublicKey.Type method may be used", these are probably constant. Thus the host key type would always be guaranteed to match the key fingerprint.

Another improvement would be to be able to use func FixedHostKey as HostKeyCallback to allow specifying the full host public key to verify, not just the fingerprint, which would also be more secure.

Timeout type

Hello Again,

In the MakeConfig struct Timeout is a time.Duration but in Run and Stream method timeout parameter is an int, don't you think it will be better to make every timeout a time.Duration?
If you want I can create a Pull Request.

Sebastien

How can I use sshproxy path in windows?

How can I use sshproxy to set path to rsa key in windows?

ssh := &easyssh.MakeConfig{
...
KeyPath: "/Users/username/.ssh/id_rsa",
...
}

Thanks for answer!

Leaking goroutines

It seems your goroutines are leaking. When I run your SSH example, I notice that the routines you create do not terminate. I noticed this when I ran out of memory after a while.

How to support aes256-cbc

ssh: handshake failed: ssh: no common algorithm for client to server cipher; client offered: [aes128-ctr aes192-ctr aes256-ctr arcfour256 arcfour128 [email protected] [email protected] arcfour], server offered: [aes256-cbc]--- PASS: TestIncorrectPasswordCiphers (91.98s)
PASS

Panic if give wrong raw key

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x11cb6a3]

goroutine 20 [running]:
testing.tRunner.func1(0xc4200871e0)
        /Users/mtk10671/.gvm/gos/go1.8.1/src/testing/testing.go:622 +0x29d
panic(0x12e83a0, 0x14bf050)
        /Users/mtk10671/.gvm/gos/go1.8.1/src/runtime/panic.go:489 +0x2cf
github.com/appleboy/easyssh-proxy/vendor/golang.org/x/crypto/ssh.publicKeyCallback.auth(0xc42010f720, 0xc42010ffa0, 0x14, 0
x20, 0x13439f3, 0x9, 0x1494360, 0xc4200d4b00, 0x148fb60, 0xc420082bd0, ...)
        /Users/mtk10671/git/go/src/github.com/appleboy/easyssh-proxy/vendor/golang.org/x/crypto/ssh/client_auth.go:193 +0xb
3

Read/write timeout connection

Need to add in config readtimeout and writetimeout. This will avoid situations where the connection is established, but nothing further happens, and the console freezes. This can happen if the port on the remote server is open, but there is no ssh!

The default size buf was used to read the command execution result, resulting in an incomplete command execution result.

func (ssh_conf *MakeConfig) Stream(command string, timeout ...time.Duration) (<-chan string, <-chan string, <-chan bool, <-chan error, error) {
	... ...
	// combine outputs, create a line-by-line scanner
	stdoutReader := io.MultiReader(outReader)
	stderrReader := io.MultiReader(errReader)
	stdoutScanner := bufio.NewScanner(stdoutReader)
	stderrScanner := bufio.NewScanner(stderrReader)
	... ...

The default buf size is 4096. This results in an incomplete output when the command stdout or stderr is greater than this value

Make connect public

Hello,
First of all, thank you for your great job.
Don't you think it will be good to give public visibility to the connect method, to authorize access to the ssh.Session()?
Sébastien

ssh not working using password.

I am able to do ssh using the password, But not using the go code.

username = "xxxxxxx"
passwords = []string{"026aaa9f-01fa-0850-095c-3d3daa0a4bd3", "4e58232c-2b79-cf4b-5e98-2bd6f0ba4946"}
fmt.Println(bastiondc)
fmt.Println(dchost)
easyconfig := &easyssh.MakeConfig{
	User:     username,
	Server:   dchost,
	Port:     "22",
	Password: passwords[1],
	Proxy: easyssh.DefaultConfig{
		User:     username,
		Server:   bastiondc,
		Port:     "22",
		Password: passwords[0],
	},
}

ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain

goroutine 1 [running]:

Getting error with the following code

panic: Can't run remote command: dial tcp: i/o timeout

package main

import (
        "fmt"

        "github.com/appleboy/easyssh-proxy"
)

func main() {
        // Create MakeConfig instance with remote username, server address and path to private key.
        ssh := &easyssh.MakeConfig{
                Server: "localhost",
                // Optional key or Password without either we try to contact your agent SOCKET
                //Password: "password",
                Key:     "/.ssh/id_rsa",
                Port:    "22",
                Timeout: 60,
        }

        // Call Run method with command you want to run on remote server.
        stdout, stderr, done, err := ssh.Run("ls -al", 60)
        // Handle errors
        if err != nil {
                panic("Can't run remote command: " + err.Error())
        } else {
                fmt.Println("don is :", done, "stdout is :", stdout, ";   stderr is :", stderr)
        }

}

Support for Pty

I had to manually add support for Pty to get commands run with sudo to work. I created a patch file. Is this something you would like to incorporate? I added the lines to the Connect() function to update the session with requestPty().

easyssh_pty.patch.txt

Handshake failed: ssh: unable to authenticate

Hi, I'm trying to run a remote command to a host but it fails with the following error message and I can't find what is wrong:

ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain

Here is the code:

const (
    PKEY = "/root/.ssh/id_rsa"
)
...
output, err := utils.RunSSHCommand(user, host, PKEY, "cat /some/file")
...
func RunSSHCommand(user, addr, key, cmd string) (string, error) {
	ssh := &easyssh.MakeConfig{
		User:    user,
		Server:  addr,
		KeyPath: key,
		Port:    "22",
		Timeout: 60 * time.Second,
	}
	stdout, stderr, done, err := ssh.Run(cmd, 60*time.Second)
	if err != nil {
		color.Error.Printf("[SSH Error] Cant run remote command (%s), error msg: %s\n", cmd, err.Error())
	} else {
		if done {
			return stdout, err
		} else {
			color.Error.Printf("[SSH Error] Command error execution msg: %s\n", stderr)
		}
	}
	return "", err
}

Any ideas of what could be the problem here? I've already check that the key exists. I'm running this code from a docker container, and I pass the private key from the server running the container to the container it self on the docker-compose file like these:

...
volumes:
- /root/.ssh/id_rsa:/root/.ssh/id_rsa
...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.