Giter Site home page Giter Site logo

gliderlabs / ssh Goto Github PK

View Code? Open in Web Editor NEW
3.4K 62.0 421.0 2.03 MB

Easy SSH servers in Golang

Home Page: https://godoc.org/github.com/gliderlabs/ssh

License: BSD 3-Clause "New" or "Revised" License

Go 99.93% Dockerfile 0.07%
ssh ssh-server golang-package golang

ssh's Introduction

gliderlabs/ssh

GoDoc CircleCI Go Report Card OpenCollective Slack Email Updates

The Glider Labs SSH server package is dope. —@bradfitz, Go team member

This Go package wraps the crypto/ssh package with a higher-level API for building SSH servers. The goal of the API was to make it as simple as using net/http, so the API is very similar:

 package main

 import (
     "github.com/gliderlabs/ssh"
     "io"
     "log"
 )

 func main() {
     ssh.Handle(func(s ssh.Session) {
         io.WriteString(s, "Hello world\n")
     })  

     log.Fatal(ssh.ListenAndServe(":2222", nil))
 }

This package was built by @progrium after working on nearly a dozen projects at Glider Labs using SSH and collaborating with @shazow (known for ssh-chat).

Examples

A bunch of great examples are in the _examples directory.

Usage

See GoDoc reference.

Contributing

Pull requests are welcome! However, since this project is very much about API design, please submit API changes as issues to discuss before submitting PRs.

Also, you can join our Slack to discuss as well.

Roadmap

  • Non-session channel handlers
  • Cleanup callback API
  • 1.0 release
  • High-level client?

Sponsors

Become a sponsor and get your logo on our README on Github with a link to your site. [Become a sponsor]

License

BSD

ssh's People

Contributors

aerth avatar aidansteele avatar belak avatar binwiederhier avatar caarlos0 avatar chirino avatar dependabot[bot] avatar gustavosbarreto avatar hloeffler avatar jbarnette avatar jm33-m0 avatar josegonzalez avatar kuehnelth avatar lemonn avatar m1yt avatar mattatcha avatar meislerj avatar mfielding avatar moul avatar muryliang avatar mvrilo avatar notnoopci avatar phil-halley avatar piamancini avatar progrium avatar rumpelsepp avatar stevemurr avatar tsl0922 avatar wxiaoguang avatar yanc0 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ssh's Issues

Cannot make it work with an ssh client from OpenSSH 7.6p1

Just tried the ssh-simple example on my Mac with 10.12.6, but whatever I do I always end up with the following output:

ssh_dispatch_run_fatal: Connection to 127.0.0.1 port 2222: Invalid key length

Please note that I am running the MacPorts OpenSSH client (with a vanilla configuration). Am I missing something obvious?

This is a complete session with debug turned on:

