Gateway: add SecretRef support for gateway.auth.token with auth-mode guardrails (#35094)

This commit is contained in:
Josh Avant
2026-03-05 12:53:56 -06:00
committed by GitHub
parent bc66a8fa81
commit 72cf9253fc
112 changed files with 5750 additions and 465 deletions

View File

@@ -5,6 +5,7 @@ import path from "node:path";
import { promisify } from "node:util";
import type { OpenClawConfig } from "../config/config.js";
import { resolveGatewayPort, resolveIsNixMode } from "../config/paths.js";
import { resolveSecretInputRef } from "../config/types.secrets.js";
import {
findExtraGatewayServices,
renderGatewayServiceCleanupHints,
@@ -22,7 +23,9 @@ import type { RuntimeEnv } from "../runtime.js";
import { note } from "../terminal/note.js";
import { buildGatewayInstallPlan } from "./daemon-install-helpers.js";
import { DEFAULT_GATEWAY_DAEMON_RUNTIME, type GatewayDaemonRuntime } from "./daemon-runtime.js";
import { resolveGatewayAuthTokenForService } from "./doctor-gateway-auth-token.js";
import type { DoctorOptions, DoctorPrompter } from "./doctor-prompter.js";
import { resolveGatewayInstallToken } from "./gateway-install-token.js";
const execFileAsync = promisify(execFile);
@@ -55,16 +58,6 @@ function normalizeExecutablePath(value: string): string {
return path.resolve(value);
}
function resolveGatewayAuthToken(cfg: OpenClawConfig, env: NodeJS.ProcessEnv): string | undefined {
const configToken = cfg.gateway?.auth?.token?.trim();
if (configToken) {
return configToken;
}
const envToken = env.OPENCLAW_GATEWAY_TOKEN ?? env.CLAWDBOT_GATEWAY_TOKEN;
const trimmedEnvToken = envToken?.trim();
return trimmedEnvToken || undefined;
}
function extractDetailPath(detail: string, prefix: string): string | null {
if (!detail.startsWith(prefix)) {
return null;
@@ -219,12 +212,35 @@ export async function maybeRepairGatewayServiceConfig(
return;
}
const expectedGatewayToken = resolveGatewayAuthToken(cfg, process.env);
const tokenRefConfigured = Boolean(
resolveSecretInputRef({
value: cfg.gateway?.auth?.token,
defaults: cfg.secrets?.defaults,
}).ref,
);
const gatewayTokenResolution = await resolveGatewayAuthTokenForService(cfg, process.env);
if (gatewayTokenResolution.unavailableReason) {
note(
`Unable to verify gateway service token drift: ${gatewayTokenResolution.unavailableReason}`,
"Gateway service config",
);
}
const expectedGatewayToken = tokenRefConfigured ? undefined : gatewayTokenResolution.token;
const audit = await auditGatewayServiceConfig({
env: process.env,
command,
expectedGatewayToken,
});
const serviceToken = command.environment?.OPENCLAW_GATEWAY_TOKEN?.trim();
if (tokenRefConfigured && serviceToken) {
audit.issues.push({
code: SERVICE_AUDIT_CODES.gatewayTokenMismatch,
message:
"Gateway service OPENCLAW_GATEWAY_TOKEN should be unset when gateway.auth.token is SecretRef-managed",
detail: "service token is stale",
level: "recommended",
});
}
const needsNodeRuntime = needsNodeRuntimeMigration(audit.issues);
const systemNodeInfo = needsNodeRuntime
? await resolveSystemNodeInfo({ env: process.env })
@@ -243,10 +259,24 @@ export async function maybeRepairGatewayServiceConfig(
const port = resolveGatewayPort(cfg, process.env);
const runtimeChoice = detectGatewayRuntime(command.programArguments);
const installTokenResolution = await resolveGatewayInstallToken({
config: cfg,
env: process.env,
});
for (const warning of installTokenResolution.warnings) {
note(warning, "Gateway service config");
}
if (installTokenResolution.unavailableReason) {
note(
`Unable to verify gateway service token drift: ${installTokenResolution.unavailableReason}`,
"Gateway service config",
);
return;
}
const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
env: process.env,
port,
token: expectedGatewayToken,
token: installTokenResolution.token,
runtime: needsNodeRuntime && systemNodePath ? "node" : runtimeChoice,
nodePath: systemNodePath ?? undefined,
warn: (message, title) => note(message, title),