Giter Site home page Giter Site logo

melbahja / goph Goto Github PK

View Code? Open in Web Editor NEW
1.6K 1.6K 123.0 106 KB

🤘 The native golang ssh client to execute your commands over ssh connection. 🚀🚀

Home Page: http://git.io/bfpiw

License: MIT License

Go 100.00%
downloader go golang golang-module goph hacktoberfest remote-execution sftp ssh ssh-agent ssh-client ssh-keys unix-systems uploader

goph's People

Contributors

antonsergeyev avatar dshemin avatar fernandezvara avatar george-zakharov avatar gjung56 avatar melbahja avatar stensonb avatar tareksaleem 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

goph's Issues

ssh -D tunneling

Any guide on ssh -D tunneling and proxycommand? Does this project support these features?

Support SSH config file

Would this package support ssh config file please?

For example with the following configuration, I want to call ssh with myhost host and the package itself autodetect the port, user, etc.:

Host myhost
  HostName 192.168.192.168
  User myuser
  Port 22

Need a function that automatically adds host to known_hosts.

Are you considering adding a function that automatically adds hosts to known_hosts for goph.Config.Callback?

For some controllable internal networks, we basically do not need to consider man-in-the-middle attacks when connecting to the server, and we use other solutions to control user login and record behavior.

ssh: handshake failed: knownhosts: key is unknown exit status 1

Not able to ssh to EC2 instance . Below is the error
ssh: handshake failed: knownhosts: key is unknown
exit status 1

package main

import (
"fmt"
"log"

"github.com/melbahja/goph"

)

func main() {

// Start new ssh connection with private key.
auth, err := goph.Key("{YOUR_PRIVATE_KEY}", "")
if err != nil {
	log.Fatal(err)
}

client, err := goph.New("ec2-user", "{YOUR_PUBLIC_IP}", auth)
if err != nil {
	log.Fatal(err)
}

// Defer closing the network connection.
defer client.Close()

// Execute your command.
out, err := client.Run("pwd")

if err != nil {
	log.Fatal(err)
}

// Get your output as []byte.
fmt.Println(string(out))

}

Making defaultPath evaluation lazy

Would you consider wrapping defaultPath in a function? The issue is on Windows environments HOME is not set by default. Simple solution would be setting it manually when the program starts:

os.Setenv("HOME", os.Getenv("USERPROFILE"))

but it doesn't get recognized by goph since it is evaluated beforehand.

Support SSH Agent

funny enough I had an internal package to a project that did most of this already. But one thing I do not see under auth is the agent. Now I know it works for Linux and Mac but unsure about Windows... For this you may need a auth_unix.go file for this to exist in so that the package still builds for other platforms just without this method.

The following function returns an ssh.AuthMethod that you use as well.

func useAgent() ssh.AuthMethod {
	sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
	if err != nil {
		log.Println(fmt.Errorf("could not find ssh agent: %w", err))
		return nil
	}
	return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
}

I can open a PR if you want, just want to know if adding a unix only method is ideal for your package.

Give precedence `~/.ssh/config` file over `$SSH_AUTH_SOCK` + ability set custom SSH socket path

Follow-up issue for: #26 (comment)


By default, goph only respects SSH_AUTH_SOCK environment variable and ignores default SSH config path. This causes incorrect behavior compared to how ssh works.

case goph.HasAgent():
		auth, err = goph.UseAgent()

ssh respects ~/.ssh/config and checks if there Host * host, then uses IdentityAgent.

We should reconsider how HasAgent will work:

func HasAgent() bool {
	return os.Getenv("SSH_AUTH_SOCK") != ""
}

We could use https://github.com/kevinburke/ssh_config to check if ssh_config.Get("*", "IdentityFile") has any value. If so, give precedence to it.

Also, we should add a new arg to use custom SSH agent socket path:

func UseAgent() (Auth, error) 

This function enforces us to set $SSH_AUTH_SOCK, but we'd prefer to set optional SSH agent socket path.

