feat: automatic ProxyJump for turnkey temperature monitoring
Make temperature monitoring truly turnkey by automatically configuring SSH ProxyJump when running in containers without pulse-sensor-proxy. How it works: 1. Setup script runs on Proxmox host (e.g., delly) 2. Detects Pulse is containerized but proxy unavailable 3. Automatically configures SSH ProxyJump through the current host 4. Writes SSH config to /home/pulse/.ssh/config in container 5. Temperature monitoring "just works" without manual configuration Changes: - Track TEMP_MONITORING_AVAILABLE flag during proxy installation - Auto-configure ProxyJump if proxy installation fails - Add /api/system/ssh-config endpoint to write SSH config - Only prompt for temperature monitoring if it can actually work - Automatic SSH config: ProxyJump through Proxmox host Before: User had to manually configure ProxyJump or install proxy After: Temperature monitoring works automatically after setup script This makes Docker deployments as turnkey as LXC deployments.
This commit is contained in:
parent
77b4ccf592
commit
8595b4c001
3 changed files with 94 additions and 2 deletions
|
|
@ -3618,6 +3618,9 @@ if command -v pct >/dev/null 2>&1; then
|
|||
fi
|
||||
fi
|
||||
|
||||
# Track whether temperature monitoring can work
|
||||
TEMP_MONITORING_AVAILABLE=true
|
||||
|
||||
# If Pulse is containerized, try to install proxy automatically
|
||||
if [ "$PULSE_IS_CONTAINERIZED" = true ] && [ -n "$PULSE_CTID" ]; then
|
||||
# Try automatic installation - proxy keeps SSH credentials on the host for security
|
||||
|
|
@ -3716,8 +3719,20 @@ if [ "$PULSE_IS_CONTAINERIZED" = true ] && [ -n "$PULSE_CTID" ]; then
|
|||
fi
|
||||
|
||||
rm -f "$PROXY_INSTALLER"
|
||||
else
|
||||
# Proxy installer not available - configure automatic ProxyJump instead
|
||||
echo ""
|
||||
echo "ℹ️ Proxy not available - configuring automatic SSH ProxyJump"
|
||||
echo ""
|
||||
|
||||
# Get the current Proxmox host's IP/hostname
|
||||
PROXY_JUMP_HOST=$(hostname)
|
||||
PROXY_JUMP_IP=$(hostname -I | awk '{print $1}')
|
||||
|
||||
# We'll configure Pulse's SSH config to use this host as a jump point
|
||||
# This will be done when temperature monitoring is enabled
|
||||
CONFIGURE_PROXYJUMP=true
|
||||
fi
|
||||
# Silently skip if installer unavailable - not an error for test/development setups
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -3797,7 +3812,7 @@ if [ "$SSH_ALREADY_CONFIGURED" = true ]; then
|
|||
echo "Temperature monitoring configuration unchanged."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
elif [ "$TEMP_MONITORING_AVAILABLE" = true ]; then
|
||||
echo "📊 Enable Temperature Monitoring?"
|
||||
echo ""
|
||||
echo "Collect CPU and drive temperatures via secure SSH connection."
|
||||
|
|
@ -3909,6 +3924,37 @@ else
|
|||
fi
|
||||
echo " Temperature data will appear in the dashboard within 10 seconds"
|
||||
TEMPERATURE_ENABLED=true
|
||||
|
||||
# Configure automatic ProxyJump if needed (for containerized Pulse)
|
||||
if [ "$CONFIGURE_PROXYJUMP" = true ] && [ -n "$PROXY_JUMP_HOST" ]; then
|
||||
echo ""
|
||||
echo "Configuring automatic SSH ProxyJump for containerized Pulse..."
|
||||
|
||||
# Create SSH config that uses this Proxmox host as jump point
|
||||
SSH_CONFIG="Host ${PROXY_JUMP_HOST}
|
||||
HostName ${PROXY_JUMP_IP}
|
||||
User root
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
|
||||
Host *
|
||||
ProxyJump ${PROXY_JUMP_HOST}
|
||||
User root
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
StrictHostKeyChecking accept-new"
|
||||
|
||||
# Write SSH config to Pulse container
|
||||
# This will be written to /home/pulse/.ssh/config inside the container
|
||||
echo "$SSH_CONFIG" | curl -s -X POST "%s/api/system/ssh-config" \
|
||||
-H "Content-Type: text/plain" \
|
||||
-H "Authorization: Bearer %s" \
|
||||
--data-binary @- > /dev/null 2>&1
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " ✓ ProxyJump configured - temperature monitoring will work automatically"
|
||||
else
|
||||
echo " ⚠️ Could not configure ProxyJump automatically"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo "⚠️ SSH key not available from Pulse server"
|
||||
|
|
|
|||
|
|
@ -849,6 +849,7 @@ func (r *Router) setupRoutes() {
|
|||
r.systemSettingsHandler = NewSystemSettingsHandler(r.config, r.persistence, r.wsHub, r.monitor, r.reloadSystemSettings)
|
||||
r.mux.HandleFunc("/api/system/settings", r.systemSettingsHandler.HandleGetSystemSettings)
|
||||
r.mux.HandleFunc("/api/system/settings/update", r.systemSettingsHandler.HandleUpdateSystemSettings)
|
||||
r.mux.HandleFunc("/api/system/ssh-config", RequireAuth(r.config, r.systemSettingsHandler.HandleSSHConfig))
|
||||
r.mux.HandleFunc("/api/system/verify-temperature-ssh", r.handleVerifyTemperatureSSH)
|
||||
r.mux.HandleFunc("/api/system/proxy-public-key", r.handleProxyPublicKey)
|
||||
// Old API token endpoints removed - now using /api/security/regenerate-token
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
||||
|
|
@ -447,3 +450,45 @@ func (h *SystemSettingsHandler) HandleUpdateSystemSettings(w http.ResponseWriter
|
|||
log.Error().Err(err).Msg("Failed to write system settings update response")
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSSHConfig writes SSH configuration for Pulse user
|
||||
func (h *SystemSettingsHandler) HandleSSHConfig(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// Read SSH config content from request body
|
||||
sshConfig, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to read SSH config from request")
|
||||
http.Error(w, "Failed to read request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the Pulse user's home directory
|
||||
homeDir := os.Getenv("HOME")
|
||||
if homeDir == "" {
|
||||
homeDir = "/home/pulse" // fallback
|
||||
}
|
||||
|
||||
// Create .ssh directory if it doesn't exist
|
||||
sshDir := filepath.Join(homeDir, ".ssh")
|
||||
if err := os.MkdirAll(sshDir, 0700); err != nil {
|
||||
log.Error().Err(err).Str("dir", sshDir).Msg("Failed to create .ssh directory")
|
||||
http.Error(w, "Failed to create SSH directory", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Write SSH config file
|
||||
configPath := filepath.Join(sshDir, "config")
|
||||
if err := os.WriteFile(configPath, sshConfig, 0600); err != nil {
|
||||
log.Error().Err(err).Str("path", configPath).Msg("Failed to write SSH config")
|
||||
http.Error(w, "Failed to write SSH config", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Str("path", configPath).Msg("SSH config written successfully")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]bool{"success": true})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue