diff --git a/cmd/pulse-host-agent/service_stub.go b/cmd/pulse-host-agent/service_stub.go index 2eb0198..8573a5a 100644 --- a/cmd/pulse-host-agent/service_stub.go +++ b/cmd/pulse-host-agent/service_stub.go @@ -12,8 +12,3 @@ import ( func runAsWindowsService(cfg hostagent.Config, logger zerolog.Logger) error { return nil } - -// runServiceDebug is a no-op on non-Windows platforms -func runServiceDebug(cfg hostagent.Config, logger zerolog.Logger) error { - return nil -} diff --git a/cmd/pulse-host-agent/service_windows.go b/cmd/pulse-host-agent/service_windows.go index 7756e2a..bc1b2a3 100644 --- a/cmd/pulse-host-agent/service_windows.go +++ b/cmd/pulse-host-agent/service_windows.go @@ -11,7 +11,6 @@ import ( "github.com/rcourtman/pulse-go-rewrite/internal/hostagent" "github.com/rs/zerolog" "golang.org/x/sys/windows/svc" - "golang.org/x/sys/windows/svc/debug" "golang.org/x/sys/windows/svc/eventlog" ) @@ -158,11 +157,3 @@ func runAsWindowsService(cfg hostagent.Config, logger zerolog.Logger) error { return nil } - -func runServiceDebug(cfg hostagent.Config, logger zerolog.Logger) error { - ws := &windowsService{ - cfg: cfg, - logger: logger, - } - return debug.Run("PulseHostAgent", ws) -} diff --git a/cmd/pulse-sensor-proxy/config_cmd.go b/cmd/pulse-sensor-proxy/config_cmd.go index d525a1a..1f39c91 100644 --- a/cmd/pulse-sensor-proxy/config_cmd.go +++ b/cmd/pulse-sensor-proxy/config_cmd.go @@ -338,36 +338,6 @@ func updateConfigMap(path string, updateFn func(map[string]interface{}) error) e }) } -// validateConfigFile parses and validates the main config file -func validateConfigFile(path string) error { - data, err := os.ReadFile(path) - if err != nil { - return fmt.Errorf("failed to read config file: %w", err) - } - - // Check for duplicate allowed_nodes blocks (the issue we're fixing) - sanitized, cleanData := sanitizeDuplicateAllowedNodesBlocks("", data) - if sanitized { - return fmt.Errorf("config contains duplicate allowed_nodes blocks (would auto-fix on service start)") - } - - // Parse YAML - cfg := &Config{} - if err := yaml.Unmarshal(cleanData, cfg); err != nil { - return fmt.Errorf("failed to parse config YAML: %w", err) - } - - // Validate required fields - if cfg.ReadTimeout <= 0 { - return fmt.Errorf("read_timeout must be positive") - } - if cfg.WriteTimeout <= 0 { - return fmt.Errorf("write_timeout must be positive") - } - - return nil -} - // validateAllowedNodesFile parses and validates the allowed_nodes.yaml file func validateAllowedNodesFile(path string) error { data, err := os.ReadFile(path) diff --git a/cmd/pulse-sensor-proxy/main.go b/cmd/pulse-sensor-proxy/main.go index 794829e..7fec79f 100644 --- a/cmd/pulse-sensor-proxy/main.go +++ b/cmd/pulse-sensor-proxy/main.go @@ -966,15 +966,6 @@ func (p *Proxy) handleConnection(conn net.Conn) { p.metrics.rpcLatency.WithLabelValues(req.Method).Observe(time.Since(startTime).Seconds()) } -// sendError sends an error response (legacy function) -func (p *Proxy) sendError(conn net.Conn, message string) { - resp := RPCResponse{ - Success: false, - Error: message, - } - p.sendResponse(conn, resp, p.writeTimeout) -} - // sendErrorV2 sends an error response with correlation ID func (p *Proxy) sendErrorV2(conn net.Conn, message, correlationID string) { resp := RPCResponse{ @@ -1013,27 +1004,6 @@ func (p *Proxy) sendResponse(conn net.Conn, resp RPCResponse, writeTimeout time. } } -// handleGetStatus returns proxy status -func (p *Proxy) handleGetStatus(req RPCRequest) RPCResponse { - pubKeyPath := filepath.Join(p.sshKeyPath, "id_ed25519.pub") - pubKey, err := os.ReadFile(pubKeyPath) - if err != nil { - return RPCResponse{ - Success: false, - Error: fmt.Sprintf("failed to read public key: %v", err), - } - } - - return RPCResponse{ - Success: true, - Data: map[string]interface{}{ - "version": Version, - "public_key": string(pubKey), - "ssh_dir": p.sshKeyPath, - }, - } -} - // ensureSSHKeypair generates SSH keypair if it doesn't exist func (p *Proxy) ensureSSHKeypair() error { privKeyPath := filepath.Join(p.sshKeyPath, "id_ed25519") @@ -1059,158 +1029,7 @@ func (p *Proxy) ensureSSHKeypair() error { return nil } -// handleEnsureClusterKeys discovers cluster nodes and pushes SSH keys -func (p *Proxy) handleEnsureClusterKeys(req RPCRequest) RPCResponse { - // Check if we're on a Proxmox host - if !isProxmoxHost() { - return RPCResponse{ - Success: false, - Error: "not running on Proxmox host - cannot discover cluster", - } - } - - // Discover cluster nodes - nodes, err := discoverClusterNodes() - if err != nil { - return RPCResponse{ - Success: false, - Error: fmt.Sprintf("failed to discover cluster: %v", err), - } - } - - log.Info().Strs("nodes", nodes).Msg("Discovered cluster nodes") - - // Push SSH key to each node - results := make(map[string]interface{}) - successCount := 0 - for _, node := range nodes { - log.Info().Str("node", node).Msg("Pushing SSH key to node") - if err := p.pushSSHKey(node); err != nil { - log.Error().Err(err).Str("node", node).Msg("Failed to push SSH key") - results[node] = map[string]interface{}{ - "success": false, - "error": err.Error(), - } - } else { - log.Info().Str("node", node).Msg("SSH key pushed successfully") - results[node] = map[string]interface{}{ - "success": true, - } - successCount++ - } - } - - return RPCResponse{ - Success: true, - Data: map[string]interface{}{ - "nodes": nodes, - "results": results, - "success_count": successCount, - "total_count": len(nodes), - }, - } -} - -// handleRegisterNodes returns discovered nodes -func (p *Proxy) handleRegisterNodes(req RPCRequest) RPCResponse { - // Check if we're on a Proxmox host - if !isProxmoxHost() { - return RPCResponse{ - Success: false, - Error: "not running on Proxmox host", - } - } - - // Discover cluster nodes - nodes, err := discoverClusterNodes() - if err != nil { - return RPCResponse{ - Success: false, - Error: fmt.Sprintf("failed to discover nodes: %v", err), - } - } - - // Test SSH connectivity to each node - nodeStatus := make([]map[string]interface{}, 0, len(nodes)) - for _, node := range nodes { - // Validate node name to prevent SSH command injection - node = strings.TrimSpace(node) - if err := validateNodeName(node); err != nil { - log.Warn().Str("node", node).Msg("Invalid node name format from cluster discovery") - continue - } - - status := map[string]interface{}{ - "name": node, - } - - if err := p.testSSHConnection(node); err != nil { - status["ssh_ready"] = false - status["error"] = err.Error() - } else { - status["ssh_ready"] = true - } - - nodeStatus = append(nodeStatus, status) - } - - return RPCResponse{ - Success: true, - Data: map[string]interface{}{ - "nodes": nodeStatus, - }, - } -} - -// handleGetTemperature fetches temperature data from a node via SSH -func (p *Proxy) handleGetTemperature(req RPCRequest) RPCResponse { - // Extract node parameter - nodeParam, ok := req.Params["node"] - if !ok { - return RPCResponse{ - Success: false, - Error: "missing 'node' parameter", - } - } - - node, ok := nodeParam.(string) - if !ok { - return RPCResponse{ - Success: false, - Error: "'node' parameter must be a string", - } - } - - // Validate node name to prevent SSH command injection - node = strings.TrimSpace(node) - if err := validateNodeName(node); err != nil { - return RPCResponse{ - Success: false, - Error: "invalid node name format", - } - } - - // Fetch temperature data with timeout - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - tempData, err := p.getTemperatureViaSSH(ctx, node) - if err != nil { - return RPCResponse{ - Success: false, - Error: fmt.Sprintf("failed to get temperatures: %v", err), - } - } - - return RPCResponse{ - Success: true, - Data: map[string]interface{}{ - "node": node, - "temperature": tempData, - }, - } -} - -// New V2 handlers with context and structured logging +// V2 handlers with context and structured logging // handleGetStatusV2 returns proxy status with context support func (p *Proxy) handleGetStatusV2(ctx context.Context, req *RPCRequest, logger zerolog.Logger) (interface{}, error) { diff --git a/cmd/pulse-sensor-proxy/ssh.go b/cmd/pulse-sensor-proxy/ssh.go index 340e17a..b9bfbaf 100644 --- a/cmd/pulse-sensor-proxy/ssh.go +++ b/cmd/pulse-sensor-proxy/ssh.go @@ -327,11 +327,6 @@ func loadProxmoxHostKeys(host string) ([][]byte, error) { return entries, nil } -// getPublicKey reads the SSH public key from the default directory -func (p *Proxy) getPublicKey() (string, error) { - return p.getPublicKeyFrom(p.sshKeyPath) -} - // getPublicKeyFrom reads the SSH public key from a specific directory func (p *Proxy) getPublicKeyFrom(keyDir string) (string, error) { pubKeyPath := filepath.Join(keyDir, "id_ed25519.pub") @@ -527,12 +522,6 @@ func (p *Proxy) pushSSHKeyFrom(nodeHost, keyDir string) error { return nil } -// pushSSHKey adds the proxy's public key to a node's authorized_keys with IP restrictions -// Automatically upgrades old keys without from= restrictions -func (p *Proxy) pushSSHKey(nodeHost string) error { - return p.pushSSHKeyFrom(nodeHost, p.sshKeyPath) -} - // testSSHConnection verifies SSH connectivity to a node func (p *Proxy) testSSHConnection(nodeHost string) error { startTime := time.Now() diff --git a/cmd/pulse-sensor-proxy/validation.go b/cmd/pulse-sensor-proxy/validation.go index 6cb3ca0..8292623 100644 --- a/cmd/pulse-sensor-proxy/validation.go +++ b/cmd/pulse-sensor-proxy/validation.go @@ -591,21 +591,6 @@ func (v *nodeValidator) getClusterMembers(ctx context.Context) (map[string]struc return result, nil } -func (v *nodeValidator) ipAllowed(ip net.IP) bool { - if ip == nil { - return false - } - if _, ok := v.allowHosts[ip.String()]; ok { - return true - } - for _, network := range v.allowCIDRs { - if network.Contains(ip) { - return true - } - } - return false -} - func ipAllowed(ip net.IP, hosts map[string]struct{}, cidrs []*net.IPNet) bool { if ip == nil { return false