Skip to content

Commit 860f8ea

Browse files
committed
Add server side private IP blocking for data source endpoints validation
Signed-off-by: Kristen Tian <tyarong@amazon.com>
1 parent 86d42bc commit 860f8ea

File tree

3 files changed

+97
-11
lines changed

3 files changed

+97
-11
lines changed

src/plugins/data_source/server/saved_objects/data_source_saved_objects_client_wrapper.ts

+3-11
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
UsernamePasswordTypedContent,
2525
} from '../../common/data_sources';
2626
import { EncryptionContext, CryptographyServiceSetup } from '../cryptography_service';
27+
import { isValidURL } from '../util';
2728

2829
/**
2930
* Describes the Credential Saved Objects Client Wrapper class,
@@ -138,15 +139,6 @@ export class DataSourceSavedObjectsClientWrapper {
138139
};
139140
};
140141

141-
private isValidUrl(endpoint: string) {
142-
try {
143-
const url = new URL(endpoint);
144-
return Boolean(url) && (url.protocol === 'http:' || url.protocol === 'https:');
145-
} catch (e) {
146-
return false;
147-
}
148-
}
149-
150142
private async validateAndEncryptAttributes<T = unknown>(attributes: T) {
151143
this.validateAttributes(attributes);
152144

@@ -254,8 +246,8 @@ export class DataSourceSavedObjectsClientWrapper {
254246
);
255247
}
256248

257-
if (!this.isValidUrl(endpoint)) {
258-
throw SavedObjectsErrorHelpers.createBadRequestError('"endpoint" attribute is not valid');
249+
if (!isValidURL(endpoint)) {
250+
throw SavedObjectsErrorHelpers.createBadRequestError('"endpoint" attribute is not valid or allowed');
259251
}
260252

261253
if (!auth) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import dns from 'dns-sync';
7+
import IPCIDR from 'ip-cidr';
8+
9+
const BLOCK_LIST = [
10+
'127.0.0.0/8',
11+
'::1/128',
12+
'169.254.0.0/16',
13+
'fe80::/10',
14+
'10.0.0.0/8',
15+
'172.16.0.0/12',
16+
'192.168.0.0/16',
17+
'fc00::/7',
18+
'0.0.0.0/8',
19+
'100.64.0.0/10',
20+
'192.0.0.0/24',
21+
'192.0.2.0/24',
22+
'198.18.0.0/15',
23+
'192.88.99.0/24',
24+
'198.51.100.0/24',
25+
'203.0.113.0/24',
26+
'224.0.0.0/4',
27+
'240.0.0.0/4',
28+
'255.255.255.255/32',
29+
'::/128',
30+
'2001:db8::/32',
31+
'ff00::/8',
32+
];
33+
34+
export const INVALID_URL_MESSAGE = `The Data Source URL provided is not valid or allowed.`;
35+
36+
export function isValidURL(endpoint: string) {
37+
// Check the format of URL, URL has be in the format as
38+
// scheme://server/path/resource otherwise an TypeError
39+
// would be thrown.
40+
let url;
41+
try {
42+
url = new URL(endpoint);
43+
} catch (err) {
44+
return false;
45+
}
46+
47+
if (!(Boolean(url) && (url.protocol === 'http:' || url.protocol === 'https:'))) {
48+
return false;
49+
}
50+
51+
const ip = getIpAddress(url);
52+
if (!ip) {
53+
return false;
54+
}
55+
56+
// IP CIDR check if a specific IP address fall in the
57+
// range of an IP address block
58+
// @param {string} bl
59+
// @returns {object} cidr
60+
for (const bl of BLOCK_LIST) {
61+
const cidr = new IPCIDR(bl);
62+
if (cidr.contains(ip)) {
63+
return false;
64+
}
65+
}
66+
return true;
67+
}
68+
69+
/**
70+
* Resolve hostname to IP address
71+
* @param {object} urlObject
72+
* @returns {string} configuredIP
73+
* or null if it cannot be resolve
74+
* According to RFC, all IPv6 IP address needs to be in []
75+
* such as [::1].
76+
* So if we detect a IPv6 address, we remove brackets.
77+
*/
78+
function getIpAddress(urlObject: URL) {
79+
const hostname = urlObject.hostname;
80+
const configuredIP = dns.resolve(hostname);
81+
if (configuredIP) {
82+
return configuredIP;
83+
}
84+
if (hostname.startsWith('[') && hostname.endsWith(']')) {
85+
return hostname.substr(1).slice(0, -1);
86+
}
87+
return null;
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export { isValidURL } from './endpoint_validator';

0 commit comments

Comments
 (0)