mirror of
https://github.com/IceWhaleTech/CasaOS.git
synced 2025-11-06 22:59:44 +00:00
wip
This commit is contained in:
parent
56d59a2c69
commit
32e84a943c
16
go.mod
16
go.mod
@ -4,18 +4,13 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d
|
||||
github.com/IceWhaleTech/CasaOS-Common v0.3.9-0.20221118234001-876079b9590f
|
||||
github.com/IceWhaleTech/CasaOS-Common v0.3.9-0.20221120152052-2d2dd9183f79
|
||||
github.com/IceWhaleTech/CasaOS-Gateway v0.3.6
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/ambelovsky/go-structs v1.1.0 // indirect
|
||||
github.com/ambelovsky/gosf v0.0.0-20201109201340-237aea4d6109
|
||||
github.com/ambelovsky/gosf-socketio v0.0.0-20201109193639-add9d32f8b19 // indirect
|
||||
github.com/containerd/containerd v1.5.7 // indirect
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/docker/distribution v2.8.0+incompatible // indirect
|
||||
github.com/docker/docker v20.10.7+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||
github.com/gin-contrib/gzip v0.0.6
|
||||
@ -25,21 +20,15 @@ require (
|
||||
github.com/gomodule/redigo v1.8.5
|
||||
github.com/google/go-github/v36 v36.0.0
|
||||
github.com/googollee/go-socket.io v1.6.2
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/hirochachacha/go-smb2 v1.1.0
|
||||
github.com/jinzhu/copier v0.3.2
|
||||
github.com/lucas-clemente/quic-go v0.25.0
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shirou/gopsutil/v3 v3.22.7
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/smartystreets/assertions v1.2.0 // indirect
|
||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/stretchr/testify v1.8.0
|
||||
@ -48,7 +37,6 @@ require (
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||
golang.org/x/mod v0.5.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
golang.org/x/tools v0.1.7 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gorm.io/driver/sqlite v1.2.6
|
||||
|
||||
@ -1,402 +0,0 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
json2 "encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func NewSshClient(user, password string, port string) (*ssh.Client, error) {
|
||||
// connet to ssh
|
||||
// addr = fmt.Sprintf("%s:%d", host, port)
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
Timeout: time.Second * 5,
|
||||
User: user,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
// HostKeyCallback: ,
|
||||
// HostKeyCallback: hostKeyCallBackFunc(h.Host),
|
||||
}
|
||||
// if h.Type == "password" {
|
||||
config.Auth = []ssh.AuthMethod{ssh.Password(password)}
|
||||
//} else {
|
||||
// config.Auth = []ssh.AuthMethod{publicKeyAuthFunc(h.Key)}
|
||||
//}
|
||||
addr := fmt.Sprintf("%s:%s", "127.0.0.1", port)
|
||||
c, err := ssh.Dial("tcp", addr, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// setup ssh shell session
|
||||
// set Session and StdinPipe here,
|
||||
// and the Session.Stdout and Session.Sdterr are also set.
|
||||
func NewSshConn(cols, rows int, sshClient *ssh.Client) (*SshConn, error) {
|
||||
sshSession, err := sshClient.NewSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdinP, err := sshSession.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
comboWriter := new(wsBufferWriter)
|
||||
|
||||
sshSession.Stdout = comboWriter
|
||||
sshSession.Stderr = comboWriter
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 1, // disable echo
|
||||
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
|
||||
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
|
||||
}
|
||||
// Request pseudo terminal
|
||||
if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Start remote shell
|
||||
if err := sshSession.Shell(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SshConn{StdinPipe: stdinP, ComboOutput: comboWriter, Session: sshSession}, nil
|
||||
}
|
||||
|
||||
type SshConn struct {
|
||||
// calling Write() to write data into ssh server
|
||||
StdinPipe io.WriteCloser
|
||||
// Write() be called to receive data from ssh server
|
||||
ComboOutput *wsBufferWriter
|
||||
Session *ssh.Session
|
||||
}
|
||||
type wsBufferWriter struct {
|
||||
buffer bytes.Buffer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (w *wsBufferWriter) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Write(p)
|
||||
}
|
||||
|
||||
func (s *SshConn) Close() {
|
||||
if s.Session != nil {
|
||||
s.Session.Close()
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
wsMsgCmd = "cmd"
|
||||
wsMsgResize = "resize"
|
||||
)
|
||||
|
||||
// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
|
||||
func ReceiveWsMsgUser(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
|
||||
// tells other go routine quit
|
||||
username := ""
|
||||
for {
|
||||
|
||||
// read websocket msg
|
||||
_, wsData, err := wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
msgObj := wsMsg{}
|
||||
if err := json2.Unmarshal(wsData, &msgObj); err != nil {
|
||||
msgObj.Type = "cmd"
|
||||
msgObj.Cmd = string(wsData)
|
||||
}
|
||||
//if err := json.Unmarshal(wsData, &msgObj); err != nil {
|
||||
// logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed")
|
||||
//}
|
||||
switch msgObj.Type {
|
||||
case wsMsgCmd:
|
||||
// handle xterm.js stdin
|
||||
// decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
|
||||
decodeBytes := []byte(msgObj.Cmd)
|
||||
if msgObj.Cmd == "\u007f" {
|
||||
if len(username) == 0 {
|
||||
continue
|
||||
}
|
||||
wsConn.WriteMessage(websocket.TextMessage, []byte("\b\x1b[K"))
|
||||
username = username[:len(username)-1]
|
||||
continue
|
||||
}
|
||||
if msgObj.Cmd == "\r" {
|
||||
return username
|
||||
}
|
||||
username += msgObj.Cmd
|
||||
|
||||
if err := wsConn.WriteMessage(websocket.TextMessage, decodeBytes); err != nil {
|
||||
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
|
||||
}
|
||||
// write input cmd to log buffer
|
||||
if _, err := logBuff.Write(decodeBytes); err != nil {
|
||||
logrus.WithError(err).Error("write received cmd into log buffer failed")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func ReceiveWsMsgPassword(wsConn *websocket.Conn, logBuff *bytes.Buffer) string {
|
||||
// tells other go routine quit
|
||||
password := ""
|
||||
for {
|
||||
|
||||
// read websocket msg
|
||||
_, wsData, err := wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("reading webSocket message failed")
|
||||
return ""
|
||||
}
|
||||
|
||||
msgObj := wsMsg{}
|
||||
if err := json2.Unmarshal(wsData, &msgObj); err != nil {
|
||||
msgObj.Type = "cmd"
|
||||
msgObj.Cmd = string(wsData)
|
||||
}
|
||||
//if err := json.Unmarshal(wsData, &msgObj); err != nil {
|
||||
// logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed")
|
||||
//}
|
||||
switch msgObj.Type {
|
||||
case wsMsgCmd:
|
||||
// handle xterm.js stdin
|
||||
// decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
|
||||
if msgObj.Cmd == "\r" {
|
||||
return password
|
||||
}
|
||||
|
||||
if msgObj.Cmd == "\u007f" {
|
||||
if len(password) == 0 {
|
||||
continue
|
||||
}
|
||||
password = password[:len(password)-1]
|
||||
continue
|
||||
}
|
||||
password += msgObj.Cmd
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
|
||||
func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff *bytes.Buffer, exitCh chan bool) {
|
||||
// tells other go routine quit
|
||||
defer setQuit(exitCh)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
// read websocket msg
|
||||
_, wsData, err := wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("reading webSocket message failed")
|
||||
return
|
||||
}
|
||||
//unmashal bytes into struct
|
||||
//msgObj := wsMsg{
|
||||
// Type: "cmd",
|
||||
// Cmd: "",
|
||||
// Rows: 50,
|
||||
// Cols: 180,
|
||||
//}
|
||||
msgObj := wsMsg{}
|
||||
if err := json2.Unmarshal(wsData, &msgObj); err != nil {
|
||||
msgObj.Type = "cmd"
|
||||
msgObj.Cmd = string(wsData)
|
||||
}
|
||||
//if err := json.Unmarshal(wsData, &msgObj); err != nil {
|
||||
// logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed")
|
||||
//}
|
||||
switch msgObj.Type {
|
||||
|
||||
case wsMsgResize:
|
||||
// handle xterm.js size change
|
||||
if msgObj.Cols > 0 && msgObj.Rows > 0 {
|
||||
if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
|
||||
logrus.WithError(err).Error("ssh pty change windows size failed")
|
||||
}
|
||||
}
|
||||
case wsMsgCmd:
|
||||
// handle xterm.js stdin
|
||||
// decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
|
||||
decodeBytes := []byte(msgObj.Cmd)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("websock cmd string base64 decoding failed")
|
||||
}
|
||||
if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil {
|
||||
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
|
||||
}
|
||||
// write input cmd to log buffer
|
||||
if _, err := logBuff.Write(decodeBytes); err != nil {
|
||||
logrus.WithError(err).Error("write received cmd into log buffer failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ssConn *SshConn) SendComboOutput(wsConn *websocket.Conn, exitCh chan bool) {
|
||||
// tells other go routine quit
|
||||
// defer setQuit(exitCh)
|
||||
|
||||
// every 120ms write combine output bytes into websocket response
|
||||
tick := time.NewTicker(time.Millisecond * time.Duration(120))
|
||||
// for range time.Tick(120 * time.Millisecond){}
|
||||
defer tick.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
// write combine output bytes into websocket response
|
||||
if err := flushComboOutput(ssConn.ComboOutput, wsConn); err != nil {
|
||||
logrus.WithError(err).Error("ssh sending combo output to webSocket failed")
|
||||
return
|
||||
}
|
||||
case <-exitCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func flushComboOutput(w *wsBufferWriter, wsConn *websocket.Conn) error {
|
||||
if w.buffer.Len() != 0 {
|
||||
err := wsConn.WriteMessage(websocket.TextMessage, w.buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.buffer.Reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
|
||||
func (ssConn *SshConn) Login(wsConn *websocket.Conn, logBuff *bytes.Buffer, exitCh chan bool) {
|
||||
// tells other go routine quit
|
||||
defer setQuit(exitCh)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
// read websocket msg
|
||||
_, wsData, err := wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("reading webSocket message failed")
|
||||
return
|
||||
}
|
||||
//unmashal bytes into struct
|
||||
//msgObj := wsMsg{
|
||||
// Type: "cmd",
|
||||
// Cmd: "",
|
||||
// Rows: 50,
|
||||
// Cols: 180,
|
||||
//}
|
||||
msgObj := wsMsg{}
|
||||
if err := json2.Unmarshal(wsData, &msgObj); err != nil {
|
||||
msgObj.Type = "cmd"
|
||||
msgObj.Cmd = string(wsData)
|
||||
}
|
||||
//if err := json.Unmarshal(wsData, &msgObj); err != nil {
|
||||
// logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed")
|
||||
//}
|
||||
switch msgObj.Type {
|
||||
|
||||
case wsMsgResize:
|
||||
// handle xterm.js size change
|
||||
if msgObj.Cols > 0 && msgObj.Rows > 0 {
|
||||
if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
|
||||
logrus.WithError(err).Error("ssh pty change windows size failed")
|
||||
}
|
||||
}
|
||||
case wsMsgCmd:
|
||||
// handle xterm.js stdin
|
||||
// decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
|
||||
decodeBytes := []byte(msgObj.Cmd)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("websock cmd string base64 decoding failed")
|
||||
}
|
||||
if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil {
|
||||
logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed")
|
||||
}
|
||||
// write input cmd to log buffer
|
||||
if _, err := logBuff.Write(decodeBytes); err != nil {
|
||||
logrus.WithError(err).Error("write received cmd into log buffer failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ssConn *SshConn) SessionWait(quitChan chan bool) {
|
||||
if err := ssConn.Session.Wait(); err != nil {
|
||||
logrus.WithError(err).Error("ssh session wait failed")
|
||||
setQuit(quitChan)
|
||||
}
|
||||
}
|
||||
|
||||
func setQuit(ch chan bool) {
|
||||
ch <- true
|
||||
}
|
||||
|
||||
type wsMsg struct {
|
||||
Type string `json:"type"`
|
||||
Cmd string `json:"cmd"`
|
||||
Cols int `json:"cols"`
|
||||
Rows int `json:"rows"`
|
||||
}
|
||||
|
||||
// 将终端的输出转发到前端
|
||||
func WsWriterCopy(reader io.Reader, writer *websocket.Conn) {
|
||||
buf := make([]byte, 8192)
|
||||
reg1 := regexp.MustCompile(`stty rows \d+ && stty cols \d+ `)
|
||||
for {
|
||||
nr, err := reader.Read(buf)
|
||||
if nr > 0 {
|
||||
result1 := reg1.FindIndex(buf[0:nr])
|
||||
if len(result1) > 0 {
|
||||
fmt.Println(result1)
|
||||
} else {
|
||||
err := writer.WriteMessage(websocket.BinaryMessage, buf[0:nr])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将前端的输入转发到终端
|
||||
func WsReaderCopy(reader *websocket.Conn, writer io.Writer) {
|
||||
for {
|
||||
messageType, p, err := reader.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if messageType == websocket.TextMessage {
|
||||
msgObj := wsMsg{}
|
||||
if err = json2.Unmarshal(p, &msgObj); err != nil {
|
||||
writer.Write(p)
|
||||
} else if msgObj.Type == wsMsgResize {
|
||||
// writer.Write([]byte("stty rows " + strconv.Itoa(msgObj.Rows) + " && stty cols " + strconv.Itoa(msgObj.Cols) + " \r"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/IceWhaleTech/CasaOS-Common/utils/common_err"
|
||||
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||
sshHelper "github.com/IceWhaleTech/CasaOS/pkg/ssh"
|
||||
sshHelper "github.com/IceWhaleTech/CasaOS-Common/utils/ssh"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user