{"record":{"id":"ef7f08d7d41e445f","repo":"nocodb/nocodb","slug":"connection-to-internal-hosts-is-not-allowed","errorCode":null,"errorMessage":"Connection to internal hosts is not allowed","messagePattern":"Connection to internal hosts is not allowed","errorType":"exception","errorClass":"SsrfBlockedHostError","httpStatus":null,"severity":"error","filePath":"packages/noco-integrations/core/src/utils/externalDbSsrf.ts","lineNumber":87,"sourceCode":"}\n\n/**\n * Throws `SsrfBlockedHostError` if `host` resolves to a non-routable range.\n * No-op when SSRF protection is disabled (see `isDbSsrfProtectionEnabled`).\n */\nexport async function assertExternalDbHostAllowed(\n  host: unknown,\n): Promise<void> {\n  if (!isDbSsrfProtectionEnabled()) return;\n  if (typeof host !== 'string' || host.length === 0) return;\n\n  const trimmed = host.trim();\n  if (\n    trimmed === '0.0.0.0' ||\n    trimmed === '::' ||\n    /^localhost$/i.test(trimmed)\n  ) {\n    throw new SsrfBlockedHostError();\n  }\n\n  // TOCTOU note: the driver re-resolves at connect-time; a controlled DNS\n  // record with short TTL could flip between this lookup and the driver's\n  // connect(). Mitigating fully requires passing the resolved IP to the\n  // driver, which is per-driver wiring out of scope here.\n  let resolvedIps: string[] = [];\n  if (isIP(trimmed)) {\n    resolvedIps = [trimmed];\n  } else {\n    try {\n      const records = await dns.lookup(trimmed, { all: true });\n      resolvedIps = records.map((r) => r.address);\n    } catch {\n      // Let the driver surface DNS failures.\n      return;\n    }\n  }","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/nocodb/nocodb/blob/d3caaf4e890acf64bd49b788cf6dc32b7644c895/packages/noco-integrations/core/src/utils/externalDbSsrf.ts#L69-L105","documentation":"Thrown by assertExternalDbHostAllowed when the external-database SSRF guard sees a hostname that is literally a non-routable sentinel: '0.0.0.0', '::', or any case variant of 'localhost'. This is the literal-hostname branch — it fires before any DNS lookup, so even a hosts-file remap will not bypass it. The guard is enabled unless NC_DISABLE_SSRF_PROTECTION=true, NC_ALLOW_LOCAL_EXTERNAL_DBS=true (self-hosted only), or the backend forced it off; on cloud, forceEnforce is always on.","triggerScenarios":"An integration tries to open an external MSSQL/Postgres/MySQL/etc. connection where the configured host string equals 'localhost', '0.0.0.0', or '::' (IPv6 any). Also triggered by UI 'Test Connection' or workflow node setup that submits such a host while SSRF protection is enabled.","commonSituations":"Developer running NocoDB + a local database on the same host and pointing an integration at 'localhost'; a config export that used 0.0.0.0 as a wildcard bind address mistakenly reused as the connect target; an IPv6-only dev box with '::' as the host.","solutions":["Point the integration at the actual reachable address (the database's private IP, a DNS name that resolves to it, or the loopback IP if you intentionally mean localhost AND you enable the bypass).","On self-hosted only: set NC_ALLOW_LOCAL_EXTERNAL_DBS=true (preferred, scoped to external DBs) or NC_DISABLE_SSRF_PROTECTION=true (broader) and restart the server.","If the DB lives on another machine, use its hostname or non-loopback IP — do not use 'localhost' as a synonym for 'the DB server'.","On cloud this guard cannot be disabled; move the database to a routable address reachable from the cloud network."],"exampleFix":"// before\nnew MssqlIntegration({ host: 'localhost', port: 1433, ... });\n// -> SsrfBlockedHostError: Connection to internal hosts is not allowed\n\n// after (self-hosted, intentional)\n// .env: NC_ALLOW_LOCAL_EXTERNAL_DBS=true\nnew MssqlIntegration({ host: 'localhost', port: 1433, ... });","handlingStrategy":"validation","validationCode":"import { isIP } from 'net';\n\nfunction isLiteralInternalHost(host: string): boolean {\n  const t = host.trim().toLowerCase();\n  return t === 'localhost' || t === '0.0.0.0' || t === '::';\n}\n\n// before opening the connection\nif (isLiteralInternalHost(host) && process.env.NC_ALLOW_LOCAL_EXTERNAL_DBS !== 'true') {\n  throw new Error(`Refusing localhost/any-address host '${host}' under SSRF protection`);\n}","typeGuard":"function isNonRoutableLiteral(host: unknown): host is string {\n  return (\n    typeof host === 'string' &&\n    ['localhost', '0.0.0.0', '::'].includes(host.trim().toLowerCase())\n  );\n}","tryCatchPattern":"import { SsrfBlockedHostError } from '@noco-integrations/core/utils/externalDbSsrf';\n\ntry {\n  await assertExternalDbHostAllowed(host);\n  await openConnection(host);\n} catch (err) {\n  if (err instanceof SsrfBlockedHostError) {\n    ui.warn('Localhost hosts are blocked by SSRF protection; ask your admin to enable NC_ALLOW_LOCAL_EXTERNAL_DBS or use a routable host.');\n    return;\n  }\n  throw err;\n}","preventionTips":["Always configure external DBs with their routable DNS name, not 'localhost'.","For dev, set NC_ALLOW_LOCAL_EXTERNAL_DBS=true in .env and remember to remove it before deploying to cloud.","Validate the host in the UI before submitting the connection-test request."],"tags":["ssrf","security","network","external-db","integrations"],"backgroundTag":null,"analyzedSha":"d3caaf4e890acf64bd49b788cf6dc32b7644c895","analyzedAt":"2026-08-12T13:07:32.092Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}