mirror of
https://github.com/tale/headplane.git
synced 2026-08-11 14:26:56 +00:00
feat: initial webssh tooling
This commit is contained in:
@@ -2,13 +2,16 @@ package hpagent
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"bytes"
|
||||
// "encoding/json"
|
||||
"os"
|
||||
"sync"
|
||||
// "sync"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/tale/headplane/agent/internal/sshutil"
|
||||
"github.com/tale/headplane/agent/internal/tsnet"
|
||||
"github.com/tale/headplane/agent/internal/util"
|
||||
"tailscale.com/tailcfg"
|
||||
// "tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
// Represents messages from the Headplane master
|
||||
@@ -16,6 +19,25 @@ type RecvMessage struct {
|
||||
NodeIDs []string
|
||||
}
|
||||
|
||||
type CborMessage struct {
|
||||
Op string `cbor:"op"`
|
||||
Payload cbor.RawMessage `cbor:"payload"`
|
||||
}
|
||||
|
||||
type SSHConnect struct {
|
||||
SessionId string `cbor:"sessionId"`
|
||||
Username string `cbor:"username"`
|
||||
Hostname string `cbor:"hostname"`
|
||||
Port int `cbor:"port"`
|
||||
}
|
||||
|
||||
type SSHMessage struct {
|
||||
op string
|
||||
username string
|
||||
hostname string
|
||||
Id string
|
||||
}
|
||||
|
||||
type SendMessage struct {
|
||||
Type string
|
||||
Data any
|
||||
@@ -25,63 +47,111 @@ type SendMessage struct {
|
||||
func FollowMaster(agent *tsnet.TSAgent) {
|
||||
log := util.GetLogger()
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
log.Info("Listening for messages from Headplane master on stdin")
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
log.Info("Got bytes delimited by newline");
|
||||
|
||||
var msg CborMessage
|
||||
decoder := cbor.NewDecoder(bytes.NewReader(line))
|
||||
err := decoder.Decode(&msg)
|
||||
|
||||
var msg RecvMessage
|
||||
err := json.Unmarshal(line, &msg)
|
||||
if err != nil {
|
||||
log.Error("Unable to unmarshal message: %s", err)
|
||||
log.Debug("Full Error: %v", err)
|
||||
continue
|
||||
log.Error("Unable to decode message from master: %s", err)
|
||||
continue;
|
||||
}
|
||||
|
||||
log.Debug("Recieved message from master: %v", line)
|
||||
|
||||
if len(msg.NodeIDs) == 0 {
|
||||
log.Debug("Message recieved had no node IDs")
|
||||
log.Debug("Full message: %s", line)
|
||||
continue
|
||||
log.Debug("Received message from master: %s", msg)
|
||||
var sshPayload SSHConnect
|
||||
err = cbor.Unmarshal(msg.Payload, &sshPayload)
|
||||
if err != nil {
|
||||
log.Error("Unable to unmarshal SSH connect payload: %s", err)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Accumulate the results since we invoke via gofunc
|
||||
results := make(map[string]*tailcfg.HostinfoView)
|
||||
mu := sync.Mutex{}
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
for _, nodeID := range msg.NodeIDs {
|
||||
wg.Add(1)
|
||||
go func(nodeID string) {
|
||||
defer wg.Done()
|
||||
result, err := agent.GetStatusForPeer(nodeID)
|
||||
if err != nil {
|
||||
log.Error("Unable to get status for node %s: %s", nodeID, err)
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
log.Debug("No status for node %s", nodeID)
|
||||
return
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
results[nodeID] = result
|
||||
mu.Unlock()
|
||||
}(nodeID)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// Send the results back to the Headplane master
|
||||
log.Debug("Sending status back to master: %v", results)
|
||||
log.Msg(&SendMessage{
|
||||
Type: "status",
|
||||
Data: results,
|
||||
log.Info("Opening SSH PTY for session %s to %s@%s:%d", sshPayload.SessionId, sshPayload.Username, sshPayload.Hostname, sshPayload.Port)
|
||||
sshutil.OpenSshPty(agent, sshutil.SshConnectParams{
|
||||
Hostname: sshPayload.Hostname,
|
||||
Port: sshPayload.Port,
|
||||
Username: sshPayload.Username,
|
||||
Id: sshPayload.SessionId,
|
||||
})
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatal("Error reading from stdin: %s", err)
|
||||
}
|
||||
|
||||
|
||||
// var msg RecvMessage
|
||||
// err := json.Unmarshal(line, &msg)
|
||||
// if err != nil {
|
||||
// var cborMsg CborMessage
|
||||
// dec := cbor.NewDecoder(bytes.NewReader(line))
|
||||
// err := dec.Decode(&cborMsg)
|
||||
|
||||
// if err == nil {
|
||||
// log.Info("Unmarshalled CBOR message: %s", cborMsg)
|
||||
// var sshPayload SSHConnect
|
||||
// err = cbor.Unmarshal(cborMsg.Payload, &sshPayload)
|
||||
// sshutil.OpenSshPty(agent, sshutil.SshConnectParams{
|
||||
// Hostname: sshPayload.Hostname,
|
||||
// Port: sshPayload.Port,
|
||||
// Username: sshPayload.Username,
|
||||
// Id: sshPayload.SessionId,
|
||||
// })
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
// log.Error("Unable to unmarshal message: %s", err)
|
||||
// log.Debug("Full Error: %v", err)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// log.Debug("Recieved message from master: %v", line)
|
||||
|
||||
// if len(msg.NodeIDs) == 0 {
|
||||
// log.Debug("Message recieved had no node IDs")
|
||||
// log.Debug("Full message: %s", line)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// // Accumulate the results since we invoke via gofunc
|
||||
// results := make(map[string]*tailcfg.HostinfoView)
|
||||
// mu := sync.Mutex{}
|
||||
// wg := sync.WaitGroup{}
|
||||
|
||||
// for _, nodeID := range msg.NodeIDs {
|
||||
// wg.Add(1)
|
||||
// go func(nodeID string) {
|
||||
// defer wg.Done()
|
||||
// result, err := agent.GetStatusForPeer(nodeID)
|
||||
// if err != nil {
|
||||
// log.Error("Unable to get status for node %s: %s", nodeID, err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// if result == nil {
|
||||
// log.Debug("No status for node %s", nodeID)
|
||||
// return
|
||||
// }
|
||||
|
||||
// mu.Lock()
|
||||
// results[nodeID] = result
|
||||
// mu.Unlock()
|
||||
// }(nodeID)
|
||||
// }
|
||||
|
||||
// wg.Wait()
|
||||
|
||||
// // Send the results back to the Headplane master
|
||||
// log.Debug("Sending status back to master: %v", results)
|
||||
// log.Msg(&SendMessage{
|
||||
// Type: "status",
|
||||
// Data: results,
|
||||
// })
|
||||
// }
|
||||
|
||||
// if err := scanner.Err(); err != nil {
|
||||
// log.Fatal("Error reading from stdin: %s", err)
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user