#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)
{
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)
{
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 },
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
^^^^^^^^^^^^^^^^^^^^^^