diff --git a/plugin/yaml/main.go b/plugin/yaml/main.go index e1a522f9..760f2868 100644 --- a/plugin/yaml/main.go +++ b/plugin/yaml/main.go @@ -14,12 +14,12 @@ func main() { Name: "yaml", Usage: "sshpiperd yaml plugin", Flags: []cli.Flag{ - &cli.StringFlag{ + &cli.StringSliceFlag{ Name: "config", - Usage: "path to yaml config file", + Usage: "path to yaml config files, can be globs as well", Required: true, EnvVars: []string{"SSHPIPERD_YAML_CONFIG"}, - Destination: &plugin.File, + Destination: &plugin.FileGlobs, }, &cli.BoolFlag{ Name: "no-check-perm", diff --git a/plugin/yaml/skel.go b/plugin/yaml/skel.go index bf1e9711..442d82f7 100644 --- a/plugin/yaml/skel.go +++ b/plugin/yaml/skel.go @@ -9,12 +9,11 @@ import ( ) type skelpipeWrapper struct { - plugin *plugin - - pipe *yamlPipe + pipe *yamlPipe + config *piperConfig } type skelpipeFromWrapper struct { - plugin *plugin + config *piperConfig from *yamlPipeFrom to *yamlPipeTo @@ -28,7 +27,7 @@ type skelpipePublicKeyWrapper struct { } type skelpipeToWrapper struct { - plugin *plugin + config *piperConfig username string to *yamlPipeTo @@ -39,7 +38,7 @@ func (s *skelpipeWrapper) From() []libplugin.SkelPipeFrom { for _, f := range s.pipe.From { w := &skelpipeFromWrapper{ - plugin: s.plugin, + config: s.config, from: &f, to: &s.pipe.To, } @@ -70,7 +69,7 @@ func (s *skelpipeToWrapper) IgnoreHostKey(conn libplugin.ConnMetadata) bool { } func (s *skelpipeToWrapper) KnownHosts(conn libplugin.ConnMetadata) ([]byte, error) { - return s.plugin.loadFileOrDecodeMany(s.to.KnownHosts, s.to.KnownHostsData, map[string]string{ + return s.config.loadFileOrDecodeMany(s.to.KnownHosts, s.to.KnownHostsData, map[string]string{ "DOWNSTREAM_USER": conn.User(), "UPSTREAM_USER": s.username, }) @@ -101,7 +100,7 @@ func (s *skelpipeFromWrapper) MatchConn(conn libplugin.ConnMetadata) (libplugin. if matched { return &skelpipeToWrapper{ - plugin: s.plugin, + config: s.config, username: targetuser, to: s.to, }, nil @@ -115,19 +114,19 @@ func (s *skelpipePasswordWrapper) TestPassword(conn libplugin.ConnMetadata, pass } func (s *skelpipePublicKeyWrapper) AuthorizedKeys(conn libplugin.ConnMetadata) ([]byte, error) { - return s.plugin.loadFileOrDecodeMany(s.from.AuthorizedKeys, s.from.AuthorizedKeysData, map[string]string{ + return s.config.loadFileOrDecodeMany(s.from.AuthorizedKeys, s.from.AuthorizedKeysData, map[string]string{ "DOWNSTREAM_USER": conn.User(), }) } func (s *skelpipePublicKeyWrapper) TrustedUserCAKeys(conn libplugin.ConnMetadata) ([]byte, error) { - return s.plugin.loadFileOrDecodeMany(s.from.TrustedUserCAKeys, s.from.TrustedUserCAKeysData, map[string]string{ + return s.config.loadFileOrDecodeMany(s.from.TrustedUserCAKeys, s.from.TrustedUserCAKeysData, map[string]string{ "DOWNSTREAM_USER": conn.User(), }) } func (s *skelpipeToWrapper) PrivateKey(conn libplugin.ConnMetadata) ([]byte, []byte, error) { - p, err := s.plugin.loadFileOrDecode(s.to.PrivateKey, s.to.PrivateKeyData, map[string]string{ + p, err := s.config.loadFileOrDecode(s.to.PrivateKey, s.to.PrivateKeyData, map[string]string{ "DOWNSTREAM_USER": conn.User(), "UPSTREAM_USER": s.username, }) @@ -144,19 +143,21 @@ func (s *skelpipeToWrapper) OverridePassword(conn libplugin.ConnMetadata) ([]byt } func (p *plugin) listPipe(_ libplugin.ConnMetadata) ([]libplugin.SkelPipe, error) { - config, err := p.loadConfig() + configs, err := p.loadConfig() if err != nil { return nil, err } var pipes []libplugin.SkelPipe - for _, pipe := range config.Pipes { - wrapper := &skelpipeWrapper{ - plugin: p, - pipe: &pipe, - } - pipes = append(pipes, wrapper) + for _, config := range configs { + for _, pipe := range config.Pipes { + wrapper := &skelpipeWrapper{ + config: &config, + pipe: &pipe, + } + pipes = append(pipes, wrapper) + } } return pipes, nil diff --git a/plugin/yaml/yaml.go b/plugin/yaml/yaml.go index 9c065b6c..a1a751d7 100644 --- a/plugin/yaml/yaml.go +++ b/plugin/yaml/yaml.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" + "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" ) @@ -76,10 +77,12 @@ type yamlPipe struct { type piperConfig struct { Version string `yaml:"version"` Pipes []yamlPipe `yaml:"pipes,flow"` + + filename string } type plugin struct { - File string + FileGlobs cli.StringSlice NoCheckPerm bool } @@ -87,8 +90,7 @@ func newYamlPlugin() *plugin { return &plugin{} } -func (p *plugin) checkPerm() error { - filename := p.File +func (p *plugin) checkPerm(filename string) error { f, err := os.Open(filename) if err != nil { return err @@ -111,28 +113,44 @@ func (p *plugin) checkPerm() error { return nil } -func (p *plugin) loadConfig() (piperConfig, error) { - var config piperConfig +func (p *plugin) loadConfig() ([]piperConfig, error) { + var allconfig []piperConfig - err := p.checkPerm() - if err != nil { - return config, err - } + for _, fg := range p.FileGlobs.Value() { + files, err := filepath.Glob(fg) + if err != nil { + return nil, err + } - configbyte, err := os.ReadFile(p.File) - if err != nil { - return config, err - } + for _, file := range files { - err = yaml.Unmarshal(configbyte, &config) - if err != nil { - return config, err + if err := p.checkPerm(file); err != nil { + return nil, err + } + + configbyte, err := os.ReadFile(file) + if err != nil { + return nil, err + } + + var config piperConfig + + err = yaml.Unmarshal(configbyte, &config) + if err != nil { + return nil, err + } + + config.filename = file + + allconfig = append(allconfig, config) + + } } - return config, nil + return allconfig, nil } -func (p *plugin) loadFileOrDecode(file string, base64data string, vars map[string]string) ([]byte, error) { +func (p *piperConfig) loadFileOrDecode(file string, base64data string, vars map[string]string) ([]byte, error) { if file != "" { file = os.Expand(file, func(placeholderName string) string { @@ -145,7 +163,7 @@ func (p *plugin) loadFileOrDecode(file string, base64data string, vars map[strin }) if !filepath.IsAbs(file) { - file = filepath.Join(filepath.Dir(p.File), file) + file = filepath.Join(filepath.Dir(p.filename), file) } return os.ReadFile(file) @@ -158,7 +176,7 @@ func (p *plugin) loadFileOrDecode(file string, base64data string, vars map[strin return nil, nil } -func (p *plugin) loadFileOrDecodeMany(files listOrString, base64data listOrString, vars map[string]string) ([]byte, error) { +func (p *piperConfig) loadFileOrDecodeMany(files listOrString, base64data listOrString, vars map[string]string) ([]byte, error) { var byteSlices [][]byte for _, file := range files.Combine() {