From fa68e191f9f3b56eb81e66cb26efce88cc9ad44e Mon Sep 17 00:00:00 2001 From: cynfo3000 Date: Sat, 28 Feb 2026 07:52:38 +0100 Subject: [PATCH] Fix Tunnel: ID-Sync Backend<->Agent, bidirektionaler Proxy, API-Key via Query-Param - Backend gibt tunnel_id vor, Agent uebernimmt sie - Proxy-Verbindung bidirektional: Daten vom Agent zurueck an TCP-Client - Middleware akzeptiert api_key als Query-Parameter (fuer WebSocket) - Getestet: OPNsense WebUI erreichbar ueber Tunnel --- agent/ws/handler.go | 7 ++++-- agent/ws/tunnel.go | 17 +++++++++------ backend/api/middleware.go | 4 ++++ backend/ws/tunnel.go | 45 ++++++++++++++++++++++++++++----------- 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/agent/ws/handler.go b/agent/ws/handler.go index 20f23f3..29dd8f5 100644 --- a/agent/ws/handler.go +++ b/agent/ws/handler.go @@ -116,8 +116,11 @@ func (h *Handler) handleTunnelOpen(msg Message) Message { } targetPort := int(targetPortFloat) - // Tunnel öffnen - tunnelID, err := h.client.tunnelManager.OpenTunnel(targetHost, targetPort) + // Tunnel-ID vom Backend uebernehmen (falls vorhanden) + backendTunnelID, _ := msg.Params["tunnel_id"].(string) + + // Tunnel oeffnen + tunnelID, err := h.client.tunnelManager.OpenTunnel(targetHost, targetPort, backendTunnelID) if err != nil { response.Status = "error" response.Error = fmt.Sprintf("Tunnel öffnen fehlgeschlagen: %v", err) diff --git a/agent/ws/tunnel.go b/agent/ws/tunnel.go index 3404eb8..f321e86 100644 --- a/agent/ws/tunnel.go +++ b/agent/ws/tunnel.go @@ -33,9 +33,14 @@ func NewTunnelManager(client *Client) *TunnelManager { } } -func (tm *TunnelManager) OpenTunnel(targetHost string, targetPort int) (string, error) { - // Eindeutige Tunnel-ID generieren - tunnelID := tm.generateTunnelID() +func (tm *TunnelManager) OpenTunnel(targetHost string, targetPort int, tunnelID ...string) (string, error) { + // Tunnel-ID vom Backend uebernehmen oder selbst generieren + id := "" + if len(tunnelID) > 0 && tunnelID[0] != "" { + id = tunnelID[0] + } else { + id = tm.generateTunnelID() + } // Verbindung zum Ziel aufbauen target := fmt.Sprintf("%s:%d", targetHost, targetPort) @@ -46,7 +51,7 @@ func (tm *TunnelManager) OpenTunnel(targetHost string, targetPort int) (string, // Tunnel erstellen tunnel := &Tunnel{ - ID: tunnelID, + ID: id, TargetHost: targetHost, TargetPort: targetPort, Conn: conn, @@ -56,13 +61,13 @@ func (tm *TunnelManager) OpenTunnel(targetHost string, targetPort int) (string, // Tunnel registrieren tm.mutex.Lock() - tm.tunnels[tunnelID] = tunnel + tm.tunnels[id] = tunnel tm.mutex.Unlock() // Data-Forwarding starten go tm.forwardData(tunnel) - return tunnelID, nil + return id, nil } func (tm *TunnelManager) CloseTunnel(tunnelID string) error { diff --git a/backend/api/middleware.go b/backend/api/middleware.go index aa3b770..11a880b 100644 --- a/backend/api/middleware.go +++ b/backend/api/middleware.go @@ -15,6 +15,10 @@ func APIKeyAuth(validKeys []string, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { key := r.Header.Get("X-API-Key") + // Fallback: Query-Parameter (fuer WebSocket-Verbindungen) + if key == "" { + key = r.URL.Query().Get("api_key") + } if key == "" || !keySet[key] { http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized) return diff --git a/backend/ws/tunnel.go b/backend/ws/tunnel.go index f9c9a60..4ec9ac0 100644 --- a/backend/ws/tunnel.go +++ b/backend/ws/tunnel.go @@ -21,6 +21,9 @@ type BackendTunnel struct { Listener net.Listener Active bool CreatedAt time.Time + // Aktive Proxy-Verbindungen (fuer Rueckkanal) + proxyConn net.Conn + proxyMutex sync.Mutex } type TunnelManager struct { @@ -86,12 +89,13 @@ func (tm *TunnelManager) OpenTunnel(agentID, targetHost string, targetPort int) // Proxy-Server starten go tm.runTunnelProxy(tunnel) - // Command an Agent senden + // Command an Agent senden — tunnel_id vorgeben damit Backend und Agent dieselbe ID nutzen cmd := map[string]interface{}{ "type": "command", "id": tm.generateTunnelID(), "command": "tunnel_open", "params": map[string]interface{}{ + "tunnel_id": tunnelID, "target_host": targetHost, "target_port": targetPort, }, @@ -220,28 +224,38 @@ func (tm *TunnelManager) handleProxyConnection(tunnel *BackendTunnel, conn net.C log.Printf("Tunnel %s: Proxy-Verbindung von %s", tunnel.ID, conn.RemoteAddr()) - // Buffer für TCP-Data + // Aktive Proxy-Verbindung registrieren (fuer Rueckkanal vom Agent) + tunnel.proxyMutex.Lock() + tunnel.proxyConn = conn + tunnel.proxyMutex.Unlock() + + defer func() { + tunnel.proxyMutex.Lock() + tunnel.proxyConn = nil + tunnel.proxyMutex.Unlock() + log.Printf("Tunnel %s: Proxy-Verbindung beendet", tunnel.ID) + }() + + // Buffer fuer TCP-Data buffer := make([]byte, 32768) - for { + for tunnel.Active { n, err := conn.Read(buffer) if err != nil { - if err.Error() != "EOF" { + if err.Error() != "EOF" && tunnel.Active { log.Printf("Tunnel %s: Proxy-Read-Fehler: %v", tunnel.ID, err) } break } if n > 0 { - // Daten über WebSocket an Agent weiterleiten + // Daten ueber WebSocket an Agent weiterleiten if err := tm.sendTunnelData(tunnel.ID, buffer[:n]); err != nil { log.Printf("Tunnel %s: Weiterleitung fehlgeschlagen: %v", tunnel.ID, err) break } } } - - log.Printf("Tunnel %s: Proxy-Verbindung beendet", tunnel.ID) } func (tm *TunnelManager) sendTunnelData(tunnelID string, data []byte) error { @@ -284,12 +298,19 @@ func (tm *TunnelManager) ProcessBinaryMessage(agentID string, data []byte) error return fmt.Errorf("Tunnel %s nicht gefunden oder inaktiv", tunnelID) } - // Hier würden wir die Daten an aktive Proxy-Verbindungen weiterleiten - // Da das komplexer ist, implementieren wir erstmal nur das Logging - log.Printf("Tunnel %s: %d Bytes von Agent erhalten", tunnelID, len(payload)) + // Daten an aktive Proxy-Verbindung weiterleiten + tunnel.proxyMutex.Lock() + conn := tunnel.proxyConn + tunnel.proxyMutex.Unlock() - // TODO: Implement actual forwarding to active proxy connections - // Das würde einen Connection-Pool pro Tunnel erfordern + if conn == nil { + return fmt.Errorf("Tunnel %s: keine aktive Proxy-Verbindung", tunnelID) + } + + _, writeErr := conn.Write(payload) + if writeErr != nil { + return fmt.Errorf("Tunnel %s: Proxy-Write fehlgeschlagen: %w", tunnelID, writeErr) + } return nil }