$ ssh -vvvv  -p 2222 127.0.0.1
OpenSSH_7.6p1, OpenSSL 1.0.2l  25 May 2017
debug1: Reading configuration data /Users/janflyborg/.ssh/config
debug1: /Users/janflyborg/.ssh/config line 1: Applying options for *
debug1: Reading configuration data /opt/local/etc/ssh/ssh_config
debug2: resolving "127.0.0.1" port 2222
debug2: ssh_connect_direct: needpriv 0
debug1: Connecting to 127.0.0.1 [127.0.0.1] port 2222.
debug1: Connection established.
debug1: identity file /Users/janflyborg/.ssh/id_rsa type 0
debug1: key_load_public: No such file or directory
debug1: identity file /Users/janflyborg/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/janflyborg/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/janflyborg/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/janflyborg/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/janflyborg/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/janflyborg/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file /Users/janflyborg/.ssh/id_ed25519-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_7.6
debug1: Remote protocol version 2.0, remote software version Go
debug1: no match: Go
debug2: fd 3 setting O_NONBLOCK
debug1: Authenticating to 127.0.0.1:2222 as 'janflyborg'
debug3: put_host_port: [127.0.0.1]:2222
debug3: hostkeys_foreach: reading file "/Users/janflyborg/.ssh/known_hosts"
debug3: send packet: type 20
debug1: SSH2_MSG_KEXINIT sent
debug3: receive packet: type 20
debug1: SSH2_MSG_KEXINIT received
debug2: local client KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,[email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,ext-info-c
debug2: host key algorithms: [email protected],[email protected],[email protected],[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
debug2: ciphers ctos: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected]
debug2: ciphers stoc: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected]
debug2: MACs ctos: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,[email protected],zlib
debug2: compression stoc: none,[email protected],zlib
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug2: peer server KEXINIT proposal
debug2: KEX algorithms: [email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1
debug2: host key algorithms: ssh-rsa
debug2: ciphers ctos: aes128-ctr,aes192-ctr,aes256-ctr,[email protected],arcfour256,arcfour128
debug2: ciphers stoc: aes128-ctr,aes192-ctr,aes256-ctr,[email protected],arcfour256,arcfour128
debug2: MACs ctos: [email protected],hmac-sha2-256,hmac-sha1,hmac-sha1-96
debug2: MACs stoc: [email protected],hmac-sha2-256,hmac-sha1,hmac-sha1-96
debug2: compression ctos: none
debug2: compression stoc: none
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug1: kex: algorithm: [email protected]
debug1: kex: host key algorithm: ssh-rsa
debug1: kex: server->client cipher: aes128-ctr MAC: [email protected] compression: none
debug1: kex: client->server cipher: aes128-ctr MAC: [email protected] compression: none
debug3: send packet: type 30
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug3: receive packet: type 31
ssh_dispatch_run_fatal: Connection to 127.0.0.1 port 2222: Invalid key length 

Channels?

Hello!
How do I send a message through a channel?
(Open session and send messages via channel)
Thx

ssh.KeysEqual(nil, nil) panics

ssh.KeysEqual() will panic if one of the "keys" is nil

i suggest to return false if one of the keys are nil
i suggest to return false if both keys are nil (because there are not keys)

Ability to catch/log/handle errors

Hey there,

thanks for your great library. We've started using it for hundreds of SSH tunnels and it works great so far!

Since in our case the tunnels come from hundreds of different networks, network blips are quite common and we started noticing a few recurring error messages:

  • ssh: unexpected packet in response to channel open: <nil> happened about 1.3k times in about 24h
  • ssh: rejected: connect failed (Connection refused) happened about 250 times in the same time span.

I understand that these are messages from the golang ssh library (ssh/mux.go and ssh/connection.go), but there is currently no way to handle/catch there errors in your library (see https://github.com/gliderlabs/ssh/blob/master/tcpip.go#L146-L151), since the errors are just printed to STDOUT.

Would it be possible to add a way to deal with these situations? If anything, just to be able to associate the error message with a specific SSH connection.

How to connect the remote host B from A when having connected the remote host A by ssh?

Hi,
I want to connect the remote host B from the remote host A, so I first connect the remote host A by ssh and creat a sshClient successfully , but when I connect the remote host B from A, it doesn't work.
For exeample: when I use session.Run("ssh userB@hostB"), the error is "Stdout is already set".
May you have another way to connect the remote host B from the remote host A?
Thank you very much.

Tag version with reverse port forwarding support?

Hey there. Great library. Thanks very much!

The current master version includes reverse port forwarding support, but there is no tagged version that supports it. I'm tying to used tagged versions only with Go modules, instead of just pointing to master.

Could you tag the current master to make that possible?

Alternative channel handlers

Continuing discussion #47

FYI, I'm using a modified version of gliderlabs/ssh to allow me using a custom channel handler

Here is the current diff state: https://github.com/moul/sshportal/compare/dev/moul/diff-gliderlabs-upstream?expand=1

The good points using this method are:

  • we can create/use custom ChannelHandler, not depending on current interface (example)
  • the channel handler can be decided conditionally at runtime, I'm currently using different channel handlers based on dynamic conditions (example)
  • minimal API change, no compatibility break (expected)

Do you want I open a PR so we can discuss or do you prefer to talk here first?

To be honest with you, I'm quite satisfied with the global result but not so much about my project architecture

Read user input

Hi!

I want to make a little shell for my app,
could you help me to understand how I can to wait user input commands?

I this something like that
but its not working(

ssh.Handle(func(s ssh.Session) {
      reader := bufio.NewReader(s)
      input, _ := reader.ReadString('\n')
      io.WriteString(s, "\n"+input)
})

thank you!

Getting powershell.exe and cmd.exe works on ssh session

The following code does not work either with powershell.exe and cmd.exe.

I can't type anything when the session starts ..

func GiveShell(s ssh.Session) {
	_, _, isPty := s.Pty()
	if isPty {
		logger.Get().Info("PTY requested")

		cmd := exec.Command("cmd")
		stdin, err := cmd.StdinPipe()
		if err != nil {
			logger.Get().Warn("Error occured: %s")
			return
		}
		stdout, err := cmd.StdoutPipe()
		if err != nil {
			logger.Get().Warn("Error occured: %s", err)
			return
		}
		stderr, err := cmd.StderrPipe()
		if err != nil {
			logger.Get().Warn("Error occured: %s", err)
			return
		}

		go func() {
			io.Copy(stdin, s)
		}()
		go func() {
			io.Copy(s, stdout)
		}()
		go func() {
			io.Copy(s, stderr)
		}()

		err = cmd.Run()
		if err == nil {
			logger.Get().Info("Session ended normally")
			s.Exit(0)
		} else {
			logger.Get().Info("Session ended with an error: %v\n", err)

			exitCode := 1
			if exitError, ok := err.(*exec.ExitError); ok {
				exitCode = exitError.ExitCode()
				logger.Get().Info("exit code: %d\n", exitCode)
			}

			s.Exit(exitCode)
		}
	} else {
		msg := "No PTY requested."
		io.WriteString(s, msg)
		logger.Get().Warn(msg)
	}
}

Panic in handleRequests

We recently upgraded to v0.1.3 from pre v0.1.1 (commit cff9b0c) so that we could utilize the DefaultServerConfigCallback. Once we released the update we started getting panics when some customers tried to connect to our SSH server. We do not know what the customer was doing since it panicked before we were able to capture any customer information.

Any ideas on where we can start to debug this?

This is the error we have in our logs:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x9144fe]
goroutine 10921 [running]:
github.com/wpengine/ssh-gateway/vendor/github.com/gliderlabs/ssh.(*Server).handleRequests(0xc0000d6540, 0x13a4340, 0xc0003ab1a0, 0xc000315680)
/go/src/github.com/wpengine/ssh-gateway/vendor/github.com/gliderlabs/ssh/server.go:275 +0xae
created by github.com/wpengine/ssh-gateway/vendor/github.com/gliderlabs/ssh.(*Server).handleConn
/go/src/github.com/wpengine/ssh-gateway/vendor/github.com/gliderlabs/ssh/server.go:255 +0x298

Make it possible to only allow one channel per connection

I'm running a fairly large SSH service and we've been looking at moving to an ssh wrapper library (rather than working on our own) and this is one of the things we do to make it easier to scale (so we know there's only one operation happening on each host)

Allow connection closure

There are cases where within the session handler you should be able to terminate the session's underlying connection.

Currently the session struct (that implements the Session interface passed to the session handler) has a reference to the the underlying connection, but does not provide any methods to manipulate that connection. Or is there already a way to do this that I missed?

Use case: in case the user has ControlPersist set to indefinite, we would like to terminate the client connection and force the client to create a new one to enforce certain time limits on connection-specific server-side resources.

How to open a program with password entering on a remote host when I have connected the remote host by ssh?

Hi,
I have connected the remote host by ssh and created a sshClient , and running simple cmd such as "ps;ls" is ok.
However, when I open a program with password entering, I have no chance to enter password, for exeample:
session.Run("./ushell"), the output is

-> Please input password!
->

-> Error password!!
-> Please input password!
->

-> Error password!!
-> Please input password!
->

-> Error password!!
-> Input password exceed 3 times ushell exit

May you have any idea to solve this problem ?
Thank You.

Terminal modes

There's a line here that mentions terminal modes:

ssh/ssh.go

Line 77 in e5ece14

// HELP WANTED: terminal modes!

What do we envision for "terminal modes"?

KeyboardInteractiveHandler

Awesome library! Would be nice to have keyboard-interactive login available. Great for chats, games, etc, to allow users without having to use "any" public key or a "blank" password. Usage such as:

func authKeyboardInteractive(ctx ssh.Context, challenge gossh.KeyboardInteractiveChallenge) bool {
	return true
}

var server = ssh.Server{
	Addr:             "0.0.0.0:4444",
	Handler:          handleEntrypoint,
	PublicKeyHandler: authPublicKey,
	KeyboardInteractiveHandler: authKeyboardInteractive,
}

You can see how I implemented a dummy keyboard-interactive handler here, but a real option would be nice

Example SFTP Drop Box code.

Hello,

Are there any examples showing how this library can be used to host a drop box type of functionality over SFTP? I am having trouble with some other code that dies when a U/P auth fails. This looks like a cleaner SSH implementation.

Thanks for any help you can provide.

Get the actual port server is listening on

I am using this library to start ssh server during our tests. In order to have repeatable tests that can work anywhere, I want the ssh server to start on random port (available port). In order to do that I order to achieve that I did following:

server := &ssh.Server{
		Addr:       "127.0.0.1:0",
	}

Note that I used 0 as port to let OS choose the available port for me. And it works when I start the server using server.ListenAndServe(). However, I did not find a way to get the actual port number the listener is listening on. This makes it difficult for the test code to connect to the server that has been started. Essentially I am looking at having something similar to httptest.Server.URL that returns the actual address (ipaddr:port) the server is listening on. Is there a way to get this using existing functionality?

Spikes in connection time

We are seeing intermittent spikes in the time it takes for a user to establish a connection with our SSH Server. The delay is in between PublicKeyHandler and Handler starting. So the customer successfully authenticates, then something happens anywhere from a couple seconds to multiple minutes (9 minutes was the most this weeks), then then the Handler kicks in and they are connected as usual. it seems to be inside the gliderlabs/ssh code while this is happening.

We have not been able to reproduce the issue ourselves. It does not happen to any particular customers or with any particular client, but we see some customer connections being delayed like this every day.

We only have questions right now:

  • Is it possible that there are some timeout values that we could configure around this?
  • Is it possible there is a prompt or some other customer interaction that it is waiting on?

Remote host identification has changed

Hi again)

when I use password option, host identification has change every app restart
and I get something like this:

ssh localhost -p 2222

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
SHA256:sfmByl+5AGxgkubPvFq3zfhZLHzenUNFV8OTzN+KsRJs.
Please contact your system administrator.

so I have to delete the host from known_hosts
and then OK again... until restart

ssh.ListenAndServe(p, nil,
		ssh.PasswordAuth(
			func(ctx ssh.Context, pass string) bool {
				return pass == password
			}),
	)

how to fix it?
its only when I use a password option, key option is ok...
its really annoy)
thank you!

Build a SSH bastion

Hi,

Is there any example of SSH bastion created with this package?

Basically I'd like to create a SSH server accepting connections, verifying the identity with Public SSH keys, and then forwarding the connections to another server.

Since I believe this is a pretty standard need, it could be interesting to add such an example in the repo.

Thanks

Allow to request PTY even if rows/cols are zero

For automation tasks, the SSH client can request a PTY with -t or -tt even if there's no TTY because stdin/stdout is redirected as part of a script or tool... like here:

ssh -tt somehost  << EOF
stty -a
exit
EOF

which results in

$ stty -a
speed 38400 baud; rows 0; columns 0; line = 0;
...

However, code in utils actually requires the cols and rows to be >0 and thus will deny a PTY. OpenSSH (where the example above is from) works just fine in this case.

I'm suggesting to remove the logic to enforce rows and columns to be >0... A PTY of size 1x1 is pretty much as unusable as one with 0x0 :)

In a fork I did, I removed the logic and it works just fine for me... Wondering what the general consensus here is?

Make it possible to log errors in the ssh protocol

As an example, handleConn in server.go just drops errors when they happen at the protocol level. This is particularly important when there are changes pushed out to x/crypto/ssh which break certain clients.

Add logging callback

It would be awesome if there was a way to log when users connect (along with what requests they run) and disconnect.

Co-maintainers wanted

This project is built and maintained 100% voluntarily. There is no company spending money on it, and everybody working on it is also busy with other projects. We can use your help keeping this project healthy if you can participate as a maintainer. This is a "commitment" to optionally be involved in:

  • Code reviewing PRs
  • API change discussions
  • Pushing PRs in the right direction
  • Merging PRs that are obviously good to go

With your help, we can slowly iterate towards the best SSH server building library on the planet. If you're interested, join our Slack and say hi in the #golang channel.

Error when building on windows

When building my ssh server on windows ... I get those errors:

./make.sh                         (*master+13) 15:29:46
[~] Compiling 
#
internal/pkg/ssh/ssh.go:17:17: not enough arguments in call to syscall.Syscall
internal/pkg/ssh/ssh.go:17:18: undefined: syscall.SYS_IOCTL
internal/pkg/ssh/ssh.go:17:53: undefined: syscall.TIOCSWINSZ
internal/pkg/ssh/ssh.go:36:13: undefined: pty.Start
An error has occurred! Aborting the script execution...

I guess this is because I am using pty and stuff in my handler ...

func giveShell(s ssh.Session) {
	cmd := exec.Command(getOSCommand())
	ptyReq, winCh, isPty := s.Pty()
	if isPty {
		cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term))
		f, err := pty.Start(cmd)
		if err != nil {
			panic(err)
		}
		go func() {
			for win := range winCh {
				setWinsize(f, win.Width, win.Height)
			}
		}()
		go func() {
			io.Copy(f, s) // stdin
		}()
		io.Copy(s, f) // stdout
	} else {
		io.WriteString(s, "No PTY requested.\n")
		s.Exit(1)
	}
}

