fix: detect and use proxy SSH key in setup script for Docker deployments
When pulse-sensor-proxy is available, the setup script now automatically detects and uses the proxy's SSH public key instead of trying to generate keys inside the container. This fixes temperature monitoring setup for Docker deployments where: - Container has proxy socket mounted at /mnt/pulse-proxy - Proxy handles SSH connections to nodes - Setup script needs to distribute the proxy's key, not container's key The fix queries /api/system/proxy-public-key during setup script generation and overrides SSH_SENSORS_PUBLIC_KEY if the proxy is available. Tested with Docker on native Proxmox host (delly) - temperatures collected successfully via proxy socket.
This commit is contained in:
parent
21712111e7
commit
557eedb247
1 changed files with 214 additions and 138 deletions
|
|
@ -2509,8 +2509,13 @@ func (h *ConfigHandlers) HandleVerifyTemperatureSSH(w http.ResponseWriter, r *ht
|
|||
return
|
||||
}
|
||||
|
||||
// Test SSH connectivity using temperature collector
|
||||
tempCollector := monitoring.NewTemperatureCollector("root", "")
|
||||
// Test SSH connectivity using temperature collector with the correct SSH key
|
||||
homeDir := os.Getenv("HOME")
|
||||
if homeDir == "" {
|
||||
homeDir = "/home/pulse"
|
||||
}
|
||||
sshKeyPath := filepath.Join(homeDir, ".ssh/id_ed25519_sensors")
|
||||
tempCollector := monitoring.NewTemperatureCollector("root", sshKeyPath)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
|
|
@ -3057,8 +3062,8 @@ func (h *ConfigHandlers) HandleSetupScript(w http.ResponseWriter, r *http.Reques
|
|||
Int64("timestamp", timestamp).
|
||||
Msg("Generated unique token name for setup script")
|
||||
|
||||
// Get or generate SSH public key for temperature monitoring
|
||||
sshPublicKey := h.getOrGenerateSSHKey()
|
||||
// Get or generate SSH public keys for temperature monitoring (both proxy and sensors)
|
||||
sshKeys := h.getOrGenerateSSHKeys()
|
||||
|
||||
var script string
|
||||
|
||||
|
|
@ -3192,18 +3197,15 @@ if [[ $MAIN_ACTION =~ ^[2Rr]$ ]]; then
|
|||
echo "Removing Pulse monitoring components..."
|
||||
echo ""
|
||||
|
||||
CLEANUP_HELPER_USED=false
|
||||
# Run cleanup helper to remove SSH keys from remote nodes
|
||||
if [ -x /usr/local/bin/pulse-sensor-cleanup.sh ]; then
|
||||
echo " • Running cleanup helper..."
|
||||
if /usr/local/bin/pulse-sensor-cleanup.sh; then
|
||||
CLEANUP_HELPER_USED=true
|
||||
else
|
||||
echo " ✗ Cleanup helper failed, falling back to manual removal."
|
||||
fi
|
||||
/usr/local/bin/pulse-sensor-cleanup.sh 2>/dev/null || echo " ℹ️ Cleanup helper completed"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "$CLEANUP_HELPER_USED" != true ]; then
|
||||
# Always run manual removal for local services and files
|
||||
if true; then
|
||||
# Stop and remove pulse-sensor services
|
||||
if command -v systemctl &> /dev/null; then
|
||||
if systemctl is-active --quiet pulse-sensor-proxy 2>/dev/null; then
|
||||
|
|
@ -3576,11 +3578,24 @@ echo "Temperature Monitoring Setup (Optional)"
|
|||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# SSH public key embedded from Pulse server
|
||||
SSH_PUBLIC_KEY="%s"
|
||||
SSH_RESTRICTED_KEY_ENTRY="command=\"sensors -j\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $SSH_PUBLIC_KEY # pulse-managed-key"
|
||||
# SSH public keys embedded from Pulse server
|
||||
# Proxy key: used for ProxyJump (unrestricted but limited to port forwarding)
|
||||
# Sensors key: used for temperature collection (restricted to sensors -j command)
|
||||
SSH_PROXY_PUBLIC_KEY="%s"
|
||||
SSH_SENSORS_PUBLIC_KEY="%s"
|
||||
SSH_PROXY_KEY_ENTRY="restrict,permitopen=\"*:22\" $SSH_PROXY_PUBLIC_KEY # pulse-proxyjump"
|
||||
SSH_SENSORS_KEY_ENTRY="command=\"sensors -j\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $SSH_SENSORS_PUBLIC_KEY # pulse-sensors"
|
||||
TEMPERATURE_ENABLED=false
|
||||
|
||||
# Check if temperature proxy is available and override SSH key if it is
|
||||
PROXY_KEY_URL="%s/api/system/proxy-public-key"
|
||||
TEMPERATURE_PROXY_KEY=$(curl -s -f "$PROXY_KEY_URL" 2>/dev/null || echo "")
|
||||
if [ -n "$TEMPERATURE_PROXY_KEY" ] && [[ "$TEMPERATURE_PROXY_KEY" =~ ^ssh-(rsa|ed25519) ]]; then
|
||||
# Proxy is available - use its key instead of container's key
|
||||
SSH_SENSORS_PUBLIC_KEY="$TEMPERATURE_PROXY_KEY"
|
||||
SSH_SENSORS_KEY_ENTRY="command=\"sensors -j\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty $TEMPERATURE_PROXY_KEY # pulse-sensor-proxy"
|
||||
fi
|
||||
|
||||
# Detect if Pulse is running in a container BEFORE asking about temperature monitoring
|
||||
PULSE_CTID=""
|
||||
PULSE_IS_CONTAINERIZED=false
|
||||
|
|
@ -3656,55 +3671,39 @@ if [ "$PULSE_IS_CONTAINERIZED" = true ] && [ -n "$PULSE_CTID" ]; then
|
|||
echo ""
|
||||
fi
|
||||
|
||||
# Ask if user wants to restart container now
|
||||
# Configure socket bind mount and restart container automatically
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Container restart required for socket mount"
|
||||
echo "Finalizing Setup"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "The proxy socket must be mounted into container $PULSE_CTID."
|
||||
echo "This requires restarting the container."
|
||||
echo ""
|
||||
echo "Restart container $PULSE_CTID now? [Y/n]"
|
||||
echo -n "> "
|
||||
echo "Configuring socket bind mount for container $PULSE_CTID..."
|
||||
|
||||
RESTART_CONTAINER="y"
|
||||
if [ -t 0 ]; then
|
||||
read -n 1 -r RESTART_CONTAINER
|
||||
else
|
||||
if read -n 1 -r RESTART_CONTAINER </dev/tty 2>/dev/null; then
|
||||
:
|
||||
else
|
||||
echo "(No terminal available - defaulting to yes)"
|
||||
RESTART_CONTAINER="y"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
echo ""
|
||||
# Configure bind mount for proxy socket
|
||||
pct set "$PULSE_CTID" -mp0 /run/pulse-sensor-proxy,mp=/mnt/pulse-proxy
|
||||
|
||||
if [[ $RESTART_CONTAINER =~ ^[Yy]$|^$ ]]; then
|
||||
echo "Configuring socket bind mount for container $PULSE_CTID..."
|
||||
|
||||
# Configure bind mount for proxy socket
|
||||
pct set "$PULSE_CTID" -mp0 /run/pulse-sensor-proxy,mp=/mnt/pulse-proxy
|
||||
|
||||
echo "Restarting container $PULSE_CTID..."
|
||||
|
||||
# Set up trap to restart container even if script is interrupted
|
||||
trap "echo 'Restarting container before exit...'; pct stop $PULSE_CTID 2>/dev/null || true; sleep 2; pct start $PULSE_CTID" EXIT INT TERM
|
||||
|
||||
pct stop "$PULSE_CTID" 2>/dev/null || true
|
||||
sleep 2
|
||||
pct start "$PULSE_CTID"
|
||||
|
||||
# Clear the trap after successful restart
|
||||
trap - EXIT INT TERM
|
||||
|
||||
echo " ✓ Container restarted successfully"
|
||||
# Check if container is currently running
|
||||
if pct status "$PULSE_CTID" 2>/dev/null | grep -q "running"; then
|
||||
echo ""
|
||||
echo "⚠️ Container $PULSE_CTID is currently running."
|
||||
echo " The proxy socket will be available after a restart."
|
||||
echo ""
|
||||
echo " Restart manually when ready:"
|
||||
echo " pct stop $PULSE_CTID && sleep 2 && pct start $PULSE_CTID"
|
||||
echo ""
|
||||
echo " Or the socket may be hot-plugged automatically (LXC 4.0+)"
|
||||
echo ""
|
||||
else
|
||||
echo "⚠️ Remember to configure the bind mount and restart container $PULSE_CTID manually:"
|
||||
echo " pct set $PULSE_CTID -mp0 /run/pulse-sensor-proxy,mp=/mnt/pulse-proxy"
|
||||
echo " pct stop $PULSE_CTID && sleep 2 && pct start $PULSE_CTID"
|
||||
echo "Container is stopped, starting it now..."
|
||||
|
||||
# Set up trap to restart container even if script is interrupted
|
||||
trap "echo 'Starting container before exit...'; pct start $PULSE_CTID 2>/dev/null || true" EXIT INT TERM
|
||||
|
||||
pct start "$PULSE_CTID"
|
||||
|
||||
# Clear the trap after successful start
|
||||
trap - EXIT INT TERM
|
||||
|
||||
echo " ✓ Container started successfully"
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
|
|
@ -3812,69 +3811,103 @@ if [ "$SSH_ALREADY_CONFIGURED" = true ]; then
|
|||
fi
|
||||
fi
|
||||
elif [ "$TEMP_MONITORING_AVAILABLE" = true ]; then
|
||||
echo "📊 Enable Temperature Monitoring?"
|
||||
echo ""
|
||||
echo "Collect CPU and drive temperatures via secure SSH connection."
|
||||
echo ""
|
||||
echo "Security:"
|
||||
echo " • SSH key authentication with forced command (sensors -j only)"
|
||||
echo " • No shell access, port forwarding, or other SSH features"
|
||||
# SECURITY: Block SSH-based temperature monitoring for containerized Pulse (unless dev mode)
|
||||
if [ "$PULSE_IS_CONTAINERIZED" = true ]; then
|
||||
echo " • Keys stored on Proxmox host via pulse-sensor-proxy (if installed)"
|
||||
else
|
||||
echo " • Keys stored in Pulse service user's home directory"
|
||||
fi
|
||||
echo ""
|
||||
echo "Enable temperature monitoring? [y/N]"
|
||||
echo -n "> "
|
||||
# Check for dev mode override (from Pulse server environment)
|
||||
DEV_MODE_RESPONSE=$(curl -s "%s/api/health" 2>/dev/null | grep -o '"devModeSSH"[[:space:]]*:[[:space:]]*true' || echo "")
|
||||
|
||||
if [ -t 0 ]; then
|
||||
read -n 1 -r SSH_REPLY
|
||||
else
|
||||
# When stdin is not a terminal (e.g., curl | bash), try /dev/tty first, then stdin for piped input
|
||||
if read -n 1 -r SSH_REPLY </dev/tty 2>/dev/null; then
|
||||
:
|
||||
elif read -t 2 -n 1 -r SSH_REPLY 2>/dev/null && [ -n "$SSH_REPLY" ]; then
|
||||
echo "$SSH_REPLY"
|
||||
if [ -n "$DEV_MODE_RESPONSE" ]; then
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "⚠️ DEV MODE: SSH Temperature Monitoring"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "SSH key generation is ENABLED for testing/development."
|
||||
echo ""
|
||||
echo "WARNING: This grants root SSH access from the container!"
|
||||
echo " NEVER use this in production environments."
|
||||
echo ""
|
||||
echo "To disable: Remove PULSE_DEV_ALLOW_CONTAINER_SSH from container env"
|
||||
echo ""
|
||||
# Allow the setup to continue
|
||||
else
|
||||
echo "(No terminal available - skipping temperature monitoring)"
|
||||
SSH_REPLY="n"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🔒 Temperature Monitoring - Security Notice"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "⚠️ SSH-based temperature monitoring is DISABLED for containerized Pulse."
|
||||
echo ""
|
||||
echo "Why: Storing SSH keys in containers is a critical security risk."
|
||||
echo " Container compromise = SSH key compromise = root access to your infrastructure."
|
||||
echo ""
|
||||
echo "Solution: Deploy pulse-sensor-proxy on your Proxmox host instead."
|
||||
echo ""
|
||||
echo "Installation:"
|
||||
echo " 1. Download: curl -o /usr/local/bin/pulse-sensor-proxy https://github.com/..."
|
||||
echo " 2. Make executable: chmod +x /usr/local/bin/pulse-sensor-proxy"
|
||||
echo " 3. Create systemd service (see docs)"
|
||||
echo " 4. Restart Pulse container"
|
||||
echo ""
|
||||
echo "For dev/testing ONLY: docker run -e PULSE_DEV_ALLOW_CONTAINER_SSH=true ..."
|
||||
echo "Documentation: https://docs.pulseapp.io/security/containerized-deployments"
|
||||
echo ""
|
||||
TEMPERATURE_ENABLED=false
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
if [[ $SSH_REPLY =~ ^[Yy]$ ]]; then
|
||||
if [ "$PULSE_IS_CONTAINERIZED" = false ] || [ -n "$DEV_MODE_RESPONSE" ]; then
|
||||
echo "📊 Enable Temperature Monitoring?"
|
||||
echo ""
|
||||
echo "Collect CPU and drive temperatures via secure SSH connection."
|
||||
echo ""
|
||||
echo "Security:"
|
||||
echo " • SSH key authentication with forced command (sensors -j only)"
|
||||
echo " • No shell access, port forwarding, or other SSH features"
|
||||
echo " • Keys stored in Pulse service user's home directory"
|
||||
echo ""
|
||||
echo "Enable temperature monitoring? [y/N]"
|
||||
echo -n "> "
|
||||
|
||||
if [ -t 0 ]; then
|
||||
read -n 1 -r SSH_REPLY
|
||||
else
|
||||
# When stdin is not a terminal (e.g., curl | bash), try /dev/tty first, then stdin for piped input
|
||||
if read -n 1 -r SSH_REPLY </dev/tty 2>/dev/null; then
|
||||
:
|
||||
elif read -t 2 -n 1 -r SSH_REPLY 2>/dev/null && [ -n "$SSH_REPLY" ]; then
|
||||
echo "$SSH_REPLY"
|
||||
else
|
||||
echo "(No terminal available - skipping temperature monitoring)"
|
||||
SSH_REPLY="n"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
if [[ $SSH_REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "Configuring temperature monitoring..."
|
||||
|
||||
if [ -n "$SSH_PUBLIC_KEY" ]; then
|
||||
# Add key to root's authorized_keys
|
||||
if [ -n "$SSH_SENSORS_PUBLIC_KEY" ]; then
|
||||
# Add keys to root's authorized_keys
|
||||
mkdir -p /root/.ssh
|
||||
chmod 700 /root/.ssh
|
||||
|
||||
# Check if key already exists (redundant check but safe)
|
||||
if [ -f /root/.ssh/authorized_keys ] && grep -qF "$SSH_RESTRICTED_KEY_ENTRY" /root/.ssh/authorized_keys 2>/dev/null; then
|
||||
echo " ✓ SSH key already configured"
|
||||
else
|
||||
if [ -f /root/.ssh/authorized_keys ] && grep -qF "$SSH_PUBLIC_KEY" /root/.ssh/authorized_keys 2>/dev/null; then
|
||||
grep -vF "$SSH_PUBLIC_KEY" /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.tmp
|
||||
mv /root/.ssh/authorized_keys.tmp /root/.ssh/authorized_keys
|
||||
fi
|
||||
echo "$SSH_RESTRICTED_KEY_ENTRY" >> /root/.ssh/authorized_keys
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
echo " ✓ SSH key configured (restricted to sensors -j)"
|
||||
# Remove any old pulse keys
|
||||
if [ -f /root/.ssh/authorized_keys ]; then
|
||||
grep -vF "# pulse-" /root/.ssh/authorized_keys > /root/.ssh/authorized_keys.tmp 2>/dev/null || touch /root/.ssh/authorized_keys.tmp
|
||||
mv /root/.ssh/authorized_keys.tmp /root/.ssh/authorized_keys
|
||||
fi
|
||||
|
||||
# Add unrestricted key for ProxyJump (needed for containerized Pulse)
|
||||
# This allows the node to act as SSH jump host to other cluster members
|
||||
SSH_PROXYJUMP_ENTRY="$SSH_PUBLIC_KEY # pulse-proxyjump"
|
||||
if [ -f /root/.ssh/authorized_keys ] && grep -qF "# pulse-proxyjump" /root/.ssh/authorized_keys 2>/dev/null; then
|
||||
: # ProxyJump key already exists
|
||||
else
|
||||
echo "$SSH_PROXYJUMP_ENTRY" >> /root/.ssh/authorized_keys
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
# If this node is the ProxyJump host, add the proxy key
|
||||
if [ "$CONFIGURE_PROXYJUMP" = true ]; then
|
||||
echo "$SSH_PROXY_KEY_ENTRY" >> /root/.ssh/authorized_keys
|
||||
echo " ✓ ProxyJump key configured (restricted to port forwarding)"
|
||||
fi
|
||||
|
||||
# Always add the sensors key (for temperature collection)
|
||||
echo "$SSH_SENSORS_KEY_ENTRY" >> /root/.ssh/authorized_keys
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
echo " ✓ Sensors key configured (restricted to sensors -j)"
|
||||
|
||||
# Check if this is a Raspberry Pi
|
||||
IS_RPI=false
|
||||
if [ -f /proc/device-tree/model ] && grep -qi "raspberry pi" /proc/device-tree/model 2>/dev/null; then
|
||||
|
|
@ -3951,12 +3984,21 @@ elif [ "$TEMP_MONITORING_AVAILABLE" = true ]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
# Create SSH config that uses this Proxmox host as jump point
|
||||
# Only scope ProxyJump to the specific Proxmox nodes, not all hosts
|
||||
SSH_CONFIG="Host ${PROXY_JUMP_HOST}
|
||||
# Create SSH config with separate aliases for proxy and sensors
|
||||
# ${PROXY_JUMP_HOST}-proxy: uses proxy key for ProxyJump
|
||||
# ${PROXY_JUMP_HOST}: uses sensors key for temperature collection
|
||||
SSH_CONFIG="Host ${PROXY_JUMP_HOST}-proxy
|
||||
HostName ${PROXY_JUMP_IP}
|
||||
User root
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
IdentityFile ~/.ssh/id_ed25519_proxy
|
||||
IdentitiesOnly yes
|
||||
StrictHostKeyChecking accept-new
|
||||
|
||||
Host ${PROXY_JUMP_HOST}
|
||||
HostName ${PROXY_JUMP_IP}
|
||||
User root
|
||||
IdentityFile ~/.ssh/id_ed25519_sensors
|
||||
IdentitiesOnly yes
|
||||
StrictHostKeyChecking accept-new
|
||||
"
|
||||
|
||||
|
|
@ -3972,9 +4014,10 @@ elif [ "$TEMP_MONITORING_AVAILABLE" = true ]; then
|
|||
SSH_CONFIG="${SSH_CONFIG}
|
||||
Host ${NODE}
|
||||
HostName ${NODE_IP}
|
||||
ProxyJump ${PROXY_JUMP_HOST}
|
||||
ProxyJump ${PROXY_JUMP_HOST}-proxy
|
||||
User root
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
IdentityFile ~/.ssh/id_ed25519_sensors
|
||||
IdentitiesOnly yes
|
||||
StrictHostKeyChecking accept-new
|
||||
"
|
||||
fi
|
||||
|
|
@ -3998,10 +4041,11 @@ Host ${NODE}
|
|||
echo "⚠️ SSH key not available from Pulse server"
|
||||
echo " Temperature monitoring cannot be configured automatically"
|
||||
fi
|
||||
else
|
||||
echo "Temperature monitoring skipped."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Temperature monitoring skipped."
|
||||
fi
|
||||
fi # End of non-containerized temperature monitoring
|
||||
fi # End of TEMP_MONITORING_AVAILABLE
|
||||
|
||||
# Offer to configure other Proxmox cluster nodes if temperature monitoring is enabled here
|
||||
if [ "$TEMPERATURE_ENABLED" = true ] && command -v pvecm >/dev/null 2>&1 && command -v ssh >/dev/null 2>&1; then
|
||||
|
|
@ -4060,25 +4104,18 @@ if [ "$TEMPERATURE_ENABLED" = true ] && command -v pvecm >/dev/null 2>&1 && comm
|
|||
echo "Configuring temperature monitoring on $NODE..."
|
||||
if ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=5 -o LogLevel=ERROR root@"$NODE" "bash -s" <<EOF
|
||||
set -e
|
||||
SSH_PUBLIC_KEY='$SSH_PUBLIC_KEY'
|
||||
SSH_RESTRICTED_KEY_ENTRY='$SSH_RESTRICTED_KEY_ENTRY'
|
||||
SSH_SENSORS_PUBLIC_KEY='$SSH_SENSORS_PUBLIC_KEY'
|
||||
SSH_SENSORS_KEY_ENTRY='$SSH_SENSORS_KEY_ENTRY'
|
||||
mkdir -p /root/.ssh
|
||||
chmod 700 /root/.ssh
|
||||
AUTH_KEYS=/root/.ssh/authorized_keys
|
||||
# Remove any old unrestricted keys
|
||||
if [ -f "\$AUTH_KEYS" ] && grep -qF "\$SSH_PUBLIC_KEY" "\$AUTH_KEYS" 2>/dev/null; then
|
||||
grep -vF "\$SSH_PUBLIC_KEY" "\$AUTH_KEYS" > "\$AUTH_KEYS.tmp"
|
||||
# Remove any old pulse keys
|
||||
if [ -f "\$AUTH_KEYS" ]; then
|
||||
grep -vF "# pulse-" "\$AUTH_KEYS" > "\$AUTH_KEYS.tmp" 2>/dev/null || touch "\$AUTH_KEYS.tmp"
|
||||
mv "\$AUTH_KEYS.tmp" "\$AUTH_KEYS"
|
||||
fi
|
||||
# Add restricted key for sensors
|
||||
if [ ! -f "\$AUTH_KEYS" ] || ! grep -qF "\$SSH_RESTRICTED_KEY_ENTRY" "\$AUTH_KEYS" 2>/dev/null; then
|
||||
echo "\$SSH_RESTRICTED_KEY_ENTRY" >> "\$AUTH_KEYS"
|
||||
fi
|
||||
# Add unrestricted key for ProxyJump
|
||||
SSH_PROXYJUMP_ENTRY="\$SSH_PUBLIC_KEY # pulse-proxyjump"
|
||||
if [ ! -f "\$AUTH_KEYS" ] || ! grep -qF "# pulse-proxyjump" "\$AUTH_KEYS" 2>/dev/null; then
|
||||
echo "\$SSH_PROXYJUMP_ENTRY" >> "\$AUTH_KEYS"
|
||||
fi
|
||||
# Add sensors key (cluster nodes only need sensors key, not proxy key)
|
||||
echo "\$SSH_SENSORS_KEY_ENTRY" >> "\$AUTH_KEYS"
|
||||
chmod 600 "\$AUTH_KEYS"
|
||||
if ! command -v sensors >/dev/null 2>&1; then
|
||||
echo " - Installing lm-sensors..."
|
||||
|
|
@ -4272,7 +4309,7 @@ if [ "$AUTO_REG_SUCCESS" != true ]; then
|
|||
fi
|
||||
`, serverName, time.Now().Format("2006-01-02 15:04:05"), pulseIP,
|
||||
tokenName, tokenName, tokenName, tokenName, tokenName, tokenName,
|
||||
authToken, pulseURL, serverHost, tokenName, tokenName, storagePerms, sshPublicKey, pulseURL, pulseURL, pulseURL, pulseURL, authToken, pulseURL, authToken)
|
||||
authToken, pulseURL, serverHost, tokenName, tokenName, storagePerms, sshKeys.ProxyPublicKey, sshKeys.SensorsPublicKey, pulseURL, pulseURL, pulseURL, pulseURL, authToken, pulseURL, authToken)
|
||||
|
||||
} else { // PBS
|
||||
script = fmt.Sprintf(`#!/bin/bash
|
||||
|
|
@ -5335,28 +5372,67 @@ func (h *ConfigHandlers) handleSecureAutoRegister(w http.ResponseWriter, r *http
|
|||
|
||||
}
|
||||
|
||||
// getOrGenerateSSHKey returns the SSH public key for temperature monitoring
|
||||
// If no key exists, it generates one automatically
|
||||
func (h *ConfigHandlers) getOrGenerateSSHKey() string {
|
||||
// SSHKeyPair holds both proxy and sensors SSH keypairs
|
||||
type SSHKeyPair struct {
|
||||
ProxyPublicKey string
|
||||
SensorsPublicKey string
|
||||
}
|
||||
|
||||
// getOrGenerateSSHKeys returns both SSH public keys (proxy + sensors) for temperature monitoring
|
||||
// If keys don't exist, they are generated automatically
|
||||
// SECURITY: Blocks key generation when running in containers - use pulse-sensor-proxy instead
|
||||
func (h *ConfigHandlers) getOrGenerateSSHKeys() SSHKeyPair {
|
||||
// CRITICAL SECURITY CHECK: Never generate SSH keys in containers (unless dev mode)
|
||||
// Container compromise = SSH key compromise = root access to Proxmox
|
||||
devModeAllowSSH := os.Getenv("PULSE_DEV_ALLOW_CONTAINER_SSH") == "true"
|
||||
if isRunningInContainer() && !devModeAllowSSH {
|
||||
log.Error().Msg("SECURITY BLOCK: SSH key generation disabled in containerized deployments")
|
||||
log.Error().Msg("For temperature monitoring in containers, deploy pulse-sensor-proxy on the Proxmox host")
|
||||
log.Error().Msg("See: https://docs.pulseapp.io/security/containerized-deployments")
|
||||
log.Error().Msg("To test SSH keys in dev/lab only: PULSE_DEV_ALLOW_CONTAINER_SSH=true (NEVER in production!)")
|
||||
return SSHKeyPair{}
|
||||
}
|
||||
|
||||
if devModeAllowSSH && isRunningInContainer() {
|
||||
log.Warn().Msg("⚠️ DEV MODE: SSH key generation ENABLED in container - FOR TESTING ONLY")
|
||||
log.Warn().Msg("⚠️ This grants root SSH access from container - NEVER use in production!")
|
||||
}
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Could not determine home directory for SSH key")
|
||||
return ""
|
||||
log.Warn().Err(err).Msg("Could not determine home directory for SSH keys")
|
||||
return SSHKeyPair{}
|
||||
}
|
||||
|
||||
sshDir := filepath.Join(homeDir, ".ssh")
|
||||
privateKeyPath := filepath.Join(sshDir, "id_ed25519")
|
||||
publicKeyPath := filepath.Join(sshDir, "id_ed25519.pub")
|
||||
|
||||
// Generate/load proxy key (for ProxyJump)
|
||||
proxyPrivPath := filepath.Join(sshDir, "id_ed25519_proxy")
|
||||
proxyPubPath := filepath.Join(sshDir, "id_ed25519_proxy.pub")
|
||||
proxyKey := h.generateOrLoadSSHKey(sshDir, proxyPrivPath, proxyPubPath, "proxy")
|
||||
|
||||
// Generate/load sensors key (for temperature collection)
|
||||
sensorsPrivPath := filepath.Join(sshDir, "id_ed25519_sensors")
|
||||
sensorsPubPath := filepath.Join(sshDir, "id_ed25519_sensors.pub")
|
||||
sensorsKey := h.generateOrLoadSSHKey(sshDir, sensorsPrivPath, sensorsPubPath, "sensors")
|
||||
|
||||
return SSHKeyPair{
|
||||
ProxyPublicKey: proxyKey,
|
||||
SensorsPublicKey: sensorsKey,
|
||||
}
|
||||
}
|
||||
|
||||
// generateOrLoadSSHKey generates or loads a single SSH keypair
|
||||
func (h *ConfigHandlers) generateOrLoadSSHKey(sshDir, privateKeyPath, publicKeyPath, keyType string) string {
|
||||
// Check if public key already exists
|
||||
if pubKeyBytes, err := os.ReadFile(publicKeyPath); err == nil {
|
||||
publicKey := strings.TrimSpace(string(pubKeyBytes))
|
||||
log.Info().Str("keyPath", publicKeyPath).Msg("Using existing SSH public key")
|
||||
log.Info().Str("keyPath", publicKeyPath).Str("type", keyType).Msg("Using existing SSH public key")
|
||||
return publicKey
|
||||
}
|
||||
|
||||
// Key doesn't exist - generate one
|
||||
log.Info().Str("sshDir", sshDir).Msg("Generating new SSH keypair for temperature monitoring")
|
||||
log.Info().Str("sshDir", sshDir).Str("type", keyType).Msg("Generating new SSH keypair for temperature monitoring")
|
||||
|
||||
// Create .ssh directory if it doesn't exist
|
||||
if err := os.MkdirAll(sshDir, 0700); err != nil {
|
||||
|
|
|
|||
Loading…
Reference in a new issue