mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 15:18:28 +00:00
fix: harden gateway control-plane restart protections
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import type { GatewayRequestHandlers, RespondFn } from "./types.js";
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import { listChannelPlugins } from "../../channels/plugins/index.js";
|
||||
import {
|
||||
@@ -19,7 +21,6 @@ import {
|
||||
} from "../../config/redact-snapshot.js";
|
||||
import { buildConfigSchema, type ConfigSchemaResponse } from "../../config/schema.js";
|
||||
import { extractDeliveryInfo } from "../../config/sessions.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import {
|
||||
formatDoctorNonInteractiveHint,
|
||||
type RestartSentinelPayload,
|
||||
@@ -27,6 +28,12 @@ import {
|
||||
} from "../../infra/restart-sentinel.js";
|
||||
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
||||
import { loadOpenClawPlugins } from "../../plugins/loader.js";
|
||||
import { diffConfigPaths } from "../config-reload.js";
|
||||
import {
|
||||
formatControlPlaneActor,
|
||||
resolveControlPlaneActor,
|
||||
summarizeChangedPaths,
|
||||
} from "../control-plane-audit.js";
|
||||
import {
|
||||
ErrorCodes,
|
||||
errorShape,
|
||||
@@ -38,7 +45,6 @@ import {
|
||||
} from "../protocol/index.js";
|
||||
import { resolveBaseHashParam } from "./base-hash.js";
|
||||
import { parseRestartRequestParams } from "./restart-request.js";
|
||||
import type { GatewayRequestHandlers, RespondFn } from "./types.js";
|
||||
import { assertValidParams } from "./validation.js";
|
||||
|
||||
function requireConfigBaseHash(
|
||||
@@ -275,7 +281,7 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
"config.patch": async ({ params, respond }) => {
|
||||
"config.patch": async ({ params, respond, client, context }) => {
|
||||
if (!assertValidParams(params, validateConfigPatchParams, "config.patch", respond)) {
|
||||
return;
|
||||
}
|
||||
@@ -349,6 +355,11 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const changedPaths = diffConfigPaths(snapshot.config, validated.config);
|
||||
const actor = resolveControlPlaneActor(client);
|
||||
context?.logGateway?.info(
|
||||
`config.patch write ${formatControlPlaneActor(actor)} changedPaths=${summarizeChangedPaths(changedPaths)} restartReason=config.patch`,
|
||||
);
|
||||
await writeConfigFile(validated.config, writeOptions);
|
||||
|
||||
const { sessionKey, note, restartDelayMs, deliveryContext, threadId } =
|
||||
@@ -365,7 +376,18 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
const restart = scheduleGatewaySigusr1Restart({
|
||||
delayMs: restartDelayMs,
|
||||
reason: "config.patch",
|
||||
audit: {
|
||||
actor: actor.actor,
|
||||
deviceId: actor.deviceId,
|
||||
clientIp: actor.clientIp,
|
||||
changedPaths,
|
||||
},
|
||||
});
|
||||
if (restart.coalesced) {
|
||||
context?.logGateway?.warn(
|
||||
`config.patch restart coalesced ${formatControlPlaneActor(actor)} delayMs=${restart.delayMs}`,
|
||||
);
|
||||
}
|
||||
respond(
|
||||
true,
|
||||
{
|
||||
@@ -381,7 +403,7 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
"config.apply": async ({ params, respond }) => {
|
||||
"config.apply": async ({ params, respond, client, context }) => {
|
||||
if (!assertValidParams(params, validateConfigApplyParams, "config.apply", respond)) {
|
||||
return;
|
||||
}
|
||||
@@ -393,6 +415,11 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
if (!parsed) {
|
||||
return;
|
||||
}
|
||||
const changedPaths = diffConfigPaths(snapshot.config, parsed.config);
|
||||
const actor = resolveControlPlaneActor(client);
|
||||
context?.logGateway?.info(
|
||||
`config.apply write ${formatControlPlaneActor(actor)} changedPaths=${summarizeChangedPaths(changedPaths)} restartReason=config.apply`,
|
||||
);
|
||||
await writeConfigFile(parsed.config, writeOptions);
|
||||
|
||||
const { sessionKey, note, restartDelayMs, deliveryContext, threadId } =
|
||||
@@ -409,7 +436,18 @@ export const configHandlers: GatewayRequestHandlers = {
|
||||
const restart = scheduleGatewaySigusr1Restart({
|
||||
delayMs: restartDelayMs,
|
||||
reason: "config.apply",
|
||||
audit: {
|
||||
actor: actor.actor,
|
||||
deviceId: actor.deviceId,
|
||||
clientIp: actor.clientIp,
|
||||
changedPaths,
|
||||
},
|
||||
});
|
||||
if (restart.coalesced) {
|
||||
context?.logGateway?.warn(
|
||||
`config.apply restart coalesced ${formatControlPlaneActor(actor)} delayMs=${restart.delayMs}`,
|
||||
);
|
||||
}
|
||||
respond(
|
||||
true,
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ type SubsystemLogger = ReturnType<typeof createSubsystemLogger>;
|
||||
export type GatewayClient = {
|
||||
connect: ConnectParams;
|
||||
connId?: string;
|
||||
clientIp?: string;
|
||||
};
|
||||
|
||||
export type RespondFn = (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { GatewayRequestHandlers } from "./types.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { extractDeliveryInfo } from "../../config/sessions.js";
|
||||
import { resolveOpenClawPackageRoot } from "../../infra/openclaw-root.js";
|
||||
@@ -9,16 +10,17 @@ import {
|
||||
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
||||
import { normalizeUpdateChannel } from "../../infra/update-channels.js";
|
||||
import { runGatewayUpdate } from "../../infra/update-runner.js";
|
||||
import { formatControlPlaneActor, resolveControlPlaneActor } from "../control-plane-audit.js";
|
||||
import { validateUpdateRunParams } from "../protocol/index.js";
|
||||
import { parseRestartRequestParams } from "./restart-request.js";
|
||||
import type { GatewayRequestHandlers } from "./types.js";
|
||||
import { assertValidParams } from "./validation.js";
|
||||
|
||||
export const updateHandlers: GatewayRequestHandlers = {
|
||||
"update.run": async ({ params, respond }) => {
|
||||
"update.run": async ({ params, respond, client, context }) => {
|
||||
if (!assertValidParams(params, validateUpdateRunParams, "update.run", respond)) {
|
||||
return;
|
||||
}
|
||||
const actor = resolveControlPlaneActor(client);
|
||||
const { sessionKey, note, restartDelayMs } = parseRestartRequestParams(params);
|
||||
const { deliveryContext, threadId } = extractDeliveryInfo(sessionKey);
|
||||
const timeoutMsRaw = (params as { timeoutMs?: unknown }).timeoutMs;
|
||||
@@ -98,8 +100,22 @@ export const updateHandlers: GatewayRequestHandlers = {
|
||||
? scheduleGatewaySigusr1Restart({
|
||||
delayMs: restartDelayMs,
|
||||
reason: "update.run",
|
||||
audit: {
|
||||
actor: actor.actor,
|
||||
deviceId: actor.deviceId,
|
||||
clientIp: actor.clientIp,
|
||||
changedPaths: [],
|
||||
},
|
||||
})
|
||||
: null;
|
||||
context?.logGateway?.info(
|
||||
`update.run completed ${formatControlPlaneActor(actor)} changedPaths=<n/a> restartReason=update.run status=${result.status}`,
|
||||
);
|
||||
if (restart?.coalesced) {
|
||||
context?.logGateway?.warn(
|
||||
`update.run restart coalesced ${formatControlPlaneActor(actor)} delayMs=${restart.delayMs}`,
|
||||
);
|
||||
}
|
||||
|
||||
respond(
|
||||
true,
|
||||
|
||||
Reference in New Issue
Block a user