2017-03-14 19:13:03 +00:00
|
|
|
package ssh
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2017-04-14 19:47:40 +00:00
|
|
|
"encoding/hex"
|
2017-07-24 21:25:45 +00:00
|
|
|
"net"
|
2019-02-23 02:11:43 +00:00
|
|
|
"sync"
|
2017-03-14 19:13:03 +00:00
|
|
|
|
|
|
|
gossh "golang.org/x/crypto/ssh"
|
|
|
|
)
|
|
|
|
|
|
|
|
// contextKey is a value for use with context.WithValue. It's used as
|
|
|
|
// a pointer so it fits in an interface{} without allocation.
|
|
|
|
type contextKey struct {
|
|
|
|
name string
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ContextKeyUser is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type string.
|
|
|
|
ContextKeyUser = &contextKey{"user"}
|
|
|
|
|
|
|
|
// ContextKeySessionID is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type string.
|
|
|
|
ContextKeySessionID = &contextKey{"session-id"}
|
|
|
|
|
|
|
|
// ContextKeyPermissions is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type *Permissions.
|
|
|
|
ContextKeyPermissions = &contextKey{"permissions"}
|
|
|
|
|
|
|
|
// ContextKeyClientVersion is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type string.
|
|
|
|
ContextKeyClientVersion = &contextKey{"client-version"}
|
|
|
|
|
|
|
|
// ContextKeyServerVersion is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type string.
|
|
|
|
ContextKeyServerVersion = &contextKey{"server-version"}
|
|
|
|
|
|
|
|
// ContextKeyLocalAddr is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type net.Addr.
|
|
|
|
ContextKeyLocalAddr = &contextKey{"local-addr"}
|
|
|
|
|
|
|
|
// ContextKeyRemoteAddr is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type net.Addr.
|
|
|
|
ContextKeyRemoteAddr = &contextKey{"remote-addr"}
|
|
|
|
|
|
|
|
// ContextKeyServer is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type *Server.
|
|
|
|
ContextKeyServer = &contextKey{"ssh-server"}
|
|
|
|
|
2017-04-14 19:47:40 +00:00
|
|
|
// ContextKeyConn is a context key for use with Contexts in this package.
|
Remote forwarding (#88)
* context: fixed documentation to be more specific about ContextKeyConn being the key for a gossh.ServerConn
Signed-off-by: Jeff Lindsay <progrium@gmail.com>
* server: fixes handler setup, changed to interface based handlers, added global request handler map
* tcpip: working remote forwarding
Signed-off-by: Jeff Lindsay <progrium@gmail.com>
* context: docs typo
Signed-off-by: Jeff Lindsay <progrium@gmail.com>
* session: always reply to unblock clients trying something
Signed-off-by: Jeff Lindsay <progrium@gmail.com>
* tcpip: stop listening when ssh clients disconnect
Signed-off-by: Jeff Lindsay <progrium@gmail.com>
* Remote forwarding (#87)
* Update generateSigner key size to 2048 (#62)
Fixes #58
* Add syntax highlighting to readme (#67)
* small api updates (#69)
These updates make it easier to implement and pass custom Session and
Context implementations
No compatibilty breaking, all tests pass
* Move channelHandlers to avoid data race (#59)
* Update tests to work with go 1.10+ (#73)
Fixes #72
* Update shutdown to use a WaitGroup rather than sleeping (#74)
* Fix race condition in TestServerClose (#75)
In test server close, 3 things need to happen in order:
- Client session start
- Server.Close
- Client session exit (With io.EOF)
This fix ensures the client won't do anything until after the call to
close which ensure's we'll get io.EOF rather than a different error.
* Update circleci config to test multiple go versions
* Update CircleCI config to test 1.9 and the latest
The x/crypto/ssh library dropped support go < 1.9 as that's the first
version to have the math/bits library.
https://github.com/golang/crypto/commit/83c378c48d6ee2ca9c20551b599aa74cb7765785
* Wait for connections to finish when shutting down
PR #74 introduced a WaitGroup for listeners, but it doesn't wait for
open connections before closing the server. This patch waits until all
conns are closed before returning from Shutdown.
* Support port forwarding of literal IPv6 addresses (#85)
* Support port forwarding of literal IPv6 addresses
To disambiguate between colons as host:port separators and as IPv6 address separators, literal IPv6 addresses use square brackets around the address (https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers). So host ::1, port 22 is written as [::1]:22, and therefore a simple concatenation of host, colon, and port doesn't work. Fortunately net.JoinHostPort already implements this functionality, so with a bit of type gymnastics we can generate dest in an IPv6-safe way.
* Support port forwarding of literal IPv6 addresses
To disambiguate between colons as host:port separators and as IPv6 address separators, literal IPv6 addresses use square brackets around the address (https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers). So host ::1, port 22 is written as [::1]:22, and therefore a simple concatenation of host, colon, and port doesn't work. Fortunately net.JoinHostPort already implements this functionality, so with a bit of type gymnastics we can generate dest in an IPv6-safe way.
* Reverse port forwarding callback added
* garbage removed
2018-11-13 16:04:02 +00:00
|
|
|
// The associated value will be of type gossh.ServerConn.
|
2017-04-14 19:47:40 +00:00
|
|
|
ContextKeyConn = &contextKey{"ssh-conn"}
|
|
|
|
|
2017-03-14 19:13:03 +00:00
|
|
|
// ContextKeyPublicKey is a context key for use with Contexts in this package.
|
|
|
|
// The associated value will be of type PublicKey.
|
|
|
|
ContextKeyPublicKey = &contextKey{"public-key"}
|
|
|
|
)
|
|
|
|
|
|
|
|
// Context is a package specific context interface. It exposes connection
|
|
|
|
// metadata and allows new values to be easily written to it. It's used in
|
|
|
|
// authentication handlers and callbacks, and its underlying context.Context is
|
2019-02-23 02:11:43 +00:00
|
|
|
// exposed on Session in the session Handler. A connection-scoped lock is also
|
|
|
|
// embedded in the context to make it easier to limit operations per-connection.
|
2017-03-14 19:13:03 +00:00
|
|
|
type Context interface {
|
|
|
|
context.Context
|
2019-02-23 02:11:43 +00:00
|
|
|
sync.Locker
|
2017-03-14 19:13:03 +00:00
|
|
|
|
|
|
|
// User returns the username used when establishing the SSH connection.
|
|
|
|
User() string
|
|
|
|
|
|
|
|
// SessionID returns the session hash.
|
|
|
|
SessionID() string
|
|
|
|
|
|
|
|
// ClientVersion returns the version reported by the client.
|
|
|
|
ClientVersion() string
|
|
|
|
|
|
|
|
// ServerVersion returns the version reported by the server.
|
|
|
|
ServerVersion() string
|
|
|
|
|
|
|
|
// RemoteAddr returns the remote address for this connection.
|
|
|
|
RemoteAddr() net.Addr
|
|
|
|
|
|
|
|
// LocalAddr returns the local address for this connection.
|
|
|
|
LocalAddr() net.Addr
|
|
|
|
|
|
|
|
// Permissions returns the Permissions object used for this connection.
|
|
|
|
Permissions() *Permissions
|
|
|
|
|
|
|
|
// SetValue allows you to easily write new values into the underlying context.
|
|
|
|
SetValue(key, value interface{})
|
|
|
|
}
|
|
|
|
|
|
|
|
type sshContext struct {
|
|
|
|
context.Context
|
2019-02-23 02:11:43 +00:00
|
|
|
*sync.Mutex
|
2017-03-14 19:13:03 +00:00
|
|
|
}
|
|
|
|
|
2017-07-24 21:25:45 +00:00
|
|
|
func newContext(srv *Server) (*sshContext, context.CancelFunc) {
|
|
|
|
innerCtx, cancel := context.WithCancel(context.Background())
|
2019-02-23 02:11:43 +00:00
|
|
|
ctx := &sshContext{innerCtx, &sync.Mutex{}}
|
2017-03-14 19:13:03 +00:00
|
|
|
ctx.SetValue(ContextKeyServer, srv)
|
|
|
|
perms := &Permissions{&gossh.Permissions{}}
|
|
|
|
ctx.SetValue(ContextKeyPermissions, perms)
|
2017-07-24 21:25:45 +00:00
|
|
|
return ctx, cancel
|
2017-03-14 19:13:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// this is separate from newContext because we will get ConnMetadata
|
|
|
|
// at different points so it needs to be applied separately
|
2018-01-09 18:15:16 +00:00
|
|
|
func applyConnMetadata(ctx Context, conn gossh.ConnMetadata) {
|
2017-03-14 19:13:03 +00:00
|
|
|
if ctx.Value(ContextKeySessionID) != nil {
|
|
|
|
return
|
|
|
|
}
|
2017-04-14 19:47:40 +00:00
|
|
|
ctx.SetValue(ContextKeySessionID, hex.EncodeToString(conn.SessionID()))
|
2017-03-14 19:13:03 +00:00
|
|
|
ctx.SetValue(ContextKeyClientVersion, string(conn.ClientVersion()))
|
|
|
|
ctx.SetValue(ContextKeyServerVersion, string(conn.ServerVersion()))
|
|
|
|
ctx.SetValue(ContextKeyUser, conn.User())
|
|
|
|
ctx.SetValue(ContextKeyLocalAddr, conn.LocalAddr())
|
|
|
|
ctx.SetValue(ContextKeyRemoteAddr, conn.RemoteAddr())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *sshContext) SetValue(key, value interface{}) {
|
|
|
|
ctx.Context = context.WithValue(ctx.Context, key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *sshContext) User() string {
|
|
|
|
return ctx.Value(ContextKeyUser).(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *sshContext) SessionID() string {
|
|
|
|
return ctx.Value(ContextKeySessionID).(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *sshContext) ClientVersion() string {
|
|
|
|
return ctx.Value(ContextKeyClientVersion).(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *sshContext) ServerVersion() string {
|
|
|
|
return ctx.Value(ContextKeyServerVersion).(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *sshContext) RemoteAddr() net.Addr {
|
|
|
|
return ctx.Value(ContextKeyRemoteAddr).(net.Addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *sshContext) LocalAddr() net.Addr {
|
|
|
|
return ctx.Value(ContextKeyLocalAddr).(net.Addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *sshContext) Permissions() *Permissions {
|
|
|
|
return ctx.Value(ContextKeyPermissions).(*Permissions)
|
|
|
|
}
|