在goroutine里并发调用sshagent出现panic的解决方案

来源:互联网 发布:k均值聚类算法opencv 编辑:程序博客网 时间:2024/06/04 11:39

之前在做一个批量处理工具的时候遇到一个棘手的问题,好几天都没有解决,把问题发到了stackoverflow,但是也没有得到一个好的回复,后来自己解决并回答了这个问题。问题地址:http://stackoverflow.com/questions/30722304/golang-goroutine-use-sshagent-auth-not-work-well-and-throw-some-unexpect-panic

在使用goroutine实现并发登陆设备时,多个goroutine同时调用sshagent就出现了如下错误:

panic: unreachablegoroutine 5 [running]:golang.org/x/crypto/ssh/agent.(*client).Sign(0xc20801e440, 0x7f053381a9f8, 0xc208094100, 0xc208121b00, 0x201, 0x240, 0xc2080a6009, 0x0, 0x0)        /usr/local/gosrc/src/golang.org/x/crypto/ssh/agent/client.go:342 +0x472golang.org/x/crypto/ssh/agent.(*agentKeyringSigner).Sign(0xc20801e560, 0x7f053380fb38, 0xc20803c2d0, 0xc208121b00, 0x201, 0x240, 0xe, 0x0, 0x0)        /usr/local/gosrc/src/golang.org/x/crypto/ssh/agent/client.go:562 +0x71golang.org/x/crypto/ssh.publicKeyCallback.auth(0xc20801e480, 0xc2080a4040, 0x14, 0x20, 0xc20802a954, 0x4, 0x7f053381a998, 0xc2080ca000, 0x7f053380fb38, 0xc20803c2d0, ...)        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client_auth.go:210 +0x5eegolang.org/x/crypto/ssh.(*connection).clientAuthenticate(0xc2080b8080, 0xc2080b2000, 0x0, 0x0)        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client_auth.go:34 +0x4e3golang.org/x/crypto/ssh.(*connection).clientHandshake(0xc2080b8080, 0xc20801e4c0, 0x11, 0xc2080b2000, 0x0, 0x0)        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client.go:112 +0x62egolang.org/x/crypto/ssh.NewClientConn(0x7f05338111e8, 0xc2080aa000, 0xc20801e4c0, 0x11, 0xc2080420a0, 0x0, 0x0, 0x0, 0x41be71, 0x0, ...)        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client.go:74 +0x140golang.org/x/crypto/ssh.Dial(0x785a50, 0x3, 0xc20801e4c0, 0x11, 0xc2080420a0, 0x11, 0x0, 0x0)        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client.go:176 +0xf9main.ExcuteRemote(0xc20802a954, 0x4, 0xc20802a9a0, 0xe, 0x7fffa171b7fd, 0x6, 0xc200000000, 0x0, 0x0)        /home/myname/easyssh/r.go:126 +0x83fmain.testchannel(0xc208036120, 0xc20802a954, 0x4, 0xc20802a9a0, 0xe, 0x7fffa171b7fd, 0x6, 0x0, 0x0)        /home/myname/easyssh/r.go:217 +0x8ccreated by main.main        /home/myname/gocode/easyssh/r.go:262 +0x638
每个goroutine里面执行的内容如下:

func ExcuteRemote(uname,host,cmd string) (bool,error) {        ip,err := GetIp(host)        if err != nil {                fmt.Println(err)                return false,err        }        auths := []ssh.AuthMethod{}        if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {                fmt.Println("get sock")                auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))                defer sshAgent.Close()        }        config := &ssh.ClientConfig{                User: uname,                Auth: auths,        }        fmt.Println("after config")        client, err := ssh.Dial("tcp", ip+":"+"22", config)        if err != nil {                fmt.Println("something wrong when dial")                fmt.Println(err)                return false,err        }        session, err := client.NewSession()        if err != nil {                return false,err        }        defer session.Close()        var b bytes.Buffer        session.Stdout = &b        err = session.Run(cmd)        if err != nil {            return false,err        }        fmt.Println(b.String())        return true,nil}

出现以上报错的关键就在于获取认证的时候,是通过sshagent获取,即采用了ssh协议中的forwarding的功能,而不是直接使用密码或者私钥。最终解决这个报错的时候,我才去的方式是不在每个goroutine里面去连接sshagent获取认证,而是在主函数里面获取一次认证,并保持连接,所有的并发里面使用主函数里面获取的认证,所有goroutine执行完成之后释放和sshagent的连接。

获取认证如下(main函数里面):

var config *ssh.ClientConfig    if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {            config = GetConfig(uname,sshAgent)            defer sshAgent.Close()    } else {            panic(err)    }
向每个goroutine里面传递参数config,goroutine执行的函数如下:

func RunInChannel(ch chan int, host,cmd string,config *ssh.ClientConfig) error{    result,_ := ExcuteRemote(host,cmd,config)    if result {        //do something    } else {        ret,err := ExcuteRemote(host,cmd,config)        if ret {            //do something        } else {            fmt.Println(err)        }    }    ch <- 1    return nil}


0 0
原创粉丝点击