mirror of
https://github.com/scylladb/scylladb.git
synced 2026-05-22 07:42:16 +00:00
Prepare API in audit for auditing Alternator. The API provides an externally-callable functions `inspect()`, for both CQL and Alternator. Both variants of the function would unpack parameters and merge into calling a common `maybe_log()`, which can then call `log()` when conditions are met. Also, while I was at it, (const) references were favoured over raw pointers. The Alternator audit_info subclass (audit_info_alternator) carries an optional consistency level — only data read/write operations have a meaningful CL, while DDL and metadata queries store an empty string in the audit table and syslog (matching the existing write_login behavior). The storage helpers are updated accordingly. Add a will_log(category, keyspace, table) method that checks whether an operation should be audited (category check AND keyspace/table filtering) without requiring a constructed audit_info object. should_log() delegates to will_log().
147 lines
4.8 KiB
C++
147 lines
4.8 KiB
C++
/*
|
|
* Copyright (C) 2017 ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1
|
|
*/
|
|
|
|
#include "audit/audit_syslog_storage_helper.hh"
|
|
|
|
#include <sys/socket.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <syslog.h>
|
|
|
|
#include <seastar/core/coroutine.hh>
|
|
#include <seastar/core/seastar.hh>
|
|
#include <seastar/net/api.hh>
|
|
|
|
#include <fmt/chrono.h>
|
|
|
|
#include "cql3/query_processor.hh"
|
|
|
|
namespace cql3 {
|
|
|
|
class query_processor;
|
|
|
|
}
|
|
|
|
namespace audit {
|
|
|
|
namespace {
|
|
|
|
static auto syslog_address_helper(const db::config& cfg)
|
|
{
|
|
return cfg.audit_unix_socket_path.is_set()
|
|
? unix_domain_addr(cfg.audit_unix_socket_path())
|
|
: unix_domain_addr(_PATH_LOG);
|
|
}
|
|
|
|
static std::string json_escape(std::string_view str) {
|
|
std::string result;
|
|
result.reserve(str.size() * 1.2);
|
|
for (auto c : str) {
|
|
if (c == '"' || c == '\\') {
|
|
result.push_back('\\');
|
|
}
|
|
result.push_back(c);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
}
|
|
|
|
future<> audit_syslog_storage_helper::syslog_send_helper(temporary_buffer<char> msg) {
|
|
try {
|
|
auto lock = co_await get_units(_semaphore, 1, std::chrono::hours(1));
|
|
co_await _sender.send(_syslog_address, std::span(&msg, 1));
|
|
}
|
|
catch (const std::exception& e) {
|
|
auto error_msg = seastar::format(
|
|
"Syslog audit backend failed (sending a message to {} resulted in {}).",
|
|
_syslog_address,
|
|
e
|
|
);
|
|
logger.error("{}", error_msg);
|
|
throw audit_exception(std::move(error_msg));
|
|
}
|
|
}
|
|
|
|
audit_syslog_storage_helper::audit_syslog_storage_helper(cql3::query_processor& qp, service::migration_manager&) :
|
|
_syslog_address(syslog_address_helper(qp.db().get_config())),
|
|
_sender(make_unbound_datagram_channel(AF_UNIX)),
|
|
_semaphore(1) {
|
|
}
|
|
|
|
audit_syslog_storage_helper::~audit_syslog_storage_helper() {
|
|
}
|
|
|
|
/*
|
|
* We don't use openlog and syslog directly because it's already used by logger.
|
|
* Audit needs to use different ident so than logger but syslog.h uses a global ident
|
|
* and it's not possible to use more than one in a program.
|
|
*
|
|
* To work around it we directly communicate with the socket.
|
|
*/
|
|
future<> audit_syslog_storage_helper::start(const db::config& cfg) {
|
|
if (this_shard_id() != 0) {
|
|
co_return;
|
|
}
|
|
|
|
co_await syslog_send_helper(temporary_buffer<char>::copy_of("Initializing syslog audit backend."));
|
|
}
|
|
|
|
future<> audit_syslog_storage_helper::stop() {
|
|
_sender.shutdown_output();
|
|
co_return;
|
|
}
|
|
|
|
future<> audit_syslog_storage_helper::write(const audit_info* audit_info,
|
|
socket_address node_ip,
|
|
socket_address client_ip,
|
|
std::optional<db::consistency_level> cl,
|
|
const sstring& username,
|
|
bool error) {
|
|
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
tm time;
|
|
localtime_r(&now, &time);
|
|
auto cl_str = cl ? format("{}", *cl) : sstring("");
|
|
sstring msg = seastar::format(R"(<{}>{:%h %e %T} scylla-audit: node="{}", category="{}", cl="{}", error="{}", keyspace="{}", query="{}", client_ip="{}", table="{}", username="{}")",
|
|
LOG_NOTICE | LOG_USER,
|
|
time,
|
|
node_ip,
|
|
audit_info->category_string(),
|
|
cl_str,
|
|
(error ? "true" : "false"),
|
|
audit_info->keyspace(),
|
|
json_escape(audit_info->query()),
|
|
client_ip,
|
|
audit_info->table(),
|
|
username);
|
|
|
|
co_await syslog_send_helper(std::move(msg).release());
|
|
}
|
|
|
|
future<> audit_syslog_storage_helper::write_login(const sstring& username,
|
|
socket_address node_ip,
|
|
socket_address client_ip,
|
|
bool error) {
|
|
|
|
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
tm time;
|
|
localtime_r(&now, &time);
|
|
sstring msg = seastar::format(R"(<{}>{:%h %e %T} scylla-audit: node="{}", category="AUTH", cl="", error="{}", keyspace="", query="", client_ip="{}", table="", username="{}")",
|
|
LOG_NOTICE | LOG_USER,
|
|
time,
|
|
node_ip,
|
|
(error ? "true" : "false"),
|
|
client_ip,
|
|
username);
|
|
|
|
co_await syslog_send_helper(std::move(msg).release());
|
|
}
|
|
|
|
}
|