diff --git a/frontend-modern/src/components/Settings/QuickSecuritySetup.tsx b/frontend-modern/src/components/Settings/QuickSecuritySetup.tsx index 350208e7f..47cbb9e73 100644 --- a/frontend-modern/src/components/Settings/QuickSecuritySetup.tsx +++ b/frontend-modern/src/components/Settings/QuickSecuritySetup.tsx @@ -69,12 +69,19 @@ export const QuickSecuritySetup: Component = () => { setCredentials(newCredentials); setShowCredentials(true); - if (result.method === 'systemd' && result.willRestart) { + // Store the command if manual action needed + if (result.command) { + (window as any).securityCommand = result.command; + } + + if (result.method === 'systemd' && result.automatic) { showSuccess('Security enabled! Pulse will restart automatically in 2 seconds...'); // Show countdown setTimeout(() => { showSuccess('Restarting now... You will need to log in with your new credentials.'); }, 2000); + } else if (result.method === 'systemd' && !result.automatic) { + showSuccess('Security configured! Run the command shown below to apply settings.'); } else if (result.method === 'docker') { showSuccess('Security configured! Please restart your Docker container with the credentials shown.'); } else { @@ -251,12 +258,32 @@ Important:
-

- ✅ Security has been automatically enabled! -

-

- Pulse will restart automatically in a few seconds. You'll need to log in with these credentials. -

+ +

+ ✅ Security configured successfully! +

+

+ Save your credentials above. Pulse will apply the security settings. +

+ + } + > +

+ ✅ One more step to enable security: +

+

+ Run this command in your terminal: +

+
+ {(window as any).securityCommand} +
+

+ This will apply the settings and restart Pulse with security enabled. +

+
diff --git a/internal/api/router.go b/internal/api/router.go index c750150af..165ee8a3c 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -267,16 +267,11 @@ func (r *Router) setupRoutes() { isDocker := os.Getenv("PULSE_DOCKER") == "true" if isSystemd { - // We're running under systemd - create override file - overridePath := "/etc/systemd/system/pulse-backend.service.d/override.conf" - overrideDir := filepath.Dir(overridePath) + // We're running under systemd but may not have root permissions + // Write config to Pulse's data directory and provide a one-liner to apply it - // Create override directory - if err := os.MkdirAll(overrideDir, 0755); err != nil { - log.Error().Err(err).Msg("Failed to create systemd override directory") - http.Error(w, "Failed to create systemd configuration", http.StatusInternalServerError) - return - } + configPath := filepath.Join(r.config.DataPath, "security-override.conf") + scriptPath := filepath.Join(r.config.DataPath, "apply-security.sh") // Create override content overrideContent := fmt.Sprintf(`# Auto-generated by Pulse Quick Security Setup @@ -288,30 +283,62 @@ Environment="API_TOKEN=%s" Environment="ENABLE_AUDIT_LOG=true" `, time.Now().Format(time.RFC3339), setupRequest.Username, setupRequest.Password, setupRequest.APIToken) - // Write override file - if err := os.WriteFile(overridePath, []byte(overrideContent), 0644); err != nil { - log.Error().Err(err).Msg("Failed to write systemd override") - http.Error(w, "Failed to write systemd configuration", http.StatusInternalServerError) + // Write override file to data directory + if err := os.WriteFile(configPath, []byte(overrideContent), 0644); err != nil { + log.Error().Err(err).Msg("Failed to write security config") + http.Error(w, "Failed to write security configuration", http.StatusInternalServerError) return } - // Reload systemd and restart service - if err := utils.RunCommand("systemctl", "daemon-reload"); err != nil { - log.Error().Err(err).Msg("Failed to reload systemd") + // Create apply script + scriptContent := fmt.Sprintf(`#!/bin/bash +# Auto-generated script to apply Pulse security settings +echo "Applying security settings to Pulse..." +sudo mkdir -p /etc/systemd/system/pulse-backend.service.d/ +sudo cp %s /etc/systemd/system/pulse-backend.service.d/override.conf +sudo systemctl daemon-reload +sudo systemctl restart pulse-backend +echo "Security enabled! Pulse is restarting..." +echo "You will need to log in with your saved credentials." +`, configPath) + + if err := os.WriteFile(scriptPath, []byte(scriptContent), 0755); err != nil { + log.Error().Err(err).Msg("Failed to write apply script") } - // Schedule restart after response (so user gets the credentials) - go func() { - time.Sleep(2 * time.Second) - log.Info().Msg("Restarting Pulse to apply security settings...") - utils.RunCommand("systemctl", "restart", "pulse-backend") - }() + // Try to apply automatically (will only work if we have sudo permissions) + if err := utils.RunCommand("sudo", "mkdir", "-p", "/etc/systemd/system/pulse-backend.service.d/"); err == nil { + if err := utils.RunCommand("sudo", "cp", configPath, "/etc/systemd/system/pulse-backend.service.d/override.conf"); err == nil { + utils.RunCommand("sudo", "systemctl", "daemon-reload") + // Schedule restart + go func() { + time.Sleep(2 * time.Second) + log.Info().Msg("Restarting Pulse to apply security settings...") + utils.RunCommand("sudo", "systemctl", "restart", "pulse-backend") + }() + + response := map[string]interface{}{ + "success": true, + "method": "systemd", + "automatic": true, + "willRestart": true, + "message": "Security enabled! Pulse will restart in 2 seconds to apply settings.", + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) + return + } + } + // If automatic didn't work, provide manual command response := map[string]interface{}{ "success": true, "method": "systemd", - "willRestart": true, - "message": "Security enabled! Pulse will restart in 2 seconds to apply settings.", + "automatic": false, + "configPath": configPath, + "scriptPath": scriptPath, + "command": fmt.Sprintf("sudo bash %s", scriptPath), + "message": "Security configured! Run the command shown to apply settings.", } w.Header().Set("Content-Type", "application/json")