Selamlar, bu yazımda Splunk alarm çıktılarından firewall external feed için kullanılabilecek IP listesinin nasıl otomatik üretilebileceğini ele alacağız. Şimdiden keyifli okumalar.

SOC operasyonlarında Splunk üzerinde yazılan alarmlar sonucunda şüpheli IP adresleri tespit edilebilir. Bu IP adresleri firewall tarafında blocklist veya external feed mekanizmalarına beslenmek istenebilir.

Bu yazıda, Splunk alarm çıktılarında oluşan IP adreslerini firewall tarafında kullanılabilecek raw TXT formatında bir dosyaya otomatik olarak yazdıracağız. Bu yapıda Splunk firewall’a doğrudan API veya CLI ile bağlanmaz; yalnızca belirlenen dizinde güncel bir feed dosyası üretir.

Amaç



Kurulacak yapı ile hedeflenen akış aşağıdaki gibidir:

1. Splunk alarmı tetiklenir.

2. Alarm sonucunda block_ip alanı oluşur.

3. Run a Script adaptive response çalışır.

4. Script alarm result dosyasını okur.

5. Sadece public IPv4 adresleri alınır.

6. Private/internal IP adresleri hariç tutulur.

7. Duplicate ve TTL kontrolü yapılır.

8. /auto/feed/blocked_ips.txt dosyası güncellenir.

Dosya Yapısı

Çalışmada iki temel dosya kullanılmaktadır:

/auto/feed/blocked_ips.txt
/auto/feed/firewall_feed_state.csv

blocked_ips.txt

Firewall veya external feed mekanizmasının okuyacağı asıl raw TXT dosyasıdır. İçinde sadece IP adresleri bulunur.

5.181.87.195
91.177.237.201
130.12.182.231

firewall_feed_state.csv

Script’in takip dosyasıdır. IP adresinin ilk görülme zamanı, son görülme zamanı, kaç kez geldiği ve en son hangi alarmdan geldiği burada tutulur.

ip,first_seen,last_seen,count,last_alert
5.181.87.195,2026-07-28 10:00:00,2026-07-28 10:30:00,3,Manual_Test_Alert
91.177.237.201,2026-07-28 10:00:00,2026-07-28 10:00:00,1,Manual_Test_Alert

Public IP Kontrolü

Script yalnızca public IPv4 adreslerini feed dosyasına yazar. Aşağıdaki private, internal veya reserved IP blokları otomatik olarak exclude edilir:

10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
127.0.0.0/8
169.254.0.0/16
0.0.0.0/8
224.0.0.0/4
255.255.255.255/32

Bu sayede 10.x, 172.16-31.x, 192.168.x gibi internal adreslerin yanlışlıkla firewall feed dosyasına yazılması engellenir.

Dosya Şişmesini Engelleme

Feed dosyasının kontrolsüz büyümemesi için script içerisinde iki limit kullanılmıştır:

TTL_DAYS=30
MAX_ENTRIES=50000

Bu ayarlara göre 30 günden eski IP adresleri listeden düşer ve feed içinde en fazla 50.000 IP tutulur. İhtiyaca göre bu değerler script içerisinden değiştirilebilir.

Script Neden App Altında Tutuldu?

Script doğrudan $SPLUNK_HOME/bin altında tutulursa upgrade veya bakım süreçlerinde kaybolma riski oluşabilir. Bu nedenle asıl script app altında tutulur:

/opt/splunk/etc/apps/TA_firewall_feed/bin/scripts/write_firewall_feed.sh

Splunk Run a Script adaptive response bazı sürümlerde script’i $SPLUNK_HOME/bin/scripts altında beklediği için bu konuma sadece symlink verilir:

/opt/splunk/bin/scripts/write_firewall_feed.sh
    -> /opt/splunk/etc/apps/TA_firewall_feed/bin/scripts/write_firewall_feed.sh

Kurulum Adımları

1. Feed Dizinini Oluşturma

sudo mkdir -p /auto/feed

sudo touch /auto/feed/blocked_ips.txt
sudo touch /auto/feed/firewall_feed_state.csv

sudo chown -R splunk:splunk /auto/feed
sudo chmod 755 /auto
sudo chmod 755 /auto/feed
sudo chmod 644 /auto/feed/blocked_ips.txt
sudo chmod 644 /auto/feed/firewall_feed_state.csv

2. Splunk App Dizinini Oluşturma

