feat: upgrade easyssh-proxy and update flag of timeout field

This commit is contained in:
Bo-Yi Wu 2018-10-23 15:11:34 +08:00
parent 3a227c8854
commit e4e10b4143
5 changed files with 63 additions and 35 deletions

View File

@ -81,7 +81,7 @@ func main() {
Usage: "connection timeout", Usage: "connection timeout",
EnvVar: "PLUGIN_TIMEOUT,SSH_TIMEOUT", EnvVar: "PLUGIN_TIMEOUT,SSH_TIMEOUT",
}, },
cli.IntFlag{ cli.DurationFlag{
Name: "command.timeout,T", Name: "command.timeout,T",
Usage: "command timeout", Usage: "command timeout",
EnvVar: "PLUGIN_COMMAND_TIMEOUT,SSH_COMMAND_TIMEOUT", EnvVar: "PLUGIN_COMMAND_TIMEOUT,SSH_COMMAND_TIMEOUT",
@ -200,7 +200,7 @@ func run(c *cli.Context) error {
Host: c.StringSlice("host"), Host: c.StringSlice("host"),
Port: c.Int("port"), Port: c.Int("port"),
Timeout: c.Duration("timeout"), Timeout: c.Duration("timeout"),
CommandTimeout: c.Int("command.timeout"), CommandTimeout: c.Duration("command.timeout"),
Script: c.StringSlice("script"), Script: c.StringSlice("script"),
ScriptStop: c.Bool("script.stop"), ScriptStop: c.Bool("script.stop"),
Secrets: c.StringSlice("secrets"), Secrets: c.StringSlice("secrets"),

View File

@ -29,7 +29,7 @@ type (
Host []string Host []string
Port int Port int
Timeout time.Duration Timeout time.Duration
CommandTimeout int CommandTimeout time.Duration
Script []string Script []string
ScriptStop bool ScriptStop bool
Secrets []string Secrets []string

View File

@ -57,7 +57,13 @@ func main() {
Server: "example.com", Server: "example.com",
// Optional key or Password without either we try to contact your agent SOCKET // Optional key or Password without either we try to contact your agent SOCKET
//Password: "password", //Password: "password",
Key: "/.ssh/id_rsa", // 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", Port: "22",
Timeout: 60 * time.Second, Timeout: 60 * time.Second,
} }

View File

@ -9,9 +9,11 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"sync"
"time" "time"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
@ -65,7 +67,12 @@ func getKeyFile(keypath string) (ssh.Signer, error) {
return pubkey, nil return pubkey, nil
} }
func getSSHConfig(config DefaultConfig) *ssh.ClientConfig { // returns *ssh.ClientConfig and io.Closer.
// if io.Closer is not nil, io.Closer.Close() should be called when
// *ssh.ClientConfig is no longer used.
func getSSHConfig(config DefaultConfig) (*ssh.ClientConfig, io.Closer) {
var sshAgent io.Closer
// auths holds the detected ssh auth methods // auths holds the detected ssh auth methods
auths := []ssh.AuthMethod{} auths := []ssh.AuthMethod{}
@ -73,54 +80,62 @@ func getSSHConfig(config DefaultConfig) *ssh.ClientConfig {
if config.Password != "" { if config.Password != "" {
auths = append(auths, ssh.Password(config.Password)) auths = append(auths, ssh.Password(config.Password))
} }
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))
defer sshAgent.Close()
}
if config.KeyPath != "" { if config.KeyPath != "" {
if pubkey, err := getKeyFile(config.KeyPath); err == nil { if pubkey, err := getKeyFile(config.KeyPath); err != nil {
log.Printf("getKeyFile: %v\n", err)
} else {
auths = append(auths, ssh.PublicKeys(pubkey)) auths = append(auths, ssh.PublicKeys(pubkey))
} }
} }
if config.Key != "" { if config.Key != "" {
if signer, err := ssh.ParsePrivateKey([]byte(config.Key)); err == nil { if signer, err := ssh.ParsePrivateKey([]byte(config.Key)); err != nil {
log.Printf("ssh.ParsePrivateKey: %v\n", err)
} else {
auths = append(auths, ssh.PublicKeys(signer)) auths = append(auths, ssh.PublicKeys(signer))
} }
} }
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))
}
return &ssh.ClientConfig{ return &ssh.ClientConfig{
Timeout: config.Timeout, Timeout: config.Timeout,
User: config.User, User: config.User,
Auth: auths, Auth: auths,
HostKeyCallback: ssh.InsecureIgnoreHostKey(), HostKeyCallback: ssh.InsecureIgnoreHostKey(),
} }, sshAgent
} }
// connect to remote server using MakeConfig struct and returns *ssh.Session // Connect to remote server using MakeConfig struct and returns *ssh.Session
func (ssh_conf *MakeConfig) connect() (*ssh.Session, error) { func (ssh_conf *MakeConfig) Connect() (*ssh.Session, error) {
var client *ssh.Client var client *ssh.Client
var err error var err error
targetConfig := getSSHConfig(DefaultConfig{ targetConfig, closer := getSSHConfig(DefaultConfig{
User: ssh_conf.User, User: ssh_conf.User,
Key: ssh_conf.Key, Key: ssh_conf.Key,
KeyPath: ssh_conf.KeyPath, KeyPath: ssh_conf.KeyPath,
Password: ssh_conf.Password, Password: ssh_conf.Password,
Timeout: ssh_conf.Timeout, Timeout: ssh_conf.Timeout,
}) })
if closer != nil {
defer closer.Close()
}
// Enable proxy command // Enable proxy command
if ssh_conf.Proxy.Server != "" { if ssh_conf.Proxy.Server != "" {
proxyConfig := getSSHConfig(DefaultConfig{ proxyConfig, closer := getSSHConfig(DefaultConfig{
User: ssh_conf.Proxy.User, User: ssh_conf.Proxy.User,
Key: ssh_conf.Proxy.Key, Key: ssh_conf.Proxy.Key,
KeyPath: ssh_conf.Proxy.KeyPath, KeyPath: ssh_conf.Proxy.KeyPath,
Password: ssh_conf.Proxy.Password, Password: ssh_conf.Proxy.Password,
Timeout: ssh_conf.Proxy.Timeout, Timeout: ssh_conf.Proxy.Timeout,
}) })
if closer != nil {
defer closer.Close()
}
proxyClient, err := ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Proxy.Server, ssh_conf.Proxy.Port), proxyConfig) proxyClient, err := ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Proxy.Server, ssh_conf.Proxy.Port), proxyConfig)
if err != nil { if err != nil {
@ -156,7 +171,7 @@ func (ssh_conf *MakeConfig) connect() (*ssh.Session, error) {
// Stream returns one channel that combines the stdout and stderr of the command // Stream returns one channel that combines the stdout and stderr of the command
// as it is run on the remote machine, and another that sends true when the // as it is run on the remote machine, and another that sends true when the
// command is done. The sessions and channels will then be closed. // command is done. The sessions and channels will then be closed.
func (ssh_conf *MakeConfig) Stream(command string, timeout int) (<-chan string, <-chan string, <-chan bool, <-chan error, error) { func (ssh_conf *MakeConfig) Stream(command string, timeout time.Duration) (<-chan string, <-chan string, <-chan bool, <-chan error, error) {
// continuously send the command's output over the channel // continuously send the command's output over the channel
stdoutChan := make(chan string) stdoutChan := make(chan string)
stderrChan := make(chan string) stderrChan := make(chan string)
@ -164,7 +179,7 @@ func (ssh_conf *MakeConfig) Stream(command string, timeout int) (<-chan string,
errChan := make(chan error) errChan := make(chan error)
// connect to remote host // connect to remote host
session, err := ssh_conf.connect() session, err := ssh_conf.Connect()
if err != nil { if err != nil {
return stdoutChan, stderrChan, doneChan, errChan, err return stdoutChan, stderrChan, doneChan, errChan, err
} }
@ -196,18 +211,29 @@ func (ssh_conf *MakeConfig) Stream(command string, timeout int) (<-chan string,
defer close(errChan) defer close(errChan)
defer session.Close() defer session.Close()
timeoutChan := time.After(time.Duration(timeout) * time.Second) timeoutChan := time.After(timeout * time.Second)
res := make(chan bool, 1) res := make(chan struct{}, 1)
var resWg sync.WaitGroup
resWg.Add(2)
go func() { go func() {
for stdoutScanner.Scan() { for stdoutScanner.Scan() {
stdoutChan <- stdoutScanner.Text() stdoutChan <- stdoutScanner.Text()
} }
resWg.Done()
}()
go func() {
for stderrScanner.Scan() { for stderrScanner.Scan() {
stderrChan <- stderrScanner.Text() stderrChan <- stderrScanner.Text()
} }
resWg.Done()
}()
go func() {
resWg.Wait()
// close all of our open resources // close all of our open resources
res <- true res <- struct{}{}
}() }()
select { select {
@ -225,7 +251,7 @@ func (ssh_conf *MakeConfig) Stream(command string, timeout int) (<-chan string,
} }
// Run command on remote machine and returns its stdout as a string // Run command on remote machine and returns its stdout as a string
func (ssh_conf *MakeConfig) Run(command string, timeout int) (outStr string, errStr string, isTimeout bool, err error) { func (ssh_conf *MakeConfig) Run(command string, timeout time.Duration) (outStr string, errStr string, isTimeout bool, err error) {
stdoutChan, stderrChan, doneChan, errChan, err := ssh_conf.Stream(command, timeout) stdoutChan, stderrChan, doneChan, errChan, err := ssh_conf.Stream(command, timeout)
if err != nil { if err != nil {
return outStr, errStr, isTimeout, err return outStr, errStr, isTimeout, err
@ -253,7 +279,7 @@ loop:
// Scp uploads sourceFile to remote machine like native scp console app. // Scp uploads sourceFile to remote machine like native scp console app.
func (ssh_conf *MakeConfig) Scp(sourceFile string, etargetFile string) error { func (ssh_conf *MakeConfig) Scp(sourceFile string, etargetFile string) error {
session, err := ssh_conf.connect() session, err := ssh_conf.Connect()
if err != nil { if err != nil {
return err return err
@ -292,9 +318,5 @@ func (ssh_conf *MakeConfig) Scp(sourceFile string, etargetFile string) error {
} }
}() }()
if err := session.Run(fmt.Sprintf("scp -tr %s", etargetFile)); err != nil { return session.Run(fmt.Sprintf("scp -tr %s", etargetFile))
return err
}
return nil
} }

10
vendor/vendor.json vendored
View File

@ -3,12 +3,12 @@
"ignore": "test", "ignore": "test",
"package": [ "package": [
{ {
"checksumSHA1": "EcF7T9tPEMMJfuRdPBB3NdRUg4c=", "checksumSHA1": "s2s4GT8UfsxkawhCzjkKWi/XWLI=",
"path": "github.com/appleboy/easyssh-proxy", "path": "github.com/appleboy/easyssh-proxy",
"revision": "33d87eae3a018c3312e32cc4eb4578d5a563aabd", "revision": "9b6972862812dafd568ffb2cfc7b49510af7d502",
"revisionTime": "2017-05-16T07:22:25Z", "revisionTime": "2018-10-17T14:39:14Z",
"version": "1.1.6", "version": "master",
"versionExact": "1.1.6" "versionExact": "master"
}, },
{ {
"checksumSHA1": "dvabztWVQX8f6oMLRyv4dLH+TGY=", "checksumSHA1": "dvabztWVQX8f6oMLRyv4dLH+TGY=",