- 内置 SSH 终端(/terminal):复用已保存的 SSH 密码/私钥与独立 known_hosts 校验 - 终端二次认证、会话审计(/terminal/audits)、录像审计与常用命令 - DDNS 服务商名称与自定义 IP,新增迁移 037/038/039 - CHANGELOG 与 VERSION 更新至 2.0.0
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// sshTerminal 把 x/crypto/ssh 的交互式会话包装成 Terminal。
|
||||
// 申请了 PTY 之后,远端 shell 的 stdout/stderr 会合并到同一条输出流,无需分别读取。
|
||||
type sshTerminal struct {
|
||||
client *ssh.Client
|
||||
session *ssh.Session
|
||||
stdin io.WriteCloser
|
||||
stdout io.Reader
|
||||
|
||||
closeOnce sync.Once
|
||||
closeErr error
|
||||
}
|
||||
|
||||
// NewSSHTerminal 在已认证的 SSH 连接上申请 PTY 并启动登录 shell。
|
||||
func NewSSHTerminal(client *ssh.Client, cols, rows int) (Terminal, error) {
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 SSH 会话失败: %w", err)
|
||||
}
|
||||
modes := ssh.TerminalModes{ssh.ECHO: 1, ssh.TTY_OP_ISPEED: 14400, ssh.TTY_OP_OSPEED: 14400}
|
||||
if err := session.RequestPty("xterm-256color", rows, cols, modes); err != nil {
|
||||
_ = session.Close()
|
||||
return nil, fmt.Errorf("申请 PTY 失败: %w", err)
|
||||
}
|
||||
stdin, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
_ = session.Close()
|
||||
return nil, fmt.Errorf("打开 SSH 输入流失败: %w", err)
|
||||
}
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
_ = session.Close()
|
||||
return nil, fmt.Errorf("打开 SSH 输出流失败: %w", err)
|
||||
}
|
||||
if err := session.Shell(); err != nil {
|
||||
_ = session.Close()
|
||||
return nil, fmt.Errorf("启动远端 shell 失败: %w", err)
|
||||
}
|
||||
return &sshTerminal{client: client, session: session, stdin: stdin, stdout: stdout}, nil
|
||||
}
|
||||
|
||||
// Read 读取远端输出。
|
||||
func (t *sshTerminal) Read(p []byte) (int, error) { return t.stdout.Read(p) }
|
||||
|
||||
// Write 把按键写入远端 PTY。
|
||||
func (t *sshTerminal) Write(p []byte) (int, error) { return t.stdin.Write(p) }
|
||||
|
||||
// Resize 通知远端窗口尺寸变化。
|
||||
func (t *sshTerminal) Resize(cols, rows int) error {
|
||||
if cols <= 0 || rows <= 0 {
|
||||
return nil
|
||||
}
|
||||
return t.session.WindowChange(rows, cols)
|
||||
}
|
||||
|
||||
// Close 关闭 SSH 会话与底层连接;重复调用安全。
|
||||
func (t *sshTerminal) Close() error {
|
||||
t.closeOnce.Do(func() {
|
||||
_ = t.session.Close()
|
||||
t.closeErr = t.client.Close()
|
||||
})
|
||||
return t.closeErr
|
||||
}
|
||||
Reference in New Issue
Block a user