Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/crypto/ssh: cannot send a Signal to remote process without Pty #48715

Open
nskforward opened this issue Oct 1, 2021 · 3 comments
Open

x/crypto/ssh: cannot send a Signal to remote process without Pty #48715

nskforward opened this issue Oct 1, 2021 · 3 comments
Labels
NeedsInvestigation
Milestone

Comments

@nskforward
Copy link

@nskforward nskforward commented Oct 1, 2021

What version of Go are you using (go version)?

$ go version
go version go1.17.1 darwin/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/a17847869/Library/Caches/go-build"
GOENV="/Users/a17847869/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/a17847869/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/a17847869/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.17.1"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/9y/91hpvx4d4kj786gtvh7j45180000gp/T/go-build1991588269=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

  1. I created a simple program that will run on remote machine via SSH:
func main() {
    fmt.Println("> remote program started")
    ch := make(chan os.Signal, 1)
    signal.Notify(ch, syscall.SIGINT)
    sig := <-ch
    fmt.Println("\n> remote program got signal:", sig)
    fmt.Println("> remote program finished")
}
  1. Compiled that code and deployed executable file to remote machine
  2. From local machine let's check that everything works as expected using local ssh command:
$ ssh -t {user}@{IP} /path/to/executable/file
> remote program started
^C
> remote program got signal: interrupt
> remote program finished
Connection to {IP} closed.

Everything looks good so far. When I push Ctrl+C on my keyboard I see the correct message from remote program that signal catches. Now let's repeat the same behavior using x/crypto/ssh:

func main(){
    var conn *ssh.Client
    /* skip connection and client initialization code */
    session, err := conn.NewSession()
    if err != nil {
        log.Fatalf("conn.NewSession failed": %v", err)
    }
    defer session.Close()

    stdin, err := session.StdinPipe()
    if err != nil {
	log.Fatalf("session.StdinPipe failed: %v", err)
    }
    session.Stdout = os.Stdout
    session.Stderr = os.Stderr

    err = session.Start("/path/to/executable/file")
    if err != nil {
	log.Fatalf("session.Start failed: %v", err)
    }

    // wait some time before interrupt
    time.Sleep(5*time.Second)
   
    // try to send SIGINT signal
    err = session.Signal(ssh.SIGINT)
    if err != nil {
	log.Fatalf("session.Signal failed: %v", err)
    }

    // Also try to simulate Ctrl+C
    _, err = stdin.Write([]byte("\x03"))
    if err != nil {
	log.Fatalf("stdin.Write failed: %v", err)
    }
    stdin.Close()
    
    time.Sleep(2 * time.Second)
    err = session.Close()
    if err != nil {
	log.Fatalf("session.Close failed:", err)
    }    

    err := session.Wait()
    if err != nil {
	log.Printf("session.Wait error: %v\n", err)
    }
    log.Println("local program finished")
}

What did you expect to see?

When I run above code I would see the following output:

> remote program started
> remote program got signal: interrupt
> remote program finished

What did you see instead?

When I run above code I actually see the following output:

> remote program started
2021/09/30 08:54:07 session.Wait error: wait: remote command exited without exit status or exit signal
@gopherbot gopherbot added this to the Unreleased milestone Oct 1, 2021
@mknyszek mknyszek changed the title x/crypto/ssh cannot send a Signal to remote process x/crypto/ssh: cannot send a Signal to remote process Oct 4, 2021
@mknyszek mknyszek added the NeedsInvestigation label Oct 4, 2021
@mknyszek
Copy link
Contributor

@mknyszek mknyszek commented Oct 4, 2021

@nskforward
Copy link
Author

@nskforward nskforward commented Oct 5, 2021

problem is no longer reproducible

@nskforward nskforward changed the title x/crypto/ssh: cannot send a Signal to remote process x/crypto/ssh: cannot send a Signal to remote process without Pty Oct 11, 2021
@nskforward
Copy link
Author

@nskforward nskforward commented Oct 11, 2021

With RequestPty works as expected. But I cannot use RequestPty because stderr always sends to stdout even I explicitly define stderr stream for session. This is known OpenSSH issue.
I need to handle stderr separately.
How I can send SIGINT signal to remote process without RequestPty function call and wait remote process exit?
Or how I can handle stderr separately of stdout using RequestPty.

@nskforward nskforward reopened this Oct 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation
Projects
None yet
Development

No branches or pull requests

3 participants