golang goroutine use SSHAgent auth doesn't work well and throw some unexpect panic

huangapple go评论71阅读模式
英文:

golang goroutine use SSHAgent auth doesn't work well and throw some unexpect panic

问题

我正在为在许多不同主机上并行执行命令编写一个小工具。所有主机都可以使用相同的私钥登录。因此,我想使用SSHAgent来获取授权登录。当我在单个主机上使用它时,它运行良好。但是当我在许多goroutine中使用它时,它不起作用。我非常困惑,有人可以帮助我吗?非常感谢。

使用SSHAgent的代码如下:

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
}

使用通道的代码如下:

func testchannel(ch chan int, uname, host, cmd string) error {
    result, err := ExcuteRemote(uname, host, cmd)
    if result {
        fmt.Println(host + " ok")
    } else {
        fmt.Println(err)
        fmt.Println(host + " failed")
    }
    ch <- 1
    return nil
}

在主函数中:

if *list != "" {
    runtime.GOMAXPROCS(runtime.NumCPU())
    hosts, err := ReadLine(*list)
    if nil != err {
        panic(err.Error())
    }
    chs := make([]chan int, len(hosts))
    for index := 0; index < len(hosts); index++ {
        chs[index] = make(chan int)
        go testchannel(chs[index], uname, hosts[index], *cmd)
    }
    for _, ch := range chs {
        <-ch
    }
}

出现的错误如下:

panic: unreachable

goroutine 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 +0x472
golang.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 +0x71
golang.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 +0x5ee
golang.org/x/crypto/ssh.(*connection).clientAuthenticate(0xc2080b8080, 0xc2080b2000, 0x0, 0x0)
        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client_auth.go:34 +0x4e3
golang.org/x/crypto/ssh.(*connection).clientHandshake(0xc2080b8080, 0xc20801e4c0, 0x11, 0xc2080b2000, 0x0, 0x0)
        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client.go:112 +0x62e
golang.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 +0x140
golang.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 +0xf9
main.ExcuteRemote(0xc20802a954, 0x4, 0xc20802a9a0, 0xe, 0x7fffa171b7fd, 0x6, 0xc200000000, 0x0, 0x0)
        /home/myname/easyssh/r.go:126 +0x83f
main.testchannel(0xc208036120, 0xc20802a954, 0x4, 0xc20802a9a0, 0xe, 0x7fffa171b7fd, 0x6, 0x0, 0x0)
        /home/myname/easyssh/r.go:217 +0x8c
created by main.main
        /home/myname/gocode/easyssh/r.go:262 +0x638
英文:

I am writing a little tool for executing commands in parallel on many different hosts. All the hosts could login with a same private key. So, I want to use SSHAgent to get auth to login. When I use it for a single host, it works well. But when I use it in many goroutines, it doesn't work. I am very confused, is there somebody can help me? Thanks very much.
The code used sshagent are as below:

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(&quot;unix&quot;, os.Getenv(&quot;SSH_AUTH_SOCK&quot;)); err == nil {
                fmt.Println(&quot;get sock&quot;)
                auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))
                defer sshAgent.Close()
        }

        config := &amp;ssh.ClientConfig{
                User: uname,
                Auth: auths,
        }
        fmt.Println(&quot;after config&quot;)

        client, err := ssh.Dial(&quot;tcp&quot;, ip+&quot;:&quot;+&quot;22&quot;, config)
        if err != nil {
                fmt.Println(&quot;something wrong when dial&quot;)
                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 = &amp;b
        err = session.Run(cmd)
        if err != nil {
            return false,err
        }

        fmt.Println(b.String())
        return true,nil
}

And the code used channel are as below :

func testchannel(ch chan int, uname,host,cmd string) error{
        result,err := ExcuteRemote(uname,host,cmd)
        if result {
                fmt.Println(host + &quot; ok&quot;)
        } else {
                fmt.Println(err)
                fmt.Println(host + &quot; failed&quot;)
        }
        ch &lt;- 1
        return nil
} 

