mirror of
https://github.com/khairul169/vaulterm.git
synced 2026-09-15 17:03:30 +07:00
feat: update
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
type Hosts struct{ db *gorm.DB }
|
||||
|
||||
func NewHostsRepository() *Hosts {
|
||||
func NewRepository() *Hosts {
|
||||
return &Hosts{db: db.Get()}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,7 @@ func (r *Hosts) GetAll() ([]*models.Host, error) {
|
||||
return rows, ret.Error
|
||||
}
|
||||
|
||||
type GetHostResult struct {
|
||||
Host *models.Host
|
||||
Key map[string]interface{}
|
||||
AltKey map[string]interface{}
|
||||
}
|
||||
|
||||
func (r *Hosts) Get(id string) (*GetHostResult, error) {
|
||||
func (r *Hosts) Get(id string) (*models.HostDecrypted, error) {
|
||||
var host models.Host
|
||||
ret := r.db.Joins("Key").Joins("AltKey").Where("hosts.id = ?", id).First(&host)
|
||||
|
||||
@@ -33,17 +27,9 @@ func (r *Hosts) Get(id string) (*GetHostResult, error) {
|
||||
return nil, ret.Error
|
||||
}
|
||||
|
||||
res := &GetHostResult{Host: &host}
|
||||
|
||||
if host.Key.Data != "" {
|
||||
if err := host.Key.DecryptData(&res.Key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if host.AltKey.Data != "" {
|
||||
if err := host.AltKey.DecryptData(&res.AltKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := host.DecryptKeys()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, ret.Error
|
||||
|
||||
@@ -19,7 +19,7 @@ func Router(app *fiber.App) {
|
||||
}
|
||||
|
||||
func getAll(c *fiber.Ctx) error {
|
||||
repo := NewHostsRepository()
|
||||
repo := NewRepository()
|
||||
rows, err := repo.GetAll()
|
||||
if err != nil {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
@@ -36,7 +36,7 @@ func create(c *fiber.Ctx) error {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
}
|
||||
|
||||
repo := NewHostsRepository()
|
||||
repo := NewRepository()
|
||||
item := &models.Host{
|
||||
Type: body.Type,
|
||||
Label: body.Label,
|
||||
@@ -47,6 +47,13 @@ func create(c *fiber.Ctx) error {
|
||||
KeyID: body.KeyID,
|
||||
AltKeyID: body.AltKeyID,
|
||||
}
|
||||
|
||||
osName, err := tryConnect(item)
|
||||
if err != nil {
|
||||
return utils.ResponseError(c, fmt.Errorf("cannot connect to the host: %s", err), 500)
|
||||
}
|
||||
item.OS = osName
|
||||
|
||||
if err := repo.Create(item); err != nil {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
}
|
||||
@@ -60,7 +67,7 @@ func update(c *fiber.Ctx) error {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
}
|
||||
|
||||
repo := NewHostsRepository()
|
||||
repo := NewRepository()
|
||||
|
||||
id := c.Params("id")
|
||||
exist, _ := repo.Exists(id)
|
||||
@@ -79,6 +86,13 @@ func update(c *fiber.Ctx) error {
|
||||
KeyID: body.KeyID,
|
||||
AltKeyID: body.AltKeyID,
|
||||
}
|
||||
|
||||
osName, err := tryConnect(item)
|
||||
if err != nil {
|
||||
return utils.ResponseError(c, fmt.Errorf("cannot connect to the host: %s", err), 500)
|
||||
}
|
||||
item.OS = osName
|
||||
|
||||
if err := repo.Update(item); err != nil {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
}
|
||||
@@ -87,7 +101,7 @@ func update(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func delete(c *fiber.Ctx) error {
|
||||
repo := NewHostsRepository()
|
||||
repo := NewRepository()
|
||||
|
||||
id := c.Params("id")
|
||||
exist, _ := repo.Exists(id)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package hosts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"rul.sh/vaulterm/app/keychains"
|
||||
"rul.sh/vaulterm/lib"
|
||||
"rul.sh/vaulterm/models"
|
||||
)
|
||||
|
||||
func tryConnect(host *models.Host) (string, error) {
|
||||
keyRepo := keychains.NewRepository()
|
||||
|
||||
var key map[string]interface{}
|
||||
var altKey map[string]interface{}
|
||||
|
||||
if host.KeyID != nil {
|
||||
keychain, _ := keyRepo.Get(*host.KeyID)
|
||||
if keychain == nil {
|
||||
return "", fmt.Errorf("key %s not found", *host.KeyID)
|
||||
}
|
||||
keychain.DecryptData(&key)
|
||||
}
|
||||
if host.AltKeyID != nil {
|
||||
keychain, _ := keyRepo.Get(*host.AltKeyID)
|
||||
if keychain == nil {
|
||||
return "", fmt.Errorf("key %s not found", *host.KeyID)
|
||||
}
|
||||
keychain.DecryptData(&altKey)
|
||||
}
|
||||
|
||||
if host.Type == "ssh" {
|
||||
c := lib.NewSSHClient(&lib.SSHClientConfig{
|
||||
HostName: host.Host,
|
||||
Port: host.Port,
|
||||
Key: key,
|
||||
AltKey: altKey,
|
||||
})
|
||||
|
||||
con, err := c.Connect()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
os, err := c.GetOS(c, con)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return os, nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
type Keychains struct{ db *gorm.DB }
|
||||
|
||||
func NewKeychainsRepository() *Keychains {
|
||||
func NewRepository() *Keychains {
|
||||
return &Keychains{db: db.Get()}
|
||||
}
|
||||
|
||||
@@ -22,3 +22,31 @@ func (r *Keychains) GetAll() ([]*models.Keychain, error) {
|
||||
func (r *Keychains) Create(item *models.Keychain) error {
|
||||
return r.db.Create(item).Error
|
||||
}
|
||||
|
||||
func (r *Keychains) Get(id string) (*models.Keychain, error) {
|
||||
var keychain models.Keychain
|
||||
if err := r.db.Where("id = ?", id).First(&keychain).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &keychain, nil
|
||||
}
|
||||
|
||||
type KeychainDecrypted struct {
|
||||
models.Keychain
|
||||
Data map[string]interface{}
|
||||
}
|
||||
|
||||
func (r *Keychains) GetDecrypted(id string) (*KeychainDecrypted, error) {
|
||||
keychain, err := r.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data map[string]interface{}
|
||||
if err := keychain.DecryptData(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &KeychainDecrypted{Keychain: *keychain, Data: data}, nil
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func Router(app *fiber.App) {
|
||||
}
|
||||
|
||||
func getAll(c *fiber.Ctx) error {
|
||||
repo := NewKeychainsRepository()
|
||||
repo := NewRepository()
|
||||
rows, err := repo.GetAll()
|
||||
if err != nil {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
@@ -33,7 +33,7 @@ func create(c *fiber.Ctx) error {
|
||||
return utils.ResponseError(c, err, 500)
|
||||
}
|
||||
|
||||
repo := NewKeychainsRepository()
|
||||
repo := NewRepository()
|
||||
|
||||
item := &models.Keychain{
|
||||
Type: body.Type,
|
||||
|
||||
+12
-14
@@ -6,13 +6,14 @@ import (
|
||||
"github.com/gofiber/contrib/websocket"
|
||||
"rul.sh/vaulterm/app/hosts"
|
||||
"rul.sh/vaulterm/lib"
|
||||
"rul.sh/vaulterm/models"
|
||||
"rul.sh/vaulterm/utils"
|
||||
)
|
||||
|
||||
func HandleTerm(c *websocket.Conn) {
|
||||
hostId := c.Query("hostId")
|
||||
|
||||
hostRepo := hosts.NewHostsRepository()
|
||||
hostRepo := hosts.NewRepository()
|
||||
data, err := hostRepo.Get(hostId)
|
||||
|
||||
if data == nil {
|
||||
@@ -33,30 +34,27 @@ func HandleTerm(c *websocket.Conn) {
|
||||
}
|
||||
}
|
||||
|
||||
func sshHandler(c *websocket.Conn, data *hosts.GetHostResult) {
|
||||
username, _ := data.Key["username"].(string)
|
||||
password, _ := data.Key["password"].(string)
|
||||
|
||||
cfg := &SSHConfig{
|
||||
func sshHandler(c *websocket.Conn, data *models.HostDecrypted) {
|
||||
cfg := lib.NewSSHClient(&lib.SSHClientConfig{
|
||||
HostName: data.Host.Host,
|
||||
Port: data.Host.Port,
|
||||
User: username,
|
||||
Password: password,
|
||||
}
|
||||
Port: data.Port,
|
||||
Key: data.Key,
|
||||
AltKey: data.AltKey,
|
||||
})
|
||||
|
||||
if err := NewSSHWebsocketSession(c, cfg); err != nil {
|
||||
c.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
func pveHandler(c *websocket.Conn, data *hosts.GetHostResult) {
|
||||
func pveHandler(c *websocket.Conn, data *models.HostDecrypted) {
|
||||
client := c.Query("client")
|
||||
username, _ := data.Key["username"].(string)
|
||||
password, _ := data.Key["password"].(string)
|
||||
|
||||
pve := &lib.PVEServer{
|
||||
HostName: data.Host.Host,
|
||||
Port: data.Host.Port,
|
||||
Port: data.Port,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
@@ -84,7 +82,7 @@ func pveHandler(c *websocket.Conn, data *hosts.GetHostResult) {
|
||||
}
|
||||
}
|
||||
|
||||
func incusHandler(c *websocket.Conn, data *hosts.GetHostResult) {
|
||||
func incusHandler(c *websocket.Conn, data *models.HostDecrypted) {
|
||||
shell := c.Query("shell")
|
||||
|
||||
cert, _ := data.Key["cert"].(string)
|
||||
@@ -97,7 +95,7 @@ func incusHandler(c *websocket.Conn, data *hosts.GetHostResult) {
|
||||
|
||||
incus := &lib.IncusServer{
|
||||
HostName: data.Host.Host,
|
||||
Port: data.Host.Port,
|
||||
Port: data.Port,
|
||||
ClientCert: cert,
|
||||
ClientKey: key,
|
||||
}
|
||||
|
||||
+15
-77
@@ -1,101 +1,37 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/contrib/websocket"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"rul.sh/vaulterm/lib"
|
||||
)
|
||||
|
||||
type SSHConfig struct {
|
||||
HostName string
|
||||
User string
|
||||
Password string
|
||||
Port int
|
||||
PrivateKey string
|
||||
PrivateKeyPassphrase string
|
||||
}
|
||||
|
||||
func NewSSHWebsocketSession(c *websocket.Conn, cfg *SSHConfig) error {
|
||||
// Set up SSH client configuration
|
||||
port := cfg.Port
|
||||
if port == 0 {
|
||||
port = 22
|
||||
}
|
||||
auth := []ssh.AuthMethod{
|
||||
ssh.Password(cfg.Password),
|
||||
}
|
||||
|
||||
if cfg.PrivateKey != "" {
|
||||
var err error
|
||||
var signer ssh.Signer
|
||||
|
||||
if cfg.PrivateKeyPassphrase != "" {
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(cfg.PrivateKey), []byte(cfg.PrivateKeyPassphrase))
|
||||
} else {
|
||||
signer, err = ssh.ParsePrivateKey([]byte(cfg.PrivateKey))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse private key: %v", err)
|
||||
}
|
||||
auth = append(auth, ssh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
sshConfig := &ssh.ClientConfig{
|
||||
User: cfg.User,
|
||||
Auth: auth,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
// Connect to SSH server
|
||||
hostName := fmt.Sprintf("%s:%d", cfg.HostName, port)
|
||||
sshConn, err := ssh.Dial("tcp", hostName, sshConfig)
|
||||
func NewSSHWebsocketSession(c *websocket.Conn, client *lib.SSHClient) error {
|
||||
con, err := client.Connect()
|
||||
if err != nil {
|
||||
log.Printf("error connecting to SSH: %v", err)
|
||||
return err
|
||||
}
|
||||
defer sshConn.Close()
|
||||
defer con.Close()
|
||||
|
||||
// Start an SSH shell session
|
||||
session, err := sshConn.NewSession()
|
||||
shell, err := client.StartPtyShell(con)
|
||||
if err != nil {
|
||||
log.Printf("error starting SSH shell: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
session := shell.Session
|
||||
defer session.Close()
|
||||
|
||||
stdoutPipe, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stderrPipe, err := session.StderrPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdinPipe, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = session.RequestPty("xterm-256color", 80, 24, ssh.TerminalModes{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := session.Shell(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Goroutine to send SSH stdout to WebSocket
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, err := stdoutPipe.Read(buf)
|
||||
n, err := shell.Stdout.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Printf("error reading from SSH stdout: %v", err)
|
||||
@@ -114,7 +50,7 @@ func NewSSHWebsocketSession(c *websocket.Conn, cfg *SSHConfig) error {
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, err := stderrPipe.Read(buf)
|
||||
n, err := shell.Stderr.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Printf("error reading from SSH stderr: %v", err)
|
||||
@@ -135,6 +71,7 @@ func NewSSHWebsocketSession(c *websocket.Conn, cfg *SSHConfig) error {
|
||||
for {
|
||||
_, msg, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
log.Printf("error reading from websocket: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -148,8 +85,10 @@ func NewSSHWebsocketSession(c *websocket.Conn, cfg *SSHConfig) error {
|
||||
continue
|
||||
}
|
||||
|
||||
stdinPipe.Write(msg)
|
||||
shell.Stdin.Write(msg)
|
||||
}
|
||||
|
||||
log.Println("SSH session closed")
|
||||
}()
|
||||
|
||||
// Wait for the SSH session to close
|
||||
@@ -158,6 +97,5 @@ func NewSSHWebsocketSession(c *websocket.Conn, cfg *SSHConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("SSH session ended normally")
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user