diff --git a/docs/index.asciidoc b/docs/index.asciidoc index 1f17ec2..a60965e 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -113,7 +113,7 @@ number of cache misses and waste memory. The path to Maxmind's database file that Logstash should use. The default database is GeoLite2-City. GeoLite2-City, GeoLite2-Country, GeoLite2-ASN are the free databases from Maxmind that are supported. -GeoIP2-City, GeoIP2-ISP, GeoIP2-Country are the commercial databases from Maxmind that are supported. +GeoIP2-City, GeoIP2-ISP, GeoIP2-Country, GeoIP2-Anonymous-IP, GeoIP2-Connection-Type are the commercial databases from Maxmind that are supported. If not specified, this will default to the GeoLite2 City database that ships with Logstash. diff --git a/src/main/java/org/logstash/filters/Fields.java b/src/main/java/org/logstash/filters/Fields.java index 1d38599..24ae218 100644 --- a/src/main/java/org/logstash/filters/Fields.java +++ b/src/main/java/org/logstash/filters/Fields.java @@ -24,8 +24,10 @@ import java.util.Locale; enum Fields { + ANONYMOUS_TAGS("anonymous_tags"), AUTONOMOUS_SYSTEM_NUMBER("asn"), AUTONOMOUS_SYSTEM_ORGANIZATION("as_org"), + CONNECTION_TYPE("connection_type"), CITY_NAME("city_name"), COUNTRY_NAME("country_name"), CONTINENT_CODE("continent_code"), @@ -70,6 +72,10 @@ public String fieldName() { static final EnumSet DEFAULT_ASN_LITE_FIELDS = EnumSet.of(Fields.IP, Fields.AUTONOMOUS_SYSTEM_NUMBER, Fields.AUTONOMOUS_SYSTEM_ORGANIZATION); + static final EnumSet DEFAULT_ANONYMOUS_FIELDS = EnumSet.of(Fields.IP, Fields.ANONYMOUS_TAGS); + + static final EnumSet DEFAULT_CONNECTION_TYPE_FIELDS = EnumSet.of(Fields.IP, Fields.CONNECTION_TYPE); + public static Fields parseField(String value) { try { return valueOf(value.toUpperCase(Locale.ROOT)); diff --git a/src/main/java/org/logstash/filters/GeoIPFilter.java b/src/main/java/org/logstash/filters/GeoIPFilter.java index f23b9ad..7cd1e95 100644 --- a/src/main/java/org/logstash/filters/GeoIPFilter.java +++ b/src/main/java/org/logstash/filters/GeoIPFilter.java @@ -25,7 +25,9 @@ import com.maxmind.geoip2.model.AsnResponse; import com.maxmind.geoip2.model.CityResponse; import com.maxmind.geoip2.model.CountryResponse; +import com.maxmind.geoip2.model.ConnectionTypeResponse; import com.maxmind.geoip2.model.IspResponse; +import com.maxmind.geoip2.model.AnonymousIpResponse; import com.maxmind.geoip2.record.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -56,6 +58,15 @@ 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 ANONYMOUS_DB_TYPE = "GeoIP2-Anonymous-IP"; + private static final String CONNECTION_TYPE_DB_TYPE = "GeoIP2-Connection-Type"; + + // The Anonymous ip issues + private static final String IS_ANONYMOUS = "is_anonymous"; + private static final String IS_ANONYMOUS_VPN = "is_anonymous_vpn"; + private static final String IS_HOSTING_PROVIDER = "is_hosting_provider"; + private static final String IS_PUBLIC_PROXY = "is_public_proxy"; + private static final String IS_TOR_EXIT_NODE = "is_tor_exit_node"; private final String sourceField; private final String targetField; @@ -99,6 +110,12 @@ private Set createDesiredFields(List fields) { case ASN_LITE_DB_TYPE: desiredFields = Fields.DEFAULT_ASN_LITE_FIELDS; break; + case ANONYMOUS_DB_TYPE: + desiredFields = Fields.DEFAULT_ANONYMOUS_FIELDS; + break; + case CONNECTION_TYPE_DB_TYPE: + desiredFields = Fields.DEFAULT_CONNECTION_TYPE_FIELDS; + break; } } else { for (String fieldName : fields) { @@ -125,7 +142,7 @@ public boolean handleEvent(RubyEvent rubyEvent) { throw new IllegalArgumentException("Expected input field value to be String or List type"); } - if (ip.trim().isEmpty()){ + if (ip.trim().isEmpty()) { return false; } @@ -153,6 +170,12 @@ public boolean handleEvent(RubyEvent rubyEvent) { case ISP_DB_TYPE: geoData = retrieveIspGeoData(ipAddress); break; + case ANONYMOUS_DB_TYPE: + geoData = retrieveAnonymousData(ipAddress); + break; + case CONNECTION_TYPE_DB_TYPE: + geoData = retrieveConnectionTypeData(ipAddress); + break; default: throw new IllegalStateException("Unsupported database type " + databaseReader.getMetadata().getDatabaseType() + ""); } @@ -181,13 +204,13 @@ private boolean applyGeoData(Map geoData, Event event) { } String s = "[" + this.targetField + "]["; - for (Map.Entry it: geoData.entrySet()) { + for (Map.Entry it : geoData.entrySet()) { event.setField(s + it.getKey() + "]", it.getValue()); } return true; } - private Map retrieveCityGeoData(InetAddress ipAddress) throws GeoIp2Exception, IOException { + private Map retrieveCityGeoData(InetAddress ipAddress) throws GeoIp2Exception, IOException { CityResponse response = databaseReader.city(ipAddress); Country country = response.getCountry(); City city = response.getCity(); @@ -302,7 +325,7 @@ private Map retrieveCityGeoData(InetAddress ipAddress) throws Geo return geoData; } - private Map retrieveCountryGeoData(InetAddress ipAddress) throws GeoIp2Exception, IOException { + private Map retrieveCountryGeoData(InetAddress ipAddress) throws GeoIp2Exception, IOException { CountryResponse response = databaseReader.country(ipAddress); Country country = response.getCountry(); Continent continent = response.getContinent(); @@ -401,4 +424,60 @@ private Map retrieveAsnGeoData(InetAddress ipAddress) throws Geo return geoData; } + + private Map retrieveAnonymousData(InetAddress ipAddress) throws GeoIp2Exception, IOException { + AnonymousIpResponse response = databaseReader.anonymousIp(ipAddress); + Map geoData = new HashMap<>(); + List anonymousTags = new ArrayList<>(); + for (Fields desiredField : this.desiredFields) { + switch (desiredField) { + case IP: + geoData.put(Fields.IP.fieldName(), ipAddress.getHostAddress()); + break; + case ANONYMOUS_TAGS: + if(response.isAnonymous()) { + anonymousTags.add(GeoIPFilter.IS_ANONYMOUS); + } + if(response.isAnonymousVpn()) { + anonymousTags.add(GeoIPFilter.IS_ANONYMOUS_VPN); + } + if(response.isHostingProvider()) { + anonymousTags.add(GeoIPFilter.IS_HOSTING_PROVIDER); + } + if(response.isPublicProxy()) { + anonymousTags.add(GeoIPFilter.IS_PUBLIC_PROXY); + } + if(response.isTorExitNode()) { + anonymousTags.add(GeoIPFilter.IS_TOR_EXIT_NODE); + } + break; + } + } + // add anonymmous tags field if there is at least one issue + if(!anonymousTags.isEmpty()){ + geoData.put(Fields.ANONYMOUS_TAGS.fieldName(), anonymousTags); + } + + return geoData; + } + + private Map retrieveConnectionTypeData(InetAddress ipAddress) throws GeoIp2Exception, IOException { + ConnectionTypeResponse response = databaseReader.connectionType(ipAddress); + Map geoData = new HashMap<>(); + for (Fields desiredField : this.desiredFields) { + switch (desiredField) { + case IP: + geoData.put(Fields.IP.fieldName(), ipAddress.getHostAddress()); + break; + case CONNECTION_TYPE: + ConnectionTypeResponse.ConnectionType connectionType = response.getConnectionType(); + if (connectionType != null) { + geoData.put(Fields.CONNECTION_TYPE.fieldName(), connectionType.name()); + } + break; + } + } + + return geoData; + } }