in main:

if *list != &quot;&quot; {
       runtime.GOMAXPROCS(runtime.NumCPU())
       hosts,err := ReadLine(*list)
       if nil != err {
               panic(err.Error())
       }
       chs := make([]chan int, len(hosts))
       for index := 0; index &lt; len(hosts); index++ {
               chs[index] = make(chan int)
               go testchannel(chs[index],uname,hosts[index],*cmd)
       }
       for _, ch := range(chs) {
               &lt;-ch
       }
}

the panic like this :

panic: unreachable

goroutine 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 +0x472
golang.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 +0x71
golang.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 +0x5ee
golang.org/x/crypto/ssh.(*connection).clientAuthenticate(0xc2080b8080, 0xc2080b2000, 0x0, 0x0)
        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client_auth.go:34 +0x4e3
golang.org/x/crypto/ssh.(*connection).clientHandshake(0xc2080b8080, 0xc20801e4c0, 0x11, 0xc2080b2000, 0x0, 0x0)
        /usr/local/gosrc/src/golang.org/x/crypto/ssh/client.go:112 +0x62e
golang.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 +0x140
golang.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 +0xf9
main.ExcuteRemote(0xc20802a954, 0x4, 0xc20802a9a0, 0xe, 0x7fffa171b7fd, 0x6, 0xc200000000, 0x0, 0x0)
        /home/myname/easyssh/r.go:126 +0x83f
main.testchannel(0xc208036120, 0xc20802a954, 0x4, 0xc20802a9a0, 0xe, 0x7fffa171b7fd, 0x6, 0x0, 0x0)
        /home/myname/easyssh/r.go:217 +0x8c
created by main.main
        /home/myname/gocode/easyssh/r.go:262 +0x638

答案1

得分: 2

我已经自己解决了这个问题。据我理解,恐慌是由多个goroutine想要使用sshagent连接到本地服务器引起的。现在,我只设置一次连接,并保持连接直到程序结束。在主函数中的代码如下:

//获取配置
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中需要一个配置时,我将配置作为参数传递给它,它工作得很好!

我的goroutine中的函数如下:

func RunInChannel(ch chan int, host, cmd string, config *ssh.ClientConfig) error {
result, _ := ExcuteRemote(host, cmd, config)
if result {
fmt.Println("\n" + host + "\t\tok\n")
} else {
ret, err := ExcuteRemote(host, cmd, config)
if ret {
fmt.Println("\n" + host + "\t\tok\n")
} else {
fmt.Println(err)
fmt.Println("\n" + host + "\t\tfailed\n")
}
}
ch <- 1
return nil
}

英文:

I have solved the problem myself. As my understanding, the panic is caused by multiple goroutines want to use the connection of sshagent to the local server. Now, I only set connection once, and keep it connected until the program over. The code in function main :

    //get config
    var config *ssh.ClientConfig
    if sshAgent, err := net.Dial(&quot;unix&quot;, os.Getenv(&quot;SSH_AUTH_SOCK&quot;)); err == nil {
            config = GetConfig(uname,sshAgent)
            defer sshAgent.Close()
    } else {
            panic(err)
    }

When I need a config in a goroutine, I pass the config as a param into it, it works well!

My function in a goroutine is as follow:

func RunInChannel(ch chan int, host,cmd string,config *ssh.ClientConfig) error{
    result,_ := ExcuteRemote(host,cmd,config)
    if result {
        fmt.Println(&quot;\n&quot; + host + &quot;\t\tok\n&quot;)
    } else {
        ret,err := ExcuteRemote(host,cmd,config)
        if ret {
            fmt.Println(&quot;\n&quot; + host + &quot;\t\tok\n&quot;)
        } else {
            fmt.Println(err)
            fmt.Println(&quot;\n&quot; + host + &quot;\t\tfailed\n&quot;)
        }
    }
    ch &lt;- 1
    return nil
}

huangapple
  • 本文由 发表于 2015年6月9日 11:00:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/30722304.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定