Skip to content

Add http ja5 filter and configuration #2320

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

Merged
merged 2 commits into from
Jan 21, 2025
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
71 changes: 50 additions & 21 deletions fw/access_log.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Tempesta FW
*
* Copyright (C) 2022-2024 Tempesta Technologies, Inc.
* Copyright (C) 2022-2025 Tempesta Technologies, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -39,27 +39,32 @@
! code that fills its value !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
#define ACCESS_LOG_LINE(FIXED, UNTRUNCATABLE, TRUNCATABLE) \
FIXED(__BNR) \
UNTRUNCATABLE(client_ip) \
FIXED(" \"") \
UNTRUNCATABLE(vhost) \
FIXED("\" \"") \
UNTRUNCATABLE(method) \
FIXED(" ") \
TRUNCATABLE(uri) \
FIXED(" ") \
UNTRUNCATABLE(version) \
FIXED("\" ") \
UNTRUNCATABLE(status) \
FIXED(" ") \
UNTRUNCATABLE(content_length) \
FIXED(" \"") \
TRUNCATABLE(referer) \
FIXED("\" \"") \
TRUNCATABLE(user_agent) \
#define ACCESS_LOG_LINE(FIXED, UNTRUNCATABLE, TRUNCATABLE) \
FIXED(__BNR) \
UNTRUNCATABLE(client_ip) \
FIXED(" \"") \
UNTRUNCATABLE(vhost) \
FIXED("\" \"") \
UNTRUNCATABLE(method) \
FIXED(" ") \
TRUNCATABLE(uri) \
FIXED(" ") \
UNTRUNCATABLE(version) \
FIXED("\" ") \
UNTRUNCATABLE(status) \
FIXED(" ") \
UNTRUNCATABLE(content_length) \
FIXED(" \"") \
TRUNCATABLE(referer) \
FIXED("\" \"") \
TRUNCATABLE(user_agent) \
FIXED("\" \"") \
UNTRUNCATABLE(ja5_tls) \
FIXED("\" \"") \
UNTRUNCATABLE(ja5_http) \
FIXED("\"")


#define ACCESS_LOG_OFF 0
#define ACCESS_LOG_DMESG 1
#define ACCESS_LOG_MMAP 2
Expand Down Expand Up @@ -289,6 +294,9 @@ do_access_log_req_mmap(TfwHttpReq *req, u16 resp_status,
char *data, *p;
struct timespec64 ts;
u16 len;
TlsJa5t *tls_ja5t = TFW_CONN_TLS(req->conn) ?
&tfw_tls_context(req->conn)->sess.ja5t : NULL;


room_size = tfw_mmap_buffer_get_room(mmap_buffer, &data);
if (room_size < sizeof(TfwBinLogEvent))
Expand Down Expand Up @@ -363,6 +371,12 @@ do_access_log_req_mmap(TfwHttpReq *req, u16 resp_status,
req->h_tbl->tbl + TFW_HTTP_HDR_USER_AGENT);
WRITE_STR_FIELD(ua);

if (tls_ja5t)
WRITE_FIELD(*tls_ja5t);
else
TFW_MMAP_LOG_FIELD_RESET(event, TFW_MMAP_LOG_JA5T);
WRITE_FIELD(req->ja5h);

if (*dropped) {
WRITE_FIELD(*dropped);
*dropped = 0;
Expand Down Expand Up @@ -394,10 +408,12 @@ do_access_log_req_dmesg(TfwHttpReq *req, int resp_status, unsigned long resp_con
BasicStr client_ip, vhost, method, version;
/* These fields are only here to hold estimation of appropriate fields
* length in characters */
BasicStr status, content_length;
BasicStr status, content_length, ja5_tls, ja5_http;
BasicStr missing = { "-", 1 };
TfwStr truncated_in[TRUNCATABLE_FIELDS_COUNT];
BasicStr truncated_out[TRUNCATABLE_FIELDS_COUNT];
TlsJa5t *tls_ja5t = TFW_CONN_TLS(req->conn) ?
&tfw_tls_context(req->conn)->sess.ja5t : NULL;

/* client_ip
*
Expand Down Expand Up @@ -466,6 +482,15 @@ do_access_log_req_dmesg(TfwHttpReq *req, int resp_status, unsigned long resp_con
ADD_HDR(idx_referer, TFW_HTTP_HDR_REFERER);
ADD_HDR(idx_user_agent, TFW_HTTP_HDR_USER_AGENT);

#define FMT_ja5_tls "ja5t=%llx"
#define ARG_ja5_tls , (tls_ja5t ? *(u64 *)tls_ja5t : 0)
ja5_tls.data = "";
ja5_tls.len = 16;
#define FMT_ja5_http "ja5h=%llx"
#define ARG_ja5_http , (*(u64 *)&req->ja5h)
ja5_http.data = "";
ja5_http.len = 16;

/* Now we calculate first estimation of
* "maximum allowed truncated string length" */
#define ESTIMATE_FIXED(str) + (sizeof(str) - 1)
Expand Down Expand Up @@ -519,6 +544,10 @@ do_access_log_req_dmesg(TfwHttpReq *req, int resp_status, unsigned long resp_con
#undef FMT_vhost
#undef ARG_client_ip
#undef FMT_client_ip
#undef FMT_ja5_tls
#undef ARG_ja5_tls
#undef FMT_ja5_http
#undef ARG_ja5_http
}

void
Expand Down
8 changes: 6 additions & 2 deletions fw/access_log.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Tempesta FW
*
* Copyright (C) 2022-2024 Tempesta Technologies, Inc.
* Copyright (C) 2022-2025 Tempesta Technologies, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -65,6 +65,8 @@ typedef enum {
TFW_MMAP_LOG_URI,
TFW_MMAP_LOG_REFERER,
TFW_MMAP_LOG_USER_AGENT,
TFW_MMAP_LOG_JA5T,
TFW_MMAP_LOG_JA5H,
TFW_MMAP_LOG_DROPPED,
TFW_MMAP_LOG_MAX
} TfwBinLogFields;
Expand All @@ -90,7 +92,9 @@ static inline int tfw_mmap_log_field_len(TfwBinLogFields field)
[TFW_MMAP_LOG_URI] = 0,
[TFW_MMAP_LOG_REFERER] = 0,
[TFW_MMAP_LOG_USER_AGENT] = 0,
[TFW_MMAP_LOG_DROPPED] = 8,
[TFW_MMAP_LOG_JA5T] = 8,
[TFW_MMAP_LOG_JA5H] = 8,
[TFW_MMAP_LOG_DROPPED] = 8
};
return TfwBinLogFieldsLens[field];
}
Expand Down
46 changes: 45 additions & 1 deletion fw/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
* created HTTP/1.1-message.
*
* Copyright (C) 2014 NatSys Lab. (info@natsys-lab.com).
* Copyright (C) 2015-2024 Tempesta Technologies, Inc.
* Copyright (C) 2015-2025 Tempesta Technologies, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -114,6 +114,8 @@
#include "access_log.h"
#include "vhost.h"
#include "websocket.h"
#include "ja5_filter.h"
#include "ja5_conf.h"

#include "sync_socket.h"
#include "lib/common.h"
Expand Down Expand Up @@ -5997,6 +5999,15 @@ __check_authority_correctness(TfwHttpReq *req)
return true;
}

