From a0067cf5795353f714b8382fdbc0fe176a0e27cd Mon Sep 17 00:00:00 2001 From: Jules LEIDELINGER <11395311+julio75012@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:29:34 +0800 Subject: [PATCH 1/4] server: add --security-log-folder parameter for audit logging Adds optional security audit logging capability to the llama.cpp HTTP server. When enabled via the --security-log-folder parameter, the server will create dated log files (security_YYYY-MM-DD.log) for security monitoring. --- common/arg.cpp | 18 +++++++++++++++++- common/common.h | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/common/arg.cpp b/common/arg.cpp index 0260d79fef..a2b3c3b31f 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -2985,6 +2985,22 @@ common_params_context common_params_parser_init(common_params & params, llama_ex } } ).set_examples({LLAMA_EXAMPLE_SERVER})); + add_opt( + common_arg( + { "--security-log-folder" }, "PATH", + "directory for security audit logs; creates dated log files security_YYYY-MM-DD.log (default: disabled)", + [](common_params & params, const std::string & value) { + params.security_log_folder = value; + if (!fs_is_directory(params.security_log_folder)) { + throw std::invalid_argument("not a directory: " + value); + } + // if doesn't end with DIRECTORY_SEPARATOR, add it + if (!params.security_log_folder.empty() && + params.security_log_folder[params.security_log_folder.size() - 1] != DIRECTORY_SEPARATOR) { + params.security_log_folder += DIRECTORY_SEPARATOR; + } + }) + .set_examples({ LLAMA_EXAMPLE_SERVER })); add_opt(common_arg( {"--models-dir"}, "PATH", "directory containing models for the router server (default: disabled)", @@ -3813,4 +3829,4 @@ void common_params_add_preset_options(std::vector & args) { // "in server router mode, do not unload this model if models_max is exceeded", // [](common_params &) { /* unused */ } // ).set_preset_only()); -} +} \ No newline at end of file diff --git a/common/common.h b/common/common.h index ae32d5053c..ffd9f930e6 100644 --- a/common/common.h +++ b/common/common.h @@ -561,7 +561,8 @@ struct common_params { bool log_json = false; std::string slot_save_path; - std::string media_path; // path to directory for loading media files + std::string media_path; // path to directory for loading media files + std::string security_log_folder; // path to directory for security audit logs float slot_prompt_similarity = 0.1f; From 5b35185afa81b787bece6c85f93f488e7b7d35c9 Mon Sep 17 00:00:00 2001 From: Jules LEIDELINGER <11395311+julio75012@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:29:50 +0800 Subject: [PATCH 2/4] server: implement security audit logging functions Implements security logging infrastructure with date-based file rotation and JSON-formatted audit events. Uses existing common_log system for minimal code footprint. Provides thread-safe initialization, cleanup, and audit event logging functions. --- tools/server/server-common.cpp | 119 +++++++++++++++++++++++++++++++++ tools/server/server-common.h | 21 ++++-- 2 files changed, 134 insertions(+), 6 deletions(-) diff --git a/tools/server/server-common.cpp b/tools/server/server-common.cpp index ff3c6d3c2b..0860f61840 100644 --- a/tools/server/server-common.cpp +++ b/tools/server/server-common.cpp @@ -9,6 +9,10 @@ #include "server-common.h" +#include +#include +#include +#include #include #include #include @@ -613,6 +617,7 @@ json json_get_nested_values(const std::vector & paths, const json & result[path] = current; } } + return result; } @@ -763,9 +768,18 @@ static server_tokens tokenize_input_subprompt(const llama_vocab * vocab, mtmd_co llama_tokens tmp = tokenize_mixed(vocab, json_prompt.at(JSON_STRING_PROMPT_KEY), add_special, parse_special); return server_tokens(tmp, false); } +<<<<<<< HEAD } else { throw std::runtime_error("\"prompt\" elements must be a string, a list of tokens, a JSON object containing a prompt string, or a list of mixed strings & tokens."); } +======= + } else { + throw std::runtime_error( + "\"prompt\" elements must be a string, a list of tokens, a JSON object containing a prompt string, or " + "a " + "list of mixed strings & tokens."); + } +>>>>>>> 6325a9c4 (server: implement security audit logging functions) } std::vector tokenize_input_prompts(const llama_vocab * vocab, mtmd_context * mctx, const json & json_prompt, bool add_special, bool parse_special) { @@ -2038,3 +2052,108 @@ server_tokens format_prompt_rerank( return result; } + +// security logging implementation +static struct common_log * g_security_log = nullptr; +static std::string g_security_log_folder; +static std::string g_current_log_file; +static std::mutex g_security_log_mutex; + +static std::string get_current_date_string() { + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + char buffer[11]; + std::strftime(buffer, sizeof(buffer), "%Y-%m-%d", &tm); + return std::string(buffer); +} + +static void rotate_security_log_if_needed() { + std::lock_guard lock(g_security_log_mutex); + + if (g_security_log_folder.empty() || !g_security_log) { + return; + } + + std::string current_date = get_current_date_string(); + std::string new_log_file = g_security_log_folder + "/security_" + current_date + ".log"; + + if (new_log_file != g_current_log_file) { + // Close old file if any + if (!g_current_log_file.empty()) { + common_log_set_file(g_security_log, nullptr); + } + + // Create directory if it doesn't exist + std::filesystem::create_directories(g_security_log_folder); + + // Set new log file + common_log_set_file(g_security_log, new_log_file.c_str()); + common_log_set_prefix(g_security_log, false); + common_log_set_timestamps(g_security_log, true); + + g_current_log_file = new_log_file; + + LOG_INF("Security logging started: %s\n", new_log_file.c_str()); + } +} + +void security_log_init(const std::string & folder_path) { + std::lock_guard lock(g_security_log_mutex); + + if (folder_path.empty()) { + return; + } + + g_security_log_folder = folder_path; + g_security_log = common_log_init(); + + if (!g_security_log) { + LOG_ERR("Failed to initialize security log\n"); + return; + } + + // Set initial log file + rotate_security_log_if_needed(); +} + +void security_log_cleanup() { + std::lock_guard lock(g_security_log_mutex); + + if (g_security_log) { + common_log_free(g_security_log); + g_security_log = nullptr; + } + + g_security_log_folder.clear(); + g_current_log_file.clear(); +} + +void security_log_audit_event(const std::string & event_type, + const std::string & endpoint, + const std::string & method, + const std::string & remote_addr, + const std::string & api_key_name, + const std::string & details) { + std::lock_guard lock(g_security_log_mutex); + + if (!g_security_log) { + return; + } + + // Rotate log file if date changed + rotate_security_log_if_needed(); + + // Create JSON log entry + json log_entry = { + {"timestamp", std::time(nullptr)}, + { "event_type", event_type }, + { "endpoint", endpoint }, + { "method", method }, + { "remote_addr", remote_addr }, + { "api_key_name", api_key_name }, + { "details", details } + }; + + // Write as JSON line + common_log_add(g_security_log, GGML_LOG_LEVEL_NONE, "%s\n", log_entry.dump().c_str()); +} diff --git a/tools/server/server-common.h b/tools/server/server-common.h index 4fb9e488df..6566915f7a 100644 --- a/tools/server/server-common.h +++ b/tools/server/server-common.h @@ -363,9 +363,18 @@ llama_tokens format_prompt_infill( const llama_tokens & tokens_prompt); // format rerank task: [BOS]query[EOS][SEP]doc[EOS]. -server_tokens format_prompt_rerank( - const struct llama_model * model, - const struct llama_vocab * vocab, - mtmd_context * mctx, - const std::string & query, - const std::string & doc); +server_tokens format_prompt_rerank(const struct llama_model * model, + const struct llama_vocab * vocab, + mtmd_context * mctx, + const std::string & query, + const std::string & doc); + +// security logging +void security_log_init(const std::string & folder_path); +void security_log_cleanup(); +void security_log_audit_event(const std::string & event_type, + const std::string & endpoint, + const std::string & method, + const std::string & remote_addr, + const std::string & api_key_name, + const std::string & details); From 0306a58fceb5f97f3aa93649341d90533a279136 Mon Sep 17 00:00:00 2001 From: Jules LEIDELINGER <11395311+julio75012@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:30:06 +0800 Subject: [PATCH 3/4] server: integrate security logging with authentication Integrates security audit logging into server initialization and cleanup lifecycle. Adds authentication audit events to API key validation middleware, logging success/failure events with endpoint, method, remote address, and key status. --- tools/server/server-http.cpp | 12 ++++++++++++ tools/server/server.cpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/tools/server/server-http.cpp b/tools/server/server-http.cpp index 129022a711..96456867f9 100644 --- a/tools/server/server-http.cpp +++ b/tools/server/server-http.cpp @@ -155,12 +155,24 @@ bool server_http_context::init(const common_params & params) { req_api_key = req_api_key.substr(prefix.size()); } + // audit logging for missing API key + if (req_api_key.empty()) { + security_log_audit_event("auth_failure", req.path, req.method, req.remote_addr, "missing", + "No API key provided"); + } + // validate the API key if (std::find(api_keys.begin(), api_keys.end(), req_api_key) != api_keys.end()) { + security_log_audit_event("auth_success", req.path, req.method, req.remote_addr, "provided", + "API key validated"); return true; // API key is valid } // API key is invalid or not provided + if (!req_api_key.empty()) { + security_log_audit_event("auth_failure", req.path, req.method, req.remote_addr, "invalid", + "Invalid API key provided"); + } res.status = 401; res.set_content( safe_json_to_str(json { diff --git a/tools/server/server.cpp b/tools/server/server.cpp index fab0bb587f..35542894cf 100644 --- a/tools/server/server.cpp +++ b/tools/server/server.cpp @@ -99,6 +99,7 @@ int main(int argc, char ** argv) { } common_init(); + security_log_init(params.security_log_folder); // struct that contains llama context and inference server_context ctx_server; @@ -216,6 +217,7 @@ int main(int argc, char ** argv) { if (models_routes.has_value()) { models_routes->models.unload_all(); } + security_log_cleanup(); llama_backend_free(); }; @@ -236,6 +238,7 @@ int main(int argc, char ** argv) { SRV_INF("%s: cleaning up before exit...\n", __func__); ctx_http.stop(); ctx_server.terminate(); + security_log_cleanup(); llama_backend_free(); }; From 92d5771e4c90dade42e92aab9c48fe70551a5e33 Mon Sep 17 00:00:00 2001 From: Jules LEIDELINGER <11395311+julio75012@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:44:19 +0800 Subject: [PATCH 4/4] server: fix filesystem dependency in security logging Replace std::filesystem::create_directories with fs_create_directory_with_parents to avoid compilation issues on systems without header. --- tools/server/server-common.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/server/server-common.cpp b/tools/server/server-common.cpp index 0860f61840..ae915c5d24 100644 --- a/tools/server/server-common.cpp +++ b/tools/server/server-common.cpp @@ -10,7 +10,6 @@ #include "server-common.h" #include -#include #include #include #include @@ -2084,7 +2083,9 @@ static void rotate_security_log_if_needed() { } // Create directory if it doesn't exist - std::filesystem::create_directories(g_security_log_folder); + if (!fs_create_directory_with_parents(g_security_log_folder)) { + LOG_WRN("Failed to create security log directory: %s\n", g_security_log_folder.c_str()); + } // Set new log file common_log_set_file(g_security_log, new_log_file.c_str());