]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/bindings: add net.proxy_allowed() + docs
authorOto Šťáva <oto.stava@nic.cz>
Thu, 16 Dec 2021 13:24:05 +0000 (14:24 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Tue, 22 Feb 2022 10:52:11 +0000 (10:52 +0000)
NEWS
daemon/bindings/net.c
daemon/bindings/net_server.rst

diff --git a/NEWS b/NEWS
index 10c48fce1bd043388ddd8a21385635abd1f64b83..d327edf419e48602ad9a874ad8d2f955fbdae254 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,7 @@ Improvements
 - prefill module: prepare for ZONEMD, improve performance (!1225)
 - validator: conditionally ignore SHA1 DS, as SHOULD by RFC4509 (!1251)
 - lib/resolve: use EDNS padding for outgoing TLS queries (!1254)
+- support for PROXYv2 protocol (!1238)
 
 Incompatible changes
 --------------------
index ff6a6c37dd5288586cd78c0bc9cb1cdffd6208c4..0636f0dec80a7d5ed6c3ef83104f465ecd2b5558 100644 (file)
@@ -11,6 +11,8 @@
 
 #include <stdlib.h>
 
+#define PROXY_DATA_STRLEN (INET6_ADDRSTRLEN + 1 + 3 + 1)
+
 /** Table and next index on top of stack -> append entries for given endpoint_array_t. */
 static int net_list_add(const char *key, void *val, void *ext)
 {
@@ -281,6 +283,97 @@ static int net_listen(lua_State *L)
        return 1;
 }
 
+/** Prints the specified `data` into the specified `dst` buffer. */
+static char *proxy_data_to_string(int af, const struct net_proxy_data *data,
+               char *dst, size_t size)
+{
+       kr_assert(size >= PROXY_DATA_STRLEN);
+       const void *in_addr = (af == AF_INET)
+               ? (void *) &data->addr.ip4
+               : (void *) &data->addr.ip6;
+       char *cur = dst;
+
+       const char *ret = inet_ntop(af, in_addr, cur, size);
+       if (!ret)
+               return NULL;
+
+       cur += strlen(cur); /*< advance cursor to after the address */
+       *(cur++) = '/';
+       int masklen = snprintf(cur, 3 + 1, "%u", data->netmask);
+       cur[masklen] = '\0';
+       return dst;
+}
+
+/** Allow PROXYv2 headers for IP address. */
+static int net_proxy_allowed(lua_State *L)
+{
+       struct network *net = &the_worker->engine->net;
+       int n = lua_gettop(L);
+       int i = 1;
+       const char *addr;
+       char addrbuf[PROXY_DATA_STRLEN];
+
+       /* Return current state */
+       if (n == 0) {
+               lua_newtable(L);
+               trie_it_t *it;
+               i = 1;
+               for (it = trie_it_begin(net->proxy_addrs4); !trie_it_finished(it); trie_it_next(it)) {
+                       lua_pushinteger(L, i);
+                       struct net_proxy_data *data = *trie_it_val(it);
+                       addr = proxy_data_to_string(AF_INET, data,
+                                       addrbuf, sizeof(addrbuf));
+                       lua_pushstring(L, addr);
+                       lua_settable(L, -3);
+                       i += 1;
+               }
+               trie_it_free(it);
+               for (it = trie_it_begin(net->proxy_addrs6); !trie_it_finished(it); trie_it_next(it)) {
+                       lua_pushinteger(L, i);
+                       struct net_proxy_data *data = *trie_it_val(it);
+                       addr = proxy_data_to_string(AF_INET6, data,
+                                       addrbuf, sizeof(addrbuf));
+                       lua_pushstring(L, addr);
+                       lua_settable(L, -3);
+                       i += 1;
+               }
+               trie_it_free(it);
+               return 1;
+       }
+
+       if (n != 1)
+               lua_error_p(L, "net.proxy_allowed() takes one parameter (string or table)");
+
+       if (!lua_istable(L, 1) && !lua_isstring(L, 1))
+               lua_error_p(L, "net.proxy_allowed() argument must be string or table");
+
+       /* Reset allowed proxy addresses */
+       network_proxy_reset(net);
+
+       /* Add new proxy addresses */
+       if (lua_istable(L, 1)) {
+               for (i = 1; !lua_isnil(L, -1); i++) {
+                       lua_pushinteger(L, i);
+                       lua_gettable(L, 1);
+                       if (lua_isnil(L, -1)) /* missing value - end iteration */
+                               break;
+                       if (!lua_isstring(L, -1))
+                               lua_error_p(L, "net.proxy_allowed() argument may only contain strings");
+                       addr = lua_tostring(L, -1);
+                       int ret = network_proxy_allow(net, addr);
+                       if (ret)
+                               lua_error_p(L, "invalid argument");
+               }
+       } else if (lua_isstring(L, 1)) {
+               addr = lua_tostring(L, 1);
+               int ret = network_proxy_allow(net, addr);
+               if (ret)
+                       lua_error_p(L, "invalid argument");
+       }
+
+       return 0;
+}
+
 /** Close endpoint. */
 static int net_close(lua_State *L)
 {
@@ -1125,6 +1218,7 @@ int kr_bindings_net(lua_State *L)
        static const luaL_Reg lib[] = {
                { "list",         net_list },
                { "listen",       net_listen },
+               { "proxy_allowed", net_proxy_allowed },
                { "close",        net_close },
                { "interfaces",   net_interfaces },
                { "bufsize",      net_bufsize },
index b652f4eb16e8772410404c23f9a90af4eaf09a72..1b549edd9d6b9ac7702ba9dd555bab94064ba98a 100644 (file)
@@ -66,6 +66,51 @@ Examples:
         addresses if the network address ranges overlap,
         and clients would probably refuse such a response.
 
+PROXYv2 protocol
+^^^^^^^^^^^^^^^^
+
+Knot Resolver supports proxies that utilize the `PROXYv2 protocol <https://www.haproxy.org/download/2.5/doc/proxy-protocol.txt>`_
+to identify clients.
+
+A PROXY header contains the IP address of the original client who sent a query.
+This allows the resolver to treat queries as if they actually came from
+the client's IP address rather than the address of the proxy they came through.
+For example, :ref:`Views and ACLs <mod-view>` are able to work properly when
+PROXYv2 is in use.
+
+Since allowing usage of the PROXYv2 protocol for all clients would be a security
+vulnerability, the resolver requires you to specify explicitly which clients
+are allowed to send PROXYv2 headers via the :func:`net.proxy_allowed` function.
+
+PROXYv2 queries from clients who are not explicitly allowed to use this protocol
+will be discarded.
+
+.. function:: net.proxy_allowed([addresses])
+
+   Allow usage of the PROXYv2 protocol headers by clients on the specified
+   ``addresses``. It is possible to permit whole networks to send PROXYv2 headers
+   by specifying the network mask using the CIDR notation
+   (e.g. ``172.22.0.0/16``). IPv4 as well as IPv6 addresses are supported.
+
+   Subsequent calls to the function overwrite the effects of all previous calls.
+   Providing a table of strings as the function parameter allows multiple
+   distinct addresses to use the PROXYv2 protocol.
+
+   When called without arguments, ``net.proxy_allowed`` returns a table of all
+   addresses currently allowed to use the PROXYv2 protocol and does not change
+   the configuration.
+
+Examples:
+
+   .. code-block:: lua
+
+       net.proxy_allowed('172.22.0.1')    -- allows '172.22.0.1' specifically
+       net.proxy_allowed('172.18.1.0/24') -- allows everyone at '172.18.1.*'
+       net.proxy_allowed({
+               '172.22.0.1', '172.18.1.0/24'
+       })                                 -- allows both of the above at once
+       net.proxy_allowed({})              -- prevents everyone from using PROXYv2
+       net.proxy_allowed()                -- returns a list of all currently allowed addresses
 
 Features for scripting
 ^^^^^^^^^^^^^^^^^^^^^^