sudo mkdir -p /opt/splunk/etc/apps/TA_firewall_feed/bin/scripts
sudo mkdir -p /opt/splunk/etc/apps/TA_firewall_feed/local

sudo chown -R splunk:splunk /opt/splunk/etc/apps/TA_firewall_feed

3. app.conf Oluşturma

sudo vi /opt/splunk/etc/apps/TA_firewall_feed/local/app.conf

[install]
is_configured = 0

[ui]
is_visible = 0
label = TA Firewall Feed

[launcher]
author = SOC
description = Firewall external feed script for Splunk alerts
version = 1.0.0

4. Script’i Eklemek

sudo vi /opt/splunk/etc/apps/TA_firewall_feed/bin/scripts/write_firewall_feed.sh

Aşağıdaki script alarm result dosyasından yalnızca block_ip kolonunu okur, private IP’leri hariç tutar, 30 günlük TTL ve 50.000 kayıt limiti uygular.

#!/bin/bash

FEED_FILE="/auto/feed/blocked_ips.txt"
STATE_FILE="/auto/feed/firewall_feed_state.csv"
LOCK_FILE="/tmp/write_firewall_feed.lock"

# IP kaç gün listede kalsın?
TTL_DAYS=30

# Maksimum kaç IP tutulsun?
MAX_ENTRIES=50000

RESULT_FILE="${SPLUNK_ARG_8:-$8}"
ALERT_NAME="${SPLUNK_ARG_4:-unknown_alert}"

if [ -z "$RESULT_FILE" ] || [ ! -r "$RESULT_FILE" ]; then
    exit 1
fi

mkdir -p "$(dirname "$FEED_FILE")"
touch "$FEED_FILE"
touch "$STATE_FILE"

TMP_CSV="$(mktemp)"
TMP_IPS="$(mktemp)"

if gzip -t "$RESULT_FILE" 2>/dev/null; then
    zcat "$RESULT_FILE" > "$TMP_CSV"
else
    cat "$RESULT_FILE" > "$TMP_CSV"
fi

