Skip to content

Commit

Permalink
feat: use ip2location as vpn detection provider
Browse files Browse the repository at this point in the history
  • Loading branch information
alexey-yarmosh committed Jul 26, 2023
1 parent 63d3287 commit 939775b
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 257 deletions.
30 changes: 7 additions & 23 deletions src/lib/geoip/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { scopedLogger } from '../logger.js';
import { getRegionByCountry } from '../location/location.js';
import { isAddrWhitelisted } from './whitelist.js';
import { ipinfoLookup } from './providers/ipinfo.js';
import { type FastlyBundledResponse, fastlyLookup } from './providers/fastly.js';
import { fastlyLookup } from './providers/fastly.js';
import { maxmindLookup } from './providers/maxmind.js';
import { ipmapLookup } from './providers/ipmap.js';
import { ip2LocationLookup } from './providers/ip2location.js';
import { type Ip2LocationBundledResponse, ip2LocationLookup } from './providers/ip2location.js';
import { normalizeRegionName } from './utils.js';

export type LocationInfo = Omit<ProbeLocation, 'region' | 'normalizedRegion'>;
Expand All @@ -39,25 +39,25 @@ export default class GeoipClient {
async lookup (addr: string): Promise<ProbeLocation> {
const results = await Promise
.allSettled([
this.lookupWithCache<LocationInfo>(`geoip:ip2location:${addr}`, async () => ip2LocationLookup(addr)),
this.lookupWithCache<Ip2LocationBundledResponse>(`geoip:ip2location:${addr}`, async () => ip2LocationLookup(addr)),
this.lookupWithCache<LocationInfo>(`geoip:ipmap:${addr}`, async () => ipmapLookup(addr)),
this.lookupWithCache<LocationInfo>(`geoip:maxmind:${addr}`, async () => maxmindLookup(addr)),
this.lookupWithCache<LocationInfo>(`geoip:ipinfo:${addr}`, async () => ipinfoLookup(addr)),
this.lookupWithCache<FastlyBundledResponse>(`geoip:fastly:${addr}`, async () => fastlyLookup(addr)),
this.lookupWithCache<LocationInfo>(`geoip:fastly:${addr}`, async () => fastlyLookup(addr)),
])
.then(([ ip2location, ipmap, maxmind, ipinfo, fastly ]) => {
const fulfilled: (LocationInfoWithProvider | null)[] = [];

// Providers here are pushed in a desc prioritized order
fulfilled.push(
ip2location.status === 'fulfilled' ? { ...ip2location.value, provider: 'ip2location' } : null,
ip2location.status === 'fulfilled' ? { ...ip2location.value.location, provider: 'ip2location' } : null,
ipmap.status === 'fulfilled' ? { ...ipmap.value, provider: 'ipmap' } : null,
maxmind.status === 'fulfilled' ? { ...maxmind.value, provider: 'maxmind' } : null,
ipinfo.status === 'fulfilled' ? { ...ipinfo.value, provider: 'ipinfo' } : null,
fastly.status === 'fulfilled' ? { ...fastly.value.location, provider: 'fastly' } : null,
fastly.status === 'fulfilled' ? { ...fastly.value, provider: 'fastly' } : null,
);

if (fastly.status === 'fulfilled' && this.isVpn(fastly.value.client) && !isAddrWhitelisted(addr)) {
if (ip2location.status === 'fulfilled' && ip2location.value.isProxy && !isAddrWhitelisted(addr)) {
throw new InternalError('vpn detected', true);
}

Expand Down Expand Up @@ -95,22 +95,6 @@ export default class GeoipClient {
};
}

private isVpn (client: {proxy_desc: string; proxy_type: string}): boolean {
if (!client) {
return false;
}

if ([ 'anonymous', 'aol', 'blackberry', 'corporate' ].includes(client.proxy_type)) {
return true;
}

if (client.proxy_desc.startsWith('tor-') || client.proxy_desc === 'vpn') {
return true;
}

return false;
}

private matchRegion (best: LocationInfo): RegionInfo {
const region = getRegionByCountry(best.country);

Expand Down
44 changes: 15 additions & 29 deletions src/lib/geoip/providers/fastly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,35 @@ import {
normalizeNetworkName,
} from '../utils.js';

type FastlyGeoInfo = {
continent_code: string;
country_code: string;
city: string;
region: string;
latitude: number;
longitude: number;
network: string;
};

type FastlyClientInfo = {
proxy_desc: string;
proxy_type: string;
};

type FastlyResponse = {
as: {
name: string;
number: number;
};
client: FastlyClientInfo;
'geo-digitalelement': FastlyGeoInfo;
};

export type FastlyBundledResponse = {
location: LocationInfo;
client: FastlyClientInfo;
client: {
proxy_desc: string;
proxy_type: string;
};
'geo-digitalelement': {
continent_code: string;
country_code: string;
city: string;
region: string;
latitude: number;
longitude: number;
network: string;
};
};

export const fastlyLookup = async (addr: string): Promise<FastlyBundledResponse> => {
export const fastlyLookup = async (addr: string): Promise<LocationInfo> => {
const result = await got(`https://globalping-geoip.global.ssl.fastly.net/${addr}`, {
timeout: { request: 5000 },
}).json<FastlyResponse>();

const data = result['geo-digitalelement'];
const city = data.city.replace(/^(private|reserved)/, '');

const location = {
return {
continent: data.continent_code,
country: data.country_code,
state: data.country_code === 'US' ? data.region : undefined,
Expand All @@ -55,9 +46,4 @@ export const fastlyLookup = async (addr: string): Promise<FastlyBundledResponse>
network: result.as.name,
normalizedNetwork: normalizeNetworkName(result.as.name),
};

return {
location,
client: result.client,
};
};
14 changes: 12 additions & 2 deletions src/lib/geoip/providers/ip2location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ type Ip2LocationResponse = {
is_proxy?: boolean;
};

export const ip2LocationLookup = async (addr: string): Promise<LocationInfo> => {
export type Ip2LocationBundledResponse = {
location: LocationInfo,
isProxy: boolean,
};

export const ip2LocationLookup = async (addr: string): Promise<Ip2LocationBundledResponse> => {
const result = await got(`https://api.ip2location.io`, {
searchParams: {
key: config.get<string>('ip2location.apiKey'),
Expand All @@ -35,7 +40,7 @@ export const ip2LocationLookup = async (addr: string): Promise<LocationInfo> =>
timeout: { request: 5000 },
}).json<Ip2LocationResponse>();

return {
const location = {
continent: result.country_code ? getContinentByCountry(result.country_code) : '',
state: result.country_code === 'US' && result.region_name ? getStateIsoByName(result.region_name) : undefined,
country: result.country_code ?? '',
Expand All @@ -47,4 +52,9 @@ export const ip2LocationLookup = async (addr: string): Promise<LocationInfo> =>
network: result.as ?? '',
normalizedNetwork: normalizeNetworkName(result.as ?? ''),
};

return {
location,
isProxy: result.is_proxy ?? false,
};
};
154 changes: 29 additions & 125 deletions test/mocks/nock-geoip.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"latitude": 32.001,
"longitude": -96.001,
"asn": "20001",
"as": "The Constant Company LLC"
"as": "The Constant Company LLC",
"is_proxy": false
},
"argentina": {
"country_code": "AR",
Expand All @@ -16,7 +17,8 @@
"latitude": -34.001,
"longitude": -58.001,
"asn": "61001",
"as": "interbs s.r.l."
"as": "interbs s.r.l.",
"is_proxy": false
},
"newYork": {
"country_code": "US",
Expand All @@ -25,7 +27,8 @@
"latitude": 40.001,
"longitude": -74.001,
"asn": "80001",
"as": "The Constant Company LLC"
"as": "The Constant Company LLC",
"is_proxy": false
},
"emptyCity": {
"country_code": "US",
Expand All @@ -34,7 +37,8 @@
"latitude": 32.001,
"longitude": -96.001,
"asn": "20001",
"as": "The Constant Company LLC"
"as": "The Constant Company LLC",
"is_proxy": false
},
"emptyNetwork": {
"country_code": "US",
Expand All @@ -43,7 +47,27 @@
"latitude": 32.001,
"longitude": -96.001,
"asn": "",
"as": ""
"as": "",
"is_proxy": false
},
"vpn": {
"country_code": "US",
"region_name": "Texas",
"city_name": "Dallas",
"latitude": 32.001,
"longitude": -96.001,
"asn": "20001",
"as": "The Constant Company LLC",
"is_proxy": true
},
"noVpn": {
"country_code": "US",
"region_name": "Texas",
"city_name": "Dallas",
"latitude": 32.001,
"longitude": -96.001,
"asn": "20001",
"as": "The Constant Company LLC"
}
},
"ipmap": {
Expand Down Expand Up @@ -378,126 +402,6 @@
"longitude": -58.005,
"region": "C"
}
},
"proxyDescVpn": {
"as": {
"name": "psychz networks",
"number": 20005
},
"geo-digitalelement": {
"city": "dallas",
"continent_code": "NA",
"country_code": "US",
"country_code3": "USA",
"country_name": "united states",
"latitude": 32.005,
"longitude": -96.005,
"region": "TX"
},
"client": {
"proxy_desc": "vpn",
"proxy_type": "unknown"
}
},
"proxyDescTor": {
"as": {
"name": "psychz networks",
"number": 20005
},
"geo-digitalelement": {
"city": "dallas",
"continent_code": "NA",
"country_code": "US",
"country_code3": "USA",
"country_name": "united states",
"latitude": 32.005,
"longitude": -96.005,
"region": "TX"
},
"client": {
"proxy_desc": "tor-exit",
"proxy_type": "corporate"
}
},
"proxyTypeCorporate": {
"as": {
"name": "psychz networks",
"number": 20005
},
"geo-digitalelement": {
"city": "dallas",
"continent_code": "NA",
"country_code": "US",
"country_code3": "USA",
"country_name": "united states",
"latitude": 32.005,
"longitude": -96.005,
"region": "TX"
},
"client": {
"proxy_desc": "",
"proxy_type": "corporate"
}
},
"proxyTypeAol": {
"as": {
"name": "psychz networks",
"number": 20005
},
"geo-digitalelement": {
"city": "dallas",
"continent_code": "NA",
"country_code": "US",
"country_code3": "USA",
"country_name": "united states",
"latitude": 32.005,
"longitude": -96.005,
"region": "TX"
},
"client": {
"proxy_desc": "",
"proxy_type": "aol"
}
},
"proxyTypeAnonymous": {
"as": {
"name": "psychz networks",
"number": 20005
},
"geo-digitalelement": {
"city": "dallas",
"continent_code": "NA",
"country_code": "US",
"country_code3": "USA",
"country_name": "united states",
"latitude": 32.005,
"longitude": -96.005,
"region": "TX"
},
"client": {
"proxy_desc": "",
"proxy_type": "anonymous"
}
},
"proxyTypeBlackberry": {
"as": {
"name": "psychz networks",
"number": 20005
},
"geo-digitalelement": {
"city": "dallas",
"continent_code": "NA",
"country_code": "US",
"country_code3": "USA",
"country_name": "united states",
"latitude": 32.005,
"longitude": -96.005,
"region": "TX"
},
"client": {
"proxy_desc": "",
"proxy_type": "blackberry"
}
}
}
}
Loading

0 comments on commit 939775b

Please sign in to comment.