Is there any way to do cross platform command execution ?

Add option for keep-alive messages

Pretty self explanatory. Just sending keepalive@whatever as a request which needs a reply is enough to get both sides to send a little data to keep the connection living. openssh uses keepalive@openssh (or something very similar) this way.

Request for example

This is a great peace of software. Thank you very much. Could you please post an example how to write a client as go routine which is waiting for user input lines and which answers each lines after processing it? Thank you very much.

ssh.Handle(func(s ssh.Session) {
		log.Printf("%s", s.RemoteAddr())
		io.WriteString(s, fmt.Sprintf("Hello %s\n", s.User()))

                // ... handle connection until it closes by user input, client close
                // ??????
	})

Thank you very much in advance.

HostKeyFile Not support ED25519

When I use ssh.HostKeyFile add a /etc/ssh/ssh_host_ed25519_key, Start ssh server error, Report: unsupported key type 'OPENSSH PRIVATE KEY'

In fact, when i use ssh.ParsePrivateKey(x/crypto/ssh), it can support ed25519

func AddHostKeyFile(filepath string, srv *ssh.Server) {
	pemBytes, err := ioutil.ReadFile(filepath)
	if err != nil {
		log.Printf("AddHostKeyFile: %s", err)
		return
	}
	hostKey, err := gossh.ParsePrivateKey(pemBytes)
	if err != nil {
		log.Printf("Fatal to parse host key: %s, %s", filepath, err)
		return
	}
	srv.AddHostKey(hostKey)
	return
}