Is there a way to specify ssh options?

I'm trying to connect to older devices which only accept ssh-rsa algorithms. If I specify ssh -oHostKeyAlgorithms=+ssh-rsa in my terminal, then I can connect. How can I specify the same option through the goph module?

example question

When switch on "passphrase" as following.
flag.BoolVar(&passphrase, "passphrase", true, "ask for private key passphrase.")

it will output error message as following:
`E:\goph-1.2.1\examples\goph>gophex.exe
Enter Private Key Passphrase: panic: The handle is invalid.

goroutine 1 [running]:
main.askPass(0xb3ae66, 0x1e, 0x0, 0x0)
E:/goph-1.2.1/examples/goph/main.go:156 +0x1a5
main.getPassphrase(...)
E:/goph-1.2.1/examples/goph/main.go:168
main.main()
E:/goph-1.2.1/examples/goph/main.go:114 +0x4d7`
No chance to input Passphrase

Custom ssh port

I see this package uses port 22 and doesn't let the user change the target port.

Is there any way to handle this case?

Authentication failiure using ssh-agent

I'm trying to connect to server using code from the examples, but the authentication fails without further information:

package main

import (
	"fmt"
	"log"

	"github.com/melbahja/goph"
)

func main() {

	auth, err := goph.UseAgent()

	if err != nil {
		log.Fatal(err)
	}

	client, err := goph.New("root", "kfbox.public", auth)

	if err != nil {
		log.Fatal(err)
	}

	// Defer closing the network connection.
	defer client.Close()

	// Execute your command.
	out, err := client.Run("ls /tmp/")

	if err != nil {
		log.Fatal(err)
	}

	// Get your output as []byte.
	fmt.Println(string(out))
}
$ go run main.go
2021/11/10 17:29:47 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
exit status 1

$  ssh [email protected]
Last login: Wed Nov 10 17:23:22 2021 from 89.0.11.182
root@kfbox:~/ >

As you can see the agent authenticates without problems when using plain ssh. Is there any way to figure out why this is failing?

Can file transfers be via SCP?

Hello,

I am developing a cross-platform Go project for Windows, Linux, and MaxOSX which needs low latency secure file transfers. I have read that SCP is significantly faster than SFTP for the actual file transfers, but that SCP does not support file operations that SFTP does.

In your goph library I am wondering if the transfers are SCP or SFTP?

Thanks

Prompt to Add to knownhosts on new connections

This is an ssh.HostKeyCallback function I came up with and probably needs to be cleaned up. This was apart of an internal package I created for ssh connections. It doesn't print fingerprints as it should. But it was ignored because the tool I was creating was an internal project.

func knownHostKey(hostname string, remote net.Addr, key ssh.PublicKey) error {
	knownFile := os.ExpandEnv("$HOME/.ssh/known_hosts")
	_, err := os.Stat(knownFile)
	if errors.Is(err, os.ErrNotExist) {
		f, ferr := os.OpenFile(knownFile, os.O_CREATE, 0600)
		if ferr != nil {
			return ferr
		}
		f.Close()
	}
	callback, err := knownhosts.New(knownFile)
	if err != nil {
		return err
	}
	err = callback(hostname, remote, key)
	if err == nil {
		return nil
	}
	var keyErr *knownhosts.KeyError
	if errors.As(err, &keyErr) && len(keyErr.Want) == 0 {
		r := bufio.NewReader(os.Stdin)
		fmt.Print("Unknown Host. Would you like to add it [y/N]: ")
		reply, rerr := r.ReadString('\n')
		if rerr != nil {
			return rerr
		}
		reply = strings.Replace(reply, "\n", "", -1)
		reply = strings.Replace(reply, "\r", "", -1)
		if strings.ToLower(reply) != "y" {
			return err
		}
		f, ferr := os.OpenFile(knownFile, os.O_WRONLY|os.O_APPEND, 0600)
		if ferr != nil {
			return ferr
		}
		defer f.Close()
		knownHost := knownhosts.Normalize(remote.String())
		_, err = f.WriteString(knownhosts.Line([]string{knownHost}, key) + "\n")
		if err != nil {
			return err
		}
		return nil
	}
	return err
}

I can add a PR if this is something you would like to support in this package.

No support for commands with sudo

When trying to execute command with sudo, e.g. sudo sleep 3, I receive the error:

sudo: no tty present and no askpass program specified

2021/08/20 17:52:03 Process exited with status 1
exit status 1

Example code:

package main

import (
	"log"
	"fmt"
	"github.com/melbahja/goph"
)

func main() {

	// Start new ssh connection with password
	auth := goph.Password("12345")

	client, err := goph.New("testssh", "172.16.1.10", auth)
	if err != nil {
		log.Fatal(err)
	}

	// Defer closing the network connection.
	defer client.Close()

	// Execute your command.
	out, err := client.Run("sudo sleep 3")

	if err != nil {
	        fmt.Println(string(out))
		log.Fatal(err)
	}

	// Get your output as []byte.
	fmt.Println(string(out))
}

how to connect with private key content, without file path

Thanks for this awesome library,
I have a problem currently with the connection with private key,

I have a private key with an actual content exist int the memory "the private key is not stored on the file system"
as the following:

-----BEGIN RSA PRIVATE KEY-----
key content
-----END RSA PRIVATE KEY-----

how can I use that private key to connect to the server, because it doesn't work with the following:

	authKey, _ := goph.Key(serverData.SshPrivateKey, "")
	fmt.Sprintf("------------- my dd %", authKey)
	goph.Key()
	client, err := goph.New(serverData.SshRootUsername, DEFAULT_SERVER_IP, authKey)

example question

When switch on "passphrase" as following.
flag.BoolVar(&passphrase, "passphrase", true, "ask for private key passphrase.")

it will output error message as following:
`E:\goph-1.2.1\examples\goph>gophex.exe
Enter Private Key Passphrase: panic: The handle is invalid.

goroutine 1 [running]:
main.askPass(0xb3ae66, 0x1e, 0x0, 0x0)
E:/goph-1.2.1/examples/goph/main.go:156 +0x1a5
main.getPassphrase(...)
E:/goph-1.2.1/examples/goph/main.go:168
main.main()
E:/goph-1.2.1/examples/goph/main.go:114 +0x4d7`
No chance to input Passphrase

Custom "port" ?

There are a few places (including command line flags) where it is mentioned but I don't see it being actually used. Is it implemented? If yes, how is it set?

How can I escape ```client.Run(cmd)``` loop?

Question: How can I escape the client.Run(cmd) loop?
When I call client.Run(cmd) or cmd.Execute(myCmd) my program can't go to the next tasks.
I am trying to run the command in the background of the remote host. then Close connection and allow the app to do other things.

defer sshClient.Close()         // The app cannot close this 
 _, _:= sshClient.Run(myCmd + "&")     // Infinite loop.
            
someEsleTasks()                // The app cannot execute this line 

Currently I'm skipping Runc(cmd) and jumping to the next code via goroutine. like this

       go func() {
           _, _:= sshClient.Run(myCmd + "&")     // Infinite loop.

           sshClient.Close()             // The app cannot execute this line 
       }()

I'm looking for a logical solution to my problem.
thank you so much

Issue with known hosts

I believe this is not completely correct. I have have a host (NAME) I should be saving that first and then the remote IP or both but right now it only save the remote IP.. but the Check for Knownhost seems to use the host rather than IP so I get a mistmatched

goph/hosts.go

Line 99 in f67d58a

knownHost := knownhosts.Normalize(remote.String())

Also I was looking at https://www.godoc.org/golang.org/x/crypto/ssh#HostKeyCallback and it seems that It receives the hostname as passed to Dial or NewClientConn

I think the logic should check if host and remote are the same otherwise write the host if not matching

Create READ/WRITE timeout after 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!

upload multiple files with same sftp connection

I noticed that when trying to upload in sequence multiple files (calling the Upload function for one file after the other) at some point Linux hosts will kick you out with a "administratively prohibited: open failed".
Is there a way around it?

/root/.ssh/known_hosts required?

I'm getting this error using simple password authentication:

open /root/.ssh/known_hosts: no such file or directory

creating the empty files seems to solve it

Invalid Handle problem

Hello,

I just tried to use goph to test it one of my servers that has ssh, but get an error:

test> go run .\examples\goph\main.go --ip 192.168.0.35 --pass --cmd ls
go: downloading github.com/pkg/sftp v1.13.4
go: downloading golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
go: downloading golang.org/x/sys v0.0.0-20211031064116-611d5d643895
Enter SSH Password: panic: The handle is invalid.

goroutine 1 [running]:
main.askPass({0x5ebee3, 0xd})
        D:/dMUSE/SCP/test07/test/examples/goph/main.go:169 +0xe5 main.main()
        D:/dMUSE/SCP/test07/test/examples/goph/main.go:116 +0x13e exit status 2

Any thoughts on how to fix this?
Thanks

Download files

I get this error "no new variables on left side of :="
on this line of code
err := client.Download("/path/to/remote/file", "/path/to/local/file")

what can i do???

Support for multiple Run() commands

In a similar vein to #13, is it possible to Run() multiple commands in a single session?

This fails with a panic on my system... a single command works just fine of course... single-command working copy, ref: route_views_go git hash 5e4bb51.

Doing this may require something similar to go-expect. Will goph support this functionality natively, or do I need a third-party library?

package main

import (
        "fmt"

        "github.com/gleich/logoru"
        "github.com/melbahja/goph"
)

func main() {

        // route-views username 'rviews' with no password
        client01, err := goph.New("rviews", "route-views.routeviews.org", goph.Password(""))
        if err != nil {
                logoru.Critical(err.Error())
        }
        defer client01.Close()

        // get the best BGP route info for 4.2.2.2
        cmdout01, err := client01.Run(fmt.Sprintf("show ip bgp %s bestpath", "4.2.2.2"))
        if err != nil {
                logoru.Critical(err.Error())
        }

        // get the best BGP route info for 8.8.8.8
        cmdout02, err := client01.Run(fmt.Sprintf("show ip bgp %s bestpath", "8.8.8.8"))
        if err != nil {
                logoru.Critical(err.Error())
        }

        logoru.Info(string(cmdout01))
        logoru.Info(string(cmdout02))
}

panic: sftp: "Failure" (SSH_FX_FAILURE)

Here is my code:

	err = client.Upload("./test.txt", "/root/")
	if err != nil {
		panic(err)
	}

it will raise error(sftp: "Failure" (SSH_FX_FAILURE)) when remotePath is not full path.
It is necessary to adapt the path without a file name

Add support to connect via ProxyJump option

Hi would you consider adding option to connect to target via jump host? My flow is that I have setup ProxyJump option in my SSH config and I do all my SSH connection via bastion host. I would like some simple API to specify the jump host connection.

How to get session.stdout change events?

I want to do something when the session.stdout is changed.
Now I can only judge whether the length is zero through the timer loop, which seems very unfriendly to performance.
For example.

	ticker := time.NewTicker(time.Duration(100) * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			data := session.Output.Bytes()
			if len(data) > 0 {
				// do something
			}
		case <-cancel:
			return
		}
	}

support pkcs#11 and ms capi ?

Can hardware based PKI be added? For example, yubikey opensc feitan, these manufacturers provide PKCS # 11 library
For windows, you can use Microsoft CryptoAPI to access the supported USBKEY.

X Forwarding

Hi, Thank you, nice work.

Is there any possibility to support X Forwarding? if yes, how it could be possible.

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.