fix: allow degraded startup without Zulip keys
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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格式
|
||||
*
|
||||
|
||||
@@ -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字符');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user