Denetim zinciri ve SIEM dışa aktarımı
QueryProxy denetim kayıtlarını nasıl zincirleyip doğrular ve her denetim olayını SIEM'iniz için stdout, syslog ya da imzalı bir webhook'a nasıl gönderirsiniz.
Güncelleme:
Her denetim kaydı iki kez korunur: uygulama veritabanı kayıtları bir hash zincirine bağlar ve isteğe bağlı sink’ler her olayı veritabanı yöneticisinin denetlemediği bir sisteme kopyalar. İkinci kısım önemlidir; çünkü yalnızca veritabanı içinde tutulan bir zinciri, veritabanının sahibi olan biri yeniden yazabilir.
Hash zinciri
Her kayıt prev_hash ve hash saklar; burada
hash = SHA-256(prev_hash || canonical_json) kaydın alanları (takım,
kullanıcı, eylem, talep, bağlantı, SQL, anahtarları sıralı metadata, IP ve
zaman) üzerinden hesaplanır. Kayıtlar, bir kilit satırını da alan tek bir
transaction içinde bağlanır; böylece zincir çatallanamaz. İki hash üzerindeki
unique index’ler, aynı öncülü olan ikinci bir kaydı reddeder.
Veritabanı trigger’ları denetim tablosunda UPDATE ve DELETE’i, PostgreSQL’de
ayrıca TRUNCATE’i reddeder (MySQL ve MariaDB trigger’ları bunu engelleyemez).
Doğrulama
docker exec queryproxy php artisan audit:verify
Komut zinciri ilk kayıttan itibaren dolaşır. Zincir sağlamsa kayıt sayısını ve
zincirin başını basar ve 0 ile çıkar. Değilse ilk bozuk kaydın id’sini ve
nedenini basar ve 1 ile çıkar. Değiştirilmiş kayıtları, kaldırılmış ya da
araya eklenmiş kayıtları, yeniden sıralanmış bir zinciri, ilk kayıttan
başlamayan bir zinciri ve yazılan kayıt sayısından kısa bir zinciri (kaldırılmış
bir kuyruk ya da boşaltılmış bir tablo) saptar. queryproxy:doctor en yeni
kayıtların ve zincir başının daha hızlı bir kontrolünü yapar;
queryproxy:doctor --full-audit tüm zinciri dolaşır.
Checkpoint’ler
Zamanlayıcı her gün audit:checkpoint çalıştırır. Son kayıt id’sini, hash’ini ve
kayıt sayısını içeren bir audit.checkpoint olayı yazar; bir öncekinden beri
hiçbir şey yazılmadıysa yazmaz. Checkpoint sıradan bir denetim olayıdır; bu
yüzden sink’lerinize de gider.
Zincirin yapamadıkları
- Trigger’ları kaldırıp zinciri yeniden hesaplayan bir veritabanı sahibini yakalayamaz. Bu güce sahip biri tutarlı bir geçmiş yazabilir. Farkı yalnızca veritabanının dışında, SIEM’inizde tutulan bir kopya gösterir. Checkpoint’lerin ve aşağıdaki sink’lerin amacı budur.
- MySQL ve MariaDB:
created_atUTC duvar saati olarak hash’lendiği için bağlantı saat dilimi UTC’ye sabitlenir. QueryProxy’nin arkasından sunucunun ya da oturumun saat dilimini değiştirmeyin. - Denetim yazıcısını çağıran uzun dış transaction’lar, bitene kadar kilit satırını tutar; bu yüzden diğer yazıcılar bekler.
SIEM’e aktarım
Her sink varsayılan olarak kapalıdır. QUERYPROXY_SIEM_SINKS onları açar:
stdout, syslog ve webhook değerlerinin virgülle ayrılmış, herhangi bir
kombinasyonlu listesi.
Teslim, denetim kaydı commit edildikten sonra kuyrukta çalışır; bu yüzden
çökmüş bir sink bir denetim yazmasını asla engellemez ya da geri almaz. Teslim
en az bir kez yapılır: alıcı olayın id’si üzerinden tekilleştirme yapmalıdır.
Başarısız olmaya devam eden bir webhook ya da syslog teslimi, ölü mektup kuyruğu
(dead-letter queue) görevi gören failed_jobs içine düşer
(php artisan queue:failed). Kuyruk backend’inin kendisi çökmüşse olay dışa
aktarılmaz ve sorun loglanır; kayıt sistemi veritabanı olarak kalır ve zinciri
eksiksiz kalır. Boşluk, SIEM kopyasında dizide eksik bir id olarak görünür.
| Değişken | Varsayılan | Anlamı |
|---|---|---|
QUERYPROXY_SIEM_SINKS |
yok | Açılacak sink’ler: stdout, syslog, webhook |
QUERYPROXY_SIEM_QUEUE |
default |
Teslimlerin çalıştığı kuyruk. Hazır işçi default’a hizmet verir; farklı bir ad için ona ait bir işçi gerekir |
QUERYPROXY_SIEM_STDOUT_STREAM |
php://stdout |
stdout sink’inin yazdığı stream |
QUERYPROXY_SIEM_SYSLOG_HOST |
yok | Syslog alıcısının host’u |
QUERYPROXY_SIEM_SYSLOG_PORT |
514 (tls için 6514) |
Alıcı portu |
QUERYPROXY_SIEM_SYSLOG_TRANSPORT |
udp |
udp, tcp veya tls |
QUERYPROXY_SIEM_SYSLOG_FRAMING |
octet-counting |
tcp ve tls için: octet-counting veya newline |
QUERYPROXY_SIEM_SYSLOG_FACILITY |
13 |
Syslog facility (13 “log audit”tir) |
QUERYPROXY_SIEM_SYSLOG_APP_NAME |
queryproxy |
APP-NAME alanı |
QUERYPROXY_SIEM_SYSLOG_HOSTNAME |
yok | HOSTNAME alanı |
QUERYPROXY_SIEM_SYSLOG_TLS_CA_FILE |
yok | Bir tls alıcısını doğrulamak için CA dosyası; boşsa sistem güven deposu kullanılır |
QUERYPROXY_SIEM_SYSLOG_TIMEOUT |
5 |
Saniye |
QUERYPROXY_SIEM_WEBHOOK_URL |
yok | Her olayı alan http:// veya https:// URL’si |
QUERYPROXY_SIEM_WEBHOOK_SECRET |
yok | İmzalama secret’ı, en az 16 karakter. QUERYPROXY_SIEM_WEBHOOK_SECRET_FILE onu bir dosyadan okur |
QUERYPROXY_SIEM_WEBHOOK_TIMEOUT |
5 |
Saniye |
QUERYPROXY_SIEM_WEBHOOK_TRIES |
6 |
İş başarısız olmadan önceki teslim denemeleri |
Yapılandırması hatalı bir sink kullanılmaz ve sorun süreç başına bir kez
loglanır. queryproxy:doctor bunu siem_sinks olarak bildirir.
Olay
Tüm sink’ler aynı JSON belgesini gönderir; belge
docs/audit-event.schema.json
ile tanımlanır (JSON Schema Draft 2020-12, additionalProperties: false,
schema_version: 1).
| Alan | Anlamı |
|---|---|
id |
Denetim kaydı id’si; kurulum başına benzersiz. Buna göre tekilleştirin |
timestamp |
UTC, saniye hassasiyetinde |
instance |
Kurulumun APP_URL’inin host’u; ayarlı değilse null |
action |
Olay adı, örneğin request.approved |
category |
action’ın noktadan önceki kısmı |
actor |
{ "id", "email" }, sistem olayları için null |
team_id, connection_id, query_request_id |
İlişkili nesneler ya da null |
ip |
İstemci adresi; konsol ve kuyruk olayları için null |
sql_redacted |
Literal’leri yer tutucularla değiştirilmiş SQL ya da null |
sql_hidden |
SQL var ama güvenle redakte edilemediğinde true |
metadata |
Olaya özgü ayrıntılar ya da null |
Ham SQL asla dışa aktarılmaz. Şemanın action listesi olay kataloğundan üretilir
ve user, request, connection, masking_rule, team, auth,
chat_integration, chat_identity, execution, audit, masking_key,
error_messages ve result kategorilerinde gruplanmış 50 olay içerir.
actor.email, olay teslim edilirken okunur; bu yüzden denetim yazısı ile teslim
arasında e-posta adresini değiştiren bir kullanıcı yeni adresiyle görünür.
stdout
Konteynerin standart çıktısında satır başına bir JSON nesnesi. Aynı stream
nginx, php-fpm ve kuyruk işçisinin çıktısını da (RUNNING ve DONE satırları)
taşır; bu yüzden toplayıcı JSON olmayan satırları atlamalıdır. Konteyner log
driver’ları ve Docker logunu okuyan agent’lar bunu alır.
syslog
Gövdesi JSON olay olan bir RFC 5424 mesajı; UDP, TCP ya da TLS üzerinden. TCP ve
TLS, newline seçmediğiniz sürece octet-counting framing (RFC 6587) kullanır.
tls ile QueryProxy alıcının sertifikasını ve adını doğrular; alıcının CA’sı
sistem güven deposunda değilse QUERYPROXY_SIEM_SYSLOG_TLS_CA_FILE ayarlayın.
Standart TLS syslog portu 6514’tür.
İmzalı webhook
QueryProxy, JSON olayı şu başlıklarla POST eder ve yönlendirmeleri izlemez:
| Başlık | İçerik |
|---|---|
X-QueryProxy-Timestamp |
Teslimin yapıldığı Unix saniyesi |
X-QueryProxy-Signature |
sha256= ve ardından secret ile "{timestamp}.{body}" üzerinden hesaplanan HMAC-SHA256’nın hex değeri |
X-QueryProxy-Event |
action |
X-QueryProxy-Event-Id |
Denetim kaydı id’si |
User-Agent |
QueryProxy-Audit/1 |
En az 16 karakterlik secret’ı olmayan bir webhook asla imzasız gönderilmez. 2xx dışındaki her HTTP durumu başarısızlık sayılır ve artan bir gecikmeyle yeniden denenir (10 sn, 1 dk, 5 dk, 15 dk, ardından 1 sa). Bir teslime güvenmeden önce doğrulayın:
import hashlib
import hmac
import time
def is_valid(secret: str, timestamp: str, raw_body: bytes, signature: str) -> bool:
# Reject stale deliveries; a retry carries a fresh timestamp.
if abs(time.time() - int(timestamp)) > 300:
return False
message = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
İmzayı, yeniden serileştirilmiş bir kopya üzerinden değil, alındığı haliyle ham gövde üzerinden hesaplayın.
Ürüne özgü connector’lar (Splunk, Elastic, Wazuh, Microsoft Sentinel) ile gelmez. Syslog kabul eden, container loglarını okuyan ya da imzalı bir HTTP POST alan herhangi bir toplayıcı bu olayları içeri alabilir.