Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

header_rewrite: allow for use of maxminddb as source of geo truth #7695

Merged
merged 1 commit into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1685,27 +1685,66 @@ AC_SUBST(use_hwloc)
#
AC_CHECK_HEADERS([GeoIP.h], [
AC_CHECK_LIB([GeoIP], [GeoIP_new], [
AC_SUBST([GEO_LIBS], ["-lGeoIP"])
AC_SUBST([GEOIP_LIBS], ["-lGeoIP"])
AC_SUBST(has_geoip, 1)
], [
AC_SUBST([GEO_LIBS], [""])
AC_SUBST([GEOIP_LIBS], [""])
AC_SUBST(has_geoip, 0)
])
])

AM_CONDITIONAL([HAS_GEOIP], [test "x${has_geoip}" = "x1" ])

#
# Check for libmaxmind. This is the maxmind v2 API where GeoIP is the legacy
# v1 dat file based API
#
AC_CHECK_HEADERS([maxminddb.h], [
AC_CHECK_LIB([maxminddb], [MMDB_open], [
AC_SUBST([MAXMINDDB_LIBS], ["-lmaxminddb"])
AC_SUBST(has_maxmind, 1)
AC_SUBST(has_maxminddb, 1)
], [
AC_SUBST([MAXMINDDB_LIBS], [""])
AC_SUBST(has_maxmind, 0)
AC_SUBST(has_maxminddb, 0)
])
])

AM_CONDITIONAL([BUILD_MAXMIND_ACL_PLUGIN], [test "x${has_maxmind}" = "x1" ])
AM_CONDITIONAL([HAS_MAXMINDDB], [test "x${has_maxminddb}" = "x1" ])

AC_ARG_WITH([hrw-geo-provider],
[AS_HELP_STRING([--with-hrw-geo-provider=geoip|maxminddb],[geo provider to use with header_rewrite [default=auto] ])],
[geo_provider=$withval],
[geo_provider="auto"]
)
use_hrw_geoip=0
use_hrw_maxminddb=0

AS_IF([test "x$geo_provider" = "xauto"], [
if test "x$has_geoip" = "x1"; then
use_hrw_geoip=1
AC_MSG_NOTICE([Using GeoIP interface for header_rewrite])
elif test "x$has_maxminddb" = "x1"; then
use_hrw_maxminddb=1
AC_MSG_NOTICE([Using MaxMindDB interface for header_rewrite])
fi
],[
case "x$geo_provider" in
xgeoip)
use_hrw_geoip=1
AC_MSG_RESULT([forced to GeoIP])
;;
xmaxminddb)
use_hrw_maxminddb=1
AC_MSG_RESULT([forced to MaxMindDB])
;;
*)
AC_MSG_RESULT([failed])
AC_MSG_FAILURE([unknown geo interface $geo_provider])
esac
])

AC_SUBST(use_hrw_geoip)
AC_SUBST(use_hrw_maxminddb)

# Right now, the healthcheck plugins requires inotify_init (and friends)
AM_CONDITIONAL([BUILD_HEALTHCHECK_PLUGIN], [ test "$ac_cv_func_inotify_init" = "yes" ])
Expand Down
11 changes: 8 additions & 3 deletions doc/admin-guide/plugins/header_rewrite.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,16 @@ This plugin may be enabled globally, so that the conditions and header
rewriting rules are evaluated for every request made to your |TS| instance.
This is done by adding the following line to your :file:`plugin.config`::

header_rewrite.so config_file_1.conf config_file_2.conf ...
header_rewrite.so [--geo-db-path=path/to/geoip.db] config_file_1.conf config_file_2.conf ...

You may specify multiple configuration files. Their rules will be evaluated in
the order the files are listed.

The plugin takes an optional switch ``--geo-db-path``. If MaxMindDB support has
been compiled in, use this switch to point at your .mmdb file. This also applies to
the remap context.


Enabling Per-Mapping
--------------------

Expand Down Expand Up @@ -219,7 +224,7 @@ GEO
cond %{GEO:<part>} <operand>

Perform a GeoIP lookup of the client-IP, using a 3rd party library and
DB. Currently only the MaxMind GeoIP API is supported. The default is to
DB. Currently the MaxMind GeoIP and MaxMindDB APIs are supported. The default is to
do a Country lookup, but the following qualifiers are supported::

%{GEO:COUNTRY} The country code (e.g. "US")
Expand Down Expand Up @@ -495,7 +500,7 @@ TCP-INFO
~~~~~~~~
::

cond %{<name>}
cond %{<name>}
add-header @PropertyName "%{TCP-INFO}"

This operation records TCP Info struct field values as an Internal remap as well as global header at the event hook specified by the condition. Supported hook conditions include TXN_START_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Global plugin and REMAP_PSEUDO_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Remap plugin. Conditions supported as request headers include TXN_START_HOOK and REMAP_PSEUDO_HOOK. The other conditions are supported as response headers. TCP Info fields currently recorded include rtt, rto, snd_cwnd and all_retrans. This operation is not supported on transactions originated within Traffic Server (for e.g using the |TS| :c:func:`TSHttpTxnIsInternal`)
Expand Down
3 changes: 3 additions & 0 deletions include/tscore/ink_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
#define TS_HAS_TLS_EARLY_DATA @has_tls_early_data@
#define TS_HAS_TLS_SESSION_TICKET @has_tls_session_ticket@

#define TS_USE_HRW_GEOIP @use_hrw_geoip@
#define TS_USE_HRW_MAXMINDDB @use_hrw_maxminddb@

#define TS_HAS_SO_PEERCRED @has_so_peercred@

/* OS API definitions */
Expand Down
4 changes: 3 additions & 1 deletion plugins/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ include esi/Makefile.inc
include generator/Makefile.inc
include compress/Makefile.inc
include header_rewrite/Makefile.inc
if BUILD_HEALTHCHECK_PLUGIN
include healthchecks/Makefile.inc
endif
include libloader/Makefile.inc
if HAS_LUAJIT
include lua/Makefile.inc
Expand Down Expand Up @@ -71,7 +73,7 @@ include experimental/hook-trace/Makefile.inc
include experimental/icap/Makefile.inc
include experimental/inliner/Makefile.inc

if BUILD_MAXMIND_ACL_PLUGIN
if HAS_MAXMINDDB
include experimental/maxmind_acl/Makefile.inc
endif

Expand Down
2 changes: 1 addition & 1 deletion plugins/experimental/geoip_acl/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ experimental_geoip_acl_geoip_acl_la_SOURCES = \
experimental/geoip_acl/acl.cc \
experimental/geoip_acl/geoip_acl.cc

experimental_geoip_acl_geoip_acl_la_LIBADD = $(GEO_LIBS)
experimental_geoip_acl_geoip_acl_la_LIBADD = $(GEOIP_LIBS)
26 changes: 24 additions & 2 deletions plugins/header_rewrite/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,38 @@ header_rewrite_header_rewrite_la_SOURCES = \
header_rewrite/value.cc \
header_rewrite/value.h

if HAS_MAXMINDDB
header_rewrite_header_rewrite_la_SOURCES += header_rewrite/conditions_geo_maxmind.cc
endif

if HAS_GEOIP
header_rewrite_header_rewrite_la_SOURCES += header_rewrite/conditions_geo_geoip.cc
endif

header_rewrite_parser_la_SOURCES = \
header_rewrite/parser.cc \
header_rewrite/parser.h

header_rewrite_header_rewrite_la_LIBADD = \
header_rewrite/parser.la \
$(GEO_LIBS)
header_rewrite/parser.la

if HAS_GEOIP
header_rewrite_header_rewrite_la_LIBADD += $(GEOIP_LIBS)
endif

if HAS_MAXMINDDB
header_rewrite_header_rewrite_la_LIBADD += $(MAXMINDDB_LIBS)
endif

check_PROGRAMS += header_rewrite/header_rewrite_test
header_rewrite_header_rewrite_test_SOURCES = \
header_rewrite/header_rewrite_test.cc
header_rewrite_header_rewrite_test_LDADD = \
header_rewrite/parser.la
if HAS_GEOIP
header_rewrite_header_rewrite_test_LDADD += $(GEOIP_LIBS)
endif

