All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graylog.plugins.map.geoip.GeoIpResolverEngine Maven / Gradle / Ivy

There is a newer version: 6.0.1
Show newest version
/*
 * Copyright (C) 2020 Graylog, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * .
 */
package org.graylog.plugins.map.geoip;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.InetAddresses;
import org.apache.commons.lang3.StringUtils;
import org.graylog.plugins.map.config.GeoIpResolverConfig;
import org.graylog.plugins.map.config.S3GeoIpFileService;
import org.graylog2.plugin.Message;
import org.graylog2.utilities.ReservedIpChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.net.InetAddress;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.codahale.metrics.MetricRegistry.name;

public class GeoIpResolverEngine {
    private static final Logger LOG = LoggerFactory.getLogger(GeoIpResolverEngine.class);

    /**
     * This is a list of schema fields defined in package org.graylog.schema as of 2022-07-15 which we want to scan for IP addresses.  If the schema changes, or we need to add/remove fields,
     * this list must be updated, until a better way of defining schema fields is developed, that allows iteration.
     */
    private static final String[] KNOWN_SCHEMA_IP_FIELDS = {org.graylog.schema.DestinationFields.DESTINATION_IP,
            org.graylog.schema.DestinationFields.DESTINATION_NAT_IP,
            org.graylog.schema.EventFields.EVENT_OBSERVER_IP,
            org.graylog.schema.SourceFields.SOURCE_NAT_IP,
            org.graylog.schema.NetworkFields.NETWORK_FORWARDED_IP,
            org.graylog.schema.SourceFields.SOURCE_IP,
            org.graylog.schema.HostFields.HOST_IP};

    /**
     * A mapping of fields (per the Graylog Schema) that to search that contain IP addresses.  When the user opts to
     * enforce the Graylog Schema ONLY these fields will be checked; otherwise, all message fields will be checked.
     * to see if they have valid Geo IP information.
     *
     * 

* The mapping is field name -> new message field prefix, where field_name is the field name expected in the message * which will be searched in the GeoIP database, and the new message field prefix is the prefix for the new field with the GeoIP data * that will be inserted into the message. *

*/ private final Map ipAddressFields = Stream.of(KNOWN_SCHEMA_IP_FIELDS) .collect(Collectors.toMap(e -> e, mapFieldNameToPrefix())); private final GeoIpResolver ipLocationResolver; private final GeoIpResolver ipAsnResolver; private final boolean enabled; private final boolean enforceGraylogSchema; public GeoIpResolverEngine(GeoIpVendorResolverService resolverService, GeoIpResolverConfig config, S3GeoIpFileService s3GeoIpFileService, MetricRegistry metricRegistry) { Timer resolveTime = metricRegistry.timer(name(GeoIpResolverEngine.class, "resolveTime")); enforceGraylogSchema = config.enforceGraylogSchema(); if (config.useS3()) { config = config.toBuilder() .asnDbPath(s3GeoIpFileService.getActiveAsnFile()) .cityDbPath(s3GeoIpFileService.getActiveCityFile()) .build(); } ipLocationResolver = resolverService.createCityResolver(config, resolveTime); ipAsnResolver = resolverService.createAsnResolver(config, resolveTime); LOG.debug("Created Geo IP Resolvers for '{}'", config.databaseVendorType()); LOG.debug("'{}' Status Enabled: {}", ipLocationResolver.getClass().getSimpleName(), ipLocationResolver.isEnabled()); LOG.debug("'{}' Status Enabled: {}", ipAsnResolver.getClass().getSimpleName(), ipAsnResolver.isEnabled()); this.enabled = ipLocationResolver.isEnabled() || ipAsnResolver.isEnabled(); } public boolean filter(Message message) { if (!enabled) { return false; } List ipFields = getIpAddressFields(message); for (String key : ipFields) { Object fieldValue = message.getField(key); final InetAddress address = getValidRoutableInetAddress(fieldValue); if (address == null) { continue; } // For reserved IPs just mark as reserved. Otherwise, enforce Graylog schema on only relevant IP fields // or add legacy fields on all IP fields in the message if enforcement is disabled. final String prefix = enforceGraylogSchema ? ipAddressFields.getOrDefault(key, key) : key; if (ReservedIpChecker.getInstance().isReservedIpAddress(address.getHostAddress())) { message.addField(prefix + "_reserved_ip", true); } else if (enforceGraylogSchema) { addGIMGeoIpDataIfPresent(message, address, prefix); } else { addLegacyGeoIpDataIfPresent(message, address, prefix); } } return true; } // Pre-4.3 logic for adding geo fields to message. private void addLegacyGeoIpDataIfPresent(Message message, InetAddress address, String key) { ipLocationResolver.getGeoIpData(address).ifPresent(locationInformation -> { // We will store the coordinates as a "lat,long" string message.addField(key + "_geolocation", locationInformation.latitude() + "," + locationInformation.longitude()); message.addField(key + "_country_code", locationInformation.countryIsoCode()); message.addField(key + "_city_name", locationInformation.cityName()); }); } private void addGIMGeoIpDataIfPresent(Message message, InetAddress address, String newFieldPrefix) { ipLocationResolver.getGeoIpData(address).ifPresent(locationInformation -> { message.addField(newFieldPrefix + "_geo_coordinates", locationInformation.latitude() + "," + locationInformation.longitude()); message.addField(newFieldPrefix + "_geo_country_iso", locationInformation.countryIsoCode()); message.addField(newFieldPrefix + "_geo_city", locationInformation.cityName()); message.addField(newFieldPrefix + "_geo_region", locationInformation.region()); message.addField(newFieldPrefix + "_geo_timezone", locationInformation.timeZone()); if (areValidGeoNames(locationInformation.countryName())) { message.addField(newFieldPrefix + "_geo_country", locationInformation.countryName()); } if (areValidGeoNames(locationInformation.cityName(), locationInformation.countryIsoCode())) { String name = String.format(Locale.ENGLISH, "%s, %s", locationInformation.cityName(), locationInformation.countryIsoCode()); message.addField(newFieldPrefix + "_geo_name", name); } }); ipAsnResolver.getGeoIpData(address).ifPresent(info -> { message.addField(newFieldPrefix + "_as_organization", info.organization()); message.addField(newFieldPrefix + "_as_number", info.asn()); }); } /** * Get the message fields that will be checked for IP addresses. * *

* If the user has chosen NOT to enforce the Graylog Schema, then all fields will be checked as any field could * have an IP address. *

* * @param message message * @return a list of field that may have an IP address */ @VisibleForTesting List getIpAddressFields(Message message) { return message.getFieldNames() .stream() .filter(e -> (!enforceGraylogSchema || ipAddressFields.containsKey(e)) && !e.startsWith(Message.INTERNAL_FIELD_PREFIX)) .collect(Collectors.toList()); } private InetAddress getValidRoutableInetAddress(Object fieldValue) { final InetAddress ipAddress; if (fieldValue instanceof InetAddress) { ipAddress = (InetAddress) fieldValue; } else if (fieldValue instanceof String) { ipAddress = getIpFromFieldValue((String) fieldValue); } else { ipAddress = null; } return ipAddress; } private boolean areValidGeoNames(String... names) { for (String name : names) { if (StringUtils.isBlank(name) || "N/A".equalsIgnoreCase(name)) { return false; } } return true; } @Nullable @VisibleForTesting InetAddress getIpFromFieldValue(String fieldValue) { try { return InetAddresses.forString(fieldValue.trim()); } catch (IllegalArgumentException e) { // Do nothing, field is not an IP } return null; } private static Function mapFieldNameToPrefix() { return string -> string.replace("_ip", ""); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy