From: Pieter Lexis Date: Mon, 15 Sep 2025 08:58:35 +0000 (+0200) Subject: feat(dnsdist): Add IPCrypt2 PFX mode to RemoteLogAction X-Git-Tag: rec-5.4.0-alpha1~208^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0834b07838fd019463f546b5358a48d5e4f2f712;p=thirdparty%2Fpdns.git feat(dnsdist): Add IPCrypt2 PFX mode to RemoteLogAction Signed-off-by: Pieter Lexis --- diff --git a/pdns/dnsdistdist/Makefile.am b/pdns/dnsdistdist/Makefile.am index 951637a4c6..19a64dc7eb 100644 --- a/pdns/dnsdistdist/Makefile.am +++ b/pdns/dnsdistdist/Makefile.am @@ -7,6 +7,7 @@ AM_CPPFLAGS += $(SYSTEMD_CFLAGS) \ $(NET_SNMP_CFLAGS) \ $(NGHTTP2_CFLAGS) \ $(LIBCAP_CFLAGS) \ + $(IPCRYPT2_CFLAGS) \ -I$(top_srcdir)/ext/protozero/include \ -I$(top_srcdir)/dnsdist-rust-lib \ -I$(top_builddir)/dnsdist-rust-lib \ @@ -22,6 +23,7 @@ SUBDIRS=dnsdist-rust-lib \ dnsdist-rust-lib/rust \ ext/arc4random \ ext/ipcrypt \ + ext/ipcrypt2 \ ext/yahttp CLEANFILES = \ @@ -204,6 +206,7 @@ dnsdist_SOURCES = \ dnsdist-healthchecks.cc dnsdist-healthchecks.hh \ dnsdist-idstate.cc dnsdist-idstate.hh \ dnsdist-internal-queries.cc dnsdist-internal-queries.hh \ + dnsdist-ipcrypt2.cc dnsdist-ipcrypt2.hh \ dnsdist-kvs.hh dnsdist-kvs.cc \ dnsdist-lbpolicies.cc dnsdist-lbpolicies.hh \ dnsdist-lua-actions.cc \ @@ -439,6 +442,7 @@ dnsdist_LDADD = \ $(NET_SNMP_LIBS) \ $(LIBCAP_LIBS) \ $(IPCRYPT_LIBS) \ + $(IPCRYPT2_LIBS) \ $(ARC4RANDOM_LIBS) testrunner_LDFLAGS = \ diff --git a/pdns/dnsdistdist/configure.ac b/pdns/dnsdistdist/configure.ac index fbfd93d68e..afe8ce4728 100644 --- a/pdns/dnsdistdist/configure.ac +++ b/pdns/dnsdistdist/configure.ac @@ -66,6 +66,8 @@ AC_SUBST([YAHTTP_CFLAGS], ['-I$(top_srcdir)/ext/yahttp']) AC_SUBST([YAHTTP_LIBS], ['$(top_builddir)/ext/yahttp/yahttp/libyahttp.la']) AC_SUBST([IPCRYPT_CFLAGS], ['-I$(top_srcdir)/ext/ipcrypt']) AC_SUBST([IPCRYPT_LIBS], ['$(top_builddir)/ext/ipcrypt/libipcrypt.la']) +AC_SUBST([IPCRYPT2_CFLAGS], ['-I$(top_srcdir)/ext/ipcrypt2/include']) +AC_SUBST([IPCRYPT2_LIBS], ['$(top_builddir)/ext/ipcrypt2/libipcrypt2.la']) AC_SUBST([ARC4RANDOM_LIBS], ['$(top_builddir)/ext/arc4random/libarc4random.la']) AC_CHECK_HEADERS([sys/random.h]) @@ -204,7 +206,8 @@ AC_CONFIG_FILES([Makefile ext/arc4random/Makefile ext/yahttp/Makefile ext/yahttp/yahttp/Makefile - ext/ipcrypt/Makefile]) + ext/ipcrypt/Makefile + ext/ipcrypt2/Makefile]) AC_OUTPUT diff --git a/pdns/dnsdistdist/dnsdist-actions-definitions.yml b/pdns/dnsdistdist/dnsdist-actions-definitions.yml index 32e36556bf..05e7f99c98 100644 --- a/pdns/dnsdistdist/dnsdist-actions-definitions.yml +++ b/pdns/dnsdistdist/dnsdist-actions-definitions.yml @@ -291,7 +291,14 @@ The function will be invoked in a per-thread Lua state, without access to the gl - name: "ip_encrypt_key" type: "String" default: "" - description: "A key, that can be generated via the :func:`makeIPCipherKey` function, to encrypt the IP address of the requestor for anonymization purposes. The encryption is done using ipcrypt for IPv4 and a 128-bit AES ECB operation for IPv6" + description: "A key to encrypt the IP address of the requestor for anonymization purposes. For the \"legacy\" method, it can be generated via the :func:`makeIPCipherKey` function. The encryption method can be set using ``ip_encrypt_method``" + - name: "ip_encrypt_method" + type: "String" + default: "legacy" + description: " + The method to encrypt the IP addresses with. + * legacy: The encryption is done using ipcrypt for IPv4 and a 128-bit AES ECB operation for IPv6. This is the default. + * ipcrypt-pfx: IPCrypt2, using prefix-preserving encryption. See `the ipcrypt website __`. ``ip_encrypt_key`` must be 32 bytes." - name: "export_tags" type: "Vec" default: "" diff --git a/pdns/dnsdistdist/dnsdist-actions-factory.cc b/pdns/dnsdistdist/dnsdist-actions-factory.cc index 320428d276..694d733b28 100644 --- a/pdns/dnsdistdist/dnsdist-actions-factory.cc +++ b/pdns/dnsdistdist/dnsdist-actions-factory.cc @@ -19,6 +19,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include #include "dnsdist-actions-factory.hh" @@ -44,6 +45,8 @@ #include "ednsoptions.hh" #include "fstrm_logger.hh" #include "ipcipher.hh" +#include "dnsdist-ipcrypt2.hh" +#include "iputils.hh" #include "remote_logger.hh" #include "svc-records.hh" #include "threadname.hh" @@ -1598,8 +1601,11 @@ class RemoteLogAction : public DNSAction, public boost::noncopyable public: // this action does not stop the processing RemoteLogAction(RemoteLogActionConfiguration& config) : - d_tagsToExport(std::move(config.tagsToExport)), d_metas(std::move(config.metas)), d_logger(config.logger), d_alterFunc(std::move(config.alterQueryFunc)), d_serverID(config.serverID), d_ipEncryptKey(config.ipEncryptKey) + d_tagsToExport(std::move(config.tagsToExport)), d_metas(std::move(config.metas)), d_logger(config.logger), d_alterFunc(std::move(config.alterQueryFunc)), d_serverID(config.serverID), d_ipEncryptKey(config.ipEncryptKey), d_ipEncryptMethod(config.ipEncryptMethod) { + if (!d_ipEncryptKey.empty() && d_ipEncryptMethod == "ipcrypt-pfx") { + d_ipcrypt2 = pdns::ipcrypt2::IPCrypt2(pdns::ipcrypt2::IPCryptMethod::pfx, d_ipEncryptKey); + } } DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override @@ -1618,10 +1624,15 @@ public: } #ifdef HAVE_IPCIPHER - if (!d_ipEncryptKey.empty()) { + if (!d_ipEncryptKey.empty() && d_ipEncryptMethod == "legacy") { message.setRequestor(encryptCA(dnsquestion->ids.origRemote, d_ipEncryptKey)); } #endif /* HAVE_IPCIPHER */ + if (d_ipcrypt2) { + auto encryptedAddress = d_ipcrypt2->encrypt(dnsquestion->ids.origRemote); + encryptedAddress.setPort(dnsquestion->ids.origRemote.getPort()); + message.setRequestor(encryptedAddress); + } if (d_tagsToExport) { addTagsToProtobuf(message, *dnsquestion, *d_tagsToExport); @@ -1656,6 +1667,8 @@ private: std::optional> d_alterFunc; std::string d_serverID; std::string d_ipEncryptKey; + std::string d_ipEncryptMethod; + std::optional d_ipcrypt2{std::nullopt}; }; #endif /* DISABLE_PROTOBUF */ diff --git a/pdns/dnsdistdist/dnsdist-actions-factory.hh b/pdns/dnsdistdist/dnsdist-actions-factory.hh index 7ef95d3810..5894670468 100644 --- a/pdns/dnsdistdist/dnsdist-actions-factory.hh +++ b/pdns/dnsdistdist/dnsdist-actions-factory.hh @@ -113,6 +113,7 @@ struct RemoteLogActionConfiguration std::shared_ptr logger; std::string serverID; std::string ipEncryptKey; + std::string ipEncryptMethod{"legacy"}; std::optional exportExtendedErrorsToMeta{std::nullopt}; bool includeCNAME{false}; }; diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc index e2b36bc2c8..a84c4f8ead 100644 --- a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc +++ b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc @@ -1666,6 +1666,7 @@ std::shared_ptr getRemoteLogAction(const RemoteLogActionConfig actionConfig.logger = std::move(logger); actionConfig.serverID = std::string(config.server_id); actionConfig.ipEncryptKey = std::string(config.ip_encrypt_key); + actionConfig.ipEncryptMethod = std::string(config.ip_encrypt_method); for (const auto& meta : config.metas) { actionConfig.metas.emplace_back(std::string(meta.key), ProtoBufMetaKey(std::string(meta.value))); } diff --git a/pdns/dnsdistdist/dnsdist-ipcrypt2.cc b/pdns/dnsdistdist/dnsdist-ipcrypt2.cc new file mode 100644 index 0000000000..6e2c0a02c8 --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-ipcrypt2.cc @@ -0,0 +1,83 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +#include "dnsdist-ipcrypt2.hh" +#include "ipcrypt2.h" +#include "iputils.hh" + +// ipcrypt2 namespace does not have to be dnsdist-specific +namespace pdns::ipcrypt2 +{ +IPCrypt2::IPCrypt2(const IPCryptMethod& method, const std::string& key) : + d_method(method) +{ + switch (method) { + case IPCryptMethod::pfx: { + if (key.size() != IPCRYPT_PFX_KEYBYTES) { + throw std::runtime_error("Key for IPCrypt PFX method is not " + std::to_string(IPCRYPT_PFX_KEYBYTES) + " bytes"); + } + d_ipcryptCtxPfx = std::make_unique(); + auto ret = ipcrypt_pfx_init(d_ipcryptCtxPfx.get(), reinterpret_cast(key.data())); + if (ret != 0) { + throw std::runtime_error("Could not initialize IPCrypt2 PFX context"); + } + } break; + default: + throw std::runtime_error("Unsupported IPCrypt2 method"); + break; + } +} + +IPCrypt2::~IPCrypt2() +{ + switch (d_method) { + case IPCryptMethod::pfx: + if (d_ipcryptCtxPfx != nullptr) { + ipcrypt_pfx_deinit(d_ipcryptCtxPfx.get()); + } + break; + default: + return; + } +}; + +ComboAddress IPCrypt2::encrypt(const ComboAddress& address) const +{ + switch (d_method) { + case IPCryptMethod::pfx: { + std::string encryptedIP; + encryptedIP.resize(IPCRYPT_MAX_IP_STR_BYTES); + auto ret = ipcrypt_pfx_encrypt_ip_str(d_ipcryptCtxPfx.get(), encryptedIP.data(), address.toString().c_str()); + // XXX: Do we *need* to resize? + encryptedIP.resize(ret); + return ComboAddress(encryptedIP); + } break; + default: + throw std::runtime_error("Unsupported method"); + break; + } +} +} diff --git a/pdns/dnsdistdist/dnsdist-ipcrypt2.hh b/pdns/dnsdistdist/dnsdist-ipcrypt2.hh new file mode 100644 index 0000000000..9222f370da --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-ipcrypt2.hh @@ -0,0 +1,61 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include +#include + +#include "iputils.hh" +#include "ipcrypt2.h" + +namespace pdns::ipcrypt2 +{ + +enum IPCryptMethod : uint8_t +{ + deterministic, + pfx, + nd, + ndx +}; + +class IPCrypt2 +{ +public: + IPCrypt2(const IPCryptMethod& method, const std::string& key); + + IPCrypt2(IPCrypt2&& rhs) = default; + IPCrypt2& operator=(IPCrypt2&& rhs) = default; + + IPCrypt2(const IPCrypt2& orig) = delete; + IPCrypt2& operator=(const IPCrypt2& orig) = delete; + + ~IPCrypt2(); + + [[nodiscard]] ComboAddress encrypt(const ComboAddress& address) const; + +private: + std::unique_ptr d_ipcryptCtxPfx; + IPCryptMethod d_method; +}; +} diff --git a/pdns/dnsdistdist/dnsdist-lua-actions.cc b/pdns/dnsdistdist/dnsdist-lua-actions.cc index 11066b005e..3d10df0753 100644 --- a/pdns/dnsdistdist/dnsdist-lua-actions.cc +++ b/pdns/dnsdistdist/dnsdist-lua-actions.cc @@ -29,6 +29,7 @@ #include "dnsdist-rule-chains.hh" #include "dnstap.hh" #include "remote_logger.hh" +#include template static void addAction(IdentifierT identifier, const luadnsrule_t& var, const std::shared_ptr& action, boost::optional& params) @@ -284,6 +285,9 @@ void setupLuaActions(LuaContext& luaCtx) }); #ifndef DISABLE_PROTOBUF + // Used for both RemoteLogAction and RemoteLogResponseAction + static const std::array s_validIpEncryptMethods = {"legacy", "ipcrypt-pfx"}; + luaCtx.writeFunction("RemoteLogAction", [](std::shared_ptr logger, boost::optional alterFunc, boost::optional> vars, boost::optional> metas) { if (logger) { // avoids potentially-evaluated-expression warning with clang. @@ -302,6 +306,7 @@ void setupLuaActions(LuaContext& luaCtx) } getOptionalValue(vars, "serverID", config.serverID); getOptionalValue(vars, "ipEncryptKey", config.ipEncryptKey); + getOptionalValue(vars, "ipEncryptMethod", config.ipEncryptMethod); getOptionalValue(vars, "exportTags", tags); if (metas) { @@ -310,6 +315,10 @@ void setupLuaActions(LuaContext& luaCtx) } } + if (std::find(s_validIpEncryptMethods.begin(), s_validIpEncryptMethods.end(), config.ipEncryptMethod) == s_validIpEncryptMethods.end()) { + throw std::runtime_error("Invalid IP Encryption method in RemoteLogAction"); + } + if (!tags.empty()) { config.tagsToExport = std::unordered_set(); if (tags != "*") { diff --git a/pdns/dnsdistdist/meson.build b/pdns/dnsdistdist/meson.build index 75ff2e0599..4cc1eb6366 100644 --- a/pdns/dnsdistdist/meson.build +++ b/pdns/dnsdistdist/meson.build @@ -97,6 +97,7 @@ dep_pdns = declare_dependency(include_directories: include_directories('.', src_ # Ext subdir('ext' / 'arc4random') subdir('ext' / 'ipcrypt') +subdir('ext' / 'ipcrypt2') subdir('ext' / 'json11') subdir('ext' / 'luawrapper') subdir('ext' / 'protozero') @@ -143,6 +144,7 @@ common_sources += files( src_dir / 'dnsdist-healthchecks.cc', src_dir / 'dnsdist-idstate.cc', src_dir / 'dnsdist-internal-queries.cc', + src_dir / 'dnsdist-ipcrypt2.cc', src_dir / 'dnsdist-kvs.cc', src_dir / 'dnsdist-lbpolicies.cc', src_dir / 'dnsdist-lua-actions.cc', @@ -381,6 +383,7 @@ deps = [ dep_dnstap, dep_htmlfiles, dep_ipcrypt, + dep_ipcrypt2, dep_libcap, dep_libcrypto, dep_gnutls,