From 8be25283dc384c00b1a0b74f200fd1c0a439ffd5 Mon Sep 17 00:00:00 2001
From: Muhammad Ibrahim
Date: Tue, 30 Sep 2025 21:38:13 +0100
Subject: [PATCH] Relaid out settings page and configured agent and other
communication to use curl flags which can be configured to ignore ssl cert if
self-hosted.
---
agents/patchmon-agent.sh | 23 ++++---
agents/patchmon_install.sh | 11 +++-
agents/patchmon_remove.sh | 7 +-
.../migration.sql | 10 +++
.../migration.sql | 4 ++
backend/prisma/schema.prisma | 6 +-
backend/src/routes/hostRoutes.js | 48 +++++++++++++-
backend/src/routes/settingsRoutes.js | 66 +++++++++++++------
backend/src/services/settingsService.js | 1 +
frontend/src/components/Layout.jsx | 3 -
frontend/src/components/SettingsLayout.jsx | 5 +-
.../settings/AgentManagementTab.jsx | 17 ++++-
.../components/settings/AgentUpdatesTab.jsx | 30 ++++++++-
frontend/src/components/settings/RolesTab.jsx | 2 -
frontend/src/components/settings/UsersTab.jsx | 1 -
frontend/src/pages/HostDetail.jsx | 31 +++++++--
frontend/src/pages/Profile.jsx | 1 -
frontend/src/pages/Settings.jsx | 9 ++-
.../src/pages/settings/SettingsHostGroups.jsx | 2 +-
19 files changed, 214 insertions(+), 63 deletions(-)
create mode 100644 backend/prisma/migrations/20250925192645_fix_dashboard_preferences_unique_constraint/migration.sql
create mode 100644 backend/prisma/migrations/20250930195941_add_ignore_ssl_self_signed/migration.sql
diff --git a/agents/patchmon-agent.sh b/agents/patchmon-agent.sh
index 6048652..091839a 100755
--- a/agents/patchmon-agent.sh
+++ b/agents/patchmon-agent.sh
@@ -11,6 +11,11 @@ CONFIG_FILE="/etc/patchmon/agent.conf"
CREDENTIALS_FILE="/etc/patchmon/credentials"
LOG_FILE="/var/log/patchmon-agent.log"
+# This placeholder will be dynamically replaced by the server when serving this
+# script based on the "ignore SSL self-signed" setting. If set to -k, curl will
+# ignore certificate validation. Otherwise, it will be empty for secure default.
+CURL_FLAGS=""
+
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -144,7 +149,7 @@ EOF
test_credentials() {
load_credentials
- local response=$(curl -ks -X POST \
+ local response=$(curl $CURL_FLAGS -X POST \
-H "Content-Type: application/json" \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
@@ -809,7 +814,7 @@ EOF
local payload=$(echo "$base_payload $merged_json" | jq -s '.[0] * .[1]')
- local response=$(curl -ks -X POST \
+ local response=$(curl $CURL_FLAGS -X POST \
-H "Content-Type: application/json" \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
@@ -870,7 +875,7 @@ EOF
ping_server() {
load_credentials
- local response=$(curl -ks -X POST \
+ local response=$(curl $CURL_FLAGS -X POST \
-H "Content-Type: application/json" \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
@@ -913,7 +918,7 @@ check_version() {
info "Checking for agent updates..."
- local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/version")
+ local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/version")
if [[ $? -eq 0 ]]; then
local current_version=$(echo "$response" | grep -o '"currentVersion":"[^"]*' | cut -d'"' -f4)
@@ -945,7 +950,7 @@ check_version() {
# Check if auto-update is enabled (both globally and for this host)
check_auto_update_enabled() {
# Get settings from server using API credentials
- local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/settings" 2>/dev/null)
+ local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/settings" 2>/dev/null)
if [[ $? -ne 0 ]]; then
return 1
fi
@@ -970,7 +975,7 @@ check_agent_update_needed() {
fi
# Get server agent info using API credentials
- local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/timestamp" 2>/dev/null)
+ local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/timestamp" 2>/dev/null)
if [[ $? -eq 0 ]]; then
local server_version=$(echo "$response" | grep -o '"version":"[^"]*' | cut -d'"' -f4)
@@ -1007,7 +1012,7 @@ check_agent_update() {
fi
# Get server agent info using API credentials
- local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/timestamp")
+ local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/timestamp")
if [[ $? -eq 0 ]]; then
local server_version=$(echo "$response" | grep -o '"version":"[^"]*' | cut -d'"' -f4)
@@ -1057,7 +1062,7 @@ update_agent() {
cp "$0" "$backup_file"
# Download new version using API credentials
- if curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -o "/tmp/patchmon-agent-new.sh" "$download_url"; then
+ if curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -o "/tmp/patchmon-agent-new.sh" "$download_url"; then
# Verify the downloaded script is valid
if bash -n "/tmp/patchmon-agent-new.sh" 2>/dev/null; then
# Replace current script
@@ -1090,7 +1095,7 @@ update_agent() {
update_crontab() {
load_credentials
info "Updating crontab with current policy..."
- local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/settings/update-interval")
+ local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/settings/update-interval")
if [[ $? -eq 0 ]]; then
local update_interval=$(echo "$response" | grep -o '"updateInterval":[0-9]*' | cut -d':' -f2)
# Fallback if not found
diff --git a/agents/patchmon_install.sh b/agents/patchmon_install.sh
index d191847..2f95887 100644
--- a/agents/patchmon_install.sh
+++ b/agents/patchmon_install.sh
@@ -1,10 +1,15 @@
#!/bin/bash
# PatchMon Agent Installation Script
-# Usage: curl -ks {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | bash
+# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | bash
set -e
+# This placeholder will be dynamically replaced by the server when serving this
+# script based on the "ignore SSL self-signed" setting. If set to -k, curl will
+# ignore certificate validation. Otherwise, it will be empty for secure default.
+CURL_FLAGS=""
+
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -145,7 +150,7 @@ if [[ -f "/usr/local/bin/patchmon-agent.sh" ]]; then
info "📋 Moved existing agent to: /usr/local/bin/patchmon-agent.sh.backup.$(date +%Y%m%d_%H%M%S)"
fi
-curl -ks \
+curl $CURL_FLAGS \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
"$PATCHMON_URL/api/v1/hosts/agent/download" \
@@ -185,7 +190,7 @@ fi
# Step 6: Get update interval policy from server and setup crontab
info "⏰ Getting update interval policy from server..."
-UPDATE_INTERVAL=$(curl -ks \
+UPDATE_INTERVAL=$(curl $CURL_FLAGS \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
"$PATCHMON_URL/api/v1/settings/update-interval" | \
diff --git a/agents/patchmon_remove.sh b/agents/patchmon_remove.sh
index c6328e4..827050b 100755
--- a/agents/patchmon_remove.sh
+++ b/agents/patchmon_remove.sh
@@ -1,11 +1,16 @@
#!/bin/bash
# PatchMon Agent Removal Script
-# Usage: curl -ks {PATCHMON_URL}/api/v1/hosts/remove | bash
+# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/remove | bash
# This script completely removes PatchMon from the system
set -e
+# This placeholder will be dynamically replaced by the server when serving this
+# script based on the "ignore SSL self-signed" setting for any curl calls in
+# future (left for consistency with install script).
+CURL_FLAGS=""
+
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
diff --git a/backend/prisma/migrations/20250925192645_fix_dashboard_preferences_unique_constraint/migration.sql b/backend/prisma/migrations/20250925192645_fix_dashboard_preferences_unique_constraint/migration.sql
new file mode 100644
index 0000000..c1f1d33
--- /dev/null
+++ b/backend/prisma/migrations/20250925192645_fix_dashboard_preferences_unique_constraint/migration.sql
@@ -0,0 +1,10 @@
+-- Fix dashboard preferences unique constraint
+-- This migration fixes the unique constraint on dashboard_preferences table
+
+-- Drop existing indexes if they exist
+DROP INDEX IF EXISTS "dashboard_preferences_card_id_key";
+DROP INDEX IF EXISTS "dashboard_preferences_user_id_card_id_key";
+DROP INDEX IF EXISTS "dashboard_preferences_user_id_key";
+
+-- Add the correct unique constraint
+ALTER TABLE "dashboard_preferences" ADD CONSTRAINT "dashboard_preferences_user_id_card_id_key" UNIQUE ("user_id", "card_id");
diff --git a/backend/prisma/migrations/20250930195941_add_ignore_ssl_self_signed/migration.sql b/backend/prisma/migrations/20250930195941_add_ignore_ssl_self_signed/migration.sql
new file mode 100644
index 0000000..f583d23
--- /dev/null
+++ b/backend/prisma/migrations/20250930195941_add_ignore_ssl_self_signed/migration.sql
@@ -0,0 +1,4 @@
+-- Add ignore_ssl_self_signed column to settings table
+-- This allows users to configure whether curl commands should ignore SSL certificate validation
+
+ALTER TABLE "settings" ADD COLUMN "ignore_ssl_self_signed" BOOLEAN NOT NULL DEFAULT false;
diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma
index 563c53f..662018e 100644
--- a/backend/prisma/schema.prisma
+++ b/backend/prisma/schema.prisma
@@ -7,7 +7,6 @@ datasource db {
url = env("DATABASE_URL")
}
-
model dashboard_preferences {
id String @id
user_id String
@@ -157,6 +156,7 @@ model settings {
update_available Boolean @default(false)
signup_enabled Boolean @default(false)
default_user_role String @default("user")
+ ignore_ssl_self_signed Boolean @default(false)
}
model update_history {
@@ -175,8 +175,6 @@ model users {
username String @unique
email String @unique
password_hash String
- first_name String?
- last_name String?
role String @default("admin")
is_active Boolean @default(true)
last_login DateTime?
@@ -185,5 +183,7 @@ model users {
tfa_backup_codes String?
tfa_enabled Boolean @default(false)
tfa_secret String?
+ first_name String?
+ last_name String?
dashboard_preferences dashboard_preferences[]
}
diff --git a/backend/src/routes/hostRoutes.js b/backend/src/routes/hostRoutes.js
index 8c8dca8..1d561f3 100644
--- a/backend/src/routes/hostRoutes.js
+++ b/backend/src/routes/hostRoutes.js
@@ -45,11 +45,26 @@ router.get("/agent/download", async (req, res) => {
}
// Read file and convert line endings
- const scriptContent = fs
+ let scriptContent = fs
.readFileSync(agentPath, "utf8")
.replace(/\r\n/g, "\n")
.replace(/\r/g, "\n");
+ // Determine curl flags dynamically from settings for consistency
+ let curlFlags = "-s";
+ try {
+ const settings = await prisma.settings.findFirst();
+ if (settings && settings.ignore_ssl_self_signed === true) {
+ curlFlags = "-sk";
+ }
+ } catch (_) {}
+
+ // Inject the curl flags into the script
+ scriptContent = scriptContent.replace(
+ 'CURL_FLAGS=""',
+ `CURL_FLAGS="${curlFlags}"`,
+ );
+
res.setHeader("Content-Type", "application/x-shellscript");
res.setHeader(
"Content-Disposition",
@@ -1101,11 +1116,21 @@ router.get("/install", async (req, res) => {
);
}
- // Inject the API credentials and server URL into the script as environment variables
+ // Determine curl flags dynamically from settings (ignore self-signed)
+ let curlFlags = "-s";
+ try {
+ const settings = await prisma.settings.findFirst();
+ if (settings && settings.ignore_ssl_self_signed === true) {
+ curlFlags = "-sk";
+ }
+ } catch (_) {}
+
+ // Inject the API credentials, server URL, and curl flags into the script
const envVars = `#!/bin/bash
export PATCHMON_URL="${serverUrl}"
export API_ID="${host.api_id}"
export API_KEY="${host.api_key}"
+export CURL_FLAGS="${curlFlags}"
`;
@@ -1141,7 +1166,24 @@ router.get("/remove", async (_req, res) => {
}
// Read the script content
- const script = fs.readFileSync(scriptPath, "utf8");
+ let script = fs.readFileSync(scriptPath, "utf8");
+
+ // Convert line endings
+ script = script.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
+
+ // Determine curl flags dynamically from settings for consistency
+ let curlFlags = "-s";
+ try {
+ const settings = await prisma.settings.findFirst();
+ if (settings && settings.ignore_ssl_self_signed === true) {
+ curlFlags = "-sk";
+ }
+ } catch (_) {}
+
+ // Prepend environment for CURL_FLAGS so script can use it if needed
+ const envPrefix = `#!/bin/bash\nexport CURL_FLAGS="${curlFlags}"\n\n`;
+ script = script.replace(/^#!/, "#");
+ script = envPrefix + script;
// Set appropriate headers for script download
res.setHeader("Content-Type", "text/plain");
diff --git a/backend/src/routes/settingsRoutes.js b/backend/src/routes/settingsRoutes.js
index e496726..0474e6d 100644
--- a/backend/src/routes/settingsRoutes.js
+++ b/backend/src/routes/settingsRoutes.js
@@ -165,19 +165,31 @@ router.put(
requireManageSettings,
[
body("serverProtocol")
+ .optional()
.isIn(["http", "https"])
.withMessage("Protocol must be http or https"),
body("serverHost")
+ .optional()
.isLength({ min: 1 })
.withMessage("Server host is required"),
body("serverPort")
+ .optional()
.isInt({ min: 1, max: 65535 })
.withMessage("Port must be between 1 and 65535"),
body("updateInterval")
+ .optional()
.isInt({ min: 5, max: 1440 })
.withMessage("Update interval must be between 5 and 1440 minutes"),
- body("autoUpdate").isBoolean().withMessage("Auto update must be a boolean"),
+ body("autoUpdate")
+ .optional()
+ .isBoolean()
+ .withMessage("Auto update must be a boolean"),
+ body("ignoreSslSelfSigned")
+ .optional()
+ .isBoolean()
+ .withMessage("Ignore SSL self-signed must be a boolean"),
body("signupEnabled")
+ .optional()
.isBoolean()
.withMessage("Signup enabled must be a boolean"),
body("defaultUserRole")
@@ -218,6 +230,7 @@ router.put(
serverPort,
updateInterval,
autoUpdate,
+ ignoreSslSelfSigned,
signupEnabled,
defaultUserRole,
githubRepoUrl,
@@ -229,32 +242,43 @@ router.put(
const currentSettings = await getSettings();
const oldUpdateInterval = currentSettings.update_interval;
- // Update settings using the service
- const normalizedInterval = normalizeUpdateInterval(updateInterval || 60);
+ // Build update object with only provided fields
+ const updateData = {};
- const updatedSettings = await updateSettings(currentSettings.id, {
- server_protocol: serverProtocol,
- server_host: serverHost,
- server_port: serverPort,
- update_interval: normalizedInterval,
- auto_update: autoUpdate || false,
- signup_enabled: signupEnabled || false,
- default_user_role:
- defaultUserRole || process.env.DEFAULT_USER_ROLE || "user",
- github_repo_url:
- githubRepoUrl !== undefined
- ? githubRepoUrl
- : "git@github.com:9technologygroup/patchmon.net.git",
- repository_type: repositoryType || "public",
- ssh_key_path: sshKeyPath || null,
- });
+ if (serverProtocol !== undefined)
+ updateData.server_protocol = serverProtocol;
+ if (serverHost !== undefined) updateData.server_host = serverHost;
+ if (serverPort !== undefined) updateData.server_port = serverPort;
+ if (updateInterval !== undefined) {
+ updateData.update_interval = normalizeUpdateInterval(updateInterval);
+ }
+ if (autoUpdate !== undefined) updateData.auto_update = autoUpdate;
+ if (ignoreSslSelfSigned !== undefined)
+ updateData.ignore_ssl_self_signed = ignoreSslSelfSigned;
+ if (signupEnabled !== undefined)
+ updateData.signup_enabled = signupEnabled;
+ if (defaultUserRole !== undefined)
+ updateData.default_user_role = defaultUserRole;
+ if (githubRepoUrl !== undefined)
+ updateData.github_repo_url = githubRepoUrl;
+ if (repositoryType !== undefined)
+ updateData.repository_type = repositoryType;
+ if (sshKeyPath !== undefined) updateData.ssh_key_path = sshKeyPath;
+
+ const updatedSettings = await updateSettings(
+ currentSettings.id,
+ updateData,
+ );
console.log("Settings updated successfully:", updatedSettings);
// If update interval changed, trigger crontab updates on all hosts with auto-update enabled
- if (oldUpdateInterval !== normalizedInterval) {
+ if (
+ updateInterval !== undefined &&
+ oldUpdateInterval !== updateData.update_interval
+ ) {
console.log(
- `Update interval changed from ${oldUpdateInterval} to ${normalizedInterval} minutes. Triggering crontab updates...`,
+ `Update interval changed from ${oldUpdateInterval} to ${updateData.update_interval} minutes. Triggering crontab updates...`,
);
await triggerCrontabUpdates();
}
diff --git a/backend/src/services/settingsService.js b/backend/src/services/settingsService.js
index b81c541..3a137ef 100644
--- a/backend/src/services/settingsService.js
+++ b/backend/src/services/settingsService.js
@@ -43,6 +43,7 @@ async function createSettingsFromEnvironment() {
update_interval: 60,
auto_update: false,
signup_enabled: false,
+ ignore_ssl_self_signed: false,
updated_at: new Date(),
},
});
diff --git a/frontend/src/components/Layout.jsx b/frontend/src/components/Layout.jsx
index 3c0de48..a2a096e 100644
--- a/frontend/src/components/Layout.jsx
+++ b/frontend/src/components/Layout.jsx
@@ -6,7 +6,6 @@ import {
ChevronRight,
Clock,
Container,
- FileText,
GitBranch,
Github,
Globe,
@@ -23,8 +22,6 @@ import {
Shield,
Star,
UserCircle,
- Users,
- Wrench,
X,
} from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
diff --git a/frontend/src/components/SettingsLayout.jsx b/frontend/src/components/SettingsLayout.jsx
index 32e2c85..5f67a26 100644
--- a/frontend/src/components/SettingsLayout.jsx
+++ b/frontend/src/components/SettingsLayout.jsx
@@ -1,13 +1,10 @@
import {
- AlertTriangle,
- ArrowLeft,
Bell,
ChevronLeft,
ChevronRight,
Code,
Folder,
RefreshCw,
- Server,
Settings,
Shield,
UserCircle,
@@ -121,7 +118,7 @@ const SettingsLayout = ({ children }) => {
const isActive = (path) => location.pathname === path;
- const getPageTitle = () => {
+ const _getPageTitle = () => {
const path = location.pathname;
if (path.startsWith("/settings/users")) return "Users";
diff --git a/frontend/src/components/settings/AgentManagementTab.jsx b/frontend/src/components/settings/AgentManagementTab.jsx
index 1c78707..60274fb 100644
--- a/frontend/src/components/settings/AgentManagementTab.jsx
+++ b/frontend/src/components/settings/AgentManagementTab.jsx
@@ -1,7 +1,7 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { AlertCircle, Code, Download, Plus, Shield, X } from "lucide-react";
import { useId, useState } from "react";
-import { agentFileAPI } from "../../utils/api";
+import { agentFileAPI, settingsAPI } from "../../utils/api";
const AgentManagementTab = () => {
const scriptFileId = useId();
@@ -19,6 +19,17 @@ const AgentManagementTab = () => {
queryFn: () => agentFileAPI.getInfo().then((res) => res.data),
});
+ // Fetch settings for dynamic curl flags
+ const { data: settings } = useQuery({
+ queryKey: ["settings"],
+ queryFn: () => settingsAPI.get().then((res) => res.data),
+ });
+
+ // Helper function to get curl flags based on settings
+ const getCurlFlags = () => {
+ return settings?.ignore_ssl_self_signed ? "-sk" : "-s";
+ };
+
const uploadAgentMutation = useMutation({
mutationFn: (scriptContent) =>
agentFileAPI.upload(scriptContent).then((res) => res.data),
@@ -171,13 +182,13 @@ const AgentManagementTab = () => {
- curl -ks {window.location.origin}
+ curl {getCurlFlags()} {window.location.origin}
/api/v1/hosts/remove | sudo bash
{
- const command = `curl -ks ${window.location.origin}/api/v1/hosts/remove | sudo bash`;
+ const command = `curl ${getCurlFlags()} ${window.location.origin}/api/v1/hosts/remove | sudo bash`;
navigator.clipboard.writeText(command);
// You could add a toast notification here
}}
diff --git a/frontend/src/components/settings/AgentUpdatesTab.jsx b/frontend/src/components/settings/AgentUpdatesTab.jsx
index 6a46a15..f60dfdc 100644
--- a/frontend/src/components/settings/AgentUpdatesTab.jsx
+++ b/frontend/src/components/settings/AgentUpdatesTab.jsx
@@ -8,11 +8,13 @@ const AgentUpdatesTab = () => {
const autoUpdateId = useId();
const signupEnabledId = useId();
const defaultRoleId = useId();
+ const ignoreSslId = useId();
const [formData, setFormData] = useState({
updateInterval: 60,
autoUpdate: false,
signupEnabled: false,
defaultUserRole: "user",
+ ignoreSslSelfSigned: false,
});
const [errors, setErrors] = useState({});
const [isDirty, setIsDirty] = useState(false);
@@ -41,8 +43,9 @@ const AgentUpdatesTab = () => {
const newFormData = {
updateInterval: settings.update_interval || 60,
autoUpdate: settings.auto_update || false,
- signupEnabled: settings.signup_enabled === true ? true : false,
+ signupEnabled: settings.signup_enabled === true,
defaultUserRole: settings.default_user_role || "user",
+ ignoreSslSelfSigned: settings.ignore_ssl_self_signed === true,
};
setFormData(newFormData);
setIsDirty(false);
@@ -300,6 +303,31 @@ const AgentUpdatesTab = () => {
+ {/* SSL Certificate Setting */}
+
+
+
+
+ handleInputChange("ignoreSslSelfSigned", e.target.checked)
+ }
+ className="rounded border-secondary-300 text-primary-600 shadow-sm focus:border-primary-300 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
+ />
+
+ Ignore SSL Self-Signed Certificates
+
+
+
+
+ When enabled, curl commands in agent scripts will use the -k flag to
+ ignore SSL certificate validation errors. Use with caution on
+ production systems as this reduces security.
+
+
+
{/* User Signup Setting */}
diff --git a/frontend/src/components/settings/RolesTab.jsx b/frontend/src/components/settings/RolesTab.jsx
index 6def1b3..735aa71 100644
--- a/frontend/src/components/settings/RolesTab.jsx
+++ b/frontend/src/components/settings/RolesTab.jsx
@@ -6,8 +6,6 @@ import {
Download,
Edit,
Package,
- Plus,
- RefreshCw,
Save,
Server,
Settings,
diff --git a/frontend/src/components/settings/UsersTab.jsx b/frontend/src/components/settings/UsersTab.jsx
index 503c8e8..d606452 100644
--- a/frontend/src/components/settings/UsersTab.jsx
+++ b/frontend/src/components/settings/UsersTab.jsx
@@ -5,7 +5,6 @@ import {
Edit,
Key,
Mail,
- Plus,
Shield,
Trash2,
User,
diff --git a/frontend/src/pages/HostDetail.jsx b/frontend/src/pages/HostDetail.jsx
index 125411b..d352239 100644
--- a/frontend/src/pages/HostDetail.jsx
+++ b/frontend/src/pages/HostDetail.jsx
@@ -59,6 +59,17 @@ const HostDetail = () => {
refetchOnWindowFocus: false, // Don't refetch when window regains focus
});
+ // Fetch settings for dynamic curl flags
+ const { data: settings } = useQuery({
+ queryKey: ["settings"],
+ queryFn: () => settingsAPI.get().then((res) => res.data),
+ });
+
+ // Helper function to get curl flags based on settings
+ const getCurlFlags = () => {
+ return settings?.ignore_ssl_self_signed ? "-sk" : "-s";
+ };
+
// Tab change handler
const handleTabChange = (tabName) => {
setActiveTab(tabName);
@@ -419,7 +430,7 @@ const HostDetail = () => {
- Auto-update
+ Agent Auto-update
{
const serverUrl = serverUrlData?.server_url || "http://localhost:3001";
+ // Fetch settings for dynamic curl flags (local to modal)
+ const { data: settings } = useQuery({
+ queryKey: ["settings"],
+ queryFn: () => settingsAPI.get().then((res) => res.data),
+ });
+
+ const getCurlFlags = () => {
+ return settings?.ignore_ssl_self_signed ? "-ks" : "-s";
+ };
+
const copyToClipboard = async (text) => {
try {
// Try modern clipboard API first
@@ -1089,7 +1110,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
@@ -1097,7 +1118,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
type="button"
onClick={() =>
copyToClipboard(
- `curl -ks ${serverUrl}/api/v1/hosts/install -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | bash`,
+ `curl ${getCurlFlags()} ${serverUrl}/api/v1/hosts/install -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | bash`,
)
}
className="btn-primary flex items-center gap-1"
@@ -1147,7 +1168,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
@@ -1155,7 +1176,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
type="button"
onClick={() =>
copyToClipboard(
- `curl -ko /usr/local/bin/patchmon-agent.sh ${serverUrl}/api/v1/hosts/agent/download -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" && sudo chmod +x /usr/local/bin/patchmon-agent.sh`,
+ `curl ${getCurlFlags()} -o /usr/local/bin/patchmon-agent.sh ${serverUrl}/api/v1/hosts/agent/download -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" && sudo chmod +x /usr/local/bin/patchmon-agent.sh`,
)
}
className="btn-secondary flex items-center gap-1"
diff --git a/frontend/src/pages/Profile.jsx b/frontend/src/pages/Profile.jsx
index 2d15576..50635f1 100644
--- a/frontend/src/pages/Profile.jsx
+++ b/frontend/src/pages/Profile.jsx
@@ -11,7 +11,6 @@ import {
Moon,
RefreshCw,
Save,
- Settings,
Shield,
Smartphone,
Sun,
diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx
index f3d218f..0636a62 100644
--- a/frontend/src/pages/Settings.jsx
+++ b/frontend/src/pages/Settings.jsx
@@ -108,6 +108,11 @@ const Settings = () => {
queryFn: () => settingsAPI.get().then((res) => res.data),
});
+ // Helper function to get curl flags based on settings
+ const getCurlFlags = () => {
+ return settings?.ignore_ssl_self_signed ? "-sk" : "-s";
+ };
+
// Fetch available roles for default user role dropdown
const { data: roles, isLoading: rolesLoading } = useQuery({
queryKey: ["rolePermissions"],
@@ -938,13 +943,13 @@ const Settings = () => {
- curl -ks {window.location.origin}
+ curl {getCurlFlags()} {window.location.origin}
/api/v1/hosts/remove | sudo bash
{
- const command = `curl -ks ${window.location.origin}/api/v1/hosts/remove | sudo bash`;
+ const command = `curl ${getCurlFlags()} ${window.location.origin}/api/v1/hosts/remove | sudo bash`;
navigator.clipboard.writeText(command);
// You could add a toast notification here
}}
diff --git a/frontend/src/pages/settings/SettingsHostGroups.jsx b/frontend/src/pages/settings/SettingsHostGroups.jsx
index be577e7..e3111ad 100644
--- a/frontend/src/pages/settings/SettingsHostGroups.jsx
+++ b/frontend/src/pages/settings/SettingsHostGroups.jsx
@@ -76,7 +76,7 @@ const SettingsHostGroups = () => {
updateMutation.mutate({ id: selectedGroup.id, data });
};
- const handleDeleteClick = (group) => {
+ const _handleDeleteClick = (group) => {
setGroupToDelete(group);
setShowDeleteModal(true);
};