# Sadece block_ip kolonunu oku.
awk -F',' '
NR==1 {
  for (i=1; i<=NF; i++) {
    gsub(/"/, "", $i)
    if ($i=="block_ip") col=i
  }
  next
}
col>0 {
  gsub(/"/, "", $col)
  print $col
}
' "$TMP_CSV" \
| grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}$' \
| awk -F. '
  $1>=0 && $1<=255 &&
  $2>=0 && $2<=255 &&
  $3>=0 && $3<=255 &&
  $4>=0 && $4<=255
' \
| sort -u > "$TMP_IPS"

if [ ! -s "$TMP_IPS" ]; then
    rm -f "$TMP_CSV" "$TMP_IPS"
    exit 0
fi

(
flock -x 200

python3 - "$TMP_IPS" "$STATE_FILE" "$FEED_FILE" "$ALERT_NAME" "$TTL_DAYS" "$MAX_ENTRIES" << 'PYCODE'
import csv
import ipaddress
import os
import sys
from datetime import datetime, timedelta

tmp_ips = sys.argv[1]
state_file = sys.argv[2]
feed_file = sys.argv[3]
alert_name = sys.argv[4]
ttl_days = int(sys.argv[5])
max_entries = int(sys.argv[6])

now = datetime.now()
now_str = now.strftime("%Y-%m-%d %H:%M:%S")
cutoff = now - timedelta(days=ttl_days)

blocked_networks = [
    ipaddress.ip_network("10.0.0.0/8"),
    ipaddress.ip_network("172.16.0.0/12"),
    ipaddress.ip_network("192.168.0.0/16"),
    ipaddress.ip_network("127.0.0.0/8"),
    ipaddress.ip_network("169.254.0.0/16"),
    ipaddress.ip_network("0.0.0.0/8"),
    ipaddress.ip_network("224.0.0.0/4"),
    ipaddress.ip_network("255.255.255.255/32"),
]

def is_allowed_public_ipv4(ip_text):
    try:
        ip_obj = ipaddress.ip_address(ip_text.strip())
        if ip_obj.version != 4:
            return False
        for net in blocked_networks:
            if ip_obj in net:
                return False
        return True
    except ValueError:
        return False

def parse_time(value):
    try:
        return datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
    except Exception:
        return None

state = {}

if os.path.exists(state_file) and os.path.getsize(state_file) > 0:
    with open(state_file, newline="") as f:
        reader = csv.DictReader(f)
        for row in reader:
            ip = row.get("ip", "").strip()
            if not is_allowed_public_ipv4(ip):
                continue

            last_seen = parse_time(row.get("last_seen", ""))
            if last_seen is None or last_seen < cutoff:
                continue

            try:
                count = int(row.get("count") or 1)
            except Exception:
                count = 1

            state[ip] = {
                "ip": ip,
                "first_seen": row.get("first_seen") or now_str,
                "last_seen": row.get("last_seen") or now_str,
                "count": count,
                "last_alert": row.get("last_alert") or "",
            }

with open(tmp_ips) as f:
    for line in f:
        ip = line.strip()
        if not is_allowed_public_ipv4(ip):
            continue

        if ip in state:
            state[ip]["last_seen"] = now_str
            state[ip]["count"] = int(state[ip].get("count", 0)) + 1
            state[ip]["last_alert"] = alert_name
        else:
            state[ip] = {
                "ip": ip,
                "first_seen": now_str,
                "last_seen": now_str,
                "count": 1,
                "last_alert": alert_name,
            }

rows = list(state.values())
rows.sort(key=lambda r: parse_time(r["last_seen"]) or now, reverse=True)
rows = rows[:max_entries]

tmp_state = state_file + ".tmp"
with open(tmp_state, "w", newline="") as f:
    fieldnames = ["ip", "first_seen", "last_seen", "count", "last_alert"]
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(rows)

os.replace(tmp_state, state_file)

ips = sorted([r["ip"] for r in rows])
tmp_feed = feed_file + ".tmp"
with open(tmp_feed, "w") as f:
    for ip in ips:
        f.write(ip + "\n")

os.replace(tmp_feed, feed_file)
PYCODE

) 200>"$LOCK_FILE"

rm -f "$TMP_CSV" "$TMP_IPS"
exit 0

5. Yetki ve Symlink

sudo chown splunk:splunk /opt/splunk/etc/apps/TA_firewall_feed/bin/scripts/write_firewall_feed.sh
sudo chmod 750 /opt/splunk/etc/apps/TA_firewall_feed/bin/scripts/write_firewall_feed.sh

sudo mkdir -p /opt/splunk/bin/scripts
sudo ln -sf /opt/splunk/etc/apps/TA_firewall_feed/bin/scripts/write_firewall_feed.sh /opt/splunk/bin/scripts/write_firewall_feed.sh
sudo chown -h splunk:splunk /opt/splunk/bin/scripts/write_firewall_feed.sh

Manuel Test

cat > /tmp/test_alert_result.csv << ‘EOF’
block_ip,event_count
5.181.87.195,20
10.113.4.44,50
91.177.237.201,12
192.168.1.10,5
EOF

sudo -u splunk SPLUNK_ARG_4=”Manual_Test_Alert” SPLUNK_ARG_8=”/tmp/test_alert_result.csv” /opt/splunk/etc/apps/TA_firewall_feed/bin/scripts/write_firewall_feed.sh

Feed dosyası kontrol edilir:

cat /auto/feed/blocked_ips.txt

Beklenen çıktı:

5.181.87.195
91.177.237.201

SPL Tarafında block_ip Standardı

Script sadece block_ip kolonunu okuduğu için alarm sonucunda engellenmek istenen IP alanı block_ip olarak normalize edilmelidir.

| eval block_ip=src_ip
| table block_ip

Data Model Bazlı Örnek

| tstats summariesonly=t
    dc(All_Traffic.dest_ip) as unique_dests
    dc(All_Traffic.dest_port) as unique_ports
    count as conn_count
    values(All_Traffic.action) as action
    values(All_Traffic.vendor_action) as vendor_action
  from datamodel=Network_Traffic.All_Traffic
  where NOT All_Traffic.src_ip IN (“10.0.0.0/12″,”172.16.0.0/16″,”192.168.0.0/12”)
    AND All_Traffic.transport IN (“tcp”)
  by _time span=5m All_Traffic.src_ip
| `drop_dm_object_name(“All_Traffic”)`
| eval other_actions=mvfilter(NOT vendor_action IN (“server-rst”,”client-rst”,”deny”,”timeout”))
| where unique_dests >= 7 AND unique_ports >= 5 AND mvcount(other_actions) > 0
| eval block_ip=src_ip
| table block_ip

Enterprise Security Detection Ayarı

AyarDeğer
Create whenNumber of Results is greater than 0
CreateOnce
Throttling1 hour
Fields to group byblock_ip
Run a scriptwrite_firewall_feed.sh

Create: Once seçilmesinin nedeni script’in alarm result dosyasındaki tüm block_ip değerlerini tek seferde okuyabilmesidir. For each result seçilirse her satır için ayrı script process’i oluşur; bu senaryoda gereksiz yük oluşturur.

Opsiyonel: HTTP Üzerinden Yayınlama

İlk kurulumda HTTP servisi zorunlu değildir. Eğer feed dosyasının URL üzerinden erişilebilir olması istenirse Apache/httpd veya mevcut web servis standardı ile /auto/feed/blocked_ips.txt dosyası yayınlanabilir.

Apache varsa:

sudo mkdir -p /var/www/html/firewall-feed

sudo ln -sf /auto/feed/blocked_ips.txt /var/www/html/firewall-feed/blocked_ips.txt

sudo chmod 755 /var/www/html/firewall-feed

sudo chmod 644 /auto/feed/blocked_ips.txt

Apache yoksa Ubuntu/Debian:

sudo apt install -y apache2

sudo systemctl enable apache2

sudo systemctl start apache2

RHEL/CentOS:

sudo yum install -y httpd

sudo systemctl enable httpd

sudo systemctl start httpd

Sonra tekrar symlink:

sudo mkdir -p /var/www/html/firewall-feed

sudo ln -sf /auto/feed/blocked_ips.txt /var/www/html/firewall-feed/blocked_ips.txt

5. URL test

Splunk sunucusunda test et:

curl http://127.0.0.1/firewall-feed/blocked_ips.txt

Sonra auto sunucusundan test edilmeli:

curl http://SPLUNK_SUNUCU_IP/firewall-feed/blocked_ips.txt

http://SPLUNK_SUNUCU_IP/firewall-feed/blocked_ips.txt

Dosya içeriği sadece şu formatta olur:

5.181.87.195

91.177.237.201

45.158.14.77

Kontrol Komutları

Dosya ve script kontrolleri:

ls -lah /opt/splunk/bin/scripts/write_firewall_feed.sh
ls -lah /opt/splunk/etc/apps/TA_firewall_feed/bin/scripts/write_firewall_feed.sh
cat /auto/feed/blocked_ips.txt
cat /auto/feed/firewall_feed_state.csv

Temizlik ve Rollback

Yanlış IP silmek için:

sudo -u splunk sed -i ‘/^YANLIS_IP$/d’ /auto/feed/blocked_ips.txt
sudo -u splunk sed -i ‘/^YANLIS_IP,/d’ /auto/feed/firewall_feed_state.csv

Tüm listeyi temizlemek için:

sudo -u splunk bash -c ‘> /auto/feed/blocked_ips.txt’
sudo -u splunk bash -c ‘> /auto/feed/firewall_feed_state.csv’

Sonuç

Bu çalışma ile Splunk alarm sonuçlarından firewall external feed için otomatik IP listesi üretildi. Yapı firewall tarafına doğrudan API/CLI bağlantısı gerektirmez; Splunk sadece raw TXT feed dosyasını üretir.

  • Script yalnızca block_ip kolonunu okur.
  • Private/internal IP adresleri exclude edilir.
  • Aynı IP tekrar tekrar yazılmaz.
  • 30 günden eski IP adresleri listeden düşer.
  • En fazla 50.000 IP tutulur.
  • Script app altında tutulduğu için upgrade süreçlerinde daha güvenli konumlandırılır.

Share this content:

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Explore More

ARP(Adress Resolution Protocol) NEDİR?

Herkese Selam! Bugün ele aldığımız konu ARP Nedir? , iyi okumalar 🙂  ARP(Adress Resolution Protocol)Adres Çözümleme Protokolü yerel bir ağda IP adresi bilinen ancak MAC adresi bilinmeyen cihazların MAC adreslerini

NESSUS NEDİR? NASIL KURULUR?

Herkese Selam! Bugün ele aldığımız konu Nessus kullanımı ve kurulumu nedir , iyi okumalar 🙂 Nessus bir güvenlik açığı tarayıcısıdır popülerliği çok fazladır web uygulamaların bilgisayar sistemlerinin ve mobil cihazların

BRUTE FORCE ATTACK NEDİR , NASIL YAPILIR ?(InstagramBruter)

Herkese selam! Bu yazımda ele aldığım konu BRUTE FORCE ATTACK NEDİR , NASIL YAPILIR ? , Keyifli Okumalar.  🙂 Türkçe karşılığı kaba kuvvet saldırısıdır. Kaba kuvvet saldırısı hedef uygulamada kimlik