前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用Go语言实现WebSSH远程连接

用Go语言实现WebSSH远程连接

作者头像
ppxai
发布2023-11-18 08:35:05
4270
发布2023-11-18 08:35:05
举报
文章被收录于专栏:皮皮星球皮皮星球

用Go语言实现WebSSH远程连接

WebSSH远程连接

WebSSH是一种通过Web浏览器远程连接到SSH服务器的技术。它允许用户在不需要本地SSH客户端的情况下,通过Web浏览器连接到远程服务器并执行命令。WebSSH的实现原理是通过WebSocket协议在Web浏览器和SSH服务器之间建立一个双向通信通道,使得用户可以在Web浏览器中输入命令并将其发送到SSH服务器,同时也可以接收SSH服务器的输出并在Web浏览器中显示。

在本文中,我们将使用Go语言的SSH和WebSocket库来实现WebSSH。我们将从创建WebSocket服务器开始,然后创建SSH客户端,创建SSH会话并请求伪终端,设置标准输入和输出管道,最后启动两个goroutine来处理从Web浏览器读取数据和从SSH服务器读取数据的操作。

创建WebSocket服务器

我们首先需要创建一个WebSocket服务器,以便Web浏览器可以连接到它。我们使用Gorilla WebSocket库来创建WebSocket服务器。在main函数中,我们使用http.HandleFunc函数来处理WebSocket连接请求,并使用websocket.Upgrader结构体来升级HTTP连接为WebSocket连接。我们还需要设置CheckOrigin函数,以便允许跨域请求。

代码语言:javascript
复制
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}

func wsHandle(w http.ResponseWriter, r *http.Request) {
	var (
		conn    *websocket.Conn
		client  *ssh.Client
		sshConn *SSHConnect
		err     error
	)
	if conn, err = upgrader.Upgrade(w, r, nil); err != nil {
		return
	}
	defer conn.Close()

	//Create ssh client
	if client, err = createSSHClient(user, password, host, port); err != nil {
		WsSendText(conn, []byte(err.Error()))
		return
	}
	defer client.Close()

	//connect to ssh
	if sshConn, err = NewSSHConnect(client); err != nil {
		WsSendText(conn, []byte(err.Error()))
		return
	}

	quit := make(chan int)
	go sshConn.Output(conn, quit)
	go sshConn.Recv(conn, quit)
	<-quit
}

func WsSendText(conn *websocket.Conn, b []byte) error {
	if err := conn.WriteMessage(1, b); err != nil {
		return err
	}
	return nil
}

创建SSH客户端

接下来,我们需要创建一个SSH客户端,以便连接到远程SSH服务器。我们使用Go语言的SSH库来创建SSH客户端。在createSSHClient函数中,我们使用用户名和密码进行身份验证,并使用ssh.Dial函数连接到远程SSH服务器。

代码语言:javascript
复制
func createSSHClient(user, password, host string, port int) (*ssh.Client, error) {
	var (
		auth         []ssh.AuthMethod
		addr         string
		clientConfig *ssh.ClientConfig
		client       *ssh.Client
		//session      *ssh.Session
		err error
	)
	auth = make([]ssh.AuthMethod, 0)
	auth = append(auth, ssh.Password(password))

	clientConfig = &ssh.ClientConfig{
		User: user,
		Auth: auth,
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			//Handling the host key
			return nil
		},
	}
	addr = fmt.Sprintf("%s:%d", host, port)
	if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
		return nil, err
	}
	return client, nil
}

创建SSH会话并请求伪终端

接下来,我们需要创建一个SSH会话,并请求一个伪终端。我们使用client.NewSession函数创建SSH会话,并使用session.RequestPty函数请求伪终端。我们还需要设置终端模式,以便在Web浏览器中正确显示输出。

代码语言:javascript
复制
func NewSSHConnect(client *ssh.Client) (sshConn *SSHConnect, err error) {
    var (
        session *ssh.Session
    )
    if session, err = client.NewSession(); err != nil {
        return
    }
    modes := ssh.TerminalModes{
        ssh.ECHO:          0,
        ssh.TTY_OP_ISPEED: 14400,
        ssh.TTY_OP_OSPEED: 14400,
    }
    if err = session.RequestPty("linux", 80, 40, modes); err != nil {
        return
    }
    // ...
}

设置标准输入和输出管道

接下来,我们需要设置标准输入和输出管道,以便可以在Web浏览器和SSH服务器之间传输数据。我们使用session.StdinPipe函数和session.StdoutPipe函数分别创建标准输入和输出管道,并将它们存储在SSHConnect结构体中。

代码语言:javascript
复制
func NewSSHConnect(client *ssh.Client) (sshConn *SSHConnect, err error) {
    var (
        session *ssh.Session
    )
    if session, err = client.NewSession(); err != nil {
        return
    }
    modes := ssh.TerminalModes{
        ssh.ECHO:          0,
        ssh.TTY_OP_ISPEED: 14400,
        ssh.TTY_OP_OSPEED: 14400,
    }
    if err = session.RequestPty("linux", 80, 40, modes); err != nil {
        return
    }

    pipe, _ := session.StdinPipe()
    stdoutPipe, _ := session.StdoutPipe()

    return &SSHConnect{
        session:    session,
        stdinPipe:  pipe,
        stdoutPipe: stdoutPipe,
    }, nil
}

处理从Web浏览器读取数据和从SSH服务器读取数据的操作

最后,我们需要启动两个goroutine来处理从Web浏览器读取数据和从SSH服务器读取数据的操作。在SSHConnect结构体中,我们定义了Recv函数和Output函数来处理这些操作。Recv函数从WebSocket连接中读取数据,并将其写入SSH服务器的标准输入管道。Output函数从SSH服务器的标准输出管道中读取数据,并将其发送到WebSocket连接。

代码语言:javascript
复制
type SSHConnect struct {
    session    *ssh.Session
    stdinPipe  io.WriteCloser
    stdoutPipe io.Reader
}

func (s *SSHConnect) Recv(conn *websocket.Conn, quit chan int) {
	defer Quit(quit)
	var (
		bytes []byte
		err   error
	)
	for {
		if _, bytes, err = conn.ReadMessage(); err != nil {
			return
		}
		if len(bytes) > 0 {
			if _, e := s.stdinPipe.Write(bytes); e != nil {
				return
			}
		}
	}
}

func (s *SSHConnect) Output(conn *websocket.Conn, quit chan int) {
	defer Quit(quit)
	var (
		read int
		err  error
	)
	tick := time.NewTicker(60 * time.Millisecond)
	defer tick.Stop()
Loop:
	for {
		select {
		case <-tick.C:
			i := make([]byte, 1024)
			if read, err = s.stdoutPipe.Read(i); err != nil {
				fmt.Println(err)
				break Loop
			}
			if err = WsSendText(conn, i[:read]); err != nil {
				fmt.Println(err)
				break Loop
			}
		}
	}
}

func Quit(quit chan int) {
	quit <- 1
}

最后运行ws服务:

代码语言:javascript
复制
func main() {
	http.HandleFunc("/ws/v1", wsHandle)
	http.ListenAndServe(":8080", nil)
}

至此,我们已经完成了WebSSH的实现。用户可以通过Web浏览器连接到WebSocket服务器,并在Web浏览器中输入命令并将其发送到SSH服务器,同时也可以接收SSH服务器的输出并在Web浏览器中显示。WebSSH的实现可以提供一种方便的方式,让用户通过Web浏览器连接到远程SSH服务器并执行命令。它可以减少用户需要安装本地SSH客户端的麻烦,并提供更加友好的用户界面。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-05-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 用Go语言实现WebSSH远程连接
  • WebSSH远程连接
    • 创建WebSocket服务器
      • 创建SSH客户端
        • 创建SSH会话并请求伪终端
          • 设置标准输入和输出管道
            • 处理从Web浏览器读取数据和从SSH服务器读取数据的操作
            相关产品与服务
            多因子身份认证
            多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档