diff --git a/Gopkg.lock b/Gopkg.lock index 2a6cd9e3..05584d34 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -14,10 +14,11 @@ revision = "d32a9ef172a1ec91efdf3ce26b240c946285bfe7" [[projects]] + branch = "ignore_unknown_ini" name = "github.com/jessevdk/go-flags" packages = ["."] - revision = "c6ca198ec95c841fdb89fc0de7496fed11ab854e" - version = "v1.4.0" + revision = "f0dfbc82957e3db331cb27732d270e275614b005" + source = "https://github.com/tg123/go-flags" [[projects]] name = "github.com/jinzhu/gorm" @@ -65,6 +66,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "52065920d52958f5461e7df92a92483c9eb78c9bb4b504e0bc82e68e38cfb191" + inputs-digest = "534da9de5467ab2fd3960701473a3fd7da82684491b7b65f21644b0a6b7147f5" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index a497b0c4..1301cb30 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -30,5 +30,6 @@ name = "github.com/jinzhu/gorm" [[constraint]] - version = "v1.4.0" + branch = "ignore_unknown_ini" name = "github.com/jessevdk/go-flags" + source = "https://github.com/tg123/go-flags" diff --git a/sshpiperd/cmd.go b/sshpiperd/cmd.go index 4a6763e4..dd2eaa3a 100644 --- a/sshpiperd/cmd.go +++ b/sshpiperd/cmd.go @@ -19,15 +19,17 @@ func (s *subCommand) Execute(args []string) error { return s.callback(args) } -func addSubCommand(parser *flags.Parser, name, desc string, callback interface{}) { - _, err := parser.AddCommand(name, desc, "", callback) +func addSubCommand(parser *flags.Command, name, desc string, callback interface{}) *flags.Command { + c, err := parser.AddCommand(name, desc, "", callback) if err != nil { panic(err) } + + return c } -func addOpt(parser *flags.Parser, name string, data interface{}) { +func addOpt(parser *flags.Group, name string, data interface{}) { _, err := parser.AddGroup(name, "", data) if err != nil { @@ -35,7 +37,7 @@ func addOpt(parser *flags.Parser, name string, data interface{}) { } } -func addPlugins(parser *flags.Parser, name string, pluginNames []string, getter func(n string) registry.Plugin) { +func addPlugins(parser *flags.Group, name string, pluginNames []string, getter func(n string) registry.Plugin) { for _, n := range pluginNames { p := getter(n) @@ -76,30 +78,41 @@ func populateFromConfig(ini *flags.IniParser, data interface{}, longopt string) func main() { parser := flags.NewNamedParser("sshpiperd", flags.Default) - parser.SubcommandsOptional = true parser.LongDescription = "SSH Piper works as a proxy-like ware, and route connections by username, src ip , etc. Please see for more information" - // version - addSubCommand(parser, "version", "show version", &subCommand{func(args []string) error { - showVersion() - return nil - }}) + // public config + configFile := &struct { + ConfigFile flags.Filename `long:"config" description:"Config file path. Will be overwriten by arg options and environment variables" default:"/etc/sshpiperd.ini" env:"SSHPIPERD_CONFIG_FILE" no-ini:"true"` + }{} + addOpt(parser.Group, "sshpiperd", configFile) - // dumpini - addSubCommand(parser, "dumpconfig", "dump current config ini to stdout", &subCommand{func(args []string) error { + loadFromConfigFile := func(c *flags.Command) { + parser := flags.NewNamedParser("sshpiperd", flags.IgnoreUnknown) + parser.Command = c ini := flags.NewIniParser(parser) - ini.Write(os.Stdout, flags.IniIncludeDefaults) - return nil - }}) + err := populateFromConfig(ini, configFile, "config") + if err != nil { + fmt.Println(fmt.Sprintf("load config file failed %v", err)) + os.Exit(1) + } + } + + // version + { + addSubCommand(parser.Command, "version", "show version", &subCommand{func(args []string) error { + showVersion() + return nil + }}) + } // manpage - addSubCommand(parser, "manpage", "write man page to stdout", &subCommand{func(args []string) error { + addSubCommand(parser.Command, "manpage", "write man page to stdout", &subCommand{func(args []string) error { parser.WriteManPage(os.Stdout) return nil }}) // plugins - addSubCommand(parser, "plugins", "list support plugins, e.g. sshpiperd plugis upstream", &subCommand{func(args []string) error { + addSubCommand(parser.Command, "plugins", "list support plugins, e.g. sshpiperd plugis upstream", &subCommand{func(args []string) error { output := func(all []string) { for _, p := range all { @@ -126,68 +139,59 @@ func main() { return nil }}) - // options, for snap only at the moment - addSubCommand(parser, "options", "list all options", &subCommand{func(args []string) error { - for _, g := range parser.Groups() { - for _, o := range g.Options() { - fmt.Println(o.LongName) - } - } - - return nil - }}) - // generate key tools - addSubCommand(parser, "genkey", "generate a 2048 rsa key to stdout", &subCommand{func(args []string) error { - key, err := sshkey.GenerateKey(sshkey.KEY_RSA, 2048) - if err != nil { + { + addSubCommand(parser.Command, "genkey", "generate a 2048 rsa key to stdout", &subCommand{func(args []string) error { + key, err := sshkey.GenerateKey(sshkey.KEY_RSA, 2048) + if err != nil { + return err + } + + out, err := sshkey.MarshalPrivate(key, "") + if err != nil { + return err + } + + _, err = fmt.Fprint(os.Stdout, string(out)) + return err - } - - out, err := sshkey.MarshalPrivate(key, "") - if err != nil { - return err - } - - _, err = fmt.Fprint(os.Stdout, string(out)) - - return err - }}) - - config := &struct { - piperdConfig - - loggerConfig - - // need to be shown in help, or will be moved to populate config - ConfigFile flags.Filename `long:"config" description:"Config file path. Will be overwriten by arg options and environment variables" default:"/etc/sshpiperd.ini" env:"SSHPIPERD_CONFIG_FILE" no-ini:"true"` - }{} - - addSubCommand(parser, "pipe", "manage pipe on current upstream driver", createPipeMgr(&config.UpstreamDriver)) - - addOpt(parser, "sshpiperd", config) - - addPlugins(parser, "upstream", upstream.All(), func(n string) registry.Plugin { return upstream.Get(n) }) - addPlugins(parser, "challenger", challenger.All(), func(n string) registry.Plugin { return challenger.Get(n) }) - addPlugins(parser, "auditor", auditor.All(), func(n string) registry.Plugin { return auditor.Get(n) }) - - // populate by config - ini := flags.NewIniParser(parser) - err := populateFromConfig(ini, config, "config") - if err != nil { - fmt.Println(fmt.Sprintf("load config file failed %v", err)) - os.Exit(1) + }}) } - parser.CommandHandler = func(command flags.Commander, args []string) error { + // pipe management + { + config := &struct { + UpstreamDriver string `long:"upstream-driver" description:"Upstream provider driver" default:"workingdir" env:"SSHPIPERD_UPSTREAM_DRIVER" ini-name:"upstream-driver"` + }{} - // no subcommand called, start to serve - if command == nil { + var c *flags.Command + c = addSubCommand(parser.Command, "pipe", "manage pipe on current upstream driver", createPipeMgr(func() (upstream.Provider, error) { - if len(args) > 0 { - return fmt.Errorf("unknown command %v", args) + loadFromConfigFile(c) + + if config.UpstreamDriver == "" { + return nil, fmt.Errorf("must provider upstream driver") } + return upstream.Get(config.UpstreamDriver).(upstream.Provider), nil + })) + + addOpt(c.Group, "sshpiperd", config) + addPlugins(c.Group, "upstream", upstream.All(), func(n string) registry.Plugin { return upstream.Get(n) }) + } + + // daemon command + { + config := &struct { + piperdConfig + loggerConfig + }{} + + var c *flags.Command + c = addSubCommand(parser.Command, "daemon", "run in daemon mode, serving traffic", &subCommand{func(args []string) error { + // populate by config + loadFromConfigFile(c) + showVersion() // dump used configure only @@ -195,7 +199,7 @@ func main() { fmt.Println() for _, gk := range []string{"sshpiperd", "upstream." + config.UpstreamDriver, "challenger." + config.ChallengerDriver, "auditor." + config.AuditorDriver} { - g := parser.Group.Find(gk) + g := c.Group.Find(gk) if g == nil { continue } @@ -210,9 +214,45 @@ func main() { } return startPiper(&config.piperdConfig, config.createLogger()) - } + }}) + c.SubcommandsOptional = true + + addOpt(c.Group, "sshpiperd", config) + addPlugins(c.Group, "upstream", upstream.All(), func(n string) registry.Plugin { return upstream.Get(n) }) + addPlugins(c.Group, "challenger", challenger.All(), func(n string) registry.Plugin { return challenger.Get(n) }) + addPlugins(c.Group, "auditor", auditor.All(), func(n string) registry.Plugin { return auditor.Get(n) }) + + // dumpini for daemon + addSubCommand(c, "dumpconfig", "dump current config for daemon ini to stdout", &subCommand{func(args []string) error { + loadFromConfigFile(c) + + parser := flags.NewNamedParser("sshpiperd", flags.Default) + parser.Command = c + ini := flags.NewIniParser(parser) + ini.Write(os.Stdout, flags.IniIncludeDefaults) + return nil + }}) + + // options, for snap only at the moment + addSubCommand(c, "options", "list all options for daemon mode", &subCommand{func(args []string) error { + + var printOpts func(*flags.Group) + + printOpts = func(group *flags.Group) { + for _, o := range group.Options() { + fmt.Println(o.LongName) + } + + for _, g := range group.Groups() { + printOpts(g) + } + + } + + printOpts(c.Group) + return nil + }}) - return command.Execute(args) } parser.Parse() diff --git a/sshpiperd/pipemgr.go b/sshpiperd/pipemgr.go index 1014fb16..ae869fc4 100644 --- a/sshpiperd/pipemgr.go +++ b/sshpiperd/pipemgr.go @@ -1,20 +1,10 @@ package main import ( - "fmt" "github.com/tg123/sshpiper/sshpiperd/upstream" ) -func createPipeMgr(driver *string) interface{} { - - load := func() (upstream.Provider, error) { - if *driver == "" { - return nil, fmt.Errorf("must provider upstream driver") - } - - return upstream.Get(*driver).(upstream.Provider), nil - } - +func createPipeMgr(load func() (upstream.Provider, error)) interface{} { // pipe management pipeMgrCmd := struct { List struct { @@ -23,12 +13,12 @@ func createPipeMgr(driver *string) interface{} { Add struct { subCommand - PiperUserName string `long:"piper-username" description:"" required:"true" no-ini:"true"` + PiperUserName string `short:"n" long:"piper-username" description:"" required:"true" no-ini:"true"` // PiperAuthorizedKeysFile flags.Filename UpstreamUserName string `long:"upstream-username" description:"mapped user name" no-ini:"true"` - UpstreamHost string `long:"host" description:"upstream sshd host" required:"true" no-ini:"true"` - UpstreamPort uint `long:"port" description:"upstream sshd port" default:"22" no-ini:"true"` + UpstreamHost string `short:"u" long:"host" description:"upstream sshd host" required:"true" no-ini:"true"` + UpstreamPort uint `short:"p" long:"port" description:"upstream sshd port" default:"22" no-ini:"true"` // UpstreamKeyFile flags.Filename // UpstreamHostKey @@ -38,7 +28,7 @@ func createPipeMgr(driver *string) interface{} { Remove struct { subCommand - Name string `long:"name" required:"true" no-ini:"true"` + Name string `short:"n" long:"piper-username" required:"true" no-ini:"true"` } `command:"remove" description:"remove a pipe from current upstream"` }{} diff --git a/sshpiperd/snap/snap/hooks/configure b/sshpiperd/snap/snap/hooks/configure index 5576d9bd..3f5bba29 100755 --- a/sshpiperd/snap/snap/hooks/configure +++ b/sshpiperd/snap/snap/hooks/configure @@ -5,7 +5,7 @@ bin=$SNAP/sshpiperd config=$SNAP_DATA/sshpiperd.ini args="" -for opt in $($bin options); do +for opt in $($bin daemon options); do v=$(snapctl get $opt) if [ "$v" != "" ]; then @@ -27,4 +27,4 @@ for opt in $($bin options); do done echo ";DO NOT MODIFY, USE snap set sshpiperd key=val, AUTO GENERATED" $(date) > $config -$bin --config /dev/null $args dumpconfig >> $config +$bin daemon --config /dev/null $args dumpconfig >> $config diff --git a/sshpiperd/snap/snapcraft.yaml b/sshpiperd/snap/snapcraft.yaml index 6c680c40..e2d79b1f 100644 --- a/sshpiperd/snap/snapcraft.yaml +++ b/sshpiperd/snap/snapcraft.yaml @@ -14,7 +14,7 @@ parts: apps: sshpiperd-daemon: - command: sshpiperd --config $SNAP_DATA/sshpiperd.ini + command: sshpiperd daemon --config $SNAP_DATA/sshpiperd.ini plugs: [network-bind] daemon: simple sshpiperd: diff --git a/vendor/github.com/jessevdk/go-flags/ini.go b/vendor/github.com/jessevdk/go-flags/ini.go index e714d3d3..593bd27e 100644 --- a/vendor/github.com/jessevdk/go-flags/ini.go +++ b/vendor/github.com/jessevdk/go-flags/ini.go @@ -505,7 +505,11 @@ func (i *IniParser) parse(ini *ini) error { groups := i.matchingGroups(name) if len(groups) == 0 { - return newErrorf(ErrUnknownGroup, "could not find option group `%s'", name) + if (p.Options & IgnoreUnknown) == None { + return newErrorf(ErrUnknownGroup, "could not find option group `%s'", name) + } + + continue } for _, inival := range section {