golang实现WebSSH的功能

article/2025/10/2 17:48:50

   在最近一次需求里,需要实现一个webSSH的功能,就是把terminal搬到web中来。要实现这个功能,可以采用websocket+ssh来说实现

1.第一步实现websocket

websocket主要是ws或wss协议,其原理就是http协议升级成ws协议,即ws是建立在http上的,所有路由正常写http的路由,然后处理一下websocket升级。

注:我用的echo框架:

路由:

backendApi.GET("/tools/ssh/ws", tools.WebSSH).Name = "webssh"

handler

func WebSSH(e echo.Context) error {var param tools.WebSShReqif err := utils.BindAndValidate(e, &param); err != nil {return err}if err := tools.Upgrade(e.Response().Writer, e.Request(), param); err != nil {return err}return e.JSON(http.StatusOK, "'")
}

:

websocket协议升级:

var upgrader = websocket.Upgrader{ReadBufferSize:  1024,WriteBufferSize: 1024,CheckOrigin: func(r *http.Request) bool {return true},
}func Upgrade(w http.ResponseWriter, r *http.Request, param WebSShReq) (err error) {conn, err := upgrader.Upgrade(w, r, nil)if err != nil {return}client := NewSSHClient(param)client.Ws = connerr = client.GenerateClient()if err != nil {fmt.Println("链接ssh错误", err)conn.WriteMessage(1, []byte(err.Error()))conn.Close()return err}go client.Write()return nil
}

2.第一版ssh实现

