Skip to content

Commit

Permalink
Added support for DB-IP Location+ISP (Enterprise) databases, [logstas…
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Kröll committed Apr 1, 2022
1 parent 099f1fd commit 14d975e
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 7 deletions.
13 changes: 8 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
## 7.2.13
- Added support for DB-IP Location+ISP (Enterprise) databases, [#174](https://github.com/logstash-plugins/logstash-filter-geoip/issues/174) and . It is recommended to explicitly configure fields in case of enabled ECS.

## 7.2.12
- [DOC] Add `http_proxy` environment variable for GeoIP service endpoint. The feature is included in 8.1.0, and was back-ported to 7.17.2 [#207](https://github.com/logstash-plugins/logstash-filter-geoip/pull/207)
- [DOC] Add `http_proxy` environment variable for GeoIP service endpoint. The feature is included in 8.1.0, and was back-ported to 7.17.2 [#207](https://github.com/logstash-plugins/logstash-filter-geoip/pull/207)

## 7.2.11
- Improved compatibility with the Elastic Common Schema [#206](https://github.com/logstash-plugins/logstash-filter-geoip/pull/206)
- Added support for ECS's composite `region_iso_code` (`US-WA`), which _replaces_ the non-ECS `region_code` (`WA`) as a default field with City databases. To get the stand-alone `region_code` in ECS mode, you must include it in the `fields` directive.
- Added support for ECS's composite `region_iso_code` (`US-WA`), which _replaces_ the non-ECS `region_code` (`WA`) as a default field with City databases. To get the stand-alone `region_code` in ECS mode, you must include it in the `fields` directive.
- [DOC] Improve ECS-related documentation

## 7.2.10
Expand Down Expand Up @@ -77,14 +80,14 @@
- Update of GeoLite2 DB [#157](https://github.com/logstash-plugins/logstash-filter-geoip/pull/157)

## 6.0.1
- Fixed deeplink to Elasticsearch Reference
- Fixed deeplink to Elasticsearch Reference
[#151](https://github.com/logstash-plugins/logstash-filter-geoip/pull/151)

## 6.0.0
- Removed obsolete lru_cache_size field

## 5.0.3
- Skip lookup operation if source field contains an empty string
- Skip lookup operation if source field contains an empty string
- Update of the GeoIP2 DB

## 5.0.2
Expand All @@ -109,7 +112,7 @@
- Add ASN data support via GeoIP2-ISP database.

## 4.1.0
- Removed from RubyGems.org since it was missing the default GeoIP2 database.
- Removed from RubyGems.org since it was missing the default GeoIP2 database.

## 4.0.6
- Docs: Remove patch classes from the main plugin file
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/logstash/filters/geoip/Fields.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ enum Fields {
LOCATION("geo.location", "location"),
LATITUDE("geo.location.lat", "latitude"),
LONGITUDE("geo.location.lon", "longitude"),
ORGANIZATION("mmdb.organization", "organization");
ORGANIZATION("mmdb.organization", "organization"),
CONNECTION_TYPE("mmdb.connection_type", "connection_type");

private final String fieldName;
private final String ecsFieldName;
Expand Down
167 changes: 166 additions & 1 deletion src/main/java/org/logstash/filters/geoip/GeoIPFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.model.AsnResponse;
import com.maxmind.geoip2.model.CityResponse;
import com.maxmind.geoip2.model.EnterpriseResponse;
import com.maxmind.geoip2.model.CountryResponse;
import com.maxmind.geoip2.model.DomainResponse;
import com.maxmind.geoip2.model.IspResponse;
import com.maxmind.geoip2.model.ConnectionTypeResponse.ConnectionType;
import com.maxmind.geoip2.record.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -58,6 +60,7 @@ public class GeoIPFilter {
private static final String CITY_SOUTH_AMERICA_DB_TYPE = "GeoIP2-City-South-America";
private static final String COUNTRY_DB_TYPE = "GeoIP2-Country";
private static final String ISP_DB_TYPE = "GeoIP2-ISP";
private static final String DBIP_LOCATION_ISP_TYPE = "DBIP-Location-ISP (compat=Enterprise)";
private static final String DOMAIN_DB_TYPE = "GeoIP2-Domain";

private final String sourceField;
Expand Down Expand Up @@ -132,6 +135,11 @@ private Set<Fields> createDesiredFields(List<String> fields, final boolean ecsCo
break;
case DOMAIN_DB_TYPE:
desiredFields = Fields.DEFAULT_DOMAIN_FIELDS;
case DBIP_LOCATION_ISP_TYPE:
desiredFields = Fields.ALL_FIELDS;
break;
default:
throw new IllegalStateException("Unsupported database type '" + databaseReader.getMetadata().getDatabaseType() + "'");
}
} else {
for (String fieldName : fields) {
Expand Down Expand Up @@ -189,8 +197,11 @@ public boolean handleEvent(RubyEvent rubyEvent) {
case DOMAIN_DB_TYPE:
geoData = retrieveDomainGeoData(ipAddress);
break;
case DBIP_LOCATION_ISP_TYPE:
geoData = retrieveEnterpriseGeoData(ipAddress);
break;
default:
throw new IllegalStateException("Unsupported database type " + databaseReader.getMetadata().getDatabaseType() + "");
throw new IllegalStateException("Unsupported database type '" + databaseReader.getMetadata().getDatabaseType() + "'");
}
} catch (UnknownHostException e) {
logger.debug("IP Field contained invalid IP address or hostname. exception={}, field={}, event={}", e, sourceField, event);
Expand Down Expand Up @@ -352,6 +363,160 @@ private Map<Fields,Object> retrieveCityGeoData(InetAddress ipAddress) throws Geo
return geoData;
}

private Map<Fields,Object> retrieveEnterpriseGeoData(InetAddress ipAddress) throws GeoIp2Exception, IOException {
EnterpriseResponse response = databaseReader.enterprise(ipAddress);
Country country = response.getCountry();
City city = response.getCity();

Location location = response.getLocation();
Continent continent = response.getContinent();
Postal postal = response.getPostal();
Subdivision subdivision = response.getMostSpecificSubdivision();
Traits traits = response.getTraits();

Map<Fields, Object> geoData = new HashMap<>();

// if location is empty, there is no point populating geo data
// and most likely all other fields are empty as well
if (location.getLatitude() == null && location.getLongitude() == null) {
return geoData;
}

for (Fields desiredField : this.desiredFields) {
switch (desiredField) {
case CITY_NAME:
String cityName = city.getName();
if (cityName != null) {
geoData.put(Fields.CITY_NAME, cityName);
}
break;
case CONTINENT_CODE:
String continentCode = continent.getCode();
if (continentCode != null) {
geoData.put(Fields.CONTINENT_CODE, continentCode);
}
break;
case CONTINENT_NAME:
String continentName = continent.getName();
if (continentName != null) {
geoData.put(Fields.CONTINENT_NAME, continentName);
}
break;
case COUNTRY_NAME:
String countryName = country.getName();
if (countryName != null) {
geoData.put(Fields.COUNTRY_NAME, countryName);
}
break;
case COUNTRY_CODE2:
String countryCode2 = country.getIsoCode();
if (countryCode2 != null) {
geoData.put(Fields.COUNTRY_CODE2, countryCode2);
}
break;
case COUNTRY_CODE3:
String countryCode3 = country.getIsoCode();
if (countryCode3 != null) {
geoData.put(Fields.COUNTRY_CODE3, countryCode3);
}
break;
case IP:
geoData.put(Fields.IP, ipAddress.getHostAddress());
break;
case POSTAL_CODE:
String postalCode = postal.getCode();
if (postalCode != null) {
geoData.put(Fields.POSTAL_CODE, postalCode);
}
break;
case DMA_CODE:
Integer dmaCode = location.getMetroCode();
if (dmaCode != null) {
geoData.put(Fields.DMA_CODE, dmaCode);
}
break;
case REGION_NAME:
String subdivisionName = subdivision.getName();
if (subdivisionName != null) {
geoData.put(Fields.REGION_NAME, subdivisionName);
}
break;
case REGION_CODE:
String subdivisionCode = subdivision.getIsoCode();
if (subdivisionCode != null) {
geoData.put(Fields.REGION_CODE, subdivisionCode);
}
break;
case REGION_ISO_CODE:
String countryCodeForRegion = country.getIsoCode();
String regionCode2 = subdivision.getIsoCode();
if (countryCodeForRegion != null && regionCode2 != null) {
geoData.put(Fields.REGION_ISO_CODE, String.format("%s-%s", countryCodeForRegion, regionCode2));
}
break;
case TIMEZONE:
String locationTimeZone = location.getTimeZone();
if (locationTimeZone != null) {
geoData.put(Fields.TIMEZONE, locationTimeZone);
}
break;
case LOCATION:
Double latitude = location.getLatitude();
Double longitude = location.getLongitude();
if (latitude != null && longitude != null) {
Map<String, Object> locationObject = new HashMap<>();
locationObject.put("lat", latitude);
locationObject.put("lon", longitude);
geoData.put(Fields.LOCATION, locationObject);
}
break;
case LATITUDE:
Double lat = location.getLatitude();
if (lat != null) {
geoData.put(Fields.LATITUDE, lat);
}
break;
case LONGITUDE:
Double lon = location.getLongitude();
if (lon != null) {
geoData.put(Fields.LONGITUDE, lon);
}
break;
case AUTONOMOUS_SYSTEM_NUMBER:
Integer asn = traits.getAutonomousSystemNumber();
if (asn != null) {
geoData.put(Fields.AUTONOMOUS_SYSTEM_NUMBER, asn);
}
break;
case AUTONOMOUS_SYSTEM_ORGANIZATION:
String aso = traits.getAutonomousSystemOrganization();
if (aso != null) {
geoData.put(Fields.AUTONOMOUS_SYSTEM_ORGANIZATION, aso);
}
break;
case ISP:
String isp = traits.getIsp();
if (isp != null) {
geoData.put(Fields.ISP, isp);
}
break;
case ORGANIZATION:
String org = traits.getOrganization();
if (org != null) {
geoData.put(Fields.ORGANIZATION, org);
}
break;
case CONNECTION_TYPE:
ConnectionType type = traits.getConnectionType();
if (type != null) {
geoData.put(Fields.CONNECTION_TYPE, type.toString());
}
break;
}
}
return geoData;
}

private Map<Fields,Object> retrieveCountryGeoData(InetAddress ipAddress) throws GeoIp2Exception, IOException {
CountryResponse response = databaseReader.country(ipAddress);
Country country = response.getCountry();
Expand Down

0 comments on commit 14d975e

Please sign in to comment.