From 77302354accd49edc4f4133249a996eb51c96c4a Mon Sep 17 00:00:00 2001 From: ANG-Server <96008766+ANGJustinl@users.noreply.github.com> Date: Mon, 20 Jul 2026 23:43:23 +0800 Subject: [PATCH] fix: allow degraded startup without Zulip keys --- .env.example | 3 +- .../services/api_key_security.service.ts | 36 ++++++++++++++++--- src/core/zulip_core/zulip.config.ts | 6 +++- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 14d844d..b7c6a4f 100644 --- a/.env.example +++ b/.env.example @@ -66,7 +66,8 @@ ZULIP_MESSAGE_MAX_LENGTH=10000 ZULIP_CONTENT_FILTER_ENABLED=true # Realtime server -WEBSOCKET_PORT=3000 +# The native WebSocket server listens separately from the REST API. +WEBSOCKET_PORT=3001 WEBSOCKET_NAMESPACE=/game WEBSOCKET_PING_INTERVAL=25000 WEBSOCKET_PING_TIMEOUT=5000 diff --git a/src/core/zulip_core/services/api_key_security.service.ts b/src/core/zulip_core/services/api_key_security.service.ts index a21cae2..24315d1 100644 --- a/src/core/zulip_core/services/api_key_security.service.ts +++ b/src/core/zulip_core/services/api_key_security.service.ts @@ -147,7 +147,7 @@ export class ApiKeySecurityService implements IApiKeySecurityService { private readonly SECURITY_LOG_RETENTION = 30 * 24 * 3600; // 30天 // 加密密钥(生产环境应从环境变量或密钥管理服务获取) - private readonly encryptionKey: Buffer; + private readonly encryptionKey: Buffer | null; constructor( @Inject('REDIS_SERVICE') @@ -155,9 +155,19 @@ export class ApiKeySecurityService implements IApiKeySecurityService { ) { // 加密密钥必须由部署环境提供,不允许回退到共享的固定密钥。 const keyFromEnv = process.env.ZULIP_API_KEY_ENCRYPTION_KEY; + const degradedModeEnabled = process.env.ZULIP_DEGRADED_MODE_ENABLED === 'true'; if (!keyFromEnv) { - throw new Error('ZULIP_API_KEY_ENCRYPTION_KEY未配置'); + this.encryptionKey = null; + + if (!degradedModeEnabled) { + throw new Error('ZULIP_API_KEY_ENCRYPTION_KEY未配置'); + } + + this.logger.warn( + 'Zulip降级模式已启用且未配置API Key加密密钥,API Key加密存取功能不可用', + ); + return; } // 如果环境变量是十六进制格式,使用hex解析;否则使用utf8。 @@ -736,10 +746,11 @@ export class ApiKeySecurityService implements IApiKeySecurityService { iv: string; authTag: string; } { + const encryptionKey = this.requireEncryptionKey(); const iv = crypto.randomBytes(this.IV_LENGTH); const cipher = crypto.createCipheriv( this.ENCRYPTION_ALGORITHM, - this.encryptionKey, + encryptionKey, iv ); @@ -764,11 +775,12 @@ export class ApiKeySecurityService implements IApiKeySecurityService { * @private */ private decrypt(encryptedData: string, ivHex: string, authTagHex: string): string { + const encryptionKey = this.requireEncryptionKey(); const iv = Buffer.from(ivHex, 'hex'); const authTag = Buffer.from(authTagHex, 'hex'); const decipher = crypto.createDecipheriv( this.ENCRYPTION_ALGORITHM, - this.encryptionKey, + encryptionKey, iv ); decipher.setAuthTag(authTag); @@ -779,6 +791,22 @@ export class ApiKeySecurityService implements IApiKeySecurityService { return decrypted; } + /** + * 获取已配置的加密密钥。 + * + * 降级模式允许服务在没有密钥时启动,但涉及Zulip API Key明文的操作仍必须失败, + * 以避免使用临时密钥导致数据在重启后无法解密。 + */ + private requireEncryptionKey(): Buffer { + if (!this.encryptionKey) { + throw new Error( + 'ZULIP_API_KEY_ENCRYPTION_KEY未配置,Zulip API Key加密存取功能不可用', + ); + } + + return this.encryptionKey; + } + /** * 验证API Key格式 * diff --git a/src/core/zulip_core/zulip.config.ts b/src/core/zulip_core/zulip.config.ts index 8808196..6794e94 100644 --- a/src/core/zulip_core/zulip.config.ts +++ b/src/core/zulip_core/zulip.config.ts @@ -349,7 +349,11 @@ export function validateZulipConfig( // 生产环境特殊验证 if (isProduction) { if (!config.security.apiKeyEncryptionKey) { - errors.push('生产环境必须配置API Key加密密钥 (ZULIP_API_KEY_ENCRYPTION_KEY)'); + if (config.errorHandling.degradedModeEnabled) { + warnings.push('降级模式未配置API Key加密密钥,Zulip API Key加密存取功能不可用'); + } else { + errors.push('生产环境必须配置API Key加密密钥 (ZULIP_API_KEY_ENCRYPTION_KEY)'); + } } else if (config.security.apiKeyEncryptionKey.length < 32) { errors.push('API Key加密密钥长度必须至少32字符'); }