Bug in:

ssh/util.go

Line 14 in 4a4de39

func signerFromBlock(block *pem.Block) (ssh.Signer, error) {

func signerFromBlock(block *pem.Block) (ssh.Signer, error) {
	var key interface{}
	var err error
	switch block.Type {
	case "RSA PRIVATE KEY":
		key, err = x509.ParsePKCS1PrivateKey(block.Bytes)
	case "EC PRIVATE KEY":
		key, err = x509.ParseECPrivateKey(block.Bytes)
	case "DSA PRIVATE KEY":
		key, err = ssh.ParseDSAPrivateKey(block.Bytes)
	default:// NOT parse ed25519
		return nil, fmt.Errorf("unsupported key type %q", block.Type)
	}
	if err != nil {
		return nil, err
	}
	signer, err := ssh.NewSignerFromKey(key)
	if err != nil {
		return nil, err
	}
	return signer, nil
}

Detecting Connection Close with access to SessionID

I'm looking for a way to detect closing of connections to cleanup some resources.

I can definitely use the ConnCallback to create a custom net.Conn but there is no access to the Context or SessionID at this point, so there is no way for me to correlate the closing connection to a given SSH SessionID.

Ideally, there would be a ConnCloseCallback which looks like below - and it would be called from serverConn.Close() passing the Context, so I have access to the SessionID during close to cleanup associated resources.

type ConnCloseCallback func(ctx Context, conn net.Conn) net.Conn

I hope I'm not missing something obvious here. If someone can validate my theory, I can create a PR with the ConnCloseCallback support.

Go 1.10: session_test.go:218: Fatalf call has arguments but no formatting directives

47df570 does not pass unit tests with Go 1.10. At least:

+ GOPATH=/builddir/build/BUILD/ssh-47df570d18ad49f77cf66f76bc3fce3e92400768/_build:/usr/share/gocode
+ go test -buildmode pie -compiler gc -ldflags '-extldflags '\''-Wl,-z,relro  '\'''
# github.com/gliderlabs/ssh
./session_test.go:218: Fatalf call has arguments but no formatting directives
./session_test.go:251: Fatalf call has arguments but no formatting directives
FAIL	github.com/gliderlabs/ssh [build failed]

Data race warning

Hello,

I'm using you awesome library to help testing something that creates a ssh client without relying on something installed on the host that execute the tests.

When testing, I got the following data race:

 ==================
 WARNING: DATA RACE
 Write at 0x00c0001680d8 by goroutine 38:
   sync.(*WaitGroup).Wait()
       /usr/local/go/src/internal/race/race.go:41 +0xef
   github.com/gliderlabs/ssh.(*Server).Shutdown.func1()
       /go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:178 +0x55

 Previous read at 0x00c0001680d8 by goroutine 25:
   sync.(*WaitGroup).Add()
       /usr/local/go/src/internal/race/race.go:37 +0x169
   github.com/gliderlabs/ssh.(*Server).trackConn()
       /go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:389 +0x107
   github.com/gliderlabs/ssh.(*Server).handleConn()
       /go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:259 +0x2c7

 Goroutine 38 (running) created at:
   github.com/gliderlabs/ssh.(*Server).Shutdown()
       /go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:176 +0xef
   app/pkg/sshx.newSSHServer.func3()
       /app/pkg/sshx/client_test.go:89 +0xd0
   app/pkg/sshx.TestNewClient.func3()
       /app/pkg/sshx/client_test.go:118 +0x39b
   testing.tRunner()
       /usr/local/go/src/testing/testing.go:865 +0x163

 Goroutine 25 (running) created at:
   github.com/gliderlabs/ssh.(*Server).Serve()
       /go/pkg/mod/github.com/gliderlabs/[email protected]/server.go:230 +0x184
   app/pkg/sshx.newSSHServer.func2()
       /app/pkg/sshx/client_test.go:77 +0x59
 ==================

It happened only one time, and I didn't succeeds to reproduce as restarting the same test without touching anything make it PASS, and so far I didn't get this data race again.

Here is how I call your library:

func TestNewClient(t *testing.T) {
	addr, stop, exited, err := newSSHServer()
	require.NoError(t, err)

	client, err := NewClient(addr) // GITHUB: this returns a *ssh.Client configured to hit the configured ssh server
	require.NoError(t, err)
	assert.Equal(t, "SSH-2.0-go-test_"+addr, string(client.ServerVersion())) // GITHUB: dumb test, just making sure we're connected on the server we wanted
	assert.NoError(t, client.Close())

	stop()
	assert.NoError(t, <-exited)
}

func newSSHServer() (string, func(), <-chan error, error) {
	listener, err := findFreeListener() // GITHUB: this helps to find a listener available
	if err != nil {
		return "", nil, nil, errors.Wrap(err, "unable to get free listener")
	}
	addr := listener.Addr().String()

	srv := gssh.Server{
		Addr:                        addr,
		Version:                     "go-test_" + addr,

		LocalPortForwardingCallback: func(gssh.Context, string, uint32) bool { return true },
		ChannelHandlers: map[string]gssh.ChannelHandler{
			"direct-tcpip": gssh.DirectTCPIPHandler,
		},
	}
	cerr := make(chan error)
	go func() {
		if err := srv.Serve(listener); err != nil {
			if errors.Cause(err) != gssh.ErrServerClosed {
				cerr <- errors.Wrap(err, "unable to serve ssh")
			}
		}
		cerr <- nil
	}()

	return addr, func() {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()

		if err := srv.Shutdown(ctx); err != nil {
			select {
			case cerr <- err:
			default:
			}
		}
	}, cerr, nil
}

I'm not sure how critical this can be, nor why I got it only one time, but I wanted to notify you about this.

Thanks for your work on this library,
krostar

KeyboardInteractive ssh.Context lost the user information

when testing with the xshell or the ssh client with the keyboardinteractive mode,I can't get the username but nil.

ssh/server.go

Line 132 in 5b6cc70

config.KeyboardInteractiveCallback = func(conn gossh.ConnMetadata, challenger gossh.KeyboardInteractiveChallenge) (*gossh.Permissions, error) {

maybe there should be the applyConnMetadata(ctx, conn) in the first line of the callback function.

Make it possible to send an exit-status other than 0

This will have to be a breaking change so we can get the return value from the handler.

EDIT: I've been informed this is already possible with Session.Exit, but there is an extra "exit-status" 0 being sent if that was called earlier in the connection.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.