mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 05:17:28 -08:00
feat(editor): Block the frontend when trying to access n8n from another host over http (#8906)
This commit is contained in:
parent
6955e8991c
commit
669bd830e9
|
@ -100,6 +100,9 @@ export class FrontendService {
|
|||
urlBaseEditor: instanceBaseUrl,
|
||||
binaryDataMode: config.getEnv('binaryDataManager.mode'),
|
||||
versionCli: '',
|
||||
authCookie: {
|
||||
secure: config.getEnv('secure_cookie'),
|
||||
},
|
||||
releaseChannel: config.getEnv('generic.releaseChannel'),
|
||||
oauthCallbackUrls: {
|
||||
oauth1: `${instanceBaseUrl}/${restEndpoint}/oauth1-credential/callback`,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { INodeTypeData, INodeTypeDescription } from 'n8n-workflow';
|
||||
import type { INodeTypeData, INodeTypeDescription, IN8nUISettings } from 'n8n-workflow';
|
||||
import { AGENT_NODE_TYPE, CHAT_TRIGGER_NODE_TYPE, MANUAL_TRIGGER_NODE_TYPE } from '@/constants';
|
||||
import nodeTypesJson from '../../../nodes-base/dist/types/nodes.json';
|
||||
import aiNodeTypesJson from '../../../@n8n/nodes-langchain/dist/types/nodes.json';
|
||||
|
@ -42,3 +42,111 @@ export function mockNodeTypesToArray(nodeTypes: INodeTypeData): INodeTypeDescrip
|
|||
|
||||
export const defaultMockNodeTypesArray: INodeTypeDescription[] =
|
||||
mockNodeTypesToArray(defaultMockNodeTypes);
|
||||
|
||||
export const defaultSettings: IN8nUISettings = {
|
||||
allowedModules: {},
|
||||
communityNodesEnabled: false,
|
||||
defaultLocale: '',
|
||||
endpointForm: '',
|
||||
endpointFormTest: '',
|
||||
endpointFormWaiting: '',
|
||||
endpointWebhook: '',
|
||||
endpointWebhookTest: '',
|
||||
enterprise: {
|
||||
sharing: false,
|
||||
ldap: false,
|
||||
saml: false,
|
||||
logStreaming: false,
|
||||
debugInEditor: false,
|
||||
advancedExecutionFilters: false,
|
||||
variables: true,
|
||||
sourceControl: false,
|
||||
auditLogs: false,
|
||||
showNonProdBanner: false,
|
||||
workflowHistory: false,
|
||||
binaryDataS3: false,
|
||||
externalSecrets: false,
|
||||
workerView: false,
|
||||
advancedPermissions: false,
|
||||
},
|
||||
expressions: {
|
||||
evaluator: 'tournament',
|
||||
},
|
||||
executionMode: 'regular',
|
||||
executionTimeout: 0,
|
||||
hideUsagePage: false,
|
||||
hiringBannerEnabled: false,
|
||||
instanceId: '',
|
||||
isNpmAvailable: false,
|
||||
license: { environment: 'development' },
|
||||
logLevel: 'info',
|
||||
maxExecutionTimeout: 0,
|
||||
oauthCallbackUrls: { oauth1: '', oauth2: '' },
|
||||
onboardingCallPromptEnabled: false,
|
||||
personalizationSurveyEnabled: false,
|
||||
releaseChannel: 'stable',
|
||||
posthog: {
|
||||
apiHost: '',
|
||||
apiKey: '',
|
||||
autocapture: false,
|
||||
debug: false,
|
||||
disableSessionRecording: false,
|
||||
enabled: false,
|
||||
},
|
||||
publicApi: { enabled: false, latestVersion: 0, path: '', swaggerUi: { enabled: false } },
|
||||
pushBackend: 'websocket',
|
||||
saveDataErrorExecution: 'DEFAULT',
|
||||
saveDataSuccessExecution: 'DEFAULT',
|
||||
saveManualExecutions: false,
|
||||
sso: {
|
||||
ldap: { loginEnabled: false, loginLabel: '' },
|
||||
saml: { loginEnabled: false, loginLabel: '' },
|
||||
},
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
templates: { enabled: false, host: '' },
|
||||
timezone: '',
|
||||
urlBaseEditor: '',
|
||||
urlBaseWebhook: '',
|
||||
authCookie: {
|
||||
secure: false,
|
||||
},
|
||||
userManagement: {
|
||||
showSetupOnFirstLoad: false,
|
||||
smtpSetup: true,
|
||||
authenticationMethod: 'email',
|
||||
quota: 10,
|
||||
},
|
||||
versionCli: '',
|
||||
versionNotifications: {
|
||||
enabled: true,
|
||||
endpoint: '',
|
||||
infoUrl: '',
|
||||
},
|
||||
workflowCallerPolicyDefaultOption: 'any',
|
||||
workflowTagsDisabled: false,
|
||||
variables: {
|
||||
limit: -1,
|
||||
},
|
||||
deployment: {
|
||||
type: 'default',
|
||||
},
|
||||
banners: {
|
||||
dismissed: [],
|
||||
},
|
||||
binaryDataMode: 'default',
|
||||
previewMode: false,
|
||||
mfa: {
|
||||
enabled: false,
|
||||
},
|
||||
ai: {
|
||||
enabled: false,
|
||||
provider: '',
|
||||
errorDebugging: false,
|
||||
},
|
||||
workflowHistory: {
|
||||
pruneTime: 0,
|
||||
licensePruneTime: 0,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,99 +1,7 @@
|
|||
import type { Server } from 'miragejs';
|
||||
import { Response } from 'miragejs';
|
||||
import type { AppSchema } from '../types';
|
||||
import type { IN8nUISettings } from 'n8n-workflow';
|
||||
|
||||
const defaultSettings: IN8nUISettings = {
|
||||
allowedModules: {},
|
||||
communityNodesEnabled: false,
|
||||
defaultLocale: '',
|
||||
endpointForm: '',
|
||||
endpointFormTest: '',
|
||||
endpointFormWaiting: '',
|
||||
endpointWebhook: '',
|
||||
endpointWebhookTest: '',
|
||||
enterprise: {
|
||||
sharing: false,
|
||||
ldap: false,
|
||||
saml: false,
|
||||
logStreaming: false,
|
||||
debugInEditor: false,
|
||||
advancedExecutionFilters: false,
|
||||
variables: true,
|
||||
sourceControl: false,
|
||||
auditLogs: false,
|
||||
showNonProdBanner: false,
|
||||
workflowHistory: false,
|
||||
debugInEditor: false,
|
||||
binaryDataS3: false,
|
||||
externalSecrets: false,
|
||||
workerView: false,
|
||||
},
|
||||
expressions: {
|
||||
evaluator: 'tournament',
|
||||
},
|
||||
executionMode: 'regular',
|
||||
executionTimeout: 0,
|
||||
hideUsagePage: false,
|
||||
hiringBannerEnabled: false,
|
||||
instanceId: '',
|
||||
isNpmAvailable: false,
|
||||
license: { environment: 'development' },
|
||||
logLevel: 'info',
|
||||
maxExecutionTimeout: 0,
|
||||
oauthCallbackUrls: { oauth1: '', oauth2: '' },
|
||||
onboardingCallPromptEnabled: false,
|
||||
personalizationSurveyEnabled: false,
|
||||
releaseChannel: 'stable',
|
||||
posthog: {
|
||||
apiHost: '',
|
||||
apiKey: '',
|
||||
autocapture: false,
|
||||
debug: false,
|
||||
disableSessionRecording: false,
|
||||
enabled: false,
|
||||
},
|
||||
publicApi: { enabled: false, latestVersion: 0, path: '', swaggerUi: { enabled: false } },
|
||||
pushBackend: 'websocket',
|
||||
releaseChannel: 'stable',
|
||||
saveDataErrorExecution: 'DEFAULT',
|
||||
saveDataSuccessExecution: 'DEFAULT',
|
||||
saveManualExecutions: false,
|
||||
sso: {
|
||||
ldap: { loginEnabled: false, loginLabel: '' },
|
||||
saml: { loginEnabled: false, loginLabel: '' },
|
||||
},
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
templates: { enabled: false, host: '' },
|
||||
timezone: '',
|
||||
urlBaseEditor: '',
|
||||
urlBaseWebhook: '',
|
||||
userManagement: {
|
||||
showSetupOnFirstLoad: false,
|
||||
smtpSetup: true,
|
||||
authenticationMethod: 'email',
|
||||
},
|
||||
versionCli: '',
|
||||
versionNotifications: {
|
||||
enabled: true,
|
||||
endpoint: '',
|
||||
infoUrl: '',
|
||||
},
|
||||
workflowCallerPolicyDefaultOption: 'any',
|
||||
workflowTagsDisabled: false,
|
||||
variables: {
|
||||
limit: -1,
|
||||
},
|
||||
deployment: {
|
||||
type: 'default',
|
||||
},
|
||||
banners: {
|
||||
dismissed: [],
|
||||
},
|
||||
binaryDataMode: 'default',
|
||||
};
|
||||
import { defaultSettings } from '../../defaults';
|
||||
|
||||
export function routesForSettings(server: Server) {
|
||||
server.get('/rest/settings', (schema: AppSchema) => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { ISettingsState } from '@/Interface';
|
||||
import { UserManagementAuthenticationMethod } from '@/Interface';
|
||||
import { defaultSettings } from './defaults';
|
||||
|
||||
export const retry = async (
|
||||
assertion: () => ReturnType<typeof expect>,
|
||||
|
@ -25,92 +26,7 @@ export const retry = async (
|
|||
export const waitAllPromises = async () => await new Promise((resolve) => setTimeout(resolve));
|
||||
|
||||
export const SETTINGS_STORE_DEFAULT_STATE: ISettingsState = {
|
||||
settings: {
|
||||
allowedModules: {},
|
||||
communityNodesEnabled: false,
|
||||
defaultLocale: '',
|
||||
endpointForm: '',
|
||||
endpointFormTest: '',
|
||||
endpointFormWaiting: '',
|
||||
endpointWebhook: '',
|
||||
endpointWebhookTest: '',
|
||||
enterprise: {
|
||||
advancedExecutionFilters: false,
|
||||
sharing: false,
|
||||
ldap: false,
|
||||
saml: false,
|
||||
logStreaming: false,
|
||||
variables: false,
|
||||
sourceControl: false,
|
||||
auditLogs: false,
|
||||
},
|
||||
executionMode: 'regular',
|
||||
executionTimeout: 0,
|
||||
hideUsagePage: false,
|
||||
hiringBannerEnabled: false,
|
||||
instanceId: '',
|
||||
isNpmAvailable: false,
|
||||
license: { environment: 'production' },
|
||||
logLevel: 'info',
|
||||
maxExecutionTimeout: 0,
|
||||
oauthCallbackUrls: { oauth1: '', oauth2: '' },
|
||||
onboardingCallPromptEnabled: false,
|
||||
personalizationSurveyEnabled: false,
|
||||
posthog: {
|
||||
apiHost: '',
|
||||
apiKey: '',
|
||||
autocapture: false,
|
||||
debug: false,
|
||||
disableSessionRecording: false,
|
||||
enabled: false,
|
||||
},
|
||||
publicApi: { enabled: false, latestVersion: 0, path: '', swaggerUi: { enabled: false } },
|
||||
pushBackend: 'sse',
|
||||
saveDataErrorExecution: 'all',
|
||||
saveDataSuccessExecution: 'all',
|
||||
saveManualExecutions: false,
|
||||
sso: {
|
||||
ldap: { loginEnabled: false, loginLabel: '' },
|
||||
saml: { loginEnabled: false, loginLabel: '' },
|
||||
},
|
||||
telemetry: { enabled: false },
|
||||
templates: { enabled: false, host: '' },
|
||||
timezone: '',
|
||||
urlBaseEditor: '',
|
||||
urlBaseWebhook: '',
|
||||
userManagement: {
|
||||
enabled: false,
|
||||
smtpSetup: false,
|
||||
authenticationMethod: UserManagementAuthenticationMethod.Email,
|
||||
},
|
||||
versionCli: '',
|
||||
versionNotifications: {
|
||||
enabled: false,
|
||||
endpoint: '',
|
||||
infoUrl: '',
|
||||
},
|
||||
workflowCallerPolicyDefaultOption: 'any',
|
||||
workflowTagsDisabled: false,
|
||||
deployment: {
|
||||
type: 'default',
|
||||
},
|
||||
variables: {
|
||||
limit: 100,
|
||||
},
|
||||
expressions: {
|
||||
evaluator: 'tournament',
|
||||
},
|
||||
banners: {
|
||||
dismissed: [],
|
||||
},
|
||||
ai: {
|
||||
enabled: false,
|
||||
},
|
||||
workflowHistory: {
|
||||
pruneTime: -1,
|
||||
licensePruneTime: -1,
|
||||
},
|
||||
},
|
||||
settings: defaultSettings,
|
||||
promptsData: {
|
||||
message: '',
|
||||
title: '',
|
||||
|
@ -118,10 +34,10 @@ export const SETTINGS_STORE_DEFAULT_STATE: ISettingsState = {
|
|||
showValueSurvey: false,
|
||||
},
|
||||
userManagement: {
|
||||
enabled: false,
|
||||
showSetupOnFirstLoad: false,
|
||||
smtpSetup: false,
|
||||
authenticationMethod: UserManagementAuthenticationMethod.Email,
|
||||
quota: defaultSettings.userManagement.quota,
|
||||
},
|
||||
templatesEndpointHealthy: false,
|
||||
api: {
|
||||
|
|
|
@ -759,3 +759,19 @@ export const ROLE = {
|
|||
Admin: 'global:admin',
|
||||
Default: 'default', // default user with no email when setting up instance
|
||||
} as const;
|
||||
|
||||
export const INSECURE_CONNECTION_WARNING = `
|
||||
<body style="margin-top: 20px; font-family: 'Open Sans', sans-serif; text-align: center;">
|
||||
<h1 style="font-size: 40px">🚫</h1>
|
||||
<h2>Your n8n server is configured to use a secure cookie, <br/>however you are visiting this via an insecure URL
|
||||
</h2>
|
||||
<br/>
|
||||
<div style="font-size: 18px; max-width: 640px; text-align: left; margin: 10px auto">
|
||||
To fix this, please consider the following options:
|
||||
<ul>
|
||||
<li>Setup TLS/HTTPS (<strong>recommended</strong>), or</li>
|
||||
<li>If you are running this locally, try using <a href="http://localhost:5678">localhost</a> instead</li>
|
||||
<li>If you prefer to disable this security feature (<strong>not recommended</strong>), set the environment variable <code>N8N_SECURE_COOKIE</code> to <code>false</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>`;
|
||||
|
|
|
@ -7,6 +7,7 @@ import { useTelemetryStore } from '@/stores/telemetry.store';
|
|||
import type { IN8nUISettings } from 'n8n-workflow';
|
||||
import { LOCAL_STORAGE_EXPERIMENT_OVERRIDES } from '@/constants';
|
||||
import { nextTick } from 'vue';
|
||||
import { defaultSettings } from '../../__tests__/defaults';
|
||||
|
||||
const DEFAULT_POSTHOG_SETTINGS: IN8nUISettings['posthog'] = {
|
||||
enabled: true,
|
||||
|
@ -21,6 +22,7 @@ const CURRENT_INSTANCE_ID = '456';
|
|||
|
||||
function setSettings(overrides?: Partial<IN8nUISettings>) {
|
||||
useSettingsStore().setSettings({
|
||||
...defaultSettings,
|
||||
posthog: DEFAULT_POSTHOG_SETTINGS,
|
||||
instanceId: CURRENT_INSTANCE_ID,
|
||||
...overrides,
|
||||
|
|
|
@ -9,7 +9,12 @@ import {
|
|||
import { getPromptsData, getSettings, submitContactInfo, submitValueSurvey } from '@/api/settings';
|
||||
import { testHealthEndpoint } from '@/api/templates';
|
||||
import type { EnterpriseEditionFeature } from '@/constants';
|
||||
import { CONTACT_PROMPT_MODAL_KEY, STORES, VALUE_SURVEY_MODAL_KEY } from '@/constants';
|
||||
import {
|
||||
CONTACT_PROMPT_MODAL_KEY,
|
||||
STORES,
|
||||
VALUE_SURVEY_MODAL_KEY,
|
||||
INSECURE_CONNECTION_WARNING,
|
||||
} from '@/constants';
|
||||
import type {
|
||||
ILdapConfig,
|
||||
IN8nPromptResponse,
|
||||
|
@ -248,6 +253,15 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
|
|||
useRootStore().setVersionCli(settings.versionCli);
|
||||
}
|
||||
|
||||
if (
|
||||
settings.authCookie.secure &&
|
||||
location.protocol === 'http:' &&
|
||||
!['localhost', '127.0.0.1'].includes(location.hostname)
|
||||
) {
|
||||
document.write(INSECURE_CONNECTION_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
const isV1BannerDismissedPermanently = (settings.banners?.dismissed || []).includes('V1');
|
||||
if (!isV1BannerDismissedPermanently && useRootStore().versionCli.startsWith('1.')) {
|
||||
useUIStore().pushBannerToStack('V1');
|
||||
|
|
|
@ -2477,6 +2477,9 @@ export interface IN8nUISettings {
|
|||
urlBaseWebhook: string;
|
||||
urlBaseEditor: string;
|
||||
versionCli: string;
|
||||
authCookie: {
|
||||
secure: boolean;
|
||||
};
|
||||
binaryDataMode: string;
|
||||
releaseChannel: 'stable' | 'beta' | 'nightly' | 'dev';
|
||||
n8nMetadata?: {
|
||||
|
|
Loading…
Reference in a new issue