From 75f8a0ccec064d9caf7f06f275fb08d871bac5fa Mon Sep 17 00:00:00 2001 From: shaoying Date: Tue, 29 Sep 2020 02:42:01 +0800 Subject: [PATCH] opt ssh --- base/nfs/nfs.go | 5 +- base/ssh/channel.go | 96 ++++++ base/ssh/connect.go | 37 ++- base/ssh/{script.go => scripts.go} | 0 base/ssh/server.go | 288 ------------------ base/ssh/service.go | 182 +++++++++-- .../{server_darwin.go => service_darwin.go} | 0 .../ssh/{server_linux.go => service_linux.go} | 0 .../{server_windows.go => service_windows.go} | 0 base/ssh/session.go | 15 +- base/ssh/ssh.go | 19 +- base/ssh/ssh.shy | 2 + base/tcp/client.go | 5 +- base/tcp/server.go | 6 +- type.go | 5 + 15 files changed, 309 insertions(+), 351 deletions(-) create mode 100644 base/ssh/channel.go rename base/ssh/{script.go => scripts.go} (100%) delete mode 100644 base/ssh/server.go rename base/ssh/{server_darwin.go => service_darwin.go} (100%) rename base/ssh/{server_linux.go => service_linux.go} (100%) rename base/ssh/{server_windows.go => service_windows.go} (100%) diff --git a/base/nfs/nfs.go b/base/nfs/nfs.go index 7daaa637..55ed2000 100644 --- a/base/nfs/nfs.go +++ b/base/nfs/nfs.go @@ -330,8 +330,11 @@ var Index = &ice.Context{Name: "nfs", Help: "存储模块", nil, []string{"time", "size", "type", "path"}) }}, }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) == 0 { + arg = append(arg, "") + } reg, _ := regexp.Compile(m.Option(DIR_REG)) - _file_list(m, kit.Select("./", m.Option(DIR_ROOT)), kit.Select("", arg, 0), + _file_list(m, kit.Select("./", m.Option(DIR_ROOT)), arg[0], 0, m.Options(DIR_DEEP), kit.Select(TYPE_BOTH, m.Option(DIR_TYPE)), reg, kit.Split(kit.Select("time size path", strings.Join(arg[1:], " ")), " ")) m.Sort(kit.MDB_TIME, "time_r") diff --git a/base/ssh/channel.go b/base/ssh/channel.go new file mode 100644 index 00000000..18b8a96e --- /dev/null +++ b/base/ssh/channel.go @@ -0,0 +1,96 @@ +package ssh + +import ( + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" + "github.com/shylinux/icebergs/base/mdb" + "github.com/shylinux/icebergs/base/tcp" + kit "github.com/shylinux/toolkits" + "golang.org/x/crypto/ssh" + + "io" + "net" + "os/exec" + "strings" +) + +type Winsize struct { + Height uint16 + Width uint16 + x uint16 + y uint16 +} + +func _ssh_exec(m *ice.Message, cmd string, arg []string, env []string, tty io.ReadWriter, done func()) { + m.Log_IMPORT(CMD, cmd, ARG, arg, ENV, env) + c := exec.Command(cmd, arg...) + // c.Env = env + + c.Stdin = tty + c.Stdout = tty + c.Stderr = tty + + m.Assert(c.Start()) + + m.Go(func() { + defer done() + c.Process.Wait() + }) +} +func _ssh_close(m *ice.Message, c net.Conn, channel ssh.Channel) { + defer channel.Close() + channel.Write([]byte(m.Conf(SERVICE, "meta.goodbye"))) +} +func _ssh_watch(m *ice.Message, meta map[string]string, h string, input io.Reader, output io.Writer, display io.Writer) { + r, w := io.Pipe() + bio := io.TeeReader(input, w) + m.Go(func() { io.Copy(output, r) }) + + i, buf := 0, make([]byte, 4096) + m.Go(func() { + for { + n, e := bio.Read(buf[i:]) + if e != nil { + break + } + + switch buf[i] { + case '\r', '\n': + cmd := strings.TrimSpace(string(buf[:i])) + m.Log_IMPORT(aaa.HOSTNAME, meta[aaa.HOSTNAME], aaa.USERNAME, meta[aaa.USERNAME], CMD, cmd) + m.Cmdy(mdb.INSERT, CHANNEL, kit.Keys(kit.MDB_HASH, h), mdb.LIST, CMD, cmd) + i = 0 + default: + if i += n; i >= 4096 { + i = 0 + } + } + } + }) +} + +const CHANNEL = "channel" + +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + CHANNEL: {Name: "channel", Help: "通道", Value: kit.Data()}, + }, + Commands: map[string]*ice.Command{ + CHANNEL: {Name: "channel hash id auto 清理", Help: "通道", Action: map[string]*ice.Action{ + mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) { + m.Cmdy(mdb.PRUNES, CHANNEL, "", mdb.HASH, kit.MDB_STATUS, tcp.CLOSE) + }}, + }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) == 0 { + m.Option(mdb.FIELDS, "time,hash,status,tty,count") + m.Cmdy(mdb.SELECT, CHANNEL, "", mdb.HASH) + return + } + + m.Option(mdb.FIELDS, kit.Select("time,id,cmd", mdb.DETAIL, len(arg) > 1)) + m.Cmdy(mdb.SELECT, CHANNEL, kit.Keys(kit.MDB_HASH, arg[0]), mdb.LIST, kit.MDB_ID, arg[1:]) + }}, + }, + }, nil) +} diff --git a/base/ssh/connect.go b/base/ssh/connect.go index 64129986..839a8d34 100644 --- a/base/ssh/connect.go +++ b/base/ssh/connect.go @@ -15,17 +15,16 @@ import ( "golang.org/x/crypto/ssh" ) -func _ssh_conn(m *ice.Message, conn net.Conn, username, host, port string) (*ssh.Client, error) { - methods := []ssh.AuthMethod{} - if key, e := ssh.ParsePrivateKey([]byte(m.Cmdx(nfs.CAT, path.Join(os.Getenv("HOME"), m.Conf(PUBLIC, "meta.private"))))); !m.Warn(e != nil) { - methods = append(methods, ssh.PublicKeys(key)) - } else { - return nil, e - } +func _ssh_conn(m *ice.Message, conn net.Conn, hostport, username string) (*ssh.Client, error) { + key, e := ssh.ParsePrivateKey([]byte(m.Cmdx(nfs.CAT, path.Join(os.Getenv("HOME"), m.Option("private"))))) + m.Assert(e) - c, chans, reqs, err := ssh.NewClientConn(conn, host+":"+port, &ssh.ClientConfig{User: username, Auth: methods, - HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { - m.Logs(CONNECT, "hostname", hostname, aaa.HOSTPORT, remote.String()) + methods := []ssh.AuthMethod{} + methods = append(methods, ssh.PublicKeys(key)) + + c, chans, reqs, err := ssh.NewClientConn(conn, hostport, &ssh.ClientConfig{ + User: username, Auth: methods, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { + m.Logs(CONNECT, aaa.HOSTNAME, hostname, aaa.HOSTPORT, remote.String()) return nil }, }) @@ -45,9 +44,12 @@ func init() { }, Commands: map[string]*ice.Command{ CONNECT: {Name: "connect hash auto 添加 清理", Help: "连接", Action: map[string]*ice.Action{ - tcp.DIAL: {Name: "dial username=shy host=shylinux.com port=22", Help: "添加", Hand: func(m *ice.Message, arg ...string) { + tcp.DIAL: {Name: "dial username=shy host=shylinux.com port=22 private=.ssh/id_rsa", Help: "添加", Hand: func(m *ice.Message, arg ...string) { m.Option(tcp.DIAL_CB, func(c net.Conn) { - client, e := _ssh_conn(m, c, kit.Select("shy", m.Option(aaa.USERNAME)), kit.Select(m.Option(tcp.HOST), "shylinux.com"), kit.Select("22", m.Option(tcp.PORT))) + client, e := _ssh_conn(m, c, + kit.Select(m.Option(tcp.HOST), "shylinux.com")+":"+kit.Select("22", m.Option(tcp.PORT)), + kit.Select("shy", m.Option(aaa.USERNAME)), + ) m.Assert(e) h := m.Rich(CONNECT, "", kit.Dict( @@ -65,14 +67,9 @@ func init() { SESSION: {Name: "session hash", Help: "会话", Hand: func(m *ice.Message, arg ...string) { m.Richs(CONNECT, "", m.Option(kit.MDB_HASH), func(key string, value map[string]interface{}) { client, ok := value[CONNECT].(*ssh.Client) - if !ok { - return - } + m.Assert(ok) - h := m.Rich(SESSION, "", kit.Data( - kit.MDB_STATUS, tcp.OPEN, - CONNECT, key, - )) + h := m.Rich(SESSION, "", kit.Data(kit.MDB_STATUS, tcp.OPEN, CONNECT, key)) if session, e := _ssh_sess(m, h, client); m.Assert(e) { session.Shell() @@ -85,7 +82,7 @@ func init() { m.Cmdy(mdb.PRUNES, CONNECT, "", mdb.HASH, kit.MDB_STATUS, tcp.CLOSE) }}, }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - m.Option(mdb.FIELDS, "time,hash,status,username,host,port") + m.Option(mdb.FIELDS, kit.Select("time,hash,status,username,host,port", mdb.DETAIL, len(arg) > 0)) m.Cmdy(mdb.SELECT, CONNECT, "", mdb.HASH, kit.MDB_HASH, arg) m.PushAction("会话") }}, diff --git a/base/ssh/script.go b/base/ssh/scripts.go similarity index 100% rename from base/ssh/script.go rename to base/ssh/scripts.go diff --git a/base/ssh/server.go b/base/ssh/server.go deleted file mode 100644 index 62330adf..00000000 --- a/base/ssh/server.go +++ /dev/null @@ -1,288 +0,0 @@ -package ssh - -import ( - ice "github.com/shylinux/icebergs" - "github.com/shylinux/icebergs/base/aaa" - "github.com/shylinux/icebergs/base/mdb" - "github.com/shylinux/icebergs/base/nfs" - "github.com/shylinux/icebergs/base/tcp" - kit "github.com/shylinux/toolkits" - - "bytes" - "encoding/base64" - "errors" - "fmt" - "io" - "net" - "os" - "os/exec" - "path" - "strings" - - "github.com/kr/pty" - "golang.org/x/crypto/ssh" -) - -type Winsize struct { - Height uint16 - Width uint16 - x uint16 - y uint16 -} - -func _ssh_exec(m *ice.Message, cmd string, arg []string, env []string, tty io.ReadWriter, done func()) { - m.Log_IMPORT("cmd", cmd, "arg", arg, "env", env) - c := exec.Command(cmd, arg...) - // c.Env = env - - c.Stdin = tty - c.Stdout = tty - c.Stderr = tty - - err := c.Start() - m.Assert(err) - - m.Gos(m, func(m *ice.Message) { - defer done() - c.Process.Wait() - }) -} -func _ssh_close(m *ice.Message, c net.Conn, channel ssh.Channel) { - defer channel.Close() - channel.Write([]byte(m.Conf(PUBLIC, "meta.goodbye"))) -} -func _ssh_watch(m *ice.Message, meta map[string]string, input io.Reader, output io.Writer, display io.Writer) { - m.Gos(m, func(m *ice.Message) { - r, w := io.Pipe() - bio := io.TeeReader(input, w) - m.Gos(m, func(m *ice.Message) { - i, buf := 0, make([]byte, 1024) - for { - n, e := bio.Read(buf[i:]) - if e != nil { - break - } - switch buf[i] { - case '\r', '\n': - cmd := strings.TrimSpace(string(buf[:i])) - m.Log_IMPORT(aaa.HOSTNAME, meta[aaa.HOSTNAME], aaa.USERNAME, meta[aaa.USERNAME], "cmd", cmd, "buf", buf[:i+n]) - m.Conf(CONNECT, kit.Keys(kit.MDB_HASH, meta[CONNECT], "duration"), m.Format("cost")) - m.Conf(SESSION, kit.Keys(kit.MDB_HASH, meta[SESSION], "cmd"), cmd) - - m.Cmdy(mdb.INSERT, m.Prefix(COMMAND), "", mdb.LIST, aaa.HOSTNAME, meta[aaa.HOSTNAME], aaa.USERNAME, meta[aaa.USERNAME], "cmd", cmd) - i = 0 - default: - if i += n; i >= 1024 { - i = 0 - } - } - } - }) - io.Copy(output, r) - }) -} -func _ssh_handle(m *ice.Message, meta map[string]string, c net.Conn, channel ssh.Channel, requests <-chan *ssh.Request) { - m.Logs(CHANNEL, aaa.HOSTPORT, c.RemoteAddr(), "->", c.LocalAddr()) - defer m.Logs("dischan", aaa.HOSTPORT, c.RemoteAddr(), "->", c.LocalAddr()) - - shell := kit.Select("bash", os.Getenv("SHELL")) - list := []string{"PATH=" + os.Getenv("PATH")} - - pty, tty, err := pty.Open() - if m.Warn(err != nil, err) { - return - } - defer tty.Close() - - h := m.Cmdx(mdb.INSERT, m.Prefix(SESSION), "", mdb.HASH, aaa.HOSTPORT, c.RemoteAddr().String(), kit.MDB_STATUS, "open", "pty", pty.Name()) - m.Richs(SESSION, "", h, func(key string, value map[string]interface{}) { value["channel"] = channel }) - meta[SESSION] = h - - for request := range requests { - m.Logs(REQUEST, aaa.HOSTPORT, c.RemoteAddr(), "type", request.Type) - - switch request.Type { - case "pty-req": - termLen := request.Payload[3] - termEnv := string(request.Payload[4 : termLen+4]) - _ssh_size(pty.Fd(), request.Payload[termLen+4:]) - list = append(list, "TERM="+termEnv) - - case "window-change": - _ssh_size(pty.Fd(), request.Payload) - - case "env": - var env struct { - Name string - Value string - } - if err := ssh.Unmarshal(request.Payload, &env); err != nil { - continue - } - list = append(list, env.Name+"="+env.Value) - - case "exec": - _ssh_exec(m, shell, []string{"-c", string(request.Payload[4 : request.Payload[3]+4])}, list, - channel, func() { channel.Close() }) - case "shell": - if meta["username"] == "ssh" { - m.I, m.O = tty, tty - m.Render(ice.RENDER_VOID) - m.Gos(m, func(m *ice.Message) { - m.Cmdy(SOURCE, pty.Name()) - _ssh_close(m, c, channel) - }) - } else { - _ssh_exec(m, shell, nil, list, tty, func() { - defer m.Cmd(mdb.MODIFY, m.Prefix(SESSION), "", mdb.HASH, kit.MDB_HASH, h, kit.MDB_STATUS, "close") - _ssh_close(m, c, channel) - }) - } - - m.Gos(m, func(m *ice.Message) { io.Copy(channel, pty) }) - _ssh_watch(m, meta, channel, pty, channel) - } - request.Reply(true, nil) - } -} -func _ssh_config(m *ice.Message) *ssh.ServerConfig { - config := &ssh.ServerConfig{ - BannerCallback: func(conn ssh.ConnMetadata) string { - m.Log_IMPORT(aaa.HOSTPORT, conn.RemoteAddr(), aaa.USERNAME, conn.User()) - return m.Conf(PUBLIC, "meta.welcome") - }, - PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { - meta, res := map[string]string{"username": conn.User()}, errors.New(ice.ErrNotAuth) - if tcp.IsLocalHost(m, strings.Split(conn.RemoteAddr().String(), ":")[0]) { - m.Log_AUTH(aaa.HOSTPORT, conn.RemoteAddr(), aaa.USERNAME, conn.User()) - res = nil - } else { - m.Richs(PUBLIC, "", kit.MDB_FOREACH, func(k string, value map[string]interface{}) { - if !strings.HasPrefix(kit.Format(value[kit.MDB_NAME]), conn.User()+"@") { - return - } - if s, e := base64.StdEncoding.DecodeString(kit.Format(value[kit.MDB_TEXT])); !m.Warn(e != nil, e) { - if pub, e := ssh.ParsePublicKey([]byte(s)); !m.Warn(e != nil) { - if bytes.Compare(pub.Marshal(), key.Marshal()) == 0 { - m.Log_AUTH(aaa.HOSTPORT, conn.RemoteAddr(), aaa.USERNAME, conn.User(), "publickey", value[kit.MDB_NAME]) - meta["hostname"] = kit.Format(value[kit.MDB_NAME]) - res = nil - } - } - } - }) - } - return &ssh.Permissions{Extensions: meta}, res - }, - PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) { - meta, res := map[string]string{"username": conn.User()}, errors.New(ice.ErrNotAuth) - m.Richs(aaa.USER, "", conn.User(), func(k string, value map[string]interface{}) { - if string(password) == kit.Format(value[aaa.PASSWORD]) { - m.Log_AUTH(aaa.HOSTPORT, conn.RemoteAddr(), aaa.USERNAME, conn.User(), aaa.PASSWORD, strings.Repeat("*", len(kit.Format(value[aaa.PASSWORD])))) - res = nil - } - }) - return &ssh.Permissions{Extensions: meta}, res - }, - } - - if key, err := ssh.ParsePrivateKey([]byte(m.Cmdx(nfs.CAT, path.Join(os.Getenv("HOME"), m.Conf(PUBLIC, "meta.private"))))); m.Assert(err) { - config.AddHostKey(key) - } - return config -} - -const ( - ADDRESS = "address" - CHANNEL = "channel" - REQUEST = "request" - COMMAND = "command" -) -const ( - METHOD = "method" - PUBLIC = "public" - LISTEN = "listen" - DIAL = "dial" -) - -func init() { - Index.Merge(&ice.Context{ - Configs: map[string]*ice.Config{ - PUBLIC: {Name: PUBLIC, Help: "公钥", Value: kit.Data( - "private", ".ssh/id_rsa", "public", ".ssh/id_rsa.pub", - "welcome", "\r\nwelcome to context world\r\n", - "goodbye", "\r\ngoodbye of context world\r\n", - kit.MDB_SHORT, kit.MDB_TEXT, - )}, - - LISTEN: {Name: LISTEN, Help: "服务", Value: kit.Data(kit.MDB_SHORT, aaa.HOSTPORT, - kit.MDB_FIELD, "time,hash,hostport,status", - )}, - COMMAND: {Name: COMMAND, Help: "命令", Value: kit.Data( - kit.MDB_FIELD, "time,id,username,hostname,cmd", - )}, - }, - Commands: map[string]*ice.Command{ - PUBLIC: {Name: "public hash=auto auto 添加 导出 导入", Help: "公钥", Action: map[string]*ice.Action{ - mdb.CREATE: {Name: "create publickey:textarea", Help: "添加", Hand: func(m *ice.Message, arg ...string) { - ls := kit.Split(m.Option("publickey")) - m.Cmdy(mdb.INSERT, m.Prefix(PUBLIC), "", mdb.HASH, kit.MDB_TYPE, ls[0], - kit.MDB_NAME, ls[len(ls)-1], kit.MDB_TEXT, strings.Join(ls[1:len(ls)-1], "+")) - }}, - mdb.DELETE: {Name: "delete", Help: "删除", Hand: func(m *ice.Message, arg ...string) { - m.Cmdy(mdb.DELETE, m.Prefix(PUBLIC), "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH)) - }}, - mdb.EXPORT: {Name: "export file=.ssh/authorized_keys", Help: "导出", Hand: func(m *ice.Message, arg ...string) { - list := []string{} - if m.Cmd(mdb.SELECT, m.Prefix(PUBLIC), "", mdb.HASH).Table(func(index int, value map[string]string, head []string) { - list = append(list, fmt.Sprintf("%s %s %s", value[kit.MDB_TYPE], value[kit.MDB_TEXT], value[kit.MDB_NAME])) - }); len(list) > 0 { - m.Cmdy(nfs.SAVE, path.Join(os.Getenv("HOME"), m.Option(kit.MDB_FILE)), strings.Join(list, "\n")+"\n") - } - }}, - mdb.IMPORT: {Name: "import file=.ssh/authorized_keys", Help: "导入", Hand: func(m *ice.Message, arg ...string) { - p := path.Join(os.Getenv("HOME"), m.Option(kit.MDB_FILE)) - for _, pub := range strings.Split(m.Cmdx(nfs.CAT, p), "\n") { - if len(pub) > 10 { - m.Cmd(PUBLIC, mdb.CREATE, "publickey", pub) - } - } - m.Echo(p) - }}, - }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) > 0 { - m.Option(mdb.FIELDS, mdb.DETAIL) - } else { - defer m.PushAction("删除") - } - m.Cmdy(mdb.SELECT, m.Prefix(PUBLIC), "", mdb.HASH, kit.MDB_HASH, arg) - }}, - - LISTEN: {Name: "listen hash=auto auto", Help: "服务", Action: map[string]*ice.Action{ - mdb.CREATE: {Name: "create name=tcp port=9030", Help: "启动", Hand: func(m *ice.Message, arg ...string) { - m.Option(tcp.LISTEN_CB, func(c net.Conn) { - m.Gos(m.Spawn(), func(msg *ice.Message) { _ssh_accept(msg, c) }) - }) - m.Gos(m, func(m *ice.Message) { - m.Cmdy(tcp.SERVER, tcp.LISTEN, kit.MDB_NAME, "ssh", tcp.PORT, m.Option(tcp.PORT)) - }) - }}, - }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if m.Option(mdb.FIELDS, m.Conf(LISTEN, kit.META_FIELD)); len(arg) > 0 { - m.Option(mdb.FIELDS, mdb.DETAIL) - } - m.Cmdy(mdb.SELECT, m.Prefix(LISTEN), "", mdb.HASH, kit.MDB_HASH, arg) - }}, - COMMAND: {Name: "command id=auto auto", Help: "命令", Action: map[string]*ice.Action{ - mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) { - m.Cmdy(mdb.PRUNES, m.Prefix(CONNECT), "", mdb.HASH, kit.MDB_STATUS, "close") - }}, - }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if m.Option(mdb.FIELDS, m.Conf(COMMAND, kit.META_FIELD)); len(arg) > 0 { - m.Option(mdb.FIELDS, mdb.DETAIL) - } - m.Cmdy(mdb.SELECT, m.Prefix(COMMAND), "", mdb.LIST, kit.MDB_ID, arg) - }}, - }, - }, nil) -} diff --git a/base/ssh/service.go b/base/ssh/service.go index 1fa138cc..98bde2e7 100644 --- a/base/ssh/service.go +++ b/base/ssh/service.go @@ -1,61 +1,199 @@ package ssh import ( - "net" - ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" "github.com/shylinux/icebergs/base/mdb" + "github.com/shylinux/icebergs/base/nfs" "github.com/shylinux/icebergs/base/tcp" kit "github.com/shylinux/toolkits" "golang.org/x/crypto/ssh" + + "bytes" + "encoding/base64" + "errors" + "fmt" + "github.com/kr/pty" + "io" + "net" + "os" + "path" + "strings" ) -func _ssh_accept(m *ice.Message, c net.Conn) { - sc, sessions, req, err := ssh.NewServerConn(c, _ssh_config(m)) +func _ssh_config(m *ice.Message, h string) *ssh.ServerConfig { + config := &ssh.ServerConfig{ + PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { + meta, err := map[string]string{aaa.USERNAME: conn.User()}, errors.New(ice.ErrNotAuth) + if tcp.IsLocalHost(m, strings.Split(conn.RemoteAddr().String(), ":")[0]) { + m.Log_AUTH(aaa.HOSTPORT, conn.RemoteAddr(), aaa.USERNAME, conn.User()) + err = nil + } else { + m.Cmd(mdb.SELECT, SERVICE, kit.Keys(kit.MDB_HASH, h), mdb.LIST).Table(func(index int, value map[string]string, head []string) { + if !strings.HasPrefix(value[kit.MDB_NAME], conn.User()+"@") { + return + } + if s, e := base64.StdEncoding.DecodeString(value[kit.MDB_TEXT]); !m.Warn(e != nil, e) { + if pub, e := ssh.ParsePublicKey([]byte(s)); !m.Warn(e != nil, e) { + + if bytes.Compare(pub.Marshal(), key.Marshal()) == 0 { + m.Log_AUTH(aaa.HOSTPORT, conn.RemoteAddr(), aaa.USERNAME, conn.User(), aaa.HOSTNAME, value[kit.MDB_NAME]) + meta[aaa.USERNAME] = value[kit.MDB_NAME] + err = nil + } + } + } + }) + } + return &ssh.Permissions{Extensions: meta}, err + }, + PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) { + meta, err := map[string]string{aaa.USERNAME: conn.User()}, errors.New(ice.ErrNotAuth) + m.Richs(aaa.USER, "", conn.User(), func(k string, value map[string]interface{}) { + if string(password) == kit.Format(value[aaa.PASSWORD]) { + m.Log_AUTH(aaa.HOSTPORT, conn.RemoteAddr(), aaa.USERNAME, conn.User(), aaa.PASSWORD, strings.Repeat("*", len(kit.Format(value[aaa.PASSWORD])))) + err = nil + } + }) + return &ssh.Permissions{Extensions: meta}, err + }, + + BannerCallback: func(conn ssh.ConnMetadata) string { + m.Log_IMPORT(aaa.HOSTPORT, conn.RemoteAddr(), aaa.USERNAME, conn.User()) + return m.Conf(SERVICE, "meta.welcome") + }, + } + + if key, err := ssh.ParsePrivateKey([]byte(m.Cmdx(nfs.CAT, path.Join(os.Getenv("HOME"), m.Option("private"))))); m.Assert(err) { + config.AddHostKey(key) + } + return config +} +func _ssh_accept(m *ice.Message, h string, c net.Conn) { + sc, chans, reqs, err := ssh.NewServerConn(c, _ssh_config(m, h)) if m.Warn(err != nil, err) { return } - m.Gos(m, func(m *ice.Message) { ssh.DiscardRequests(req) }) + m.Go(func() { ssh.DiscardRequests(reqs) }) - for session := range sessions { - channel, requests, err := session.Accept() + for ch := range chans { + channel, requests, err := ch.Accept() if m.Warn(err != nil, err) { continue } func(channel ssh.Channel, requests <-chan *ssh.Request) { - m.Gos(m, func(m *ice.Message) { - _ssh_handle(m, sc.Permissions.Extensions, c, channel, requests) - }) + m.Go(func() { _ssh_handle(m, sc.Permissions.Extensions, c, channel, requests) }) }(channel, requests) } } +func _ssh_handle(m *ice.Message, meta map[string]string, c net.Conn, channel ssh.Channel, requests <-chan *ssh.Request) { + m.Logs(CHANNEL, aaa.HOSTPORT, c.RemoteAddr(), "->", c.LocalAddr()) + defer m.Logs("dischan", aaa.HOSTPORT, c.RemoteAddr(), "->", c.LocalAddr()) + + shell := kit.Select("bash", os.Getenv("SHELL")) + list := []string{"PATH=" + os.Getenv("PATH")} + + pty, tty, err := pty.Open() + if m.Warn(err != nil, err) { + return + } + defer tty.Close() + + h := m.Rich(CHANNEL, "", kit.Data(kit.MDB_STATUS, tcp.OPEN, TTY, tty.Name(), aaa.HOSTPORT, c.RemoteAddr().String())) + meta[CHANNEL] = h + + for request := range requests { + m.Logs("request", aaa.HOSTPORT, c.RemoteAddr(), kit.MDB_TYPE, request.Type) + + switch request.Type { + case "pty-req": + termLen := request.Payload[3] + termEnv := string(request.Payload[4 : termLen+4]) + _ssh_size(pty.Fd(), request.Payload[termLen+4:]) + list = append(list, "TERM="+termEnv) + + case "window-change": + _ssh_size(pty.Fd(), request.Payload) + + case "env": + var env struct{ Name, Value string } + if err := ssh.Unmarshal(request.Payload, &env); err != nil { + continue + } + list = append(list, env.Name+"="+env.Value) + + case "exec": + _ssh_exec(m, shell, []string{"-c", string(request.Payload[4 : request.Payload[3]+4])}, list, channel, func() { + channel.Close() + }) + case "shell": + m.Go(func() { io.Copy(channel, pty) }) + + _ssh_exec(m, shell, nil, list, tty, func() { + defer m.Cmd(mdb.MODIFY, CHANNEL, "", mdb.HASH, kit.MDB_HASH, h, kit.MDB_STATUS, tcp.CLOSE) + _ssh_close(m, c, channel) + }) + + _ssh_watch(m, meta, h, channel, pty, channel) + } + request.Reply(true, nil) + } +} const SERVICE = "service" func init() { Index.Merge(&ice.Context{ Configs: map[string]*ice.Config{ - SERVICE: {Name: SERVICE, Help: "服务", Value: kit.Data()}, + SERVICE: {Name: SERVICE, Help: "服务", Value: kit.Data( + "welcome", "\r\nwelcome to context world\r\n", + "goodbye", "\r\ngoodbye of context world\r\n", + )}, }, Commands: map[string]*ice.Command{ - SERVICE: {Name: "service", Help: "服务", Action: map[string]*ice.Action{ - tcp.LISTEN: {Name: "listen name=tcp port=9030", Help: "监听", Hand: func(m *ice.Message, arg ...string) { - m.Option(tcp.LISTEN_CB, func(c net.Conn) { - m.Gos(m.Spawn(), func(msg *ice.Message) { _ssh_accept(msg, c) }) - }) - m.Gos(m, func(m *ice.Message) { - m.Cmdy(tcp.SERVER, tcp.LISTEN, kit.MDB_NAME, "ssh", tcp.PORT, m.Option(tcp.PORT)) + SERVICE: {Name: "service hash id auto 监听 清理", Help: "服务", Action: map[string]*ice.Action{ + tcp.LISTEN: {Name: "listen port=9030 private=.ssh/id_rsa", Help: "监听", Hand: func(m *ice.Message, arg ...string) { + h := m.Cmdx(mdb.INSERT, SERVICE, "", mdb.HASH, kit.MDB_STATUS, tcp.OPEN, arg) + + m.Option(tcp.LISTEN_CB, func(c net.Conn) { m.Go(func() { _ssh_accept(m, h, c) }) }) + m.Go(func() { m.Cmdy(tcp.SERVER, tcp.LISTEN, kit.MDB_NAME, SSH, tcp.PORT, m.Option(tcp.PORT)) }) + }}, + + mdb.EXPORT: {Name: "export file=.ssh/authorized_keys", Help: "导出", Hand: func(m *ice.Message, arg ...string) { + list := []string{} + m.Cmd(mdb.SELECT, SERVICE, kit.Keys(kit.MDB_HASH, m.Option(kit.MDB_HASH)), mdb.LIST).Table(func(index int, value map[string]string, head []string) { + list = append(list, fmt.Sprintf("%s %s %s", value[kit.MDB_TYPE], value[kit.MDB_TEXT], value[kit.MDB_NAME])) }) + + if len(list) > 0 { + m.Cmdy(nfs.SAVE, path.Join(os.Getenv("HOME"), m.Option(kit.MDB_FILE)), strings.Join(list, "\n")+"\n") + } + }}, + mdb.IMPORT: {Name: "import file=.ssh/authorized_keys", Help: "导入", Hand: func(m *ice.Message, arg ...string) { + p := path.Join(os.Getenv("HOME"), m.Option(kit.MDB_FILE)) + for _, pub := range strings.Split(m.Cmdx(nfs.CAT, p), "\n") { + if ls := kit.Split(pub); len(pub) > 10 { + m.Cmd(mdb.INSERT, SERVICE, kit.Keys(kit.MDB_HASH, m.Option(kit.MDB_HASH)), mdb.LIST, + kit.MDB_TYPE, ls[0], kit.MDB_NAME, ls[len(ls)-1], kit.MDB_TEXT, strings.Join(ls[1:len(ls)-1], "+")) + } + } + m.Echo(p) + }}, + mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) { + m.Cmdy(mdb.PRUNES, SERVICE, "", mdb.HASH, kit.MDB_STATUS, tcp.CLOSE) }}, }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if m.Option(mdb.FIELDS, m.Conf(LISTEN, kit.META_FIELD)); len(arg) > 0 { - m.Option(mdb.FIELDS, mdb.DETAIL) + if len(arg) == 0 { + m.Option(mdb.FIELDS, "time,hash,status,port,private,count") + m.Cmdy(mdb.SELECT, SERVICE, "", mdb.HASH, kit.MDB_HASH, arg) + m.PushAction("导入") + return } - m.Option(mdb.FIELDS, "time,hash,status,host,port") - m.Cmdy(mdb.SELECT, m.Prefix(LISTEN), "", mdb.HASH, kit.MDB_HASH, arg) + m.Option(mdb.FIELDS, kit.Select("time,id,type,name,text", mdb.DETAIL, len(arg) > 1)) + m.Cmdy(mdb.SELECT, SERVICE, kit.Keys(kit.MDB_HASH, arg[0]), mdb.LIST, kit.MDB_ID, arg[1:]) }}, }, }, nil) diff --git a/base/ssh/server_darwin.go b/base/ssh/service_darwin.go similarity index 100% rename from base/ssh/server_darwin.go rename to base/ssh/service_darwin.go diff --git a/base/ssh/server_linux.go b/base/ssh/service_linux.go similarity index 100% rename from base/ssh/server_linux.go rename to base/ssh/service_linux.go diff --git a/base/ssh/server_windows.go b/base/ssh/service_windows.go similarity index 100% rename from base/ssh/server_windows.go rename to base/ssh/service_windows.go diff --git a/base/ssh/session.go b/base/ssh/session.go index d3087eaa..2395c7d7 100644 --- a/base/ssh/session.go +++ b/base/ssh/session.go @@ -1,14 +1,14 @@ package ssh import ( - "io" - ice "github.com/shylinux/icebergs" "github.com/shylinux/icebergs/base/ctx" "github.com/shylinux/icebergs/base/mdb" "github.com/shylinux/icebergs/base/tcp" kit "github.com/shylinux/toolkits" + "golang.org/x/crypto/ssh" + "io" ) func _ssh_sess(m *ice.Message, h string, client *ssh.Client) (*ssh.Session, error) { @@ -23,7 +23,7 @@ func _ssh_sess(m *ice.Message, h string, client *ssh.Client) (*ssh.Session, erro m.Go(func() { for { - buf := make([]byte, 1024) + buf := make([]byte, 4096) n, e := out.Read(buf) if e != nil { break @@ -45,9 +45,10 @@ func _ssh_sess(m *ice.Message, h string, client *ssh.Client) (*ssh.Session, erro } const ( - CMD = "cmd" - ARG = "arg" + TTY = "tty" ENV = "env" + ARG = "arg" + CMD = "cmd" RES = "res" ) @@ -63,7 +64,7 @@ func init() { ctx.COMMAND: {Name: "command cmd=pwd", Help: "命令", Hand: func(m *ice.Message, arg ...string) { m.Richs(SESSION, "", m.Option(kit.MDB_HASH), func(key string, value map[string]interface{}) { if w, ok := kit.Value(value, "meta.input").(io.Writer); ok { - m.Grow(SESSION, kit.Keys(kit.MDB_HASH, key), kit.Dict(kit.MDB_TYPE, RES, kit.MDB_TEXT, m.Option(CMD))) + m.Grow(SESSION, kit.Keys(kit.MDB_HASH, key), kit.Dict(kit.MDB_TYPE, CMD, kit.MDB_TEXT, m.Option(CMD))) n, e := w.Write([]byte(m.Option(CMD) + "\n")) m.Debug("%v %v", n, e) } @@ -82,7 +83,7 @@ func init() { return } - m.Option(mdb.FIELDS, "time,id,type,text") + m.Option(mdb.FIELDS, kit.Select("time,id,type,text", mdb.DETAIL, len(arg) > 1)) m.Cmdy(mdb.SELECT, SESSION, kit.Keys(kit.MDB_HASH, arg[0]), mdb.LIST, kit.MDB_ID, arg[1:]) m.Sort(kit.MDB_ID) }}, diff --git a/base/ssh/ssh.go b/base/ssh/ssh.go index b1e5cf1e..d0721a64 100644 --- a/base/ssh/ssh.go +++ b/base/ssh/ssh.go @@ -16,16 +16,16 @@ var Index = &ice.Context{Name: SSH, Help: "终端模块", Commands: map[string]* if _, ok := m.Target().Server().(*Frame); ok { m.Done() } - m.Richs(CONNECT, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) { - if value[kit.MDB_META] != nil { - value = value[kit.MDB_META].(map[string]interface{}) - } - kit.Value(value, "status", tcp.CLOSE) + m.Richs(SERVICE, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) { + kit.Value(value, "meta.status", tcp.CLOSE) + }) + m.Richs(CHANNEL, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) { + kit.Value(value, "meta.status", tcp.CLOSE) }) m.Richs(SESSION, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) { - if value[kit.MDB_META] != nil { - value = value[kit.MDB_META].(map[string]interface{}) - } + kit.Value(value, "meta.status", tcp.CLOSE) + }) + m.Richs(CONNECT, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) { kit.Value(value, "status", tcp.CLOSE) }) m.Save() @@ -35,6 +35,7 @@ var Index = &ice.Context{Name: SSH, Help: "终端模块", Commands: map[string]* func init() { ice.Index.Register(Index, &Frame{}, SOURCE, TARGET, PROMPT, RETURN, - CONNECT, SESSION, SERVICE, + CONNECT, SESSION, + SERVICE, CHANNEL, ) } diff --git a/base/ssh/ssh.shy b/base/ssh/ssh.shy index cc577438..0d1fe4f5 100644 --- a/base/ssh/ssh.shy +++ b/base/ssh/ssh.shy @@ -5,6 +5,8 @@ refer ` 源码 https://github.com/openssh/openssh-portable ` +field "服务" ssh.service +field "通道" ssh.channel field "连接" ssh.connect field "会话" ssh.session return diff --git a/base/tcp/client.go b/base/tcp/client.go index 0a07ce58..924e3c59 100644 --- a/base/tcp/client.go +++ b/base/tcp/client.go @@ -24,7 +24,8 @@ func init() { CLIENT: {Name: "client hash auto 连接 清理", Help: "客户端", Action: map[string]*ice.Action{ DIAL: {Name: "dial host=localhost port=9010", Help: "连接", Hand: func(m *ice.Message, arg ...string) { c, e := net.Dial(TCP, m.Option(HOST)+":"+m.Option(PORT)) - h := m.Cmdx(mdb.INSERT, CLIENT, "", mdb.HASH, HOST, m.Option(HOST), PORT, m.Option(PORT), kit.MDB_STATUS, kit.Select(ERROR, OPEN, e == nil), kit.MDB_ERROR, kit.Format(e)) + h := m.Cmdx(mdb.INSERT, CLIENT, "", mdb.HASH, HOST, m.Option(HOST), PORT, m.Option(PORT), + kit.MDB_NAME, m.Option(kit.MDB_NAME), kit.MDB_STATUS, kit.Select(ERROR, OPEN, e == nil), kit.MDB_ERROR, kit.Format(e)) c = &Conn{h: h, m: m, s: &Stat{}, Conn: c} if e == nil { @@ -57,7 +58,7 @@ func init() { m.Cmdy(mdb.PRUNES, CLIENT, "", mdb.HASH, kit.MDB_STATUS, CLOSE) }}, }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - m.Option(mdb.FIELDS, kit.Select(mdb.DETAIL, "time,hash,status,host,port,error,nread,nwrite", len(arg) == 0)) + m.Option(mdb.FIELDS, kit.Select(mdb.DETAIL, "time,hash,status,name,host,port,error,nread,nwrite", len(arg) == 0)) m.Cmdy(mdb.SELECT, CLIENT, "", mdb.HASH, kit.MDB_HASH, arg) m.PushAction("删除") }}, diff --git a/base/tcp/server.go b/base/tcp/server.go index 0ac169fe..0146fc1a 100644 --- a/base/tcp/server.go +++ b/base/tcp/server.go @@ -55,7 +55,8 @@ func (l Listener) Accept() (net.Conn, error) { if strings.Contains(c.RemoteAddr().String(), "[") { ls = strings.Split(strings.TrimPrefix(c.RemoteAddr().String(), "["), "]:") } - h := l.m.Cmdx(mdb.INSERT, CLIENT, "", mdb.HASH, HOST, ls[0], PORT, ls[1], kit.MDB_STATUS, kit.Select(ERROR, OPEN, e == nil), kit.MDB_ERROR, kit.Format(e)) + h := l.m.Cmdx(mdb.INSERT, CLIENT, "", mdb.HASH, HOST, ls[0], PORT, ls[1], + kit.MDB_NAME, l.m.Option(kit.MDB_NAME), kit.MDB_STATUS, kit.Select(ERROR, OPEN, e == nil), kit.MDB_ERROR, kit.Format(e)) c = &Conn{h: h, m: l.m, s: &Stat{}, Conn: c} return c, e @@ -105,9 +106,10 @@ func init() { case func(net.Conn): for { c, e := l.Accept() - if cb(c); e != nil { + if e != nil { break } + cb(c) } case func(net.Conn, error): for { diff --git a/type.go b/type.go index 481ba897..abb99fe2 100644 --- a/type.go +++ b/type.go @@ -81,6 +81,11 @@ func (c *Context) Cap(key string, arg ...interface{}) string { func (c *Context) _hand(m *Message, cmd *Command, key string, k string, h *Action, arg ...string) *Message { m.Log(LOG_CMDS, "%s.%s %s %d %v %s", c.Name, key, k, len(arg), arg, kit.FileLine(h.Hand, 3)) if len(h.List) > 0 { + for _, v := range h.List { + name := kit.Format(kit.Value(v, "name")) + value := kit.Format(kit.Value(v, "value")) + m.Option(name, value) + } for i := 0; i < len(arg)-1; i += 2 { m.Option(arg[i], arg[i+1]) }