go: x/crypto/ssh: Signal method doesn't work
Starting a process over SSH using the /x/crypto/ssh library and then trying to send a signal to it, seems to have no effect. I’ve found a few musings on the internet about users with the same problem.
I’ve attached a full reproducer, including the crappy workaround.
package main
import (
"bytes"
"fmt"
"golang.org/x/crypto/ssh"
"time"
)
func main() {
// An SSH client is represented with a ClientConn.
//
// To authenticate with the remote server you must pass at least one
// implementation of AuthMethod via the Auth field in ClientConfig.
config := &ssh.ClientConfig{
User: "someuser", // XXX
Auth: []ssh.AuthMethod{
ssh.Password("somepass"), // XXX
},
}
client, err := ssh.Dial("tcp", "localhost:22", config)
if err != nil {
panic("Failed to dial: " + err.Error())
}
// Each ClientConn can support multiple interactive sessions,
// represented by a Session.
session, err := client.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
defer session.Close()
// Once a Session is created, you can execute a single command on
// the remote side using the Run method.
var b bytes.Buffer
session.Stdout = &b
go func() {
time.Sleep(2 * time.Second)
// XXX none of these signals work! :(
// fmt.Println("Running signal!")
// session.Signal(ssh.SIGINT)
// session.Signal(ssh.SIGKILL)
// session.Signal(ssh.SIGQUIT)
// session.Signal(ssh.SIGHUP)
// session.Signal(ssh.SIGPIPE)
// fmt.Println("Done running signal!")
s2, err := client.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
defer s2.Close()
var c bytes.Buffer
s2.Stdout = &c
if err := s2.Run("echo start && pidof sleep && killall sleep"); err != nil {
fmt.Println("s2 error!")
}
fmt.Println("Sig run done!")
fmt.Println(c.String())
}()
if err := session.Run("echo $0 && /usr/bin/sleep 10s"); err != nil {
if e, ok := err.(*ssh.ExitError); ok {
fmt.Printf("Remote: Exit msg: %s", e.Waitmsg.Msg()) // XXX (does this ever return anything useful?)
fmt.Printf("Remote: Exit signal: %s", e.Waitmsg.Signal())
fmt.Printf("Remote: Error: Output...\n%s", b.String())
fmt.Printf("Exited (%d) with: %s", e.Waitmsg.ExitStatus(), e.Error())
} else if e, ok := err.(*ssh.ExitMissingError); ok {
fmt.Printf("Exit code missing: %s", e.Error())
}
panic("Failed to run: " + err.Error())
}
fmt.Println("Done!")
fmt.Println(b.String())
}
Uncomment the signal you want, and you’ll see that they all do nothing to kill the running sleep command.
Tested with go version: 1.5.4 (but I have no reason to expect this is fixed with newer versions!) on Fedora GNU/Linux 24 as the SSH server, running OpenSSH.
Thanks!
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 1
- Comments: 17 (6 by maintainers)
I was able to verify that this issue is resolved in OpenSSH 1.7.9 by running the following code against two versions of sshd on Ubuntu 18.04 LTS (bionic).
Running against Ubuntu Bionic (18.04 LTS) openssh-server 1.7.6
Again with pinned Ubuntu Disco(19.04) package openssh-server 1.7.9
As I’ve discovered elsewhere, sending
0x03(or anything viaSignal) only seems to work if you’ve requested that a pseudo TTY be allocated.I have asked about the open ticket a couple times in the last few months and never got a reply.
https://marc.info/?l=openssh-unix-dev&m=151872213119286&w=2 https://marc.info/?l=openssh-unix-dev&m=152104163506271&w=2
It looks like the fix was finally integrated into openssh version 7.9:
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/session.c.diff?r1=1.305&r2=1.306&f=h
though I haven’t verified yet that it works.
God I though that was my code’s problem… This lib is awesome, hope this problem can be fixed soon. thank you. I guess i have to hack this problem for now.
I’m hitting this too. I’d even be happy with ssh client workarounds. I’m using to go crypto/ssh package to run interactive sessions and am facing problems with signal handling not propagating and other terminal related hell like getting vi to work on the server side session.