130 lines
3.4 KiB
TypeScript
130 lines
3.4 KiB
TypeScript
/**
|
||
* NAC知识引擎管理后台 - 密钥安全管理模块
|
||
*
|
||
* 安全原则:
|
||
* 1. 所有密钥必须来自环境变量,绝不硬编码
|
||
* 2. 任何必要密钥缺失时,服务拒绝启动
|
||
* 3. 密钥值永远不出现在日志、错误信息或代码中
|
||
* 4. 所有密钥通过此模块统一访问,不直接读取 process.env
|
||
*/
|
||
|
||
type SecretKey =
|
||
| "NAC_MYSQL_URL"
|
||
| "NAC_MONGO_URL"
|
||
| "NAC_JWT_SECRET"
|
||
| "NAC_ADMIN_TOKEN"
|
||
| "DATABASE_URL"
|
||
| "JWT_SECRET";
|
||
|
||
// 必须存在的密钥列表(缺失则服务拒绝启动)
|
||
const REQUIRED_SECRETS: SecretKey[] = [
|
||
"NAC_MYSQL_URL",
|
||
"NAC_MONGO_URL",
|
||
"NAC_JWT_SECRET",
|
||
];
|
||
|
||
// 可选密钥列表(缺失时记录警告但不阻止启动)
|
||
const OPTIONAL_SECRETS: SecretKey[] = [
|
||
"NAC_ADMIN_TOKEN",
|
||
"DATABASE_URL",
|
||
"JWT_SECRET",
|
||
];
|
||
|
||
/**
|
||
* 安全读取单个密钥
|
||
* 密钥值不会出现在任何错误消息中
|
||
*/
|
||
function readSecret(key: SecretKey): string | undefined {
|
||
return process.env[key];
|
||
}
|
||
|
||
/**
|
||
* 验证所有必要密钥是否已配置
|
||
* 在服务启动时调用,任何缺失则抛出错误阻止启动
|
||
*/
|
||
export function validateSecrets(): void {
|
||
const missing: string[] = [];
|
||
|
||
for (const key of REQUIRED_SECRETS) {
|
||
const value = readSecret(key);
|
||
if (!value || value.trim() === "") {
|
||
missing.push(key);
|
||
}
|
||
}
|
||
|
||
if (missing.length > 0) {
|
||
// 只记录缺失的密钥名称,绝不记录任何密钥值
|
||
console.error(`[Secrets] 启动失败:以下必要密钥未配置: ${missing.join(", ")}`);
|
||
console.error("[Secrets] 请通过环境变量配置所有必要密钥,绝不要在代码中硬编码密钥值");
|
||
throw new Error(`缺少必要的安全密钥配置: ${missing.join(", ")}`);
|
||
}
|
||
|
||
for (const key of OPTIONAL_SECRETS) {
|
||
const value = readSecret(key);
|
||
if (!value || value.trim() === "") {
|
||
console.warn(`[Secrets] 可选密钥未配置: ${key}`);
|
||
}
|
||
}
|
||
|
||
console.log("[Secrets] 所有必要密钥验证通过");
|
||
}
|
||
|
||
/**
|
||
* 获取NAC MySQL连接字符串
|
||
* 仅用于建立数据库连接,不得用于日志输出
|
||
*/
|
||
export function getNacMysqlUrl(): string {
|
||
const val = readSecret("NAC_MYSQL_URL");
|
||
if (!val) throw new Error("[Secrets] NAC_MYSQL_URL 未配置");
|
||
return val;
|
||
}
|
||
|
||
/**
|
||
* 获取NAC MongoDB连接字符串
|
||
* 仅用于建立数据库连接,不得用于日志输出
|
||
*/
|
||
export function getNacMongoUrl(): string {
|
||
const val = readSecret("NAC_MONGO_URL");
|
||
if (!val) throw new Error("[Secrets] NAC_MONGO_URL 未配置");
|
||
return val;
|
||
}
|
||
|
||
/**
|
||
* 获取NAC JWT签名密钥
|
||
* 仅用于JWT签名和验证,不得用于日志输出
|
||
*/
|
||
export function getNacJwtSecret(): string {
|
||
const val = readSecret("NAC_JWT_SECRET");
|
||
if (!val) throw new Error("[Secrets] NAC_JWT_SECRET 未配置");
|
||
return val;
|
||
}
|
||
|
||
/**
|
||
* 获取NAC管理员Token
|
||
* 仅用于CIB服务调用,不得用于日志输出
|
||
*/
|
||
export function getNacAdminToken(): string {
|
||
const val = readSecret("NAC_ADMIN_TOKEN");
|
||
if (!val) {
|
||
console.warn("[Secrets] NAC_ADMIN_TOKEN 未配置,CIB管理功能将不可用");
|
||
return "";
|
||
}
|
||
return val;
|
||
}
|
||
|
||
/**
|
||
* 获取系统数据库连接字符串(MySQL/TiDB)
|
||
*/
|
||
export function getDatabaseUrl(): string {
|
||
const val = readSecret("DATABASE_URL");
|
||
if (!val) throw new Error("[Secrets] DATABASE_URL 未配置");
|
||
return val;
|
||
}
|
||
|
||
/**
|
||
* 获取系统JWT密钥(用于Manus会话,可选)
|
||
*/
|
||
export function getJwtSecret(): string {
|
||
return readSecret("JWT_SECRET") ?? "";
|
||
}
|