vaulterm/server/main.go

162 lines
3.8 KiB
Go

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() {
defer session.Close()
for {
_, msg, err := wsConn.Read(context.Background())
if err != nil {
break
}
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/ssh", sshWebSocketHandler)
log.Println("Server started on :3000")
log.Fatal(http.ListenAndServe(":3000", nil))
}