mirror of
https://github.com/khairul169/vaulterm.git
synced 2026-09-15 17:03:30 +07:00
update working ssh shell
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
module rul.sh/vaulterm
|
||||
|
||||
go 1.21.1
|
||||
|
||||
require golang.org/x/crypto v0.28.0
|
||||
|
||||
require (
|
||||
github.com/coder/websocket v1.8.12
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
+155
@@ -1,4 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// Replace with actual SSH server credentials
|
||||
var sshHost = "10.0.0.102"
|
||||
var sshUser = "root"
|
||||
var sshPassword = "ausya2"
|
||||
|
||||
func sshWebSocketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
wsConn, err := websocket.Accept(w, r, &websocket.AcceptOptions{
|
||||
OriginPatterns: []string{"*"}, // Adjust origin policy as needed
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("failed to accept websocket: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Set up SSH client configuration
|
||||
sshConfig := &ssh.ClientConfig{
|
||||
User: sshUser,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(sshPassword),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
// Connect to SSH server
|
||||
sshConn, err := ssh.Dial("tcp", sshHost+":22", sshConfig)
|
||||
if err != nil {
|
||||
wsConn.Write(context.Background(), websocket.MessageText, []byte(err.Error()))
|
||||
wsConn.Close(websocket.StatusInternalError, "failed to connect to SSH")
|
||||
return
|
||||
}
|
||||
defer sshConn.Close()
|
||||
|
||||
// Start an SSH shell session
|
||||
session, err := sshConn.NewSession()
|
||||
if err != nil {
|
||||
wsConn.Write(context.Background(), websocket.MessageText, []byte(err.Error()))
|
||||
wsConn.Close(websocket.StatusInternalError, "failed to start SSH session")
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
stdoutPipe, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
wsConn.Close(websocket.StatusInternalError, "failed to get stdout pipe")
|
||||
return
|
||||
}
|
||||
|
||||
stderrPipe, err := session.StderrPipe()
|
||||
if err != nil {
|
||||
wsConn.Close(websocket.StatusInternalError, "failed to get stderr pipe")
|
||||
return
|
||||
}
|
||||
|
||||
stdinPipe, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
wsConn.Close(websocket.StatusInternalError, "failed to get stdin pipe")
|
||||
return
|
||||
}
|
||||
|
||||
err = session.RequestPty("xterm-256color", 80, 24, ssh.TerminalModes{})
|
||||
if err != nil {
|
||||
wsConn.Close(websocket.StatusInternalError, "failed to request pty")
|
||||
return
|
||||
}
|
||||
|
||||
if err := session.Shell(); err != nil {
|
||||
wsConn.Close(websocket.StatusInternalError, "failed to start shell")
|
||||
return
|
||||
}
|
||||
|
||||
// Goroutine to send SSH stdout to WebSocket
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, err := stdoutPipe.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Printf("error reading from SSH stdout: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if writeErr := wsConn.Write(context.Background(), websocket.MessageBinary, buf[:n]); writeErr != nil {
|
||||
log.Printf("error writing to websocket: %v", writeErr)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Goroutine to handle SSH stderr
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, err := stderrPipe.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Printf("error reading from SSH stderr: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if writeErr := wsConn.Write(context.Background(), websocket.MessageBinary, buf[:n]); writeErr != nil {
|
||||
log.Printf("error writing to websocket: %v", writeErr)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Handle WebSocket to SSH data streaming
|
||||
go func() {
|
||||
for {
|
||||
_, msg, err := wsConn.Read(context.Background())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(string(msg), "\x01") {
|
||||
parts := strings.Split(string(msg[1:]), ",")
|
||||
if len(parts) == 2 {
|
||||
width, _ := strconv.Atoi(parts[0])
|
||||
height, _ := strconv.Atoi(parts[1])
|
||||
session.WindowChange(height, width)
|
||||
}
|
||||
} else {
|
||||
stdinPipe.Write(msg)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for the SSH session to close
|
||||
if err := session.Wait(); err != nil {
|
||||
log.Printf("SSH session ended with error: %v", err)
|
||||
wsConn.Write(context.Background(), websocket.MessageText, []byte(err.Error()))
|
||||
} else {
|
||||
log.Println("SSH session ended normally")
|
||||
}
|
||||
|
||||
// Ensure WebSocket is closed after SSH logout
|
||||
wsConn.Close(websocket.StatusNormalClosure, "SSH session closed")
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/ws", sshWebSocketHandler)
|
||||
log.Println("Server started on :3000")
|
||||
log.Fatal(http.ListenAndServe(":3000", nil))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user