Skip to content

Commit a53f34b

Browse files
authored
Add support for MaxMind GeoIP2 Enterprise and Anonymous-IP databases (#223)
This commit added support for MaxMind GeoIP2 Enterprise and Anonymous-IP databases. In addition to that, It also: - Changed the database type detection to use the same strategy so it doesn't rely on specific database types. - Updated MaxMind dependencies. - Added unit tests for the Java classes, including the verification of the plugin's basic operations against all supported databases. - Changed to get the gem version from the `VERSION` file.
1 parent 1a7ccd0 commit a53f34b

24 files changed

+1113
-229
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 7.3.0
2+
- Added support for MaxMind GeoIP2 Enterprise and Anonymous-IP databases ([#223](https://github.com/logstash-plugins/logstash-filter-geoip/pull/223))
3+
- Updated MaxMind dependencies.
4+
- Added tests for the Java classes.
5+
16
## 7.2.13
27
- [DOC] Add documentation for database auto-update configuration [#210](https://github.com/logstash-plugins/logstash-filter-geoip/pull/210)
38

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
7.3.0

build.gradle

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,17 @@ apply plugin: "java"
2222
apply plugin: "distribution"
2323
apply plugin: "idea"
2424

25-
// TODO(sissel): Move this to a file shared by the gemspec.
2625
group "org.logstash.filters"
27-
version "6.0.0"
26+
version "${new File("VERSION").text.trim()}"
2827
project.archivesBaseName = "logstash-filter-geoip"
2928

29+
String junitVersion = '5.9.2'
30+
String maxmindGeoip2Version = '2.17.0'
31+
String maxmindDbVersion = '2.1.0'
32+
String log4jVersion = '2.17.1'
33+
String jrubyCompleteVersion = '9.1.13.0'
34+
String mockitoVersion = '4.11.0'
35+
3036
sourceCompatibility = JavaVersion.VERSION_1_8
3137
targetCompatibility = JavaVersion.VERSION_1_8
3238

@@ -53,29 +59,38 @@ configurations {
5359
}
5460

5561
dependencies {
56-
compileOnly group: "org.apache.logging.log4j", name: "log4j-api", version: "2.17.1"
57-
compileOnly group: "org.apache.logging.log4j", name: "log4j-core", version: "2.17.1"
58-
compileOnly group: "com.maxmind.geoip2", name: "geoip2", version: "2.9.0"
59-
compileOnly group: "com.maxmind.db", name: "maxmind-db", version: "1.2.2"
60-
compileOnly group: 'org.jruby', name: 'jruby-complete', version: "1.7.26"
62+
compileOnly group: "org.apache.logging.log4j", name: "log4j-api", version: log4jVersion
63+
compileOnly group: "org.apache.logging.log4j", name: "log4j-core", version: log4jVersion
64+
compileOnly group: 'org.jruby', name: 'jruby-complete', version: jrubyCompleteVersion
6165
compileOnly fileTree(dir: logstashCoreGemPath, include: '**/*.jar')
6266

63-
runtimeOnly group: "com.maxmind.geoip2", name: "geoip2", version: "2.9.0"
64-
runtimeOnly group: "com.maxmind.db", name: "maxmind-db", version: "1.2.2"
67+
implementation group: "com.maxmind.geoip2", name: "geoip2", version: maxmindGeoip2Version
68+
implementation group: "com.maxmind.db", name: "maxmind-db", version: maxmindDbVersion
6569

66-
testImplementation group: 'junit', name: 'junit', version: '4.12'
67-
testImplementation group: "org.apache.logging.log4j", name: "log4j-api", version: "2.17.1"
68-
testImplementation group: "org.apache.logging.log4j", name: "log4j-core", version: "2.17.1"
69-
testImplementation group: 'org.jruby', name: 'jruby-complete', version: "1.7.26"
70-
testImplementation group: "com.maxmind.geoip2", name: "geoip2", version: "2.9.0"
71-
testImplementation group: "com.maxmind.db", name: "maxmind-db", version: "1.2.2"
72-
testImplementation fileTree(dir: logstashCoreGemPath, include: '**/*.jar')
70+
testImplementation group: "org.junit.jupiter", name: "junit-jupiter-api", version: junitVersion
71+
testImplementation group: "org.junit.jupiter", name: "junit-jupiter-params", version: junitVersion
72+
testRuntimeOnly group: "org.junit.jupiter", name: "junit-jupiter-engine", version: junitVersion
73+
testImplementation group: "org.apache.logging.log4j", name: "log4j-api", version: log4jVersion
74+
testImplementation group: 'org.jruby', name: 'jruby-complete', version: jrubyCompleteVersion
75+
testImplementation group: "org.mockito", name: "mockito-core", version: mockitoVersion
7376

7477
geolite2('org.elasticsearch:geolite2-databases:20191119') {
7578
transitive = false
7679
}
7780
}
7881

82+
test {
83+
useJUnitPlatform()
84+
85+
testLogging {
86+
events "started", "passed", "skipped", "failed", "standard_out", "standard_error"
87+
}
88+
}
89+
90+
configurations {
91+
testImplementation.extendsFrom compileOnly
92+
}
93+
7994
task generateGemJarRequiresFile {
8095
doLast {
8196
File jars_file = file("lib/${project.archivesBaseName}_jars.rb")

docs/index.asciidoc

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -190,37 +190,44 @@ When ECS compatibility is enabled, the fields are structured to fit into an ECS
190190
|===========================
191191
| Database Field Name | ECS Field | Example
192192

193-
| `ip` | `[ip]` | `12.34.56.78`
194-
195-
| `city_name` | `[geo][city_name]` | `Seattle`
196-
| `country_name` | `[geo][country_name]` | `United States`
197-
| `continent_code` | `[geo][continent_code]` | `NA`
198-
| `continent_name` | `[geo][continent_name]` | `North America`
199-
| `country_code2` | `[geo][country_iso_code]` | `US`
200-
| `country_code3` | _N/A_ | `US`
201-
202-
_maintained for legacy
203-
support, but populated
204-
with 2-character country
205-
code_
206-
207-
| `postal_code` | `[geo][postal_code]` | `98106`
208-
| `region_name` | `[geo][region_name]` | `Washington`
209-
| `region_code` | `[geo][region_code]` | `WA`
210-
| `region_iso_code`* | `[geo][region_iso_code]` | `US-WA`
211-
| `timezone` | `[geo][timezone]` | `America/Los_Angeles`
212-
| `location`* | `[geo][location]` | `{"lat": 47.6062, "lon": -122.3321}"`
213-
| `latitude` | `[geo][location][lat]` | `47.6062`
214-
| `longitude` | `[geo][location][lon]` | `-122.3321`
215-
216-
| `domain` | `[domain]` | `example.com`
217-
218-
| `asn` | `[as][number]` | `98765`
219-
| `as_org` | `[as][organization][name]` | `Elastic, NV`
220-
221-
| `isp` | `[mmdb][isp]` | `InterLink Supra LLC`
222-
| `dma_code` | `[mmdb][dma_code]` | `819`
223-
| `organization` | `[mmdb][organization]` | `Elastic, NV`
193+
| `ip` | `[ip]` | `12.34.56.78`
194+
| `anonymous` | `[ip_traits][anonymous]` | `false`
195+
| `anonymous_vpn` | `[ip_traits][anonymous_vpn]` | `false`
196+
| `hosting_provider` | `[ip_traits][hosting_provider]` | `true`
197+
| `network` | `[ip_traits][network]` | `12.34.56.78/20`
198+
| `public_proxy` | `[ip_traits][public_proxy]` | `true`
199+
| `residential_proxy` | `[ip_traits][residential_proxy]` | `false`
200+
| `tor_exit_node` | `[ip_traits][tor_exit_node]` | `true`
201+
202+
| `city_name` | `[geo][city_name]` | `Seattle`
203+
| `country_name` | `[geo][country_name]` | `United States`
204+
| `continent_code` | `[geo][continent_code]` | `NA`
205+
| `continent_name` | `[geo][continent_name]` | `North America`
206+
| `country_code2` | `[geo][country_iso_code]` | `US`
207+
| `country_code3` | _N/A_ | `US`
208+
209+
_maintained for legacy
210+
support, but populated
211+
with 2-character country
212+
code_
213+
214+
| `postal_code` | `[geo][postal_code]` | `98106`
215+
| `region_name` | `[geo][region_name]` | `Washington`
216+
| `region_code` | `[geo][region_code]` | `WA`
217+
| `region_iso_code`* | `[geo][region_iso_code]` | `US-WA`
218+
| `timezone` | `[geo][timezone]` | `America/Los_Angeles`
219+
| `location`* | `[geo][location]` | `{"lat": 47.6062, "lon": -122.3321}"`
220+
| `latitude` | `[geo][location][lat]` | `47.6062`
221+
| `longitude` | `[geo][location][lon]` | `-122.3321`
222+
223+
| `domain` | `[domain]` | `example.com`
224+
225+
| `asn` | `[as][number]` | `98765`
226+
| `as_org` | `[as][organization][name]` | `Elastic, NV`
227+
228+
| `isp` | `[mmdb][isp]` | `InterLink Supra LLC`
229+
| `dma_code` | `[mmdb][dma_code]` | `819`
230+
| `organization` | `[mmdb][organization]` | `Elastic, NV`
224231
|===========================
225232

226233
NOTE: `*` indicates a composite field, which is only populated if GeoIP lookup result contains all components.
@@ -301,7 +308,7 @@ number of cache misses and waste memory.
301308
The path to MaxMind's database file that Logstash should use.
302309
The default database is `GeoLite2-City`.
303310
This plugin supports several free databases (`GeoLite2-City`, `GeoLite2-Country`, `GeoLite2-ASN`)
304-
and a selection of commercially-licensed databases (`GeoIP2-City`, `GeoIP2-ISP`, `GeoIP2-Country`).
311+
and a selection of commercially-licensed databases (`GeoIP2-City`, `GeoIP2-ISP`, `GeoIP2-Country`, `GeoIP2-Domain`, `GeoIP2-Enterprise`, `GeoIP2-Anonymous-IP`).
305312

306313
Database auto-update applies to the default distribution.
307314
When `database` points to user's database path, auto-update is disabled.

lib/logstash-filter-geoip_jars.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AUTOGENERATED BY THE GRADLE SCRIPT. DO NOT EDIT.
22

33
require 'jar_dependencies'
4-
require_jar('com.maxmind.geoip2', 'geoip2', '2.9.0')
5-
require_jar('com.maxmind.db', 'maxmind-db', '1.2.2')
6-
require_jar('org.logstash.filters', 'logstash-filter-geoip', '6.0.0')
4+
require_jar('com.maxmind.geoip2', 'geoip2', '2.17.0')
5+
require_jar('com.maxmind.db', 'maxmind-db', '2.1.0')
6+
require_jar('org.logstash.filters', 'logstash-filter-geoip', '7.3.0')

lib/logstash/filters/geoip.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,17 @@ def fail_filter
171171
end
172172

173173
def close
174-
@database_manager.unsubscribe_database_path(@default_database_type, self) if @database_manager
174+
begin
175+
@database_manager.unsubscribe_database_path(@default_database_type, self) if @database_manager
176+
rescue => e
177+
@logger.error("Error unsubscribing geoip database path", :path => @database, :exception => e)
178+
end
179+
180+
begin
181+
@geoipfilter.close if @geoipfilter
182+
rescue => e
183+
@logger.error("Error closing GeoIPFilter", :exception => e)
184+
end
175185
end
176186

177187
def select_database_path

logstash-filter-geoip.gemspec

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
VERSION = File.read(File.expand_path(File.join(File.dirname(__FILE__), "VERSION"))).strip unless defined?(VERSION)
2+
13
Gem::Specification.new do |s|
24

35
s.name = 'logstash-filter-geoip'
4-
s.version = '7.2.13'
6+
s.version = VERSION
57
s.licenses = ['Apache License (2.0)']
68
s.summary = "Adds geographical information about an IP address"
79
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package org.logstash.filters.geoip;
2+
3+
import java.util.Collections;
4+
import java.util.EnumSet;
5+
import java.util.Set;
6+
7+
enum Database {
8+
9+
CITY(
10+
"City",
11+
EnumSet.of(
12+
Field.IP,
13+
Field.CITY_NAME,
14+
Field.CONTINENT_CODE,
15+
Field.COUNTRY_NAME,
16+
Field.COUNTRY_CODE2,
17+
Field.COUNTRY_CODE3,
18+
Field.POSTAL_CODE,
19+
Field.DMA_CODE,
20+
Field.REGION_NAME,
21+
Field.REGION_ISO_CODE,
22+
Field.TIMEZONE,
23+
Field.LOCATION,
24+
Field.LATITUDE,
25+
Field.LONGITUDE
26+
)
27+
),
28+
COUNTRY(
29+
"Country",
30+
EnumSet.of(
31+
Field.IP,
32+
Field.COUNTRY_CODE2,
33+
Field.COUNTRY_NAME,
34+
Field.CONTINENT_NAME
35+
)
36+
),
37+
DOMAIN(
38+
"GeoIP2-Domain",
39+
EnumSet.of(
40+
Field.DOMAIN
41+
)
42+
),
43+
ASN(
44+
"GeoLite2-ASN",
45+
EnumSet.of(
46+
Field.IP,
47+
Field.AUTONOMOUS_SYSTEM_NUMBER,
48+
Field.AUTONOMOUS_SYSTEM_ORGANIZATION
49+
)
50+
),
51+
ISP(
52+
"GeoIP2-ISP",
53+
EnumSet.of(
54+
Field.IP,
55+
Field.AUTONOMOUS_SYSTEM_NUMBER,
56+
Field.AUTONOMOUS_SYSTEM_ORGANIZATION,
57+
Field.ISP,
58+
Field.ORGANIZATION
59+
)
60+
),
61+
ANONYMOUS_IP(
62+
"GeoIP2-Anonymous-IP",
63+
EnumSet.of(
64+
Field.HOSTING_PROVIDER,
65+
Field.TOR_EXIT_NODE,
66+
Field.ANONYMOUS_VPN,
67+
Field.ANONYMOUS,
68+
Field.PUBLIC_PROXY,
69+
Field.RESIDENTIAL_PROXY
70+
)
71+
),
72+
ENTERPRISE(
73+
"Enterprise",
74+
EnumSet.of(
75+
Field.IP,
76+
Field.COUNTRY_CODE2,
77+
Field.COUNTRY_NAME,
78+
Field.CONTINENT_NAME,
79+
Field.REGION_ISO_CODE,
80+
Field.REGION_NAME,
81+
Field.CITY_NAME,
82+
Field.LOCATION
83+
)
84+
),
85+
UNKNOWN(
86+
"Unknown",
87+
EnumSet.noneOf(Field.class)
88+
);
89+
90+
private final String databaseType;
91+
private final Set<Field> defaultFields;
92+
93+
Database(String databaseType, final Set<Field> defaultFields) {
94+
this.databaseType = databaseType;
95+
this.defaultFields = defaultFields;
96+
}
97+
98+
public Set<Field> getDefaultFields() {
99+
return Collections.unmodifiableSet(defaultFields);
100+
}
101+
102+
public static Database fromDatabaseType(final String type) {
103+
// It follows the same com.maxmind.geoip2.DatabaseReader#getDatabaseType logic
104+
if (type.contains(CITY.databaseType)) {
105+
return Database.CITY;
106+
} else if (type.contains(COUNTRY.databaseType)) {
107+
return Database.COUNTRY;
108+
} else if (type.contains(DOMAIN.databaseType)) {
109+
return Database.DOMAIN;
110+
} else if (type.contains(ASN.databaseType)) {
111+
return Database.ASN;
112+
} else if (type.contains(ISP.databaseType)) {
113+
return Database.ISP;
114+
} else if (type.contains(ENTERPRISE.databaseType)) {
115+
return Database.ENTERPRISE;
116+
} else if (type.contains(ANONYMOUS_IP.databaseType)) {
117+
return Database.ANONYMOUS_IP;
118+
}
119+
120+
// The reason why we have this UNKNOWN type here, is to keep it backward compatible,
121+
// allowing the pipeline to start, even if the plugin is configured to a non supported
122+
// database type.
123+
return Database.UNKNOWN;
124+
}
125+
}

0 commit comments

Comments
 (0)