Skip to content

Commit

Permalink
Add support for import ip deny list (#12025)
Browse files Browse the repository at this point in the history
* Add support for import ip deny list

* Fix typo
  • Loading branch information
rijkvanzanten committed Mar 7, 2022
1 parent c1a49d7 commit 6da3f1e
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 3 deletions.
3 changes: 3 additions & 0 deletions api/src/env.ts
Expand Up @@ -77,6 +77,8 @@ const defaults: Record<string, any> = {
IP_TRUST_PROXY: true,
IP_CUSTOM_HEADER: false,

IMPORT_IP_DENY_LIST: '0.0.0.0',

SERVE_APP: true,

RELATIONAL_BATCH_SIZE: 25000,
Expand All @@ -95,6 +97,7 @@ const typeMap: Record<string, string> = {
DB_PORT: 'number',

DB_EXCLUDE_TABLES: 'array',
IMPORT_IP_DENY_LIST: 'array',
};

let env: Record<string, any> = {
Expand Down
59 changes: 56 additions & 3 deletions api/src/services/files.ts
Expand Up @@ -5,7 +5,9 @@ import { clone } from 'lodash';
import { extension } from 'mime-types';
import path from 'path';
import sharp from 'sharp';
import url from 'url';
import url, { URL } from 'url';
import { promisify } from 'util';
import { lookup } from 'dns';
import emitter from '../emitter';
import env from '../env';
import { ForbiddenException, ServiceUnavailableException } from '../exceptions';
Expand All @@ -14,6 +16,10 @@ import storage from '../storage';
import { AbstractServiceOptions, File, PrimaryKey, MutationOptions } from '../types';
import { toArray } from '@directus/shared/utils';
import { ItemsService } from './items';
import net from 'net';
import os from 'os';

const lookupDNS = promisify(lookup);

export class FilesService extends ItemsService {
constructor(options: AbstractServiceOptions) {
Expand Down Expand Up @@ -161,15 +167,62 @@ export class FilesService extends ItemsService {
throw new ForbiddenException();
}

let resolvedUrl;

try {
resolvedUrl = new URL(importURL);
} catch (err: any) {
logger.warn(err, `Requested URL ${importURL} isn't a valid URL`);
throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
service: 'external-file',
});
}

let ip = resolvedUrl.hostname;

if (net.isIP(ip) === 0) {
try {
ip = (await lookupDNS(ip)).address;
} catch (err: any) {
logger.warn(err, `Couldn't lookup the DNS for url ${importURL}`);
throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
service: 'external-file',
});
}
}

if (env.IMPORT_IP_DENY_LIST.includes('0.0.0.0')) {
const networkInterfaces = os.networkInterfaces();

for (const networkInfo of Object.values(networkInterfaces)) {
if (!networkInfo) continue;

for (const info of networkInfo) {
if (info.address === ip) {
logger.warn(`Requested URL ${importURL} resolves to localhost.`);
throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
service: 'external-file',
});
}
}
}
}

if (env.IMPORT_IP_DENY_LIST.includes(ip)) {
logger.warn(`Requested URL ${importURL} resolves to a denied IP address.`);
throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
service: 'external-file',
});
}

let fileResponse: AxiosResponse<NodeJS.ReadableStream>;

try {
fileResponse = await axios.get<NodeJS.ReadableStream>(importURL, {
responseType: 'stream',
});
} catch (err: any) {
logger.warn(`Couldn't fetch file from url "${importURL}"`);
logger.warn(err);
logger.warn(err, `Couldn't fetch file from url "${importURL}"`);
throw new ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
service: 'external-file',
});
Expand Down
1 change: 1 addition & 0 deletions docs/configuration/config-options.md
Expand Up @@ -280,6 +280,7 @@ All the `DB_POOL_` prefixed options are passed to [`tarn.js`](https://github.com
| `IP_CUSTOM_HEADER` | What custom request header to use for the IP address | false |
| `CONTENT_SECURITY_POLICY` | Custom overrides for the Content-Security-Policy header. See [helmet's documentation](https://helmetjs.github.io) for more information. | -- |
| `ASSETS_CONTENT_SECURITY_POLICY` | Custom overrides for the Content-Security-Policy header for the /assets endpoint. See [helmet's documentation](https://helmetjs.github.io) for more information. | -- |
| `IMPORT_IP_DENY_LIST` | Deny importing files from these IP addresses. Use `0.0.0.0` for any local IP address | `0.0.0.0` |

::: tip Cookie Strictness

Expand Down

0 comments on commit 6da3f1e

Please sign in to comment.