diff --git a/sshpiperd/auditor/loader/load.go b/sshpiperd/auditor/loader/load.go new file mode 100644 index 00000000..a122b33a --- /dev/null +++ b/sshpiperd/auditor/loader/load.go @@ -0,0 +1,5 @@ +package loader + +import ( + _ "github.com/tg123/sshpiper/sshpiperd/auditor/typescript_logger" +) diff --git a/sshpiperd/auditor/provider.go b/sshpiperd/auditor/provider.go new file mode 100644 index 00000000..410ff1c8 --- /dev/null +++ b/sshpiperd/auditor/provider.go @@ -0,0 +1,43 @@ +package auditor + +import ( + "golang.org/x/crypto/ssh" + + "github.com/tg123/sshpiper/sshpiperd/registry" +) + +type AuditorHook func(conn ssh.ConnMetadata, msg []byte) ([]byte, error) + +type Auditor interface { + GetUpstreamHook() AuditorHook + GetDownstreamHook() AuditorHook + + Close() error +} + +type AuditorProvider interface { + registry.Plugin + + CreateAuditor(ssh.ConnMetadata) (Auditor, error) +} + +var ( + drivers = registry.NewRegistry() +) + +func Register(name string, driver AuditorProvider) { + drivers.Register(name, driver) +} + +func All() []string { + return drivers.Drivers() +} + +func Get(name string) AuditorProvider { + if d, ok := drivers.Get(name).(AuditorProvider); ok { + return d + + } + + return nil +} diff --git a/sshpiperd/auditor/audit.go b/sshpiperd/auditor/typescript_logger/audit.go similarity index 77% rename from sshpiperd/auditor/audit.go rename to sshpiperd/auditor/typescript_logger/audit.go index d7b072bd..9e6f863b 100644 --- a/sshpiperd/auditor/audit.go +++ b/sshpiperd/auditor/typescript_logger/audit.go @@ -1,8 +1,9 @@ -package main +package typescript_logger import ( "fmt" "os" + "path" "time" "golang.org/x/crypto/ssh" @@ -19,13 +20,13 @@ type filePtyLogger struct { oldtime time.Time } -func newFilePtyLogger(user string) (*filePtyLogger, error) { +func newFilePtyLogger(outputdir string) (*filePtyLogger, error) { now := time.Now() filename := fmt.Sprintf("%d", now.Unix()) - typescript, err := os.OpenFile(userSpecFile(user, fmt.Sprintf("%v.typescript", filename)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + typescript, err := os.OpenFile(path.Join(outputdir, fmt.Sprintf("%v.typescript", filename)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return nil, err @@ -37,7 +38,7 @@ func newFilePtyLogger(user string) (*filePtyLogger, error) { return nil, err } - timing, err := os.OpenFile(userSpecFile(user, fmt.Sprintf("%v.timing", filename)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + timing, err := os.OpenFile(path.Join(outputdir, fmt.Sprintf("%v.timing", filename)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return nil, err diff --git a/sshpiperd/auditor/typescript_logger/plugin.go b/sshpiperd/auditor/typescript_logger/plugin.go new file mode 100644 index 00000000..06cab95b --- /dev/null +++ b/sshpiperd/auditor/typescript_logger/plugin.go @@ -0,0 +1,52 @@ +package typescript_logger + +import ( + "log" + "os" + "path" + + "golang.org/x/crypto/ssh" + + "github.com/tg123/sshpiper/sshpiperd/auditor" +) + +type plugin struct { + Config struct { + OutputDir string `long:"auditor-typescriptlogger-outputdir" default:"/var/sshpiper" description:"Place where logged typescript files were saved" ini-name:"auditor-typescriptlogger-outputdir"` + } +} + +func (p *plugin) GetName() string { + return "typescript-logger" +} + +func (p *plugin) GetOpts() interface{} { + return &p.Config +} + +func (p *plugin) CreateAuditor(conn ssh.ConnMetadata) (auditor.Auditor, error) { + dir := path.Join(p.Config.OutputDir, conn.User()) + err := os.MkdirAll(dir, 0700) + if err != nil { + return nil, err + } + + return newFilePtyLogger(dir) +} + +func (p *plugin) Init(logger *log.Logger) error { + + return nil +} + +func (l *filePtyLogger) GetUpstreamHook() auditor.AuditorHook { + return l.loggingTty +} + +func (l *filePtyLogger) GetDownstreamHook() auditor.AuditorHook { + return nil +} + +func init() { + auditor.Register("typescript-logger", new(plugin)) +} diff --git a/sshpiperd/challenger/challenger.go b/sshpiperd/challenger/provider.go similarity index 100% rename from sshpiperd/challenger/challenger.go rename to sshpiperd/challenger/provider.go diff --git a/sshpiperd/cmd.go b/sshpiperd/cmd.go index f234c63c..2d858bd3 100644 --- a/sshpiperd/cmd.go +++ b/sshpiperd/cmd.go @@ -6,6 +6,8 @@ import ( "github.com/jessevdk/go-flags" + "github.com/tg123/sshpiper/sshpiperd/auditor" + _ "github.com/tg123/sshpiper/sshpiperd/auditor/loader" "github.com/tg123/sshpiper/sshpiperd/challenger" _ "github.com/tg123/sshpiper/sshpiperd/challenger/loader" "github.com/tg123/sshpiper/sshpiperd/registry" @@ -87,6 +89,7 @@ func main() { 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) }) if _, err := parser.Parse(); err != nil { return @@ -97,7 +100,7 @@ func main() { err := ini.ParseFile(string(config.ConfigFile)) if err != nil { - // set by user or + // set by user if !o.IsSetDefault() { fmt.Printf("load config file %v failed %v", config.ConfigFile, err) fmt.Println() @@ -108,7 +111,7 @@ func main() { // init log initLogger(config.Logfile) - // start to serve + // no subcommand called, start to serve if parser.Active == nil { showVersion() dumpConfig() diff --git a/sshpiperd/sshpiperd.go b/sshpiperd/sshpiperd.go index ddb7f639..cba0c5d5 100644 --- a/sshpiperd/sshpiperd.go +++ b/sshpiperd/sshpiperd.go @@ -7,6 +7,7 @@ import ( "golang.org/x/crypto/ssh" + "github.com/tg123/sshpiper/sshpiperd/auditor" "github.com/tg123/sshpiper/sshpiperd/challenger" "github.com/tg123/sshpiper/sshpiperd/upstream" ) @@ -18,6 +19,7 @@ type piperdConfig struct { UpstreamDriver string `short:"u" long:"upstream-driver" description:"Upstream provider driver" default:"workingdir" env:"SSHPIPERD_UPSTREAM_DRIVER" ini-name:"upstream-driver"` ChallengerDriver string `short:"c" long:"challenger-driver" description:"Additional challenger name, e.g. pam, empty for no additional challenge" env:"SSHPIPERD_CHALLENGER" ini-name:"challenger-driver"` + AuditorDriver string `long:"auditor-driver" description:"Auditor for ssh connections piped by SSH Piper " env:"SSHPIPERD_AUDITOR" ini-name:"auditor-driver"` } func startPiper(config *piperdConfig) { @@ -47,6 +49,19 @@ func startPiper(config *piperdConfig) { piper.AdditionalChallenge = ac.GetChallengerHandler() } + var bigbro auditor.AuditorProvider + // install auditor + if config.AuditorDriver != "" { + bigbro = auditor.Get(config.AuditorDriver) + + if bigbro == nil { + logger.Fatalf("auditor driver %v not found", config.AuditorDriver) + } + + logger.Printf("using auditor %s", config.AuditorDriver) + bigbro.Init(logger) + } + privateBytes, err := ioutil.ReadFile(config.PiperKeyFile) if err != nil { logger.Fatalln(err) @@ -83,18 +98,18 @@ func startPiper(config *piperdConfig) { return } - //if config.RecordTypescript { - // auditor, err := newFilePtyLogger(p.DownstreamConnMeta().User()) + if bigbro != nil { + a, err := bigbro.CreateAuditor(p.DownstreamConnMeta()) + if err != nil { + logger.Printf("connection from %v failed to create auditor reason: %v", c.RemoteAddr(), err) + return + } + defer a.Close() - // if err != nil { - // logger.Printf("connection from %v failed to create auditor reason: %v", c.RemoteAddr(), err) - // return - // } + p.HookUpstreamMsg = a.GetUpstreamHook() + p.HookDownstreamMsg = a.GetDownstreamHook() - // defer auditor.Close() - - // p.HookUpstreamMsg = auditor.loggingTty - //} + } err = p.Wait() logger.Printf("connection from %v closed reason: %v", c.RemoteAddr(), err) diff --git a/sshpiperd/upstream/workingdir/workingdir.go b/sshpiperd/upstream/workingdir/workingdir.go index e2ee3b0e..36684cdf 100644 --- a/sshpiperd/upstream/workingdir/workingdir.go +++ b/sshpiperd/upstream/workingdir/workingdir.go @@ -12,6 +12,7 @@ import ( "io/ioutil" "net" "os" + "path" "regexp" "strings" @@ -36,7 +37,7 @@ func init() { } func userSpecFile(user, file string) string { - return fmt.Sprintf("%s/%s/%s", config.WorkingDir, user, file) + return path.Join(config.WorkingDir, user, file) } func (file userFile) read(user string) ([]byte, error) {