- Windows-Dienst (golang.org/x/sys/windows/svc) - Collector: CPU (GetSystemTimes), RAM (GlobalMemoryStatusEx), Disks (GetDiskFreeSpaceEx) - WS-Client: gleiche Verbindungslogik wie Linux/BSD Agent - Commands: exec, winget list/install/upgrade/upgrade-all, Windows Updates check/install, reboot - install.ps1: automatische Service-Installation mit Parametern - make agent-windows VERSION=x.x.x
130 lines
2.7 KiB
Go
130 lines
2.7 KiB
Go
package ws
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
type Client struct {
|
|
backendURL string
|
|
agentID string
|
|
apiKey string
|
|
insecure bool
|
|
conn *websocket.Conn
|
|
handler *Handler
|
|
reconnectWait time.Duration
|
|
}
|
|
|
|
func NewClient(backendURL, agentID, apiKey string, insecure bool, handler *Handler) *Client {
|
|
return &Client{
|
|
backendURL: backendURL,
|
|
agentID: agentID,
|
|
apiKey: apiKey,
|
|
insecure: insecure,
|
|
handler: handler,
|
|
reconnectWait: 5 * time.Second,
|
|
}
|
|
}
|
|
|
|
func (c *Client) Run(stop <-chan struct{}) {
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
}
|
|
|
|
if err := c.connect(stop); err != nil {
|
|
log.Printf("WS-Verbindung fehlgeschlagen: %v — erneuter Versuch in %v", err, c.reconnectWait)
|
|
select {
|
|
case <-stop:
|
|
return
|
|
case <-time.After(c.reconnectWait):
|
|
}
|
|
if c.reconnectWait < 60*time.Second {
|
|
c.reconnectWait *= 2
|
|
}
|
|
} else {
|
|
c.reconnectWait = 5 * time.Second
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Client) connect(stop <-chan struct{}) error {
|
|
u, err := url.Parse(c.backendURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch u.Scheme {
|
|
case "https":
|
|
u.Scheme = "wss"
|
|
case "http":
|
|
u.Scheme = "ws"
|
|
}
|
|
u.Path = "/api/v1/agent/ws"
|
|
q := u.Query()
|
|
q.Set("agent_id", c.agentID)
|
|
q.Set("api_key", c.apiKey)
|
|
u.RawQuery = q.Encode()
|
|
|
|
dialer := websocket.DefaultDialer
|
|
if c.insecure {
|
|
dialer = &websocket.Dialer{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
HandshakeTimeout: 10 * time.Second,
|
|
}
|
|
}
|
|
|
|
conn, _, err := dialer.Dial(u.String(), nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Dial fehlgeschlagen: %w", err)
|
|
}
|
|
defer conn.Close()
|
|
c.conn = conn
|
|
log.Printf("WS verbunden mit %s", c.backendURL)
|
|
|
|
conn.SetReadDeadline(time.Now().Add(70 * time.Second))
|
|
conn.SetPongHandler(func(string) error {
|
|
conn.SetReadDeadline(time.Now().Add(70 * time.Second))
|
|
return nil
|
|
})
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
for {
|
|
_, msg, err := conn.ReadMessage()
|
|
if err != nil {
|
|
if !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
|
|
log.Printf("WS Read-Fehler: %v", err)
|
|
}
|
|
return
|
|
}
|
|
if resp := c.handler.Handle(msg); resp != nil {
|
|
conn.WriteMessage(websocket.TextMessage, resp)
|
|
}
|
|
}
|
|
}()
|
|
|
|
select {
|
|
case <-stop:
|
|
conn.WriteMessage(websocket.CloseMessage,
|
|
websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
|
return nil
|
|
case <-done:
|
|
return fmt.Errorf("Verbindung getrennt")
|
|
}
|
|
}
|
|
|
|
func (c *Client) SendMessage(data []byte) error {
|
|
if c.conn == nil {
|
|
return fmt.Errorf("nicht verbunden")
|
|
}
|
|
return c.conn.WriteMessage(websocket.TextMessage, data)
|
|
}
|