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:
rcourtman 2025-10-18 23:17:38 +00:00
parent 77b4ccf592
commit 8595b4c001
3 changed files with 94 additions and 2 deletions

View file

@ -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"

View file

@ -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

View file

@ -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})
}