* add support for longer than 2048bit keys * added support for PKCS#1, PKCS#8, DSA, ECDSA keys
126 lines
2.7 KiB
Go
126 lines
2.7 KiB
Go
package database
|
|
|
|
import (
|
|
"bytes"
|
|
"github.com/jinzhu/gorm"
|
|
"net"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
func (p *plugin) findUpstream(conn ssh.ConnMetadata, challengeContext ssh.AdditionalChallengeContext) (net.Conn, *ssh.AuthPipe, error) {
|
|
|
|
user := conn.User()
|
|
d, err := lookupDownstreamWithFallback(p.db, user)
|
|
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
addr := d.Upstream.Server.Address
|
|
upuser := d.Upstream.Username
|
|
|
|
if upuser == "" {
|
|
upuser = d.Username
|
|
}
|
|
|
|
logger.Printf("mapping user [%v] to [%v@%v]", user, upuser, addr)
|
|
|
|
c, err := dial(addr)
|
|
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
hostKeyCallback := ssh.InsecureIgnoreHostKey()
|
|
|
|
if !d.Upstream.Server.IgnoreHostKey {
|
|
|
|
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(d.Upstream.Server.HostKey.Key.Data))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
hostKeyCallback = ssh.FixedHostKey(key)
|
|
}
|
|
|
|
pipe := ssh.AuthPipe{
|
|
User: upuser,
|
|
|
|
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (ssh.AuthPipeType, ssh.AuthMethod, error) {
|
|
|
|
expectKey := key.Marshal()
|
|
for _, k := range d.AuthorizedKeys {
|
|
publicKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k.Key.Data))
|
|
|
|
if err != nil {
|
|
logger.Printf("parse [keyid = %v] error :%v. skip to next key", k.Key.ID, err)
|
|
continue
|
|
}
|
|
|
|
if bytes.Equal(publicKey.Marshal(), expectKey) {
|
|
|
|
kinterf, err := ssh.ParseRawPrivateKey([]byte(d.Upstream.PrivateKey.Key.Data))
|
|
signer, err := ssh.NewSignerFromKey(kinterf)
|
|
|
|
if err != nil || signer == nil {
|
|
break
|
|
}
|
|
|
|
return ssh.AuthPipeTypeMap, ssh.PublicKeys(signer), nil
|
|
}
|
|
}
|
|
|
|
return ssh.AuthPipeTypeNone, nil, nil
|
|
},
|
|
|
|
UpstreamHostKeyCallback: hostKeyCallback,
|
|
}
|
|
return c, &pipe, nil
|
|
}
|
|
|
|
func lookupDownstreamWithFallback(db *gorm.DB, user string) (*downstream, error) {
|
|
d, err := lookupDownstream(db, user)
|
|
|
|
if gorm.IsRecordNotFoundError(err) {
|
|
fallback, _ := lookupConfigValue(db, fallbackUserEntry)
|
|
|
|
if len(fallback) > 0 {
|
|
return lookupDownstream(db, fallback)
|
|
}
|
|
}
|
|
|
|
return d, err
|
|
}
|
|
|
|
func lookupDownstream(db *gorm.DB, user string) (*downstream, error) {
|
|
d := downstream{}
|
|
|
|
if err := db.Set("gorm:auto_preload", true).Where(&downstream{Username: user}).First(&d).Error; err != nil {
|
|
|
|
return nil, err
|
|
}
|
|
|
|
return &d, nil
|
|
}
|
|
|
|
func lookupConfigValue(db *gorm.DB, entry string) (string, error) {
|
|
c := config{}
|
|
if err := db.Where(&config{Entry: entry}).First(&c).Error; err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return c.Value, nil
|
|
}
|
|
|
|
func dial(addr string) (net.Conn, error) {
|
|
|
|
if _, _, err := net.SplitHostPort(addr); err != nil && addr != "" {
|
|
// test valid after concat :22
|
|
if _, _, err := net.SplitHostPort(addr + ":22"); err == nil {
|
|
addr += ":22"
|
|
}
|
|
}
|
|
|
|
return net.Dial("tcp", addr)
|
|
}
|