diff --git a/pkg/config/config.go b/pkg/config/config.go index 40fc1d02768e62fbd8208bee4b1eb39f9be62f7d..46135c66a2220f99854f865eaad5c1e6b8b268a8 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -25,20 +25,23 @@ type Config struct { SessionKeepDuration int `json:"TERMINAL_SESSION_KEEP_DURATION"` TelnetRegex string `json:"TERMINAL_TELNET_REGEX"` MaxIdleTime time.Duration `json:"SECURITY_MAX_IDLE_TIME"` + SftpRoot string `json:"TERMINAL_SFTP_ROOT" yaml:"SFTP_ROOT"` Name string `yaml:"NAME"` + SecretKey string `yaml:"SECRET_KEY"` HostKeyFile string `yaml:"HOST_KEY_FILE"` CoreHost string `yaml:"CORE_HOST"` BootstrapToken string `yaml:"BOOTSTRAP_TOKEN"` BindHost string `yaml:"BIND_HOST"` SSHPort int `yaml:"SSHD_PORT"` HTTPPort int `yaml:"HTTPD_PORT"` + SSHTimeout int `yaml:"SSH_TIMEOUT"` AccessKey string `yaml:"ACCESS_KEY"` AccessKeyFile string `yaml:"ACCESS_KEY_FILE"` LogLevel string `yaml:"LOG_LEVEL"` HeartbeatDuration time.Duration `yaml:"HEARTBEAT_INTERVAL"` - RootPath string - Comment string - Language string + RootPath string `yaml:"ROOT_PATH"` + Comment string `yaml:"COMMENT"` + Language string `yaml:"LANG"` mux sync.RWMutex } @@ -107,6 +110,7 @@ var Conf = &Config{ BootstrapToken: "", BindHost: "0.0.0.0", SSHPort: 2222, + SSHTimeout: 60, HTTPPort: 5000, AccessKey: "", AccessKeyFile: "access_key", @@ -115,7 +119,7 @@ var Conf = &Config{ HostKey: "", RootPath: rootPath, Comment: "Coco", - Language: "zh_CN", + Language: "zh", ReplayStorage: map[string]string{}, CommandStorage: map[string]string{}, } diff --git a/pkg/handler/session.go b/pkg/handler/session.go index 3c73f8783fcfa271ab5607fb14e245df38300fe4..55a8a36ec93fa10762d4917f00006ef2f8df15e8 100644 --- a/pkg/handler/session.go +++ b/pkg/handler/session.go @@ -323,8 +323,11 @@ func (i *InteractiveHandler) searchNodeAssets(num int) (assets []model.Asset) { } func (i *InteractiveHandler) Proxy(ctx context.Context) { + i.assetSelect = &model.Asset{Hostname: "centos", Port: 22, Ip: "192.168.244.185"} + i.systemUserSelect = &model.SystemUser{Name: "web", UserName: "web", Password: "redhat"} p := proxy.ProxyServer{ Session: i.sess, + User: i.user, Asset: i.assetSelect, SystemUser: i.systemUserSelect, } diff --git a/pkg/proxy/parser.go b/pkg/proxy/parser.go index 75a4813a8e19631570703ca08eb9aaa0d4cd27b6..021e660a910155410b3c28fbac07baf392cb20d0 100644 --- a/pkg/proxy/parser.go +++ b/pkg/proxy/parser.go @@ -171,3 +171,11 @@ func (p *Parser) ParseServerOutput(b []byte) []byte { func (p *Parser) SetCMDFilterRules(rules []model.SystemUserFilterRule) { p.cmdFilterRules = rules } + +func (p *Parser) SetReplayRecorder() { + +} + +func (p *Parser) SetCommandRecorder() { + +} diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go index 0feedad97d47149f0729f5e24aad897be29d38bd..0a4f6adb12f77e38495efca9678d5881f356fc93 100644 --- a/pkg/proxy/proxy.go +++ b/pkg/proxy/proxy.go @@ -1,8 +1,15 @@ package proxy import ( + "fmt" + "io" + "strings" + "time" + "github.com/ibuler/ssh" + "cocogo/pkg/config" + "cocogo/pkg/i18n" "cocogo/pkg/logger" "cocogo/pkg/model" "cocogo/pkg/service" @@ -37,39 +44,63 @@ func (p *ProxyServer) validatePermission() bool { return true } -func (p *ProxyServer) getServerConn() { - +func (p *ProxyServer) getServerConn() (srvConn ServerConnection, err error) { + srvConn = &ServerSSHConnection{ + host: "192.168.244.145", + port: "22", + user: "root", + password: "redhat", + } + pty, _, ok := p.Session.Pty() + if !ok { + logger.Error("User not request Pty") + return + } + done := make(chan struct{}) + go p.sendConnectingMsg(done) + err = srvConn.Connect(pty.Window.Height, pty.Window.Width, pty.Term) + _, _ = io.WriteString(p.Session, "\r\n") + done <- struct{}{} + return } -func (p *ProxyServer) sendConnectingMsg() { - +func (p *ProxyServer) sendConnectingMsg(done chan struct{}) { + delay := 0.0 + msg := fmt.Sprintf(i18n.T("Connecting to %s@%s %.1f"), p.SystemUser.UserName, p.Asset.Ip, delay) + _, _ = io.WriteString(p.Session, msg) + for int(delay) < config.Conf.SSHTimeout { + select { + case <-done: + return + default: + delayS := fmt.Sprintf("%.1f", delay) + data := strings.Repeat("\x08", len(delayS)) + delayS + _, _ = io.WriteString(p.Session, data) + time.Sleep(100 * time.Millisecond) + delay += 0.1 + } + } } func (p *ProxyServer) Proxy() { if !p.checkProtocol() { return } - conn := ServerSSHConnection{ - host: "192.168.244.185", - port: "22", - user: "root", - password: "redhat", - } - ptyReq, _, ok := p.Session.Pty() - if !ok { - logger.Error("Pty not ok") - return - } - err := conn.Connect(ptyReq.Window.Height, ptyReq.Window.Width, ptyReq.Term) + srvConn, err := p.getServerConn() if err != nil { + logger.Errorf("Connect host error: %s\n", err) return } - sw := Switch{ - userConn: p.Session, - serverConn: &conn, - parser: parser, + userConn := &UserSSHConnection{Session: p.Session, winch: make(chan ssh.Window)} + sw := NewSwitch(userConn, srvConn) + cmdRules, err := service.GetSystemUserFilterRules(p.SystemUser.Id) + if err != nil { + logger.Error("Get system user filter rule error: ", err) } + sw.parser.SetCMDFilterRules(cmdRules) + sw.parser.SetReplayRecorder() + sw.parser.SetCommandRecorder() _ = sw.Bridge() - _ = conn.Close() + _ = srvConn.Close() } diff --git a/pkg/proxy/switch.go b/pkg/proxy/switch.go index 31fd85b2197d2567142ceec83d4dd8829b63589c..ba2af50aa8a8fdd8b05f6be59b71475083c2f3b4 100644 --- a/pkg/proxy/switch.go +++ b/pkg/proxy/switch.go @@ -2,7 +2,6 @@ package proxy import ( "cocogo/pkg/logger" - "cocogo/pkg/service" "context" "github.com/ibuler/ssh" "github.com/satori/go.uuid" @@ -10,13 +9,7 @@ import ( ) func NewSwitch(userConn UserConnection, serverConn ServerConnection) (sw *Switch) { - rules, err := service.GetSystemUserFilterRules("") - if err != nil { - logger.Error("Get system user filter rule error: ", err) - } - parser := &Parser{ - cmdFilterRules: rules, - } + parser := new(Parser) parser.Initial() sw = &Switch{userConn: userConn, serverConn: serverConn, parser: parser} return sw @@ -98,7 +91,6 @@ func (s *Switch) readUserToServer(ctx context.Context) { s.cancelFunc() } buf2 := s.parser.ParseUserInput(p) - logger.Debug("Send to server: ", string(buf2)) _, err := s.serverTran.Write(buf2) if err != nil { return diff --git a/pkg/proxy/userconn.go b/pkg/proxy/userconn.go index 4960b295a5ee608d46e2b03ae0df52a2c668ac16..541ba2672afda9b54226cc3cef2987860ee0c37c 100644 --- a/pkg/proxy/userconn.go +++ b/pkg/proxy/userconn.go @@ -12,25 +12,24 @@ type UserConnection interface { Protocol() string WinCh() <-chan ssh.Window User() string - Name() string LoginFrom() string RemoteAddr() string } -type SSHUserConnection struct { +type UserSSHConnection struct { ssh.Session winch <-chan ssh.Window } -func (uc *SSHUserConnection) Protocol() string { +func (uc *UserSSHConnection) Protocol() string { return "ssh" } -func (uc *SSHUserConnection) User() string { +func (uc *UserSSHConnection) User() string { return uc.Session.User() } -func (uc *SSHUserConnection) WinCh() (winch <-chan ssh.Window) { +func (uc *UserSSHConnection) WinCh() (winch <-chan ssh.Window) { _, winch, ok := uc.Pty() if ok { return @@ -38,10 +37,10 @@ func (uc *SSHUserConnection) WinCh() (winch <-chan ssh.Window) { return nil } -func (uc *SSHUserConnection) LoginFrom() string { +func (uc *UserSSHConnection) LoginFrom() string { return "T" } -func (uc *SSHUserConnection) RemoteAddr() string { +func (uc *UserSSHConnection) RemoteAddr() string { return strings.Split(uc.Session.RemoteAddr().String(), ":")[0] } diff --git a/pkg/record/cmd.go b/pkg/record/cmd.go deleted file mode 100644 index f17b821795514cd17f32efa3d75b384846a7b154..0000000000000000000000000000000000000000 --- a/pkg/record/cmd.go +++ /dev/null @@ -1,10 +0,0 @@ -package record - -import ( - "time" -) - -type Command struct { - SessionID string - StartTime time.Time -} diff --git a/pkg/record/interface.go b/pkg/record/interface.go deleted file mode 100644 index a9a310abf8b52f8d9dd280c4cbeeb47aa82b21c1..0000000000000000000000000000000000000000 --- a/pkg/record/interface.go +++ /dev/null @@ -1,15 +0,0 @@ -package record - -type Storage interface { - Upload(gZipFile, target string) -} - -func NewStorageServer() Storage { - //conf := config.GetGlobalConfig() - // - //switch conf.TermConfig.RePlayStorage["TYPE"] { - //case "server": - // return NewJmsStorage() - //} - return nil -} diff --git a/pkg/recorder/cmd.go b/pkg/recorder/cmd.go new file mode 100644 index 0000000000000000000000000000000000000000..12040431241cac880c2064ceae035b3ab5a95c33 --- /dev/null +++ b/pkg/recorder/cmd.go @@ -0,0 +1,25 @@ +package recorder + +import ( + "time" +) + +type CommandRecorder struct { + SessionID string + StartTime time.Time +} + +type Command struct { + SessionId string `json:"session"` + OrgId string `json:"org_id"` + Input string `json:"input"` + Output string `json:"output"` + User string `json:"user"` + Server string `json:"asset"` + SystemUser string `json:"system_user"` + Timestamp time.Time `json:"timestamp"` +} + +func (c *CommandRecorder) Record(cmd *Command) { + +} diff --git a/pkg/record/replay.go b/pkg/recorder/replay.go similarity index 81% rename from pkg/record/replay.go rename to pkg/recorder/replay.go index c67c8734fd809fc6fc286743999ed83d4fb2c0fa..420458c83f67b0daeef147b22e7dd5e36206205b 100644 --- a/pkg/record/replay.go +++ b/pkg/recorder/replay.go @@ -1,4 +1,4 @@ -package record +package recorder import ( "compress/gzip" @@ -12,12 +12,12 @@ import ( "time" "cocogo/pkg/config" - "cocogo/pkg/storage" + "cocogo/pkg/recorder/storage" ) var conf = config.Conf -func NewReplyRecord(sessionID string) *Reply { +func NewReplyRecord(sessionID string) *ReplyRecorder { rootPath := conf.RootPath currentData := time.Now().UTC().Format("2006-01-02") gzFileName := sessionID + ".replay.gz" @@ -25,7 +25,7 @@ func NewReplyRecord(sessionID string) *Reply { absGzFilePath := filepath.Join(rootPath, "data", "replays", currentData, gzFileName) target := strings.Join([]string{currentData, gzFileName}, "/") - return &Reply{ + return &ReplyRecorder{ SessionID: sessionID, FileName: sessionID, absFilePath: absFilePath, @@ -36,7 +36,7 @@ func NewReplyRecord(sessionID string) *Reply { } } -type Reply struct { +type ReplyRecorder struct { SessionID string FileName string gzFileName string @@ -47,19 +47,19 @@ type Reply struct { StartTime time.Time } -func (r *Reply) Record(b []byte) { +func (r *ReplyRecorder) Record(b []byte) { interval := time.Now().UTC().Sub(r.StartTime).Seconds() data, _ := json.Marshal(string(b)) _, _ = r.WriteF.WriteString(fmt.Sprintf("\"%0.6f\":%s,", interval, data)) } -func (r *Reply) StartRecord() { +func (r *ReplyRecorder) Start() { //auth.MakeSureDirExit(r.absFilePath) //r.WriteF, _ = os.Create(r.absFilePath) //_, _ = r.WriteF.Write([]byte("{")) } -func (r *Reply) EndRecord(ctx context.Context) { +func (r *ReplyRecorder) End(ctx context.Context) { select { case <-ctx.Done(): _, _ = r.WriteF.WriteString(`"0":""}`) @@ -68,10 +68,10 @@ func (r *Reply) EndRecord(ctx context.Context) { r.uploadReplay() } -func (r *Reply) uploadReplay() { +func (r *ReplyRecorder) uploadReplay() { _ = GzipCompressFile(r.absFilePath, r.absGzFilePath) - if sto := storage.NewStorageServer(); sto != nil { - sto.Upload(r.absGzFilePath, r.target) + if store := storage.NewStorageServer(); store != nil { + store.Upload(r.absGzFilePath, r.target) } _ = os.Remove(r.absFilePath) _ = os.Remove(r.absGzFilePath) diff --git a/pkg/recorder/storage/interface.go b/pkg/recorder/storage/interface.go new file mode 100644 index 0000000000000000000000000000000000000000..27ef6ef6899a0eb1fa007e83900cd537c1b2d5de --- /dev/null +++ b/pkg/recorder/storage/interface.go @@ -0,0 +1,9 @@ +package storage + +type ReplayStorage interface { + Upload(gZipFile, target string) +} + +func NewStorageServer() ReplayStorage { + return nil +} diff --git a/pkg/record/jms.go b/pkg/recorder/storage/jms.go similarity index 88% rename from pkg/record/jms.go rename to pkg/recorder/storage/jms.go index c7a21da79bd1476f17300b19dc55db5faa389b4c..e23e6f1ff2f04bb148cfc2b578c23e446e62d87a 100644 --- a/pkg/record/jms.go +++ b/pkg/recorder/storage/jms.go @@ -1,8 +1,8 @@ -package record +package storage //var client = service.Client -func NewJmsStorage() Storage { +func NewJmsStorage() ReplayStorage { //appService := auth.GetGlobalService() //return &Server{ // StorageType: "jms",