fix(core): Fix N8N_ENCRYPTION_KEY_FILE environment variable not working (#20230)

This commit is contained in:
JHTosas
2025-10-07 16:41:06 +03:00
committed by GitHub
parent ca84331761
commit 502dd71811
3 changed files with 18 additions and 17 deletions
@@ -12,6 +12,14 @@ export class InstanceSettingsConfig {
@Env('N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS')
enforceSettingsFilePermissions: boolean = false;
/**
* Encryption key to use for encrypting and decrypting credentials.
* If none is provided, a random key will be generated and saved to the settings file on the first launch.
* Can be provided directly via N8N_ENCRYPTION_KEY or via a file path using N8N_ENCRYPTION_KEY_FILE.
*/
@Env('N8N_ENCRYPTION_KEY')
encryptionKey: string = '';
/**
* The home folder path of the user.
* If none can be found it falls back to the current working directory
@@ -51,15 +51,13 @@ describe('InstanceSettings', () => {
it('should throw if the env and file keys do not match', () => {
mockFs.readFileSync.mockReturnValue(JSON.stringify({ encryptionKey: 'key_1' }));
process.env.N8N_ENCRYPTION_KEY = 'key_2';
expect(() => createInstanceSettings()).toThrowError();
expect(() => createInstanceSettings({ encryptionKey: 'key_2' })).toThrowError();
});
it('should check if the settings file has the correct permissions', () => {
process.env.N8N_ENCRYPTION_KEY = 'test_key';
mockFs.readFileSync.mockReturnValueOnce(JSON.stringify({ encryptionKey: 'test_key' }));
mockFs.statSync.mockReturnValueOnce({ mode: 0o600 } as fs.Stats);
const settings = createInstanceSettings();
const settings = createInstanceSettings({ encryptionKey: 'test_key' });
expect(settings.encryptionKey).toEqual('test_key');
expect(settings.instanceId).toEqual(
'6ce26c63596f0cc4323563c529acfca0cccb0e57f6533d79a60a42c9ff862ae7',
@@ -103,8 +101,7 @@ describe('InstanceSettings', () => {
});
it('should create a new settings file without explicit permissions if N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS is not set', () => {
process.env.N8N_ENCRYPTION_KEY = 'key_2';
const settings = createInstanceSettings();
const settings = createInstanceSettings({ encryptionKey: 'key_2' });
expect(settings.encryptionKey).not.toEqual('test_key');
expect(mockFs.mkdirSync).toHaveBeenCalledWith('/test/.n8n', { recursive: true });
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
@@ -119,8 +116,7 @@ describe('InstanceSettings', () => {
it('should create a new settings file without explicit permissions if N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false', () => {
process.env.N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS = 'false';
process.env.N8N_ENCRYPTION_KEY = 'key_2';
const settings = createInstanceSettings();
const settings = createInstanceSettings({ encryptionKey: 'key_2' });
expect(settings.encryptionKey).not.toEqual('test_key');
expect(mockFs.mkdirSync).toHaveBeenCalledWith('/test/.n8n', { recursive: true });
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
@@ -135,9 +131,9 @@ describe('InstanceSettings', () => {
it('should create a new settings file with explicit permissions if N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true', () => {
process.env.N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS = 'true';
process.env.N8N_ENCRYPTION_KEY = 'key_2';
const settings = createInstanceSettings({
enforceSettingsFilePermissions: true,
encryptionKey: 'key_2',
});
expect(settings.encryptionKey).not.toEqual('test_key');
expect(mockFs.mkdirSync).toHaveBeenCalledWith('/test/.n8n', { recursive: true });
@@ -151,9 +147,8 @@ describe('InstanceSettings', () => {
);
});
it('should pick up the encryption key from env var N8N_ENCRYPTION_KEY', () => {
process.env.N8N_ENCRYPTION_KEY = 'env_key';
const settings = createInstanceSettings();
it('should pick up the encryption key from config', () => {
const settings = createInstanceSettings({ encryptionKey: 'env_key' });
expect(settings.encryptionKey).toEqual('env_key');
expect(settings.instanceId).toEqual(
'2c70e12b7a0646f92279f427c7b38e7334d8e5389cff167a1dc30e73f826b683',
@@ -171,9 +166,8 @@ describe('InstanceSettings', () => {
});
it("should not set the permissions of the settings file if 'N8N_IGNORE_SETTINGS_FILE_PERMISSIONS' is true", () => {
process.env.N8N_ENCRYPTION_KEY = 'key_2';
process.env.N8N_IGNORE_SETTINGS_FILE_PERMISSIONS = 'true';
const settings = createInstanceSettings();
const settings = createInstanceSettings({ encryptionKey: 'key_2' });
expect(settings.encryptionKey).not.toEqual('test_key');
expect(mockFs.mkdirSync).toHaveBeenCalledWith('/test/.n8n', { recursive: true });
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
@@ -195,11 +189,10 @@ describe('InstanceSettings', () => {
describe('constructor', () => {
it('should generate a `hostId`', () => {
const encryptionKey = 'test_key';
process.env.N8N_ENCRYPTION_KEY = encryptionKey;
mockFs.existsSync.mockReturnValueOnce(true);
mockFs.readFileSync.mockReturnValueOnce(JSON.stringify({ encryptionKey }));
const settings = createInstanceSettings();
const settings = createInstanceSettings({ encryptionKey });
const [instanceType, hostId] = settings.hostId.split('-');
expect(instanceType).toEqual('main');
@@ -175,7 +175,7 @@ export class InstanceSettings {
* settings file with an auto-generated encryption key.
*/
private loadOrCreate(): Settings {
const encryptionKeyFromEnv = process.env.N8N_ENCRYPTION_KEY;
const encryptionKeyFromEnv = this.config.encryptionKey || undefined;
if (existsSync(this.settingsFile)) {
const content = readFileSync(this.settingsFile, 'utf8');
this.ensureSettingsFilePermissions();