From cbd1e30dead58a0cb59611cbf8ac7debec2f5af5 Mon Sep 17 00:00:00 2001 From: SmsS Date: Sat, 29 Jan 2022 14:37:37 +0330 Subject: [PATCH 1/5] Add sudo --- client.go | 18 +++++++++++++++++- cmd.go | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index 0e4267c..a281617 100644 --- a/client.go +++ b/client.go @@ -5,10 +5,12 @@ package goph import ( "context" + "errors" "fmt" "io" "net" "os" + "strings" "time" "github.com/pkg/sftp" @@ -29,11 +31,17 @@ type Config struct { Port uint Timeout time.Duration Callback ssh.HostKeyCallback + Pass string } // DefaultTimeout is the timeout of ssh client connection. var DefaultTimeout = 20 * time.Second +// Set sudo password +func (client *Client) SetPass(pass string) { + client.Config.Pass = pass +} + // New starts a new ssh connection, the host public key must be in known hosts. func New(user string, addr string, auth Auth) (c *Client, err error) { @@ -92,7 +100,15 @@ func Dial(proto string, c *Config) (*ssh.Client, error) { // Run starts a new SSH session and runs the cmd, it returns CombinedOutput and err if any. func (c Client) Run(cmd string) ([]byte, error) { - + if strings.HasPrefix(cmd, "sudo") { + if c.Config.Pass == "" { + return nil, errors.New("Config.Pass is not set") + } + // if c.Config.Pass {} + /// you have to run sudo commands like this: + /// echo '[password]' | sudo -S [command] + cmd = "echo " + c.Config.Pass + "| sudo -S " + cmd[5:] + } var ( err error sess *ssh.Session diff --git a/cmd.go b/cmd.go index 3a4608f..2178463 100644 --- a/cmd.go +++ b/cmd.go @@ -6,9 +6,10 @@ package goph import ( "context" "fmt" + "strings" + "github.com/pkg/errors" "golang.org/x/crypto/ssh" - "strings" ) // Cmd it's like os/exec.Cmd but for ssh session. From 2abcf30fcdf5530d8ed10d93e771b3a227d77b27 Mon Sep 17 00:00:00 2001 From: SmsS Date: Sun, 30 Jan 2022 17:48:35 +0330 Subject: [PATCH 2/5] Add config ssh --- client.go | 42 ++++++++++++++++++++++++++---------------- go.mod | 1 + go.sum | 2 ++ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/client.go b/client.go index a281617..ba416a4 100644 --- a/client.go +++ b/client.go @@ -5,14 +5,14 @@ package goph import ( "context" - "errors" "fmt" "io" + "log" "net" "os" - "strings" "time" + "github.com/kevinburke/ssh_config" "github.com/pkg/sftp" "golang.org/x/crypto/ssh" ) @@ -31,20 +31,26 @@ type Config struct { Port uint Timeout time.Duration Callback ssh.HostKeyCallback - Pass string } // DefaultTimeout is the timeout of ssh client connection. var DefaultTimeout = 20 * time.Second -// Set sudo password -func (client *Client) SetPass(pass string) { - client.Config.Pass = pass +// NewWithConfigFile starts a new ssh connection with given config file, the host public key must be in known hosts. +func NewWithConfigFile(name string) (*Client, error) { + auth, err := Key(ssh_config.Get(name, "IdentityFile"), "") + if err != nil { + log.Fatal(err) + } + return New( + ssh_config.Get(name, "User"), + ssh_config.Get(name, "HostName"), + auth, + ) } // New starts a new ssh connection, the host public key must be in known hosts. func New(user string, addr string, auth Auth) (c *Client, err error) { - callback, err := DefaultKnownHosts() if err != nil { @@ -77,6 +83,18 @@ func NewUnknown(user string, addr string, auth Auth) (*Client, error) { }) } +func NewWithConfigFileUnknown(name string) (*Client, error) { + auth, err := Key(ssh_config.Get(name, "IdentityFile"), "") + if err != nil { + log.Fatal(err) + } + return NewUnknown( + ssh_config.Get(name, "User"), + ssh_config.Get(name, "HostName"), + auth, + ) +} + // NewConn returns new client and error if any. func NewConn(config *Config) (c *Client, err error) { @@ -100,15 +118,7 @@ func Dial(proto string, c *Config) (*ssh.Client, error) { // Run starts a new SSH session and runs the cmd, it returns CombinedOutput and err if any. func (c Client) Run(cmd string) ([]byte, error) { - if strings.HasPrefix(cmd, "sudo") { - if c.Config.Pass == "" { - return nil, errors.New("Config.Pass is not set") - } - // if c.Config.Pass {} - /// you have to run sudo commands like this: - /// echo '[password]' | sudo -S [command] - cmd = "echo " + c.Config.Pass + "| sudo -S " + cmd[5:] - } + var ( err error sess *ssh.Session diff --git a/go.mod b/go.mod index c92865c..61ef6fc 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/melbahja/goph go 1.13 require ( + github.com/kevinburke/ssh_config v1.1.0 // indirect github.com/pkg/errors v0.9.1 github.com/pkg/sftp v1.13.4 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 diff --git a/go.sum b/go.sum index 6b463d2..0e8ac2e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o= +github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= From 7be676abfef43ec60449004f5048074e917c72ec Mon Sep 17 00:00:00 2001 From: SmsS4 Date: Mon, 18 Sep 2023 00:34:53 +0330 Subject: [PATCH 3/5] Returns instead of fatal log --- client.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/client.go b/client.go index ba416a4..6ea5e46 100644 --- a/client.go +++ b/client.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "io" - "log" "net" "os" "time" @@ -36,11 +35,19 @@ type Config struct { // DefaultTimeout is the timeout of ssh client connection. var DefaultTimeout = 20 * time.Second +func GetAuth(host string) (Auth, error) { + auth, err := Key(ssh_config.Get(host, "IdentityFile"), "") + if err != nil { + return nil, fmt.Errorf("get key: %w", err) + } + return auth, nil +} + // NewWithConfigFile starts a new ssh connection with given config file, the host public key must be in known hosts. func NewWithConfigFile(name string) (*Client, error) { - auth, err := Key(ssh_config.Get(name, "IdentityFile"), "") + auth, err := GetAuth(name) if err != nil { - log.Fatal(err) + return nil, err } return New( ssh_config.Get(name, "User"), @@ -84,9 +91,9 @@ func NewUnknown(user string, addr string, auth Auth) (*Client, error) { } func NewWithConfigFileUnknown(name string) (*Client, error) { - auth, err := Key(ssh_config.Get(name, "IdentityFile"), "") + auth, err := GetAuth(name) if err != nil { - log.Fatal(err) + return nil, err } return NewUnknown( ssh_config.Get(name, "User"), From 160047f5f808183597be99f79fbb07d271ec2a9b Mon Sep 17 00:00:00 2001 From: SmsS4 Date: Thu, 21 Sep 2023 00:28:23 +0330 Subject: [PATCH 4/5] Use NewConn instead of New --- client.go | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/client.go b/client.go index db5bff0..3b08b78 100644 --- a/client.go +++ b/client.go @@ -6,6 +6,8 @@ package goph import ( "context" "fmt" + "strconv" + "strings" "io" "net" "os" @@ -44,17 +46,39 @@ func GetAuth(host string) (Auth, error) { return auth, nil } +func getSshConfig(alias string, key string) string { + // to fix https://github.com/kevinburke/ssh_config/issues/61 issue + return strings.Trim(ssh_config.Get(alias, key), "\"") +} + +func NewWithConfigFileHostKeyCallback(name string, callback ssh.HostKeyCallback) (*Client, error){ + auth, err := GetAuth(name) + if err != nil { + return nil, err + } + port, err := strconv.Atoi(getSshConfig(name, "Port")) + if err != nil { + return nil, err + } + + return NewConn(&Config{ + User: getSshConfig(name, "User"), + Addr: getSshConfig(name, "HostName"), + Port: uint(port), + Auth: auth, + Timeout: DefaultTimeout, + Callback: callback, + }) + +} + // NewWithConfigFile starts a new ssh connection with given config file, the host public key must be in known hosts. func NewWithConfigFile(name string) (*Client, error) { - auth, err := GetAuth(name) + callback, err := DefaultKnownHosts() if err != nil { return nil, err } - return New( - ssh_config.Get(name, "User"), - ssh_config.Get(name, "HostName"), - auth, - ) + return NewWithConfigFileHostKeyCallback(name, callback) } // New starts a new ssh connection, the host public key must be in known hosts. @@ -92,15 +116,7 @@ func NewUnknown(user string, addr string, auth Auth) (*Client, error) { } func NewWithConfigFileUnknown(name string) (*Client, error) { - auth, err := GetAuth(name) - if err != nil { - return nil, err - } - return NewUnknown( - ssh_config.Get(name, "User"), - ssh_config.Get(name, "HostName"), - auth, - ) + return NewWithConfigFileHostKeyCallback(name, ssh.InsecureIgnoreHostKey()) } // NewConn returns new client and error if any. From 8e7a79cb75d48b24c6f941edd9ad8aa547ab9834 Mon Sep 17 00:00:00 2001 From: SmsS4 Date: Thu, 21 Sep 2023 00:30:38 +0330 Subject: [PATCH 5/5] Fix GetAuth --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 3b08b78..07e37cf 100644 --- a/client.go +++ b/client.go @@ -39,7 +39,7 @@ type Config struct { var DefaultTimeout = 20 * time.Second func GetAuth(host string) (Auth, error) { - auth, err := Key(ssh_config.Get(host, "IdentityFile"), "") + auth, err := Key(getSshConfig(host, "IdentityFile"), "") if err != nil { return nil, fmt.Errorf("get key: %w", err) }