add doc for sshpiper api

This commit is contained in:
tgic 2014-12-17 15:53:11 +08:00 committed by Boshi Lian
parent c44d089252
commit d29a484fe3
3 changed files with 82 additions and 40 deletions

View file

@ -3,7 +3,10 @@
// license that can be found in the LICENSE file.
/*
Package ssh implements an SSH client and server.
Package ssh in sshpiper is compatible with golang.org/x/crypto/ssh.
All func and datatype left unchanged. You can use it like golang.org/x/crypto/ssh.
Also, sshpiper provide additional APIs that allows build a piped ssh connection.
SSH is a transport security protocol, an authentication protocol and a
family of application protocols. The most typical application level

View file

@ -1,3 +1,8 @@
// Copyright 2014, 2015 tgic<farmer1992@gmail.com>. All rights reserved.
// this file is governed by MIT-license
//
// https://github.com/tg123/sshpiper
package ssh
import (
@ -6,12 +11,33 @@ import (
"net"
)
// SSHPiperConfig holds SSHPiper specific configuration data.
type SSHPiperConfig struct {
AdditionalChallenge func(conn ConnMetadata, client KeyboardInteractiveChallenge) (bool, error)
FindUpstream func(conn ConnMetadata) (net.Conn, *ClientConfig, error)
MapPublicKey func(conn ConnMetadata, key PublicKey) (Signer, error)
Config
downstreamConfig ServerConfig
hostKeys []Signer
// AdditionalChallenge, if non-nil, is called before calling FindUpstream.
// This allows you do a KeyboardInteractiveChallenge before connecting to upstream.
// It must return true if downstream passed the challenge, otherwise,
// the piped connection will be closed.
AdditionalChallenge func(conn ConnMetadata, client KeyboardInteractiveChallenge) (bool, error)
// FindUpstream, must not be nil, is called when SSHPiper decided to establish a
// ssh connection to upstream server. a connection, net.Conn, to upstream
// should be returned.
// If any error occurs, the piped connection will be closed.
FindUpstream func(conn ConnMetadata) (net.Conn, error)
// MapPublicKey, if non-nil, is called when downstream requests a publickey auth.
// SSHPiper will sign the auth packet message using the returned Signer.
// This func might be called twice, one is for query message, the other
// is real auth packet message.
// If any error occurs during this period, a NoneAuth packet will be sent to
// upstream ssh server instead.
//
// More info: https://github.com/tg123/sshpiper#publickey-sign-again
MapPublicKey func(conn ConnMetadata, key PublicKey) (Signer, error)
}
type upstream struct{ *connection }
@ -22,39 +48,54 @@ type pipedConn struct {
downstream *downstream
processAuthMsg func(msg *userAuthRequestMsg) (*userAuthRequestMsg, error)
clientConfig *ClientConfig
}
type SSHPipe struct{ *pipedConn }
// SSHPiperConn is a piped SSH connection, linking upstream ssh server and
// downstream ssh client together. After the piped connection was created,
// The downstream ssh client is authenticated by upstream ssh server and
// AdditionalChallenge from SSHPiper.
type SSHPiperConn struct {
*pipedConn
}
func (p *SSHPipe) Wait() error {
// Wait blocks until the piped connection has shut down, and returns the
// error causing the shutdown.
func (p *SSHPiperConn) Wait() error {
return p.pipedConn.loop()
}
func (p *SSHPipe) Close() {
// Close the piped connection create by SSHPiper
func (p *SSHPiperConn) Close() {
p.pipedConn.Close()
}
func (p *SSHPipe) GetUpstreamClientConfig() *ClientConfig {
return p.pipedConn.clientConfig
}
func (piper *SSHPiperConfig) GetDownstreamServerConfig() *ServerConfig {
return &piper.downstreamConfig
}
func (piper *SSHPiperConfig) AddHostKey(key Signer) {
piper.downstreamConfig.AddHostKey(key)
}
func (piper *SSHPiperConfig) Serve(conn net.Conn) (pipe *SSHPipe, err error) {
if piper.FindUpstream == nil {
return nil, fmt.Errorf("FindUpstream func not found")
// AddHostKey adds a private key as a SSHPiper host key. If an existing host
// key exists with the same algorithm, it is overwritten. Each SSHPiper
// config must have at least one host key.
func (s *SSHPiperConfig) AddHostKey(key Signer) {
for i, k := range s.hostKeys {
if k.PublicKey().Type() == key.PublicKey().Type() {
s.hostKeys[i] = key
return
}
}
d, err := newDownstream(conn, &piper.downstreamConfig)
s.hostKeys = append(s.hostKeys, key)
}
// NewSSHPiperConn starts a piped ssh connection witch conn as its downstream transport.
// It handshake with downstream ssh client and upstream ssh server provicde by FindUpstream.
// If either handshake is unsuccessful, the whole piped connection will be closed.
func NewSSHPiperConn(conn net.Conn, piper *SSHPiperConfig) (pipe *SSHPiperConn, err error) {
if piper.FindUpstream == nil {
panic("FindUpstream func not found")
}
d, err := newDownstream(conn, &ServerConfig{
Config: piper.Config,
hostKeys: piper.hostKeys,
})
if err != nil {
return nil, err
}
@ -106,14 +147,14 @@ func (piper *SSHPiperConfig) Serve(conn net.Conn) (pipe *SSHPipe, err error) {
}
}
upconn, upconfig, err := piper.FindUpstream(d)
upconn, err := piper.FindUpstream(d)
if err != nil {
return nil, err
}
addr := upconn.RemoteAddr().String()
u, err := newUpstream(upconn, addr, upconfig)
u, err := newUpstream(upconn, addr, &ClientConfig{})
if err != nil {
return nil, err
}
@ -124,9 +165,8 @@ func (piper *SSHPiperConfig) Serve(conn net.Conn) (pipe *SSHPipe, err error) {
}()
p := &pipedConn{
upstream: u,
downstream: d,
clientConfig: upconfig,
upstream: u,
downstream: d,
}
p.processAuthMsg = func(msg *userAuthRequestMsg) (*userAuthRequestMsg, error) {
@ -182,8 +222,7 @@ func (piper *SSHPiperConfig) Serve(conn net.Conn) (pipe *SSHPipe, err error) {
return nil, err
}
// block until connection closed or errors occur
return &SSHPipe{p}, nil
return &SSHPiperConn{p}, nil
}
func (pipe *pipedConn) validAndAck(upKey, downKey PublicKey) (*userAuthRequestMsg, error) {

View file

@ -75,17 +75,17 @@ func (file userFile) check400(user string) error {
return nil
}
func findUpstreamFromUserfile(conn ssh.ConnMetadata) (net.Conn, *ssh.ClientConfig, error) {
func findUpstreamFromUserfile(conn ssh.ConnMetadata) (net.Conn, error) {
user := conn.User()
err := UserUpstreamFile.check400(user)
if err != nil {
return nil, nil, err
return nil, err
}
addr, err := UserUpstreamFile.read(user)
if err != nil {
return nil, nil, err
return nil, err
}
saddr := strings.TrimSpace(string(addr))
@ -94,10 +94,10 @@ func findUpstreamFromUserfile(conn ssh.ConnMetadata) (net.Conn, *ssh.ClientConfi
c, err := net.Dial("tcp", saddr)
if err != nil {
return nil, nil, err
return nil, err
}
return c, &ssh.ClientConfig{}, nil
return c, nil
}
func mapPublicKeyFromUserfile(conn ssh.ConnMetadata, key ssh.PublicKey) (ssh.Signer, error) {
@ -212,7 +212,7 @@ func main() {
logger.Printf("connection accepted: %v", c.RemoteAddr())
go func() {
p, err := piper.Serve(c)
p, err := ssh.NewSSHPiperConn(c, piper)
if err != nil {
logger.Printf("connection from %v establishing failed reason: %v", c.RemoteAddr(), err)