type SSHClient struct {Username  string `json:"username"`Password  string `json:"password"`IpAddress string `json:"ipaddress"`Port      int    `json:"port"`Client    *ssh.ClientWs        *websocket.ConnSession   *ssh.Session
}// NewSSHClient 创建新的ssh客户端时
func NewSSHClient(param WebSShReq) SSHClient {client := SSHClient{}client.Username = param.Usernameclient.Port = param.Portclient.IpAddress = param.IpAddressclient.Password = param.Passwordreturn client
}
func (t *SSHClient) GenerateClient() error {var (auth         []ssh.AuthMethodaddr         stringclientConfig *ssh.ClientConfigclient       *ssh.Clientconfig       ssh.Configerr          error)auth = make([]ssh.AuthMethod, 0)auth = append(auth, ssh.Password(t.Password))config = ssh.Config{Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},}clientConfig = &ssh.ClientConfig{User:    t.Username,Auth:    auth,Timeout: 5 * time.Second,Config:  config,HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {return nil},}addr = fmt.Sprintf("%s:%d", t.IpAddress, t.Port)if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {return err}t.Client = clientreturn nil
}func (t *SSHClient) send(out, stderr *bytes.Buffer, cmd []byte) error {session, err := t.Client.NewSession()if err != nil {return errors.New("ssh session创建失败")}defer session.Close()session.Stdout = outsession.Stderr = stderrreturn session.Run(string(cmd))
}
func (t *SSHClient) Write() {defer t.Client.Close()for {// p为用户输入_, p, err := t.Ws.ReadMessage()if err != nil {return}fmt.Println("webssh:", string(p))go func(data []byte) {var (out   bytes.BufferstdEr bytes.Buffer)err2 := t.send(&out, &stdEr, data)if err2 != nil {t.Ws.WriteMessage(1, stdEr.Bytes())return}t.Ws.WriteMessage(1, out.Bytes())}(p)}
}

这样能实现正常指令,但是有个问题;不能切换目录,不完善

3.改进

使用终端交互模式,做到真正的webssh, 直接上代码

改进方法:

func (t *SSHClient) RunTerminal(stdout, stderr io.Writer, stdin io.Reader) error {session, err := t.Client.NewSession()if err != nil {return err}defer session.Close()session.Stdout = io.MultiWriter(os.Stdout, stdout)session.Stderr = io.MultiWriter(os.Stderr, stderr)session.Stdin = stdinmodes := ssh.TerminalModes{ssh.ECHO:          0,ssh.TTY_OP_ISPEED: 14400,ssh.TTY_OP_OSPEED: 14400,}if err := session.RequestPty("xterm", 32, 160, modes); err != nil {return err}if err = session.Shell(); err != nil {log.Fatalf("start shell error: %s", err.Error())}if err = session.Wait(); err != nil {log.Fatalf("return error: %s", err.Error())}return nil
}

此方法需要输入,输出,和错误,使用标准的输入及标准输出,能实现交互,但是我是需要接收websocket发的消息,及返回websocket输出。

故,需要实现io.writer和io.reader接口

type stdout struct {ws *websocket.Conn
}func (s *stdout) Write(p []byte) (n int, err error) {// fmt.Println("SSH output:", string(p))err = s.ws.WriteMessage(1, p)return len(p), err
}type stdIn struct {ws *websocket.Conn
}func (s *stdIn) Read(p []byte) (n int, err error) {_, p2, err := s.ws.ReadMessage()// 指令需要加一个回车if err != nil {return 0, err}n = copy(p, []byte(fmt.Sprintf("%s\n", string(p2))))// fmt.Println("####", string(p))return n, err
}
func (s *stdIn) Close() error {return nil
}

注: 实现read方法时,注意加个回车,不然指令是不会执行的,我在这里就卡了很久......

最后,将upgrade方法中的,

go client.Write() 

换成

go client.RunTerminal(&stdout{conn}, &stdout{conn}, &stdIn{conn})

就ok了。

4.最终效果如图:

 


http://chatgpt.dhexx.cn/article/rSVdAkMg.shtml

相关文章

纯Java实现一个网页版的Xshell

前言 最近由于项目需求,项目中需要实现一个WebSSH连接终端的功能,由于自己第一次做这类型功能,所以首先上了GitHub找了找有没有现成的轮子可以拿来直接用,当时看到了很多这方面的项目,例如:GateOne、webss…

webssh

vue djangolinux实现webssh 技术栈 xterm.js:做到vue的表现型 websocket: 做vue和django之间的通信 paramiko: 建立SSH连接通道 ssh: 与linux进行连接 threading:多条数据返回前端,io密集型,用…

webSSH如何安装?如何使用?解决Web端远程连接终端~~运维篇

Hi~由于博主公司业务有相当多的Linux终端设备,每次连上设备需要使用到外部工具,如:Xshell,每次都得去输入IP,端口,账号,密码相当的繁琐;偶尔看到阿里云远程连接终端功能挺有意思的&a…

超强功能WebSSH安装,解决Web远程SSH终端

项目地址:https://github.com/huashengdun/webssh 一个简单的 Web 应用程序,用作 ssh 客户端以连接到您的 ssh 服务器。它是用 Python 编写的,基于 tornado、paramiko 和 xterm.js。 特征: 支持SSH密码认证,包括空密…

webssh —— 浏览器上的终端

需求 近期接到一个需求,实现一个运行在浏览器上的终端,用于快速连接到公司设备。 Tip:只求实现的可直接跳到 「最终方案」 处 。 需求有以下几点 1、设备都不在公网状态下 2、webshell 需要免密登陆 3、动态连接的端口、账号、密码 4、可显…

webssh的安装与使用

最近研究了一下在web端实现一个远程连接终端操作的类似网页版xshell的实现。在网上搜索了一下发现已经有类似的操作在这里主要介绍以下两种。 https://github.com/huashengdun/websshhttps://github.com/billchurch/WebSSH2 我用的是虚拟机centos7系统,别的linux系…

简单分析实现运维利器---webssh终端

背景 现在几乎所有东西都向往着自动化,在运维界更是如此,运维人员都向往自动化代替人工操作、解决人工操作大量重复性工作的问题、故障主动恢复:及时发现;流程;解决。运维规范化:角色定义和责任划分、流程化等。但这些种种的目的,都离不开非常细小的技术支持,下面我们…

WebsocketWebSSH

什么是WebSSH? webssh 泛指一种技术可以在网页上实现一个 SSH 终端ssh终端:用来通过ssh协议,连接服务器进行管理运维开发方向:堡垒机登录、线上机器管理(因为运维人员不可能24小时携带电脑)在线编程:提供…

WebSSH神器sshwifty的安装与使用

本文章最初发表在XJHui’s Blog,未经允许,任何人禁止转载! 为使您获得最好的阅读体验,强烈建议您点击 这里 前往 XJHui’s Blog 查看! WebSSH工具 初衷 9.9买的一年服务器,不用实属可惜由于是计算机专业…

Sql Server查看表结构

1、表结构 2、通过 t abc 查询出的表结构 3、存储过程 t CREATE proc t TableName nvarchar(200) as SELECT (select top 1 isnull(value,) from sys.extended_properties ex_p where…

sql 查看表结构改动的记录

系统更新迭代比较大的情况,可能改动了比较多的数据库,留个笔记。。 select * from sys.objects order by modify_date desc 这句是查看数据库表的改动,希望对自己有帮助 -20180613

Access、SQLServer以及SQLite如何查询数据表结构

日期:2021年11月05日 作者:Commas 注释:整理了一下不同数据库如何获取数据表结构,比较有意思的玩法就是将不同数据库进行相互转换,由于篇幅问题,暂且仅讨论"数据表结构"的获取…… 目录 文章目录…

sqlServer 查看表结构(字段类型) (更新中)

对MySQL和Oracle熟悉的朋友知道用desc就可以查询一张表的结构,但是在SQL Server里执行desc命令会报错。 现提供两条命令查询表结构: 1.sp_help table_name; 2.sp_columns table_name; sp_help 直通车报表上周宝贝$; sp_columns 直通车报表…

用SQL语句查看Oracle数据表的结构信息

文章目录 获取所有表的列表查看表结构查看某表的注释查看某表的字段注释 获取所有表的列表 相知道相应用户下有哪些表,可以通过以下 SQL 语句进行查询 select table_name from user_tables; //当前用户的表 select table_name from all_tables; //所有用户…

使用SQL语句修改表结构(SQL Server)

使用SQL语句修改表结构(SQL Server) 文章目录 使用SQL语句修改表结构(SQL Server)ALTER TABLE的语法格式如下:添加列删除列修改表的列数据类型和名称 ALTER TABLE的语法格式如下: 上述格式主要参数说明如下…

sql查看mysql表结构_MySQL 查看表结构简单命令

一、简单描述表结构,字段类型 desc tabl_name; 显示表结构,字段类型,主键,是否为空等属性,但不显示外键。 例如:desc table_name 二、查询表中列的注释信息 select * from information_schema.columns where table_schema = db #表所在数据库 and table_name = tablename…

sqlserver数据库如何快速查看表结构sql

有时候,我们熟悉一个新系统,首先就需要熟悉其数据库的各个表结构。这时候,其实我们可以通过一个SQL清晰的查看表结构。 SQL如下: SELECT CASEWHENcol.colorder 1 THENobj.name ELSE END AS 表名,col.colorder AS 序号 ,col.na…

SQL语句查询表结构

代码如下: SELECT表名 Case When A.colorder1 Then D.name Else End,表说明 Case When A.colorder1 Then isnull(F.value,) Else End,字段序号 A.colorder,字段名 A.name,字段说明 isnull(G.[value],),标识 Case When COLUMNPROPE…

SQL语句获取表结构

目录 语句演示千人全栈VIP答疑群联系博主帮忙解决报错 语句 获取表的结构数据,如表名,数据类型,精度等数据,代码如下: SELECT t.table_name, -- 表名c.column_name, -- 字段名c.data_type, -- 字段类型c.numeric_pre…

matlab 频数分布直方图_matlab 统计直方图

展开全部 1、首先随机产生一组数据作为例子,在这里,我们32313133353236313431303231363533e4b893e5b19e31333431363566随机产生一组数据,并将其圆整为整数,方法是:around(10*rand(5,1)) 2、接下来,可以用MA…