if HAS_MAXMINDDB
header_rewrite_header_rewrite_test_LDADD += $(MAXMINDDB_LIBS)
endif
152 changes: 4 additions & 148 deletions plugins/header_rewrite/conditions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
See the License for the specific language governing permissions and
limitations under the License.
*/

//////////////////////////////////////////////////////////////////////////////////////////////
// conditions.cc: Implementation of the condition classes
//
//

#include <sys/time.h>
#include <unistd.h>
#include <arpa/inet.h>
Expand Down Expand Up @@ -740,155 +742,11 @@ ConditionNow::eval(const Resources &res)
return static_cast<const MatcherType *>(_matcher)->test(now);
}

// ConditionGeo: Geo-based information (integer). See ConditionGeoCountry for the string version.
#if HAVE_GEOIP_H
const char *
ConditionGeo::get_geo_string(const sockaddr *addr) const
{
const char *ret = "(unknown)";
int v = 4;

if (addr) {
switch (_geo_qual) {
// Country database
case GEO_QUAL_COUNTRY:
switch (addr->sa_family) {
case AF_INET:
if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);

ret = GeoIP_country_code_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
}
break;
case AF_INET6: {
if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;

v = 6;
ret = GeoIP_country_code_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
}
} break;
default:
break;
}
TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country: %s", v, ret);
break;

// ASN database
case GEO_QUAL_ASN_NAME:
switch (addr->sa_family) {
case AF_INET:
if (gGeoIP[GEOIP_ASNUM_EDITION]) {
uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);

ret = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
}
break;
case AF_INET6: {
if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;

v = 6;
ret = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
}
} break;
default:
break;
}
TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN Name: %s", v, ret);
break;

default:
break;
}
}

return ret ? ret : "(unknown)";
}

int64_t
ConditionGeo::get_geo_int(const sockaddr *addr) const
{
int64_t ret = -1;
int v = 4;

if (!addr) {
return 0;
}

switch (_geo_qual) {
// Country Database
case GEO_QUAL_COUNTRY_ISO:
switch (addr->sa_family) {
case AF_INET:
if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);

ret = GeoIP_id_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
}
break;
case AF_INET6: {
if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;

v = 6;
ret = GeoIP_id_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
}
} break;
default:
break;
}
TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country ISO: %" PRId64, v, ret);
break;

case GEO_QUAL_ASN: {
const char *asn_name = nullptr;

switch (addr->sa_family) {
case AF_INET:
if (gGeoIP[GEOIP_ASNUM_EDITION]) {
uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);

asn_name = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
}
break;
case AF_INET6:
if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;

v = 6;
asn_name = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
}
break;
}
if (asn_name) {
// This is a little odd, but the strings returned are e.g. "AS1234 Acme Inc"
while (*asn_name && !(isdigit(*asn_name))) {
++asn_name;
}
ret = strtol(asn_name, nullptr, 10);
}
}
TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN #: %" PRId64, v, ret);
break;

// Likely shouldn't trip, should we assert?
default:
break;
}

return ret;
}

#else

// No Geo library available, these are just stubs.

const char *
std::string
ConditionGeo::get_geo_string(const sockaddr *addr) const
{
TSError("[%s] No Geo library available!", PLUGIN_NAME);
return nullptr;
return "";
}

int64_t
Expand All @@ -898,8 +756,6 @@ ConditionGeo::get_geo_int(const sockaddr *addr) const
return 0;
}

#endif

void
ConditionGeo::initialize(Parser &p)
{
Expand Down
8 changes: 4 additions & 4 deletions plugins/header_rewrite/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,12 @@ class ConditionGeo : public Condition
_int_type = flag;
}

private:
virtual int64_t get_geo_int(const sockaddr *addr) const;
virtual std::string get_geo_string(const sockaddr *addr) const;

protected:
bool eval(const Resources &res) override;

private:
int64_t get_geo_int(const sockaddr *addr) const;
const char *get_geo_string(const sockaddr *addr) const;
GeoQualifiers _geo_qual = GEO_QUAL_COUNTRY;
bool _int_type = false;
};
Expand Down
Loading