mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
fix(core): Prevent issues with missing or mismatching encryption key (#8332)
This commit is contained in:
parent
7bb2d1799e
commit
d4c93b1607
|
@ -259,6 +259,13 @@ export class Worker extends BaseCommand {
|
|||
|
||||
constructor(argv: string[], cmdConfig: IConfig) {
|
||||
super(argv, cmdConfig);
|
||||
|
||||
if (!process.env.N8N_ENCRYPTION_KEY) {
|
||||
throw new ApplicationError(
|
||||
'Missing encryption key. Worker started without the required N8N_ENCRYPTION_KEY env var. More information: https://docs.n8n.io/hosting/environment-variables/configuration-methods/#encryption-key',
|
||||
);
|
||||
}
|
||||
|
||||
this.setInstanceType('worker');
|
||||
this.setInstanceQueueModeId();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ if (inE2ETests) {
|
|||
process.env.N8N_AI_ENABLED = 'true';
|
||||
} else if (inTest) {
|
||||
process.env.N8N_LOG_LEVEL = 'silent';
|
||||
process.env.N8N_ENCRYPTION_KEY = 'test-encryption-key';
|
||||
process.env.N8N_ENCRYPTION_KEY = 'test_key';
|
||||
process.env.N8N_PUBLIC_API_DISABLED = 'true';
|
||||
process.env.SKIP_STATISTICS_EVENTS = 'true';
|
||||
} else {
|
||||
|
|
|
@ -11,6 +11,6 @@ process.env.N8N_USER_FOLDER = testDir;
|
|||
|
||||
writeFileSync(
|
||||
join(testDir, '.n8n/config'),
|
||||
JSON.stringify({ encryptionKey: 'testkey', instanceId: '123' }),
|
||||
JSON.stringify({ encryptionKey: 'test_key', instanceId: '123' }),
|
||||
'utf-8',
|
||||
);
|
||||
|
|
|
@ -41,9 +41,9 @@ export class Credentials extends ICredentials {
|
|||
throw new ApplicationError('No data is set so nothing can be returned.');
|
||||
}
|
||||
|
||||
const decryptedData = this.cipher.decrypt(this.data);
|
||||
|
||||
try {
|
||||
const decryptedData = this.cipher.decrypt(this.data);
|
||||
|
||||
return jsonParse(decryptedData);
|
||||
} catch (e) {
|
||||
throw new ApplicationError(
|
||||
|
|
|
@ -2,7 +2,7 @@ import path from 'path';
|
|||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { createHash, randomBytes } from 'crypto';
|
||||
import { Service } from 'typedi';
|
||||
import { jsonParse } from 'n8n-workflow';
|
||||
import { ApplicationError, jsonParse } from 'n8n-workflow';
|
||||
|
||||
interface ReadOnlySettings {
|
||||
encryptionKey: string;
|
||||
|
@ -14,6 +14,8 @@ interface WritableSettings {
|
|||
|
||||
type Settings = ReadOnlySettings & WritableSettings;
|
||||
|
||||
const inTest = process.env.NODE_ENV === 'test';
|
||||
|
||||
@Service()
|
||||
export class InstanceSettings {
|
||||
private readonly userHome = this.getUserHome();
|
||||
|
@ -57,26 +59,44 @@ export class InstanceSettings {
|
|||
return process.env.N8N_USER_FOLDER ?? process.env[homeVarName] ?? process.cwd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load instance settings from the settings file. If missing, create a new
|
||||
* settings file with an auto-generated encryption key.
|
||||
*/
|
||||
private loadOrCreate(): Settings {
|
||||
let settings: Settings;
|
||||
const { settingsFile } = this;
|
||||
if (existsSync(settingsFile)) {
|
||||
const content = readFileSync(settingsFile, 'utf8');
|
||||
settings = jsonParse(content, {
|
||||
errorMessage: `Error parsing n8n-config file "${settingsFile}". It does not seem to be valid JSON.`,
|
||||
if (existsSync(this.settingsFile)) {
|
||||
const content = readFileSync(this.settingsFile, 'utf8');
|
||||
|
||||
const settings = jsonParse<Settings>(content, {
|
||||
errorMessage: `Error parsing n8n-config file "${this.settingsFile}". It does not seem to be valid JSON.`,
|
||||
});
|
||||
} else {
|
||||
// Ensure that the `.n8n` folder exists
|
||||
mkdirSync(this.n8nFolder, { recursive: true });
|
||||
// If file doesn't exist, create new settings
|
||||
const encryptionKey = process.env.N8N_ENCRYPTION_KEY ?? randomBytes(24).toString('base64');
|
||||
settings = { encryptionKey };
|
||||
this.save(settings);
|
||||
// console.info(`UserSettings were generated and saved to: ${settingsFile}`);
|
||||
|
||||
if (!inTest) console.info(`User settings loaded from: ${this.settingsFile}`);
|
||||
|
||||
const { encryptionKey, tunnelSubdomain } = settings;
|
||||
|
||||
if (process.env.N8N_ENCRYPTION_KEY && encryptionKey !== process.env.N8N_ENCRYPTION_KEY) {
|
||||
throw new ApplicationError(
|
||||
`Mismatching encryption keys. The encryption key in the settings file ${this.settingsFile} does not match the N8N_ENCRYPTION_KEY env var. Please make sure both keys match. More information: https://docs.n8n.io/hosting/environment-variables/configuration-methods/#encryption-key`,
|
||||
);
|
||||
}
|
||||
|
||||
return { encryptionKey, tunnelSubdomain };
|
||||
}
|
||||
|
||||
const { encryptionKey, tunnelSubdomain } = settings;
|
||||
return { encryptionKey, tunnelSubdomain };
|
||||
mkdirSync(this.n8nFolder, { recursive: true });
|
||||
|
||||
const encryptionKey = process.env.N8N_ENCRYPTION_KEY ?? randomBytes(24).toString('base64');
|
||||
|
||||
const settings: Settings = { encryptionKey };
|
||||
|
||||
this.save(settings);
|
||||
|
||||
if (!inTest && !process.env.N8N_ENCRYPTION_KEY) {
|
||||
console.info(`No encryption key found - Auto-generated and saved to: ${this.settingsFile}`);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
private generateInstanceId() {
|
||||
|
|
|
@ -24,6 +24,12 @@ describe('InstanceSettings', () => {
|
|||
readSpy.mockReturnValue('{"encryptionKey":"test_key"');
|
||||
expect(() => new InstanceSettings()).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw if the env and file keys do not match', () => {
|
||||
readSpy.mockReturnValue(JSON.stringify({ encryptionKey: 'key_1' }));
|
||||
process.env.N8N_ENCRYPTION_KEY = 'key_2';
|
||||
expect(() => new InstanceSettings()).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('If the settings file does not exist', () => {
|
||||
|
|
|
@ -1259,38 +1259,22 @@ export class HttpRequestV3 implements INodeType {
|
|||
genericCredentialType = this.getNodeParameter('genericAuthType', 0) as string;
|
||||
|
||||
if (genericCredentialType === 'httpBasicAuth') {
|
||||
try {
|
||||
httpBasicAuth = await this.getCredentials('httpBasicAuth', itemIndex);
|
||||
} catch {}
|
||||
httpBasicAuth = await this.getCredentials('httpBasicAuth', itemIndex);
|
||||
} else if (genericCredentialType === 'httpDigestAuth') {
|
||||
try {
|
||||
httpDigestAuth = await this.getCredentials('httpDigestAuth', itemIndex);
|
||||
} catch {}
|
||||
httpDigestAuth = await this.getCredentials('httpDigestAuth', itemIndex);
|
||||
} else if (genericCredentialType === 'httpHeaderAuth') {
|
||||
try {
|
||||
httpHeaderAuth = await this.getCredentials('httpHeaderAuth', itemIndex);
|
||||
} catch {}
|
||||
httpHeaderAuth = await this.getCredentials('httpHeaderAuth', itemIndex);
|
||||
} else if (genericCredentialType === 'httpQueryAuth') {
|
||||
try {
|
||||
httpQueryAuth = await this.getCredentials('httpQueryAuth', itemIndex);
|
||||
} catch {}
|
||||
httpQueryAuth = await this.getCredentials('httpQueryAuth', itemIndex);
|
||||
} else if (genericCredentialType === 'httpCustomAuth') {
|
||||
try {
|
||||
httpCustomAuth = await this.getCredentials('httpCustomAuth', itemIndex);
|
||||
} catch {}
|
||||
httpCustomAuth = await this.getCredentials('httpCustomAuth', itemIndex);
|
||||
} else if (genericCredentialType === 'oAuth1Api') {
|
||||
try {
|
||||
oAuth1Api = await this.getCredentials('oAuth1Api', itemIndex);
|
||||
} catch {}
|
||||
oAuth1Api = await this.getCredentials('oAuth1Api', itemIndex);
|
||||
} else if (genericCredentialType === 'oAuth2Api') {
|
||||
try {
|
||||
oAuth2Api = await this.getCredentials('oAuth2Api', itemIndex);
|
||||
} catch {}
|
||||
oAuth2Api = await this.getCredentials('oAuth2Api', itemIndex);
|
||||
}
|
||||
} else if (authentication === 'predefinedCredentialType') {
|
||||
try {
|
||||
nodeCredentialType = this.getNodeParameter('nodeCredentialType', 0) as string;
|
||||
} catch {}
|
||||
nodeCredentialType = this.getNodeParameter('nodeCredentialType', 0) as string;
|
||||
}
|
||||
|
||||
const requestMethod = this.getNodeParameter('method', itemIndex) as string;
|
||||
|
|
Loading…
Reference in a new issue