static bool
tfw_http_check_ja5h_req_limit(TfwHttpReq *req)
{
u64 limit = http_get_ja5_recs_limit(req->ja5h);
u64 rate = ja5h_get_records_rate(req->ja5h);

return rate > limit;
}

/**
* @return zero on success and negative value otherwise.
* TODO enter the function depending on current GFSM state.
Expand Down Expand Up @@ -6158,6 +6169,14 @@ tfw_http_req_process(TfwConn *conn, TfwStream *stream, struct sk_buff *skb,

req->ja5h.method = req->method;

if (tfw_http_check_ja5h_req_limit(req)) {
TFW_INC_STAT_BH(clnt.msgs_filtout);
return tfw_http_req_parse_block(req, 403,
"parsed request exceeded ja5h limit",
HTTP2_ECODE_PROTO);
}


/*
* The message is fully parsed, the rest of the data in the
* stream may represent another request or its part.
Expand Down Expand Up @@ -7303,6 +7322,7 @@ tfw_http_start(void)
{
TfwVhost *dflt_vh = tfw_vhost_lookup_default();
bool misconfiguration;
u64 storage_size = http_get_ja5_storage_size();

if (WARN_ON_ONCE(!dflt_vh))
return -1;
Expand All @@ -7319,9 +7339,18 @@ tfw_http_start(void)
return -1;
}

if (storage_size && !ja5h_init_filter(storage_size))
return -ENOMEM;

return 0;
}

static void
tfw_http_stop(void)
{
ja5h_close_filter();
}

/*
* ------------------------------------------------------------------------
* configuration handling
Expand Down Expand Up @@ -7957,12 +7986,27 @@ static TfwCfgSpec tfw_http_specs[] = {
.allow_none = true,
.cleanup = tfw_cfgop_cleanup_max_header_list_size,
},
{
.name = "ja5h",
.deflt = NULL,
.handler = tfw_cfg_handle_children,
.cleanup = http_ja5_cfgop_cleanup,
.dest = ja5_hash_specs,
.spec_ext = &(TfwCfgSpecChild) {
.begin_hook = ja5_cfgop_begin,
.finish_hook = http_ja5_cfgop_finish
},
.allow_none = true,
.allow_repeat = false,
.allow_reconfig = true,
},
{ 0 }
};

TfwMod tfw_http_mod = {
.name = "http",
.start = tfw_http_start,
.stop = tfw_http_stop,
.specs = tfw_http_specs,
};

Expand Down
Loading