first move of working dir
This commit is contained in:
parent
54d1f08ade
commit
fd0a0c628e
5 changed files with 133 additions and 28 deletions
|
|
@ -116,6 +116,8 @@ func (g *GrpcPlugin) InstallPiperConfig(config *ssh.PiperConfig) error {
|
|||
}
|
||||
case "Banner":
|
||||
config.BannerCallback = g.BannerCallback
|
||||
case "VerifyHostKey":
|
||||
// ignore
|
||||
default:
|
||||
return fmt.Errorf("unknown callback %s", c)
|
||||
}
|
||||
|
|
@ -184,11 +186,13 @@ func (g *GrpcPlugin) NextAuthMethodsLocal(conn ssh.ConnMetadata, challengeCtx ss
|
|||
return allow, nil
|
||||
}
|
||||
|
||||
func toMeta(challengeCtx ssh.ChallengeContext) *libplugin.ConnMeta {
|
||||
func toMeta(challengeCtx ssh.ChallengeContext, conn ssh.ConnMetadata) *libplugin.ConnMeta {
|
||||
switch meta := challengeCtx.(type) {
|
||||
case *connMeta:
|
||||
meta.UserName = conn.User()
|
||||
return (*libplugin.ConnMeta)(meta)
|
||||
case *chainConnMeta:
|
||||
meta.UserName = conn.User()
|
||||
return (*libplugin.ConnMeta)(&meta.connMeta)
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +200,7 @@ func toMeta(challengeCtx ssh.ChallengeContext) *libplugin.ConnMeta {
|
|||
}
|
||||
|
||||
func (g *GrpcPlugin) NextAuthMethodsRemote(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) ([]string, error) {
|
||||
meta := toMeta(challengeCtx)
|
||||
meta := toMeta(challengeCtx, conn)
|
||||
reply, err := g.client.NextAuthMethods(context.Background(), &libplugin.NextAuthMethodsRequest{
|
||||
Meta: meta,
|
||||
})
|
||||
|
|
@ -231,7 +235,7 @@ func (g *GrpcPlugin) UpstreamAuthFailureCallbackLocal(onn ssh.ConnMetadata, meth
|
|||
g.allowedMethod[method] = false
|
||||
}
|
||||
|
||||
func (g *GrpcPlugin) UpstreamAuthFailureCallbackRemote(onn ssh.ConnMetadata, method string, err error, challengeCtx ssh.ChallengeContext) {
|
||||
func (g *GrpcPlugin) UpstreamAuthFailureCallbackRemote(conn ssh.ConnMetadata, method string, err error, challengeCtx ssh.ChallengeContext) {
|
||||
noMoreMethodErr, ok := err.(ssh.NoMoreMethodsErr)
|
||||
allowed := make([]libplugin.AuthMethod, len(noMoreMethodErr.Allowed))
|
||||
if ok {
|
||||
|
|
@ -246,14 +250,14 @@ func (g *GrpcPlugin) UpstreamAuthFailureCallbackRemote(onn ssh.ConnMetadata, met
|
|||
}
|
||||
|
||||
g.client.UpstreamAuthFailureNotice(context.Background(), &libplugin.UpstreamAuthFailureNoticeRequest{
|
||||
Meta: toMeta(challengeCtx),
|
||||
Meta: toMeta(challengeCtx, conn),
|
||||
Method: method,
|
||||
Error: err.Error(),
|
||||
AllowedMethods: allowed,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GrpcPlugin) createUpstream(challengeCtx ssh.ChallengeContext, upstream *libplugin.Upstream) (*ssh.Upstream, error) {
|
||||
func (g *GrpcPlugin) createUpstream(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext, upstream *libplugin.Upstream) (*ssh.Upstream, error) {
|
||||
if upstream.GetNextPlugin() != nil {
|
||||
if g.OnNextPlugin == nil {
|
||||
return nil, fmt.Errorf("next plugin is not supported")
|
||||
|
|
@ -261,7 +265,7 @@ func (g *GrpcPlugin) createUpstream(challengeCtx ssh.ChallengeContext, upstream
|
|||
return nil, g.OnNextPlugin(challengeCtx, upstream.GetNextPlugin())
|
||||
}
|
||||
|
||||
meta := toMeta(challengeCtx)
|
||||
meta := toMeta(challengeCtx, conn)
|
||||
|
||||
port := upstream.Port
|
||||
if port <= 0 {
|
||||
|
|
@ -275,6 +279,7 @@ func (g *GrpcPlugin) createUpstream(challengeCtx ssh.ChallengeContext, upstream
|
|||
}
|
||||
|
||||
config := ssh.ClientConfig{
|
||||
User: upstream.UserName,
|
||||
HostKeyCallback: func(_ string, _ net.Addr, key ssh.PublicKey) error {
|
||||
if upstream.IgnoreHostKey {
|
||||
return nil
|
||||
|
|
@ -345,7 +350,7 @@ func (g *GrpcPlugin) createUpstream(challengeCtx ssh.ChallengeContext, upstream
|
|||
}
|
||||
|
||||
func (g *GrpcPlugin) NoneAuthCallback(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) (*ssh.Upstream, error) {
|
||||
meta := toMeta(challengeCtx)
|
||||
meta := toMeta(challengeCtx, conn)
|
||||
reply, err := g.client.NoneAuth(context.Background(), &libplugin.NoneAuthRequest{
|
||||
Meta: meta,
|
||||
})
|
||||
|
|
@ -354,11 +359,11 @@ func (g *GrpcPlugin) NoneAuthCallback(conn ssh.ConnMetadata, challengeCtx ssh.Ch
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return g.createUpstream(challengeCtx, reply.Upstream)
|
||||
return g.createUpstream(conn, challengeCtx, reply.Upstream)
|
||||
}
|
||||
|
||||
func (g *GrpcPlugin) PasswordCallback(conn ssh.ConnMetadata, password []byte, challengeCtx ssh.ChallengeContext) (*ssh.Upstream, error) {
|
||||
meta := toMeta(challengeCtx)
|
||||
meta := toMeta(challengeCtx, conn)
|
||||
reply, err := g.client.PasswordAuth(context.Background(), &libplugin.PasswordAuthRequest{
|
||||
Meta: meta,
|
||||
Password: password,
|
||||
|
|
@ -368,11 +373,11 @@ func (g *GrpcPlugin) PasswordCallback(conn ssh.ConnMetadata, password []byte, ch
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return g.createUpstream(challengeCtx, reply.Upstream)
|
||||
return g.createUpstream(conn, challengeCtx, reply.Upstream)
|
||||
}
|
||||
|
||||
func (g *GrpcPlugin) PublicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey, challengeCtx ssh.ChallengeContext) (*ssh.Upstream, error) {
|
||||
meta := toMeta(challengeCtx)
|
||||
meta := toMeta(challengeCtx, conn)
|
||||
reply, err := g.client.PublicKeyAuth(context.Background(), &libplugin.PublicKeyAuthRequest{
|
||||
Meta: meta,
|
||||
PublicKey: key.Marshal(),
|
||||
|
|
@ -382,7 +387,7 @@ func (g *GrpcPlugin) PublicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return g.createUpstream(challengeCtx, reply.Upstream)
|
||||
return g.createUpstream(conn, challengeCtx, reply.Upstream)
|
||||
}
|
||||
|
||||
func (g *GrpcPlugin) KeyboardInteractiveCallback(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge, challengeCtx ssh.ChallengeContext) (*ssh.Upstream, error) {
|
||||
|
|
@ -430,7 +435,7 @@ func (g *GrpcPlugin) KeyboardInteractiveCallback(conn ssh.ConnMetadata, client s
|
|||
}
|
||||
}
|
||||
} else if r := msg.GetMetaRequest(); r != nil {
|
||||
meta := toMeta(challengeCtx)
|
||||
meta := toMeta(challengeCtx, conn)
|
||||
if err := stream.Send(&libplugin.KeyboardInteractiveAuthMessage{
|
||||
Message: &libplugin.KeyboardInteractiveAuthMessage_MetaResponse{
|
||||
MetaResponse: &libplugin.KeyboardInteractiveMetaResponse{
|
||||
|
|
@ -443,7 +448,7 @@ func (g *GrpcPlugin) KeyboardInteractiveCallback(conn ssh.ConnMetadata, client s
|
|||
|
||||
} else if r := msg.GetFinishRequest(); r != nil {
|
||||
if r.GetUpstream() != nil {
|
||||
return g.createUpstream(challengeCtx, r.GetUpstream())
|
||||
return g.createUpstream(conn, challengeCtx, r.GetUpstream())
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("auth failed: %s", r.GetErrorMessage())
|
||||
|
|
@ -452,7 +457,7 @@ func (g *GrpcPlugin) KeyboardInteractiveCallback(conn ssh.ConnMetadata, client s
|
|||
}
|
||||
|
||||
func (g *GrpcPlugin) BannerCallback(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) string {
|
||||
meta := toMeta(challengeCtx)
|
||||
meta := toMeta(challengeCtx, conn)
|
||||
reply, err := g.client.Banner(context.Background(), &libplugin.BannerRequest{
|
||||
Meta: meta,
|
||||
})
|
||||
|
|
|
|||
4
go.mod
4
go.mod
|
|
@ -10,7 +10,6 @@ replace (
|
|||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0
|
||||
github.com/dcu/go-authy v1.0.1
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/jessevdk/go-flags v1.5.0
|
||||
|
|
@ -39,8 +38,6 @@ require (
|
|||
github.com/denisenkom/go-mssqldb v0.11.0 // indirect
|
||||
github.com/go-logr/logr v1.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/gojektech/heimdall v5.0.2+incompatible // indirect
|
||||
github.com/gojektech/valkyrie v0.0.0-20190210220504-8f62c1e7ba45 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
|
|
@ -60,7 +57,6 @@ require (
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
|
|
|
|||
7
go.sum
7
go.sum
|
|
@ -78,8 +78,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dcu/go-authy v1.0.1 h1:9LtF0otuGKQOD0AzyAUKzT+etvzGZxpvf4x20/xiW1Y=
|
||||
github.com/dcu/go-authy v1.0.1/go.mod h1:SJ8cuAYQ9c9ZGGIGNk5gyH87/Q0uJXzhb1DR8MNnn98=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL/7ND8HI=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
|
|
@ -122,10 +120,6 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC
|
|||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/gojektech/heimdall v5.0.2+incompatible h1:mfGLnHNTKN7b1OMTO4ZvL3oT2P13kqTTV7owK7BZDck=
|
||||
github.com/gojektech/heimdall v5.0.2+incompatible/go.mod h1:8hRIZ3+Kz0r3GAFI9QrUuvZht8ypg5Rs8schCXioLOo=
|
||||
github.com/gojektech/valkyrie v0.0.0-20190210220504-8f62c1e7ba45 h1:MO2DsGCZz8phRhLnpFvHEQgTH521sVN/6F2GZTbNO3Q=
|
||||
github.com/gojektech/valkyrie v0.0.0-20190210220504-8f62c1e7ba45/go.mod h1:tDYRk1s5Pms6XJjj5m2PxAzmQvaDU8GqDf1u6x7yxKw=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||
|
|
@ -276,7 +270,6 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
|||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ type SshPiperPluginConfig struct {
|
|||
|
||||
KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Upstream, error)
|
||||
|
||||
UpstreamAuthFailureCallback func(conn ConnMetadata, method string, err error)
|
||||
UpstreamAuthFailureCallback func(conn ConnMetadata, method string, err error, allowmethods []string)
|
||||
|
||||
BannerCallback func(conn ConnMetadata) string
|
||||
|
||||
|
|
@ -341,7 +341,17 @@ func (s *server) UpstreamAuthFailureNotice(ctx context.Context, req *UpstreamAut
|
|||
return nil, status.Errorf(codes.Unimplemented, "method UpstreamAuthFailureNotice not implemented")
|
||||
}
|
||||
|
||||
s.config.UpstreamAuthFailureCallback(req.Meta, req.Method, fmt.Errorf(req.Error))
|
||||
var methods []string
|
||||
|
||||
for _, method := range req.GetAllowedMethods() {
|
||||
m := AuthMethodTypeToName(method)
|
||||
if m == "" {
|
||||
continue
|
||||
}
|
||||
methods = append(methods, m)
|
||||
}
|
||||
|
||||
s.config.UpstreamAuthFailureCallback(req.Meta, req.Method, fmt.Errorf(req.Error), methods)
|
||||
|
||||
return &UpstreamAuthFailureNoticeResponse{}, nil
|
||||
}
|
||||
|
|
|
|||
101
plugin/workingdir/main.go
Normal file
101
plugin/workingdir/main.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/tg123/sshpiper/libplugin"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func createWorkingdir(c *cli.Context, user string) (*workingdir, error) {
|
||||
if !c.Bool("allow-baduser-name") {
|
||||
if !isUsernameSecure(user) {
|
||||
return nil, fmt.Errorf("bad username: %s", user)
|
||||
}
|
||||
}
|
||||
|
||||
root := c.String("root")
|
||||
|
||||
return &workingdir{
|
||||
path: path.Join(root, user),
|
||||
noCheckPerm: c.Bool("no-check-perm"),
|
||||
strict: c.Bool("strict-hostkey"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
libplugin.CreateAndRunPluginTemplate(&libplugin.PluginTemplate{
|
||||
Name: "workingdir",
|
||||
Usage: "sshpiperd workingdir plugin",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "root",
|
||||
Usage: "path to root working directory",
|
||||
Value: "/var/sshpiper",
|
||||
EnvVars: []string{"SSHPIPERD_WORKINGDIR_ROOT"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "allow-baduser-name",
|
||||
Usage: "allow bad username",
|
||||
EnvVars: []string{"SSHPIPERD_WORKINGDIR_ALLOWBADUSERNAME"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "no-check-perm",
|
||||
Usage: "disable 0400 checking",
|
||||
EnvVars: []string{"SSHPIPERD_WORKINGDIR_NOCHECKPERM"},
|
||||
},
|
||||
// &cli.StringFlag{
|
||||
// Name: "fallback-username",
|
||||
// Usage: "fallback to a user when user does not exists in directory",
|
||||
// EnvVars: []string{"SSHPIPERD_WORKINGDIR_FALLBACKUSERNAME"},
|
||||
// },
|
||||
&cli.BoolFlag{
|
||||
Name: "strict-hostkey",
|
||||
Usage: "upstream host public key must be in known_hosts file, otherwise drop the connection",
|
||||
EnvVars: []string{"SSHPIPERD_WORKINGDIR_STRICTHOSTKEY"},
|
||||
},
|
||||
},
|
||||
CreateConfig: func(c *cli.Context) (*libplugin.SshPiperPluginConfig, error) {
|
||||
|
||||
return &libplugin.SshPiperPluginConfig{
|
||||
PasswordCallback: func(conn libplugin.ConnMetadata, password []byte) (*libplugin.Upstream, error) {
|
||||
w, err := createWorkingdir(c, conn.User())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := w.createUpstream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.Auth = libplugin.CreatePasswordAuth(password)
|
||||
return u, nil
|
||||
},
|
||||
|
||||
PublicKeyCallback: func(conn libplugin.ConnMetadata, key []byte) (*libplugin.Upstream, error) {
|
||||
w, err := createWorkingdir(c, conn.User())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := w.createUpstream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k, err := w.mapkey(key)
|
||||
u.Auth = libplugin.CreatePrivateKeyAuth(k)
|
||||
|
||||
return u, nil
|
||||
},
|
||||
|
||||
VerifyHostKeyCallback: func(conn libplugin.ConnMetadata, key []byte) (bool, error) {
|
||||
return true, nil
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue