diff --git a/cmd/sshpiperd/grpc.go b/cmd/sshpiperd/grpc.go new file mode 100644 index 00000000..ac7db6dd --- /dev/null +++ b/cmd/sshpiperd/grpc.go @@ -0,0 +1,102 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + + "github.com/tg123/sshpiper/cmd/sshpiperd/internal/plugin" + "github.com/urfave/cli/v2" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" +) + +func createNetGrpcPlugin(args []string) (grpcPlugin *plugin.GrpcPlugin, err error) { + app := &cli.App{ + Name: "grpc", + Usage: "sshpiperd grpc plugin", + HideHelpCommand: true, + HideHelp: true, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "endpoint", + Usage: "grpc endpoint address", + EnvVars: []string{"SSHPIPERD_GRPC_ENDPOINT"}, + Required: true, + }, + &cli.BoolFlag{ + Name: "insecure", + Usage: "disable tls", + EnvVars: []string{"SSHPIPERD_GRPC_INSECURE"}, + }, + &cli.StringFlag{ + Name: "key", + Usage: "grpc client key path", + EnvVars: []string{"SSHPIPERD_GRPC_KEY"}, + }, + &cli.StringFlag{ + Name: "cert", + Usage: "grpc client cert path", + EnvVars: []string{"SSHPIPERD_GRPC_CERT"}, + }, + &cli.StringFlag{ + Name: "cacert", + Usage: "grpc ca cert path", + EnvVars: []string{"SSHPIPERD_GRPC_CACERT"}, + }, + }, + Action: func(c *cli.Context) error { + + var secopt grpc.DialOption + if c.Bool("insecure") { + secopt = grpc.WithTransportCredentials(insecure.NewCredentials()) + } else { + + clientCert, err := tls.LoadX509KeyPair(c.String("cert"), c.String("key")) + if err != nil { + return err + } + + config := &tls.Config{ + Certificates: []tls.Certificate{clientCert}, + } + + cacert := c.String("cacert") + if cacert != "" { + ca, err := ioutil.ReadFile(cacert) + if err != nil { + return err + } + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(ca) { + return fmt.Errorf("failed to append ca") + } + + config.RootCAs = certPool + } + + secopt = grpc.WithTransportCredentials(credentials.NewTLS(config)) + } + + conn, err := grpc.Dial(c.String("endpoint"), secopt, grpc.WithBlock()) + if err != nil { + return err + } + + grpcPlugin, err = plugin.DialGrpc(conn) + if err != nil { + return err + } + + return nil + }, + } + + if err := app.Run(args); err != nil { + return nil, err + } + + return grpcPlugin, nil +} diff --git a/cmd/sshpiperd/main.go b/cmd/sshpiperd/main.go index 15edc1f8..fdd1adc7 100644 --- a/cmd/sshpiperd/main.go +++ b/cmd/sshpiperd/main.go @@ -48,6 +48,29 @@ func splitByDash(args []string) ([]string, []string) { return args, nil } +func createPlugin(args []string) (*plugin.GrpcPlugin, error) { + exe := args[0] + + switch exe { + case "grpc": + log.Info("starting net grpc plugin: ") + return createNetGrpcPlugin(args) + + default: + cmd := exec.Command(exe) + cmd.Args = args + + log.Info("starting child process plugin: ", cmd.Args) + + p, err := plugin.DialCmd(cmd) + if err != nil { + return nil, err + } + + return &p.GrpcPlugin, nil + } +} + func main() { app := &cli.App{ @@ -91,18 +114,6 @@ func main() { EnvVars: []string{"SSHPIPERD_LOG_LEVEL"}, }, }, - // Commands: []*cli.Command{ - // &cli.Command{ - // Name: "plug", - - // }, - // // &cli.Command{ - // // Name: "grpc", - // // Action: func(ctx *cli.Context) error { - // // return fmt.Errorf("not implemented") - // // }, - // // }, - // }, Action: func(ctx *cli.Context) error { level, err := log.ParseLevel(ctx.String("log-level")) if err != nil { @@ -134,19 +145,13 @@ func main() { continue } - exe := args[0] - cmd := exec.Command(exe) - cmd.Args = args - - log.Info("starting plugin: ", cmd.Args) - - p, err := plugin.DialCmd(cmd) + p, err := createPlugin(args) if err != nil { return err } - go p.RecvLogs(log.StandardLogger().Out) - plugins = append(plugins, &p.GrpcPlugin) + go p.RecvLogs(log.StandardLogger().Out) + plugins = append(plugins, p) } if err := d.install(plugins...); err != nil { diff --git a/go.sum b/go.sum index d091fd50..a8679897 100644 --- a/go.sum +++ b/go.sum @@ -69,7 +69,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -93,7 +92,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= @@ -206,8 +204,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -451,7 +447,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -599,8 +594,6 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -638,8 +631,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/plugin/fixed/main.go b/plugin/fixed/main.go index 9e56622e..c80ef34d 100644 --- a/plugin/fixed/main.go +++ b/plugin/fixed/main.go @@ -1,44 +1,65 @@ package main import ( + "fmt" "os" log "github.com/sirupsen/logrus" "github.com/tg123/sshpiper/libplugin" + "github.com/urfave/cli/v2" ) func main() { - if len(os.Args) < 2 { - log.Fatal("no target address provided") - } - target := os.Args[1] + app := &cli.App{ + Name: "fixed", + Usage: "sshpiperd fixed plugin, only password auth is supported", + HideHelpCommand: true, + HideHelp: true, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "target", + Usage: "target ssh endpoint address", + EnvVars: []string{"SSHPIPERD_FIXED_TARGET"}, + Required: true, + }, + }, + Writer: os.Stderr, + ErrWriter: os.Stderr, + Action: func(c *cli.Context) error { + target := c.String("target") - host, port, err := libplugin.SplitHostPortForSSH(target) - if err != nil { - panic(err) - } + host, port, err := libplugin.SplitHostPortForSSH(target) + if err != nil { + return err + } - config := libplugin.SshPiperPluginConfig{ - PasswordCallback: func(conn libplugin.ConnMetadata, password []byte) (*libplugin.Upstream, error) { - log.Info("routing to ", target) - return &libplugin.Upstream{ - Host: host, - Port: int32(port), - IgnoreHostKey: true, - Auth: libplugin.CreatePasswordAuth(password), - }, nil + config := libplugin.SshPiperPluginConfig{ + PasswordCallback: func(conn libplugin.ConnMetadata, password []byte) (*libplugin.Upstream, error) { + log.Info("routing to ", target) + return &libplugin.Upstream{ + Host: host, + Port: int32(port), + IgnoreHostKey: true, + Auth: libplugin.CreatePasswordAuth(password), + }, nil + }, + } + + p, err := libplugin.NewFromStdio(config) + if err != nil { + return err + } + + libplugin.ConfigStdioLogrus(p, nil) + + log.Printf("starting fix routing to ssh endpoint %v (password only)", target) + return p.Serve() }, } - p, err := libplugin.NewFromStdio(config) - if err != nil { - panic(err) + if err := app.Run(os.Args); err != nil { + fmt.Fprintf(os.Stderr, "cannot start plugin: %v\n", err) } - - libplugin.ConfigStdioLogrus(p, nil) - - log.Printf("starting fix routing to ssh %v (password only)", target) - panic(p.Serve()) }