melbahja / goph Goto Github PK
View Code? Open in Web Editor NEW🤘 The native golang ssh client to execute your commands over ssh connection. 🚀🚀
Home Page: http://git.io/bfpiw
License: MIT License
🤘 The native golang ssh client to execute your commands over ssh connection. 🚀🚀
Home Page: http://git.io/bfpiw
License: MIT License
Any guide on ssh -D tunneling and proxycommand? Does this project support these features?
os/exec.Cmd, cmd:=exec.Command(); cmd.Dir="/data/" ;
Why Goph.Cmd do not have cmd.Dir,Set command execution directory
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
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.
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))
}
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.
Hello!
It seems that goph currently does not support two-factor auth with a key AND a password. See https://security.stackexchange.com/questions/17931/possible-to-use-both-private-key-and-password-authentication-for-ssh-login. Thank you!
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.
when i use private key, has error, pls fix
How can i make an interactive shell using this package?
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.
خويا تبارك الله عليك. خدمة نقية. هاد المكتبة عاونتني بزاف.
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?
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
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?
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?
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
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.
See golang/go#26643
The relevant fix can be seen here: https://github.com/cybozu-go/cke/pull/81/files
I hit this using the latest goph
client on a remote machine with a badly behaving SSH Server.
Workaround is to timeout the channel in which the goph
calls are being executed.
select {
case result := <- someChannel:
fmt.Println(result)
case <- time.After(timeout * time.Second):
fmt.Println("ssh new session was hung")
}
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))
}
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)
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
Hi,
Could someone tell me how to specify a custom known_hosts file?
Thanks!
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?
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
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
Line 99 in f67d58a
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
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!
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?
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
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
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???
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))
}
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
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.
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
}
}
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.
How i can login with password in goph.NewConn? :)
Hi, Thank you, nice work.
Is there any possibility to support X Forwarding? if yes, how it could be possible.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.