diff options
author | Daniel Salzman <daniel.salzman@nic.cz> | 2024-12-03 11:23:24 +0100 |
---|---|---|
committer | Daniel Salzman <daniel.salzman@nic.cz> | 2024-12-03 11:23:24 +0100 |
commit | 04329a6e3128fc32f30f5810b2bc39d005dceb9f (patch) | |
tree | 5945043352edd341cc32d536718566ba05bc1c81 | |
parent | Merge branch 'keyroll_2active' into 'master' (diff) | |
parent | mod-rrl: increase default limits (diff) | |
download | knot-04329a6e3128fc32f30f5810b2bc39d005dceb9f.tar.xz knot-04329a6e3128fc32f30f5810b2bc39d005dceb9f.zip |
Merge branch 'rrl_auth'
fixes #943
-rw-r--r-- | src/knot/modules/rrl/functions.c | 29 | ||||
-rw-r--r-- | src/knot/modules/rrl/functions.h | 10 | ||||
-rw-r--r-- | src/knot/modules/rrl/rrl.c | 59 | ||||
-rw-r--r-- | src/knot/modules/rrl/rrl.rst | 19 | ||||
-rw-r--r-- | src/knot/nameserver/internet.h | 1 | ||||
-rw-r--r-- | src/knot/server/handler.c | 12 | ||||
-rw-r--r-- | src/knot/server/handler.h | 25 | ||||
-rw-r--r-- | src/knot/server/tcp-handler.c | 13 | ||||
-rw-r--r-- | src/knot/server/xdp-handler.c | 10 | ||||
-rw-r--r-- | src/libknot/quic/quic_conn.h | 1 | ||||
-rw-r--r-- | src/libknot/quic/tls.h | 1 | ||||
-rw-r--r-- | src/libknot/xdp/tcp.c | 1 | ||||
-rw-r--r-- | src/libknot/xdp/tcp.h | 5 |
13 files changed, 133 insertions, 53 deletions
diff --git a/src/knot/modules/rrl/functions.c b/src/knot/modules/rrl/functions.c index 01d89cb66..5534b4437 100644 --- a/src/knot/modules/rrl/functions.c +++ b/src/knot/modules/rrl/functions.c @@ -68,18 +68,35 @@ static void addr_tostr(char *dst, size_t maxlen, const struct sockaddr_storage * } } -static void rrl_log_limited(knotd_mod_t *mod, const struct sockaddr_storage *ss, +static void rrl_log_limited(rrl_log_params_t *params, const struct sockaddr_storage *ss, const uint8_t prefix, bool rate) { - if (mod == NULL) { + if (params == NULL) { return; } char addr_str[SOCKADDR_STRLEN]; addr_tostr(addr_str, sizeof(addr_str), ss); - knotd_mod_log(mod, LOG_NOTICE, "address %s limited on /%d by %s", - addr_str, prefix, rate ? "rate" : "time"); + const char *proto_str = "UDP"; + const char *qname_str = NULL; + knot_dname_txt_storage_t buf; + if (params->qdata != NULL) { + qname_str = knot_dname_to_str(buf, knot_pkt_qname(params->qdata->query), + sizeof(buf)); + } else { + switch (params->proto) { + case KNOTD_QUERY_PROTO_TCP: proto_str = "TCP"; break; + case KNOTD_QUERY_PROTO_QUIC: proto_str = "QUIC"; break; + case KNOTD_QUERY_PROTO_TLS: proto_str = "TLS"; break; + default: break; + } + } + + knotd_mod_log(params->mod, LOG_NOTICE, "address %s %s limited on /%d by %s%s%s", + addr_str, proto_str, prefix, rate ? "rate" : "time", + (qname_str != NULL ? ", qname " : ""), + (qname_str != NULL ? qname_str : "")); } rrl_table_t *rrl_create(size_t size, uint32_t instant_limit, uint32_t rate_limit, @@ -131,7 +148,7 @@ rrl_table_t *rrl_create(size_t size, uint32_t instant_limit, uint32_t rate_limit return rrl; } -int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote, knotd_mod_t *mod) +int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote, rrl_log_params_t *log) { assert(rrl); assert(remote); @@ -186,7 +203,7 @@ int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote, knotd_mod do { if (atomic_compare_exchange_weak_explicit(&rrl->log_time, &log_time_orig, now, memory_order_relaxed, memory_order_relaxed)) { - rrl_log_limited(mod, remote, prefix, rrl->rw_mode); + rrl_log_limited(log, remote, prefix, rrl->rw_mode); break; } } while (now - log_time_orig + 1024 >= rrl->log_period + 1024); diff --git a/src/knot/modules/rrl/functions.h b/src/knot/modules/rrl/functions.h index 0941c8378..8fe1383c5 100644 --- a/src/knot/modules/rrl/functions.h +++ b/src/knot/modules/rrl/functions.h @@ -37,6 +37,12 @@ typedef struct rrl_table rrl_table_t; rrl_table_t *rrl_create(size_t size, uint32_t instant_limit, uint32_t rate_limit, bool rw_mode, uint32_t log_period); +typedef struct { + knotd_mod_t *mod; + knotd_qdata_t *qdata; // For rate limiting. + knotd_query_proto_t proto; // For time limiting. +} rrl_log_params_t; + /*! * \brief Query the RRL table for accept or deny, when the rate limit is reached. * @@ -44,12 +50,12 @@ rrl_table_t *rrl_create(size_t size, uint32_t instant_limit, uint32_t rate_limit * * \param rrl RRL table. * \param remote Source address. - * \param mod Query module (needed for logging). + * \param log Logging parameters (can be NULL). * * \retval KNOT_EOK if passed. * \retval KNOT_ELIMIT when the limit is reached. */ -int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote, knotd_mod_t *mod); +int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote, rrl_log_params_t *log); /*! * \brief Update the RRL table. diff --git a/src/knot/modules/rrl/rrl.c b/src/knot/modules/rrl/rrl.c index 17e773812..162eb8875 100644 --- a/src/knot/modules/rrl/rrl.c +++ b/src/knot/modules/rrl/rrl.c @@ -30,10 +30,10 @@ #define MOD_DRY_RUN "\x07""dry-run" const yp_item_t rrl_conf[] = { - { MOD_INST_LIMIT, YP_TINT, YP_VINT = { 1, (1ll << 32) / 768 - 1, 50 } }, - { MOD_RATE_LIMIT, YP_TINT, YP_VINT = { 0, ((1ll << 32) / 768 - 1) * 1000, 20 } }, + { MOD_INST_LIMIT, YP_TINT, YP_VINT = { 1, (1ll << 32) / 768 - 1, 125 } }, + { MOD_RATE_LIMIT, YP_TINT, YP_VINT = { 0, ((1ll << 32) / 768 - 1) * 1000, 50 } }, { MOD_T_INST_LIMIT, YP_TINT, YP_VINT = { 1, 1000000, 5000 } }, - { MOD_T_RATE_LIMIT, YP_TINT, YP_VINT = { 0, 1000000000, 4000 } }, + { MOD_T_RATE_LIMIT, YP_TINT, YP_VINT = { 0, 1000000000, 5000 } }, { MOD_SLIP, YP_TINT, YP_VINT = { 0, 100, 1 } }, { MOD_TBL_SIZE, YP_TINT, YP_VINT = { 1, INT32_MAX, 524288 } }, { MOD_WHITELIST, YP_TNET, YP_VNONE, YP_FMULTI }, @@ -64,8 +64,7 @@ int rrl_conf_check(knotd_conf_check_args_t *args) typedef struct { ALIGNED_CPU_CACHE // Ensures that one thread context occupies one cache line. struct timespec start_time; // Start time of the measurement. - bool whitelist_checked; // Indication whether whitelist check took place. - bool skip; // Skip the rest of the module callbacks. + bool skip; // Skip the time table update. } thrd_ctx_t; typedef struct { @@ -87,24 +86,26 @@ static knotd_proto_state_t protolimit_start(knotd_proto_state_t state, knotd_qdata_params_t *params, knotd_mod_t *mod) { + assert(params && mod); + rrl_ctx_t *ctx = knotd_mod_ctx(mod); thrd_ctx_t *thrd = &ctx->thrd_ctx[params->thread_id]; thrd->skip = false; - // Check if a whitelisted client. - thrd->whitelist_checked = true; - if (knotd_conf_addr_range_match(&ctx->whitelist, params->remote)) { - thrd->skip = true; + // Time limiting not supported for UDP (source address can be forged). + if (params->proto == KNOTD_QUERY_PROTO_UDP) { return state; } - // UDP time limiting not implemented (source address can be forged). - if (params->proto == KNOTD_QUERY_PROTO_UDP) { + // Check if a whitelisted client. + if (knotd_conf_addr_range_match(&ctx->whitelist, params->remote)) { + thrd->skip = true; return state; } // Check if the packet is limited. - if (rrl_query(ctx->time_table, params->remote, mod) != KNOT_EOK) { + rrl_log_params_t log = { .mod = mod, .proto = params->proto }; + if (rrl_query(ctx->time_table, params->remote, &log) != KNOT_EOK) { thrd->skip = true; knotd_mod_stats_incr(mod, params->thread_id, 2, 0, 1); return ctx->dry_run ? state : KNOTD_PROTO_STATE_BLOCK; @@ -118,13 +119,21 @@ static knotd_proto_state_t protolimit_end(knotd_proto_state_t state, knotd_qdata_params_t *params, knotd_mod_t *mod) { + assert(params && mod); + rrl_ctx_t *ctx = knotd_mod_ctx(mod); thrd_ctx_t *thrd = &ctx->thrd_ctx[params->thread_id]; + // Time rate limit is applied to non-UDP. if (thrd->skip || params->proto == KNOTD_QUERY_PROTO_UDP) { return state; } + // Don't limit authorized operations. + if (params->flags & KNOTD_QUERY_FLAG_AUTHORIZED) { + return state; + } + // Update the time table. struct timespec end_time; clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end_time); @@ -142,36 +151,26 @@ static knotd_state_t ratelimit_apply(knotd_state_t state, knot_pkt_t *pkt, assert(pkt && qdata && mod); rrl_ctx_t *ctx = knotd_mod_ctx(mod); - thrd_ctx_t *thrd = &ctx->thrd_ctx[qdata->params->thread_id]; - - if (thrd->skip) { - return state; - } - - // Don't limit authorized operations. - if (qdata->params->flags & KNOTD_QUERY_FLAG_AUTHORIZED) { - thrd->skip = true; - return state; - } - // Rate limit is applied to UDP only. + // Rate limiting is applied only to UDP. if (qdata->params->proto != KNOTD_QUERY_PROTO_UDP) { return state; } - // Check for whitelisted client IF PER-ZONE module (no proto callbacks). - if (!thrd->whitelist_checked && - knotd_conf_addr_range_match(&ctx->whitelist, qdata->params->remote)) { - thrd->skip = true; + // NOTE: (qdata->params->flags & KNOTD_QUERY_FLAG_AUTHORIZED) can't be true here. + + // Check for whitelisted client. + if (knotd_conf_addr_range_match(&ctx->whitelist, qdata->params->remote)) { return state; } - // Rate limit is not applied to responses with a valid cookie. + // Rate limiting is not applied to responses with a valid cookie. if (qdata->params->flags & KNOTD_QUERY_FLAG_COOKIE) { return state; } - if (rrl_query(ctx->rate_table, knotd_qdata_remote_addr(qdata), mod) == KNOT_EOK) { + rrl_log_params_t log = { .mod = mod, .qdata = qdata }; + if (rrl_query(ctx->rate_table, knotd_qdata_remote_addr(qdata), &log) == KNOT_EOK) { // Rate limiting not applied. return state; } diff --git a/src/knot/modules/rrl/rrl.rst b/src/knot/modules/rrl/rrl.rst index 2b558d6c7..04c37edad 100644 --- a/src/knot/modules/rrl/rrl.rst +++ b/src/knot/modules/rrl/rrl.rst @@ -15,7 +15,8 @@ responses as truncated or by dropping them altogether. This module can also help protect the server from excessive utilization by limiting incoming packets (including handshakes) based on consumed time. If a packet is time rate limited, it's dropped. This function works with -all supported non-UDP transport protocols and cannot be configured per zone. +all supported non-UDP transport protocols (TCP, TLS, and QUIC) and cannot +be configured per zone. .. NOTE:: This module introduces three statistics counters: @@ -28,6 +29,12 @@ all supported non-UDP transport protocols and cannot be configured per zone. If the :ref:`Cookies<mod-cookies>` module is active, RRL is not applied to UDP responses with a valid DNS cookie. +.. NOTE:: + The time limiting applies even to handshakes of incoming authorized requests + (e.g. NOTIFY, AXFR). In such cases, setting :ref:`mod-rrl_whitelist` or reusing + already established connections (e.g. :ref:`server_remote-pool-timeout` on + the remote server) can mitigate this issue. + Example ------- @@ -96,7 +103,9 @@ i.e. they are lowered by a constant fraction of their value each millisecond. The specified rate limit is reached, when the number of queries is the same every millisecond; sending many queries once a second or even a larger timespan leads to a more strict limiting. -*Default:* ``20`` +Set to 0 to disable the rate limiting. + +*Default:* ``50`` .. _mod-rrl_instant-limit: @@ -115,7 +124,7 @@ is periodically lowered. The :ref:`mod-rrl_instant-limit` may be at least :ref:`mod-rrl_rate-limit` **/ 1000**, at which point the counters are zeroed each millisecond. -*Default:* ``50`` +*Default:* ``125`` .. _mod-rrl_slip: @@ -161,7 +170,9 @@ time-rate-limit This limit works similarly to :ref:`mod-rrl_rate-limit` but considers the time consumed (in microseconds) by the remote over non-UDP transport protocols. -*Default:* ``4000`` (microseconds) +Set to 0 to disable the time limiting. + +*Default:* ``5000`` (microseconds) .. _mod-rrl_time-instant-limit: diff --git a/src/knot/nameserver/internet.h b/src/knot/nameserver/internet.h index bb6927ddc..594631ed7 100644 --- a/src/knot/nameserver/internet.h +++ b/src/knot/nameserver/internet.h @@ -64,6 +64,7 @@ knot_layer_state_t internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata) #define NS_NEED_AUTH(qdata, action) \ if (!process_query_acl_check(conf(), (action), (qdata)) || \ process_query_verify(qdata) != KNOT_EOK) { \ + qdata->params->flags &= ~KNOTD_QUERY_FLAG_AUTHORIZED; \ return KNOT_STATE_FAIL; \ } else { \ qdata->params->flags |= KNOTD_QUERY_FLAG_AUTHORIZED; \ diff --git a/src/knot/server/handler.c b/src/knot/server/handler.c index bcd0ec560..2bc27da81 100644 --- a/src/knot/server/handler.c +++ b/src/knot/server/handler.c @@ -94,6 +94,13 @@ static void handle_quic_stream(knot_quic_conn_t *conn, int64_t stream_id, struct } handle_finish(layer); + + // Store the qdata params AUTH flag to the connection. + if (params->flags & KNOTD_QUERY_FLAG_AUTHORIZED) { + conn->flags |= KNOT_QUIC_CONN_AUTHORIZED; + } else { + conn->flags &= ~KNOT_QUIC_CONN_AUTHORIZED; + } } void handle_quic_streams(knot_quic_conn_t *conn, knotd_qdata_params_t *params, @@ -101,14 +108,15 @@ void handle_quic_streams(knot_quic_conn_t *conn, knotd_qdata_params_t *params, { uint8_t ans_buf[KNOT_WIRE_MAX_PKTSIZE]; + params_update_quic(params, conn); + int64_t stream_id; knot_quic_stream_t *stream; - while (conn != NULL && (stream = knot_quic_stream_get_process(conn, &stream_id)) != NULL) { assert(stream->inbufs != NULL); assert(stream->inbufs->n_inbufs > 0); struct iovec *inbufs = stream->inbufs->inbufs; - params_update_quic(params, knot_quic_conn_rtt(conn), conn, stream_id); + params_update_quic_stream(params, stream_id); // NOTE: only the first msg in the stream is used, the rest is dropped. handle_quic_stream(conn, stream_id, &inbufs[0], layer, params, ans_buf, sizeof(ans_buf)); diff --git a/src/knot/server/handler.h b/src/knot/server/handler.h index 4b63a0c16..23dbcf7e8 100644 --- a/src/knot/server/handler.h +++ b/src/knot/server/handler.h @@ -53,18 +53,30 @@ inline static knotd_qdata_params_t params_init(knotd_query_proto_t proto, return params; } -inline static void params_update_tcp(knotd_qdata_params_t *params, uint32_t rtt) +inline static void params_update_tcp(knotd_qdata_params_t *params, + knot_tcp_conn_t *conn) { - params->measured_rtt = rtt; + params->measured_rtt = conn->establish_rtt; + if (conn->flags & KNOT_TCP_CONN_AUTHORIZED) { + params->flags |= KNOTD_QUERY_FLAG_AUTHORIZED; + } } #ifdef ENABLE_QUIC -inline static void params_update_quic(knotd_qdata_params_t *params, uint32_t rtt, - knot_quic_conn_t *conn, int64_t stream_id) +inline static void params_update_quic(knotd_qdata_params_t *params, + knot_quic_conn_t *conn) { params->quic_conn = conn; + if (conn->flags & KNOT_QUIC_CONN_AUTHORIZED) { + params->flags |= KNOTD_QUERY_FLAG_AUTHORIZED; + } +} + +inline static void params_update_quic_stream(knotd_qdata_params_t *params, + int64_t stream_id) +{ params->quic_stream = stream_id; - params->measured_rtt = rtt; + params->measured_rtt = knot_quic_conn_rtt(params->quic_conn); } #endif // ENABLE_QUIC @@ -72,6 +84,9 @@ inline static void params_update_tls(knotd_qdata_params_t *params, knot_tls_conn_t *conn) { params->tls_conn = conn; + if (params->tls_conn->flags & KNOT_TLS_CONN_AUTHORIZED) { + params->flags |= KNOTD_QUERY_FLAG_AUTHORIZED; + } } #ifdef ENABLE_XDP diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c index 54bc3a27a..455dafc45 100644 --- a/src/knot/server/tcp-handler.c +++ b/src/knot/server/tcp-handler.c @@ -175,7 +175,9 @@ static int tcp_handle(tcp_context_t *tcp, knotd_qdata_params_t *params, recv = knot_tls_recv_dns(params->tls_conn, rx->iov_base, rx->iov_len); break; default: // E.g. handshake timeout. - return ret; + assert(ret < 0); + recv = ret; + break; } } else { recv = net_dns_tcp_recv(params->socket, rx->iov_base, rx->iov_len, tcp->io_timeout); @@ -212,6 +214,15 @@ static int tcp_handle(tcp_context_t *tcp, knotd_qdata_params_t *params, handle_finish(&tcp->layer); + if (params->tls_conn != NULL) { + // Store the qdata params AUTH flag to the connection. + if (params->flags & KNOTD_QUERY_FLAG_AUTHORIZED) { + params->tls_conn->flags |= KNOT_TLS_CONN_AUTHORIZED; + } else { + params->tls_conn->flags &= ~KNOT_TLS_CONN_AUTHORIZED; + } + } + return KNOT_EOK; } diff --git a/src/knot/server/xdp-handler.c b/src/knot/server/xdp-handler.c index 403bb70ca..8e83d1936 100644 --- a/src/knot/server/xdp-handler.c +++ b/src/knot/server/xdp-handler.c @@ -223,7 +223,6 @@ static void handle_udp(xdp_handle_ctx_t *ctx, knot_layer_t *layer, static void handle_tcp(xdp_handle_ctx_t *ctx, knot_layer_t *layer, knotd_qdata_params_t *params) { - uint8_t ans_buf[KNOT_WIRE_MAX_PKTSIZE]; for (uint32_t i = 0; i < ctx->msg_recv_count; i++) { @@ -251,8 +250,7 @@ static void handle_tcp(xdp_handle_ctx_t *ctx, knot_layer_t *layer, } else if (knot_tcp_relay_empty(rl)) { continue; } - - params_update_tcp(params, rl->conn->establish_rtt); + params_update_tcp(params, rl->conn); // Process all complete DNS queries in one TCP stream. for (size_t j = 0; rl->inbf != NULL && j < rl->inbf->n_inbufs; j++) { @@ -273,6 +271,12 @@ static void handle_tcp(xdp_handle_ctx_t *ctx, knot_layer_t *layer, } handle_finish(layer); + + if (params->flags & KNOTD_QUERY_FLAG_AUTHORIZED) { + rl->conn->flags |= KNOT_TCP_CONN_AUTHORIZED; + } else { + rl->conn->flags &= ~KNOT_TCP_CONN_AUTHORIZED; + } } (void)process_query_proto(params, KNOTD_STAGE_PROTO_END); diff --git a/src/libknot/quic/quic_conn.h b/src/libknot/quic/quic_conn.h index b19d50e0b..bab15c2fa 100644 --- a/src/libknot/quic/quic_conn.h +++ b/src/libknot/quic/quic_conn.h @@ -74,6 +74,7 @@ typedef enum { KNOT_QUIC_CONN_HANDSHAKE_DONE = (1 << 0), KNOT_QUIC_CONN_SESSION_TAKEN = (1 << 1), KNOT_QUIC_CONN_BLOCKED = (1 << 2), + KNOT_QUIC_CONN_AUTHORIZED = (1 << 3), } knot_quic_conn_flag_t; typedef struct knot_quic_conn { diff --git a/src/libknot/quic/tls.h b/src/libknot/quic/tls.h index 2acced1a9..db7182ed0 100644 --- a/src/libknot/quic/tls.h +++ b/src/libknot/quic/tls.h @@ -35,6 +35,7 @@ typedef enum { KNOT_TLS_CONN_HANDSHAKE_DONE = (1 << 0), KNOT_TLS_CONN_SESSION_TAKEN = (1 << 1), // unused, to be implemeted later KNOT_TLS_CONN_BLOCKED = (1 << 2), + KNOT_TLS_CONN_AUTHORIZED = (1 << 3), } knot_tls_conn_flag_t; typedef struct knot_tls_ctx { diff --git a/src/libknot/xdp/tcp.c b/src/libknot/xdp/tcp.c index d219db977..2374953a4 100644 --- a/src/libknot/xdp/tcp.c +++ b/src/libknot/xdp/tcp.c @@ -227,6 +227,7 @@ static void conn_init_from_msg(knot_tcp_conn_t *conn, knot_xdp_msg_t *msg) conn->last_active = get_timestamp(); conn->state = XDP_TCP_NORMAL; conn->establish_rtt = 0; + conn->flags = 0; memset(&conn->inbuf, 0, sizeof(conn->inbuf)); memset(&conn->outbufs, 0, sizeof(conn->outbufs)); diff --git a/src/libknot/xdp/tcp.h b/src/libknot/xdp/tcp.h index 39a30fd41..bad28498e 100644 --- a/src/libknot/xdp/tcp.h +++ b/src/libknot/xdp/tcp.h @@ -61,6 +61,10 @@ typedef enum { XDP_TCP_IGNORE_FIN = (1 << 2), } knot_tcp_ignore_t; +typedef enum { + KNOT_TCP_CONN_AUTHORIZED = (1 << 0), +} knot_tcp_conn_flag_t; + typedef struct knot_tcp_conn { struct { struct knot_tcp_conn *list_node_next; @@ -79,6 +83,7 @@ typedef struct knot_tcp_conn { uint32_t last_active; uint32_t establish_rtt; // in microseconds knot_tcp_state_t state; + knot_tcp_conn_flag_t flags; struct iovec inbuf; struct knot_tcp_outbuf *outbufs; struct knot_tcp_conn *next; |