2021-02-25

Powershell ile sayma

Varsayalım ki şöyle bir hedefimiz var; bugün uygulama olay günlüğü (Application Event Log) içinde kaydı düşülen olayların ID'lerine göre hangisinin kaç kez olduğunu bulmak istiyoruz.

Get-WinEvent -FilterHashTable @{LogName="Application";StartTime="2021-02-25 00:00"} |
Group-Object -Property ID -NoElement |
Sort-Object -Property Count -Descending

Bunu bir de Log Parser ile yapmak istedim:

SELECT EventID,
COUNT(EventID) As IdCount
FROM Application
WHERE (TimeGenerated>TO_TIMESTAMP('2021-02-25 00:00:00','yyyy-MM-dd HH:mm:ss'))
GROUP BY EventID
ORDER BY IdCount DESC

Aralarındaki benzerik hoşuma gitti.

2021-02-23

IP adresinden coğrafi konum öğrenme

Sysmon Tools'un sayfasını incelerken basit bir IP adresini coğrafi konuma çeviren servisi öğrendim, hoşuma gitti. Ücretsiz sürüm ayda 10.000 sorguya kadar izin veriyor. Bu sebeple Powershell ile kullanma yoluna gittim.

Herşeyden önce ipstack.com üzerinde ücretsiz bir hesap açıp API access key'ine sahip olmak gerekiyor.

Varsayalım ki elimde $IPAdresi adında bir değişen var ve içeriğinde coğrafi konumunu sorgulamak istediğim bir IP adresi var.

Sorguları göndermek için $IPAdresi değişkenini kullanarak şöyle bir URI oluşturuyoruz.

$uri  = "http://api.ipstack.com/" + $IPAdresi + "?access_key=xxxx-xxx-xxx-xxx"

Daha sonra bu adrese bir Invoke-WebRequest ile istek gönderdim ve sonucu $wr değişkenine kaydettim.

$wr = Invoke-WebRequest -uri $uri -UseBasicParsing

IPStack.com'un cevabı bir JSON nesnesi. Bunu dönüştürebilmek için de şu satırı kullandım:

$json = $wr.content | ConvertFrom-Json

Bu adımdan sonra $json nesnesinin şu alt alanları döndü ki çok işime yaradı.

continent_code: 2 harfli kıta kodu (AF: Afrika, AS: Asya, EU: Avrupa, NA: Kuzey Amerika, OC: Okyanusya, SA: Güney Amerika, AN: Antarktika)

continent_name: Yukarıdakinin tam hali, kıta adı

country_code: 2 harfli ülke kodu (tam liste için Wikipedia sayfasına bakın)

country_name: Yukarıdakinin tam hali, ülke adı

region_code: Bölge kodu. Amerika için eyalet kodları, Türkiye için plaka numaraları.

region_name: Bölge ismi. Amerika için eyalet ismi, Türkiye için şehir ismi.

city: Şehir. Türkye için yukarıdaki ile aynı.

Ayrıntılar için şu sayfaya bakılabilir.

Sonradan aklıma geldi, ama Invoke-WebRequest | ConvertFrom-Json kullanmak yerine tek seferde Invoke-RestMethod kullanılabilir. İkisi de Powershell 3.0 ile gelmiş.

2021-01-30

Bir RaspberryPI projesi

Evde boş duran bir raspberry pi 2 vardı. Sebebi bu yazının konusundan bağımsız bir düşünceden dolayı evdeki kablosuz ağımızda bilinen ve/veya bilinmeyen ne gibi cihazlar var; davetsiz misafirlerimiz var mı diye meraklandığım bir anda bu raspberry pi için bir amaç buldum. Bir şekilde yerel ağımızdaki cihazlar için periyodik ARP taraması yapacak, MAC adreslerini listeyecekti. Ben de sonra bu sonuçlara bakarak hangisi tanıdık, hangisi istenmeyen bilebilecektim.

Bu amaçla önce raspberry pi'ya bir ARM tabanlı Ubuntu türevi olan Raspbery PI OS işletim sistemini yükledim. Eskiden Raspbian vardı, adı artık değişti. ARP taramasını önce nmap ile yapmak aklımdan geçti. Ama daha sonra arpscan diye bir aracın varlığını öğrendim. Kullanımı basit.

$ sudo arp-scan -l

yazınca yerel ağdaki cihazların listesini, 2 satır header ve 2 satır footer arasında sekmelerle ayrılmış olarak veriyor, aşağıdaki gibi:

Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9.5 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.xx.xx    xx:xx:xx:xx:xx:xx    (Unknown)
192.168.xx.xx    xx:xx:xx:xx:xx:xx    (Unknown)
192.168.xx.xx    xx:xx:xx:xx:xx:xx    (Unknown)

3 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.5: 256 hosts scanned in 4.335 seconds (59.05 hosts/sec). 3 responded

Bu tamam. Bunu bir bash script'ine koydum (scan.sh):

#!/bin/bash
_now=$(date +"%Y-%m-%dH%H-%M")
_file="/home/pi/arpscan/log_$_now.log"
sudo arp-scan -l > "$_file"

Bu script, çalıştığında ARP taramasının sonuçlarını log_2021-01-29H21-15.log gibi bir dosyaya kaydedecek. Peki nasıl çalışacak?

# sudo crontab -e

ile bir cronjob oluşturdum, aşağıdaki gibi

0,15,30,45 * * * * sh  /home/pi/arpscan/scan.sh

Yani, her 15 dakikada bir çalışıp ağdaki her cihazın MAC adresini, o anın tarih ve saatini içeren  isme sahip bir log dosyasına kaydedecek. Buraya kadar güzel. Ama bu dosyaları oturup incelemek uzun iş. Onun için önce evdeki cihazların bir envanterini çıkardım. MAC adreslerini belirledim. Sonra da bir python script'i oluşturarak, bu envanterin dışında kalan MAC adreslerini bulmasını sağladım (read.py):

#!/usr/bin/python3.7
import glob

def get_mac(line):
    line_ = line.split('\t')
    if (len(line_)>2):
        mac = line_[1]
        if (mac.count(':')==5) or (mac.count('-')==5):
            return mac.upper()
    return ''

def mac_in_list(mac):
    mac_list = {"xx:xx:xx:xx:xx:xx",... }
    for item in mac_list:
        if mac==item:
            return True
        else:
            continue
    return False

logPath = "/home/pi/arpscan/log*.log"
outPath = '/home/pi/arpscan/all_macs_py.txt'

loglist  = glob.glob(logPath,recursive=False)

for item in loglist:
    oLogFiles = open(item,'r')
    content_ = oLogFiles.read()
    content = content_.split('\n')
    for line in content:
        mac = get_mac(line)
        if (len(mac)>0):
            if not mac_in_list(mac):
                print(mac,"is new!",item)
    oLogFiles.close()

 

Gün içinde her 15 dakikada bir yapılacak ARP taramasının sonuçları bir klasörde birikecek, günün bir saatinde de bu dosya çalışacak, yabancı MAC adresi taraması yapacak, eski logları sıkıştırıp arşivleyecek ve gereksiz dosyaları temizleyecek (temizle.sh):

#!/bin/bash

cd /home/pi/arpscan
./read3.py > all_macs_py.txt
_today=$(date +"%F")
_file="/home/pi/arpscan/log_$_today.7z"
_move_target="/home/pi/arpscan/all_macs_$_today.txt"
7z a $_file /home/pi/arpscan/log_*.log
rm /home/pi/arpscan/log_*.log -f
mv /home/pi/arpscan/all_macs_py.txt $_move_target -f
sudo cp $_move_target /var/www/html/arp.lan/

Bunu da benzer bir şekilde günün bir saatinde çalışmak üzere bir cronjob'a atadım. En son satırdaki dosyanın /var/www/html/arp.lan/ klasörüne kopyalanması bu yazının son bölümünde anlatacağım adımdan sonra netlik kazanacak.

Sonra bu cihazın herkesin erişimine açık olmaması için öncelikle güvenlik duvarını etkinleştirdim:

$ sudo ufw enable

$ sudo ufw allow ssh 

Sonra da fail2ban paketini kurdum. Bu paketi özellikle çok sevdim. Aslında Windows alışkanlığı olarak account lockout diye arattım, ama bu daha da güzel. Belli bir sayıda giriş başarısız olursa kaynak IP adresini güvenlik duvarı üzerinde engelliyor.

$ sudo apt install fail2ban

Sonrasında şu adreste yazdığı gibi yapılandırmaya başladım. Öncelikle /etc/fail2ban/jail.conf dosyasının bir kopyasını alıp jail.local olarak kaydettim:

$ sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

sonra bu dosyasının ortalarına denk gelen bir bölümde [sshd] başlığı altındaki bölüme şu satırları ekledim (siyah : var olan, kırmızı : yeni eklenen)

[sshd]
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry= 3
bantime = -1
findtime=6h

Bu şekilde varsayılan ssh portu üzerinden 6 saatlik zaman diliminde (findtime=6h) 3 kez yanlış şifre girilmesi durumunda (maxretry=3) IP adresini süresiz (bantime=-1) engelliyor. Bunların logunu da /var/log/auth.log dosyasında tutuyor.

Kurulduktan sonra fail2ban hizmetini etkinleştirip çalıştırdım:

$ sudo systemctl enable fail2ban.service

$ sudo systemctl start fail2ban.service

Sistemin çalıştığından emin olmak istedim. 5 kez yanlış şifre sonunda IP adresim engellendi. IP adresimi değiştirerek tekrar giriş yapmam mümkün oldu. Girdikten sonra da

$ sudo fail2ban-client unban 192.168.xx.xx

ile engellemeyi kaldırdım. Herhangi bir anda engellenen IP var mı diye bakmak için

$ sudo fail2ban-client status sshd

komutunu kullanabilirim.

Aklımda başka bir fikir daha vardı. Modemimin loglarını saklamak için rsyslog'u ayarladım. Artık sadece ARP tarama sonuçlarını değil, modemimin loglarını da raspberry pi'ın üzerinde saklıyorum. Bunu yapabilmek için Raspberry PI OS ile gelen rsyslog'un yapılandırma dosyası olan /etc/rsyslog.conf'u açtım. 514 numaralı port üzerinden TCP ve UDP'den gelecek syslog kayıtlarını etkinleştirmek için aşağıdaki satırların başındaki hash karakterini sildim.

module(load="imudp")
input(type="imudp" port="514")

module(load="imtcp")
input(type="imtcp" port="514")

Bu portları güvenlik duvarı üzerinde açmayı da unutmayalım:

$ sudo ufw allow 514

Ardından cihazımdan gelecek logların saklanacağı dosyayı belirledim

$template PerHostLog,"/var/log/rsyslog/netmaster.log"
if $fromhost-ip startswith '192.168.0' then -?PerHostLog
& STOP

Elbette modemimin üzerindeki ilgili sayfaya giderek logları syslog sunucuya göndermek için raspberry PI'ın IP adresini yazmam da gerekti.

Bu da yetmedi, bir süredi adını çok duyduğum PI-hole'u kurmayı denedim. Ev ağımızda reklamlardan kurtulmak için faydalı olduğunu okumuştum. Kurma işlemi gayet basit, sayfada anlatıldığı gibi. Kurduktan sonra yine (aynı zamanda DHCP sunucum olan) modemime gittim ve IP atamaları sırasında internet hizmet sağlayıcımın değil de PI-hole cihazım üzerinden IP sorgularını çözmesini sağladım. Bu şekilde reklam ve diğer istenmeyen IP adreslerinin kara listeye alınmasını sağladım. Ama önceden belirtmekte fayda var; youtube reklamları bu yöntemle engellenmiyor. Çoğu durumda uBlock yine vazgeçilmez.

PI-hole ile birlikte lighttpd web sunucusu geliyor ve çoklu sanal sunucuları destekliyor. Varsayılan olarak bir sanal sunucuda kendi portalını çalıştırıyor. İkinci bir sanal sunucu oluştursam da ARP taraması sonuçlarını ssh ile giriş yapmadan bir web arayüzünden görüntülesem diye düşündüm. Bu kısım beni biraz uğraştırdı. Lighttpd web sunucusuna ayrı bir yazı yazmak isterim ileride; çok yetenekli bir sunucu. Yapılandırmasını çözmem biraz zaman aldı. Apache ile ilgili tonlarca kaynak var, ama lighttpd ile ilgili bilgi daha az.

Önce şu videoda anlatıldığı gibi /etc/dnsmasq.d klasörünün altına 02-lan.conf dosyasını oluşturarak PI-hole üzerinde yerel DNS kayıtları oluşturmak istedim. Bu dosyanın içeriğini

addn-hosts=/etc/pihole/lan.list

olarak kaydettim. Sonra /etc/pihole/lan.list dosyasının içeriğini de 

192.168.xx.xx    arp.lan

olarak kaydettim. Telefonumdaki bir tarayıcıdan bile http://arp.lan ile bu siteye girebileceğim :)

Buraya kadarki kısım sadece yerel DNS kaydı oluşturmak içindi (ve güncellemelerde varsayılan doslaların üzerine yazılmasından kaçınmak için). Bundan sonra asıl sanal sunucuyu oluşturmak için /etc/lighttpd/external.conf dosyasına şu içeriği kaydettim:

$HTTP["host"] =~ "(^|.)arp.lan$" {
    server.document-root = "/var/www/html/arp.lan/"
    server.dir-listing = "enable"
}

Sonra da elbette /var/www/html/arp.lan klasörünü oluşturdum. Buradaki içeriğin de daha önce bahsettiğim ARP taramasında envanterimde olmayan cihazların MAC adreslerini içeren dosyaların kopyalanmasını (temizle.sh dosyasının son satırı) sağladım.

DNS sorguları ve web sitesi için sırasıyla 53 ve 80 numaralı portları da açmayı unutmayalım:

$ sudo ufw allow 53 # DNS için hem UDP hem TCP

$ sudo ufw allow 80/TCP # HTTP için sadece TCP

Başka ilave listeler de bulup PI-hole'a ekledim, malware ve daha fazla reklam engellemesi için. Şu anda yerel ağımızdaki DNS sorgularının %10'unun PI-hole tarafından engelleniyor. Bu trafiğin ve bağlantı için harcanan zamanın %10'u değil, sadece sayısal olarak yapılan DNS sorgularının oranı. Ölçmedim, ama büyük olasılıkla daha yüksek bir oranda trafik ve zaman kazancı sağlayacaktır.

Tam bitti derken reddit'te PI.Alert projesini okudum. Niye, ama niye?

2021-01-08

iptables

Linux'un güçlü bir güvenlik duvarı var. Giriş seviyesinde bazı noktalara değinmek istiyorum.

Bilgisayar trafiğinin 3 temel zincirden oluştuğu düşünülmüş: giriş (INPUT), çıkış (OUTPUT) ve yönlendirme (FORWARD). Mevcut kuralları görmek için

# iptables -L

veya daha ayrıntılı bilgiler için ek olarak -v (verbose) kullanılabilir. Bu anahtarla her kural ile engellenen paket sayıları gibi istatistikleri sıfırlamak için -Z anahtarı kullanılır. Her satırın numaralandırarak listelemek için

# iptables -L --line-numbers

kullanılabilir. Satır numararaları belli bir kuralı silmek veya öncesi/sonrasına yeni kural eklemek için kullanılır.

Sadece INPUT zincirinin kurallarını görmek için

# iptables -L INPUT

Farklı bir şekilde kuralların listesini almak için -S anahtarı kullanılabilir.

# iptables -S

Bir güvenlik duvarının temel amacı istenmeyen trafiği engellemektir. Bu trafik de genelde gelen trafiktir. Dolayısıyla giriş (INPUT) zincirine odaklanacağız. Çıkış (OUTPUT) ve yönlendirme (FORWARD) trafiklerinin varsayılan olarak izin verilen yapıda olduklarını kabul edeceğiz.

Bilgisayarımızda dış dünyayla paylaşacak hiçbir şeyimiz (bir web veya ssh sunucu) olmayabilir. Bu durumda gelen tüm trafiği engelle diyebiliriz:

# iptables -P INPUT DROP

Ama bu durumda Firefox'un internete bağlanma isteklerine alacağı cevapları bile engellemiş oluruz. Bu durumda sadece bunun gibi bizim bilgisayarımızdan dış dünyaya gönderilen istekler sonucu oluşturulmuş (ESTABLISHED) veya süre gelen bağlantılarla ilgili (RELATED) bir trafiğe izin verebilmek için şu komut gerekir:

# iptables  -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Eğer bir de bilgisayarımızda örneğin bir ssh sunucusu varsa ve buna ulaşmak istiyorsak varsayılan 22 portu üzerinden aşağıdaki gibi bir kural yazabiliriz:

# iptables -A INPUT -p tcp --dport 22 -j ACCEPT

İlk satırda verdiğim ve tüm gelen bağlantıları engelleyen varsayılan politikanın yerine, kurallar dizisinin en sonunda engelle kuralı da ekleyebiliriz:

# iptables -A INPUT -j DROP

Bu şekilde giriş zinciri için varsayılan politika izin ver (ACCEPT) olarak bırakılabilir, ama izin verdiğimiz 2 kural haricindeki bütün trafiği de en son girdiğimiz kuralla engellemiş de oluruz. Bunun tek avantajı, bir sebeple bütün kuralları silerek güvenlik duvarını sıfırlama ihtiyacı durumunda kendimizi de makineye uzaktan bağlanamaz durumda bırakmamaktır.

Listede INPUT zincirindeki 3 numaralı kuralı silmek için

# input -D INPUT 3

veya bütün kuralları (tüm zincirlerdeki) silmek için

# iptables -F

kullanabiliriz.

Mevcut kuralların bir yedeğini almak istersek

# iptables-save > fw_state.txt

Benzer şekilde bu dosyadan durumu geri yüklemek için de

# iptables-restore < fw_state.txt

kullanabiliz.

iptables komutu kullanarak oluşturduğumuz kurallar, geçicidir ve bir sonraki tekrar başlatma sonrasında silinirler. Bunları kuralları

# iptables-save

komutu ile kalıcı yapabiliriz.

Bunların dışında eğer gelen ping'lere cevap verilmesini gerektiren bir durum varsa (elbette en sonda bir DROP kuralı kullandıysak bundan önce bir yerlerde)

# iptables -A INPUT -p icmp -j ACCEPT

ve eğer engellenen trafiğin bir kaydını tutmak istersek de (kuralın yeri yukarıdaki gibi)

# iptables -A INPUT -j LOG --log-prefix "FW-REJECT "

kullanılabilir.

Başka bir güzel özellik de herhangi bir port için "bağlantı sınırı" (rate limitting) yapabilmesi. Örneğin 22/tcp (SSH) portu üzerinden yapılan bağlantılar için 60 saniyede 5 adet "yeni bağlantı" sınırlaması uygulamak için aşağıdaki satırlar yeterli.

# Yeni SSH bağlantıları için bir SSH listesi oluşturur
iptables -A INPUT  -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
# Aynı IP'den son 60 saniye içinde 5 veya daha fazla yeni bağlantı girişimi olmuşsa bir kayıt tutar
iptables -A INPUT  -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 \
    --rttl --name SSH -j LOG --log-prefix 'SSH-HIT-RATE: '
# Aynı IP'den son 60 saniye içinde 5 veya daha fazla yeni bağlantı girişimi olmuşsa paketi kabul etmez (drop)
iptables -A INPUT  -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 \
    --rttl --name SSH -j DROP

Bu komut satırı arayüzü ilk başta biraz karmaşık geldiği için farklı linux dağıtımlarının bunu yönetmek için farklı çözümleri vardır. Örneğin Fedora ve türevleri firewalld'yi [4] kullanır. Bu arayüz üzerinden güvenlik duvarının mevcut durumunu görmek için

# firewall-cmd --state

kullanılır. Bunun sonucunda tek satırlık 'running' dönüyorsa güvenlik duvarı devrededir. Burada başka bir şey yazıyorsa systemctl ile firewalld.service durumu kontrol edilmelidir. Mevcut profili ve ayrıntıları görmek için

# firewall-cmd --list-all

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens33
  sources:
  services: dhcpv6-client mdns ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

 

Profiller burada zone olarak isimlendirilirler. Mevcut zone'umuz ilk satırda yazan public zone'udur. Zone'ların listesini almak için (boşlukla ayrılmış tek satırlık liste)

# firewall-cmd --get-zones

ya da daha ayrıntılı bir bilgi için

# firewall-cmd --list-all-zones

Güvenlik duvarı üzerinden akan trafiği kontrol etmek için kaynak ve hedef IP adresleri, portlar ve hizmetler gibi kavramlar kullanılır. Örneğin firewall üzerinde basit bir port (örneğin TCP 514) açmak için

# firewall-cmd --add-port=514/tcp

kullanılır. Ya da bunu bir hizmet olarak yapmak için --add-service kullanılabilir:

# firewall-cmd --add-service=syslog

Benzer şekilde tanımlanmış hizmetlerin listesini görebilmek için

# firewall-cmd --list-services

kullanılabilir. Firewalld'nin iki modu vardır. Birincisi şu anda çalışan ayarları ifade eden çalışma zamanı (runtime) modu. Bu mod kaydedilmezse bir sonraki tekrar başlatmada kaybolur. Diğer mod ise en son kaydedilmeden sonraki durumu içeren kalıcı (permanent) moddur. Varsayılan olarak firewall-cmd ile yaptığımız tüm işlemler çalışma zamanı modunu değiştirir. Yaptığımız işlemlerin kalıcı modu etkilemesi için --permanent parametresi kullanılır. Çalışma zamanı mod için bir parametre yoktur, varsayılan olarak komutlar çalışma zamanını etkiler.

Çalışma zamanında yapılan ayarları kalıcı yapmak için 

# firewall-cmd --runtime-to-permanent

kullanılır. Ya da kalıcı modda yapılan ayarları çalışma zamanına aktarmak için

# firewall-cmd --reload

kullanılır.

Ubuntu'da ise ufw (uncomplicated firewall) kullanılır. Durum kontrolü için

# ufw status 

Güvenlik duvarını etkinleştirmek için

# ufw enable

22 numaralı TCP portunu açmak için

# ufw allow 22/TCP

Bir IP adresini engellemek için

    # ufw deny from xxx.xxx.xxx.xxx to any

Her komut sonucu zaten kaydettiği için save gibi komutlar yok.

İleri Seviye Bazı teknikler

Dünya açtığınız bir hizmetin yaşayabileceği riskler çok büyüktür. Çeşitli tip ve sayıda port taraması, keşif yöntemleri vs maruz kalır. [5]'te söylendiği gibi karmaşık DDoS saldırılarına karşı sadece iptables'a güvenmemeliyiz. Ama en azından daha yetkinliğinin ilk günlerini yaşayan bir yavru hacker'ın (script kiddy veya lamer) kötü emellerine alet edilmekten kurtulmak için bazı şeyler yapılabilir. Şunlar önerilmiş:

# iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP

Bu kural ile TCP bitlerinden hiçbirisi 1 olmayan paketlerin göz ardı edilmesi sağlanmış. İletişimi başlatan ilk paketlerin mutlaka bazı TCP özel alanları (TCP flags olarak bilinen) 1 olmalı. Olmayanlar, keşif amaçlı port taraması vs için gönderilmiş öncü saldırılardır.

# iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP

Bu kural ile SYN-flood saldırısının engellenmesi sağlanır.

# iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP

Bu kural ile de XMAS saldırısı olarak bilinen saldırı türünden korunma sğlanır.

---

[1] https://www.digitalocean.com/community/tutorials/iptables-essentials-common-firewall-rules-and-commands

[2] https://docs.fedoraproject.org/en-US/Community_Services_Infrastructure/1/html/Security_Policy/HostIptables-Standard-Introduction-Prerequisites.html

[3] https://www.hostinger.com/tutorials/iptables-tutorial

[4] https://docs.fedoraproject.org/en-US/quick-docs/firewalld/

[5] https://www.digitalocean.com/community/tutorials/how-to-set-up-a-basic-iptables-firewall-on-centos-6

[6] https://www.digitalocean.com/community/tutorials/how-to-list-and-delete-iptables-firewall-rules

[7] https://linuxize.com/post/how-to-setup-a-firewall-with-firewalld-on-centos-7/

2020-11-29

Linux'ta beklenmeyen kapanmalar

İşletim sisteminden bağımsız olarak tüm bilgisayar sistemlerindeki tüm kapanmaların düzgün (graceful) kapanma olması gerekir. Yarım kalan bir yazma işlemi, kapanmamış dosyalar vs bir sonraki açılışta en basitinden birkaç hata ve disk denetlemesinden, geri getirilemez veri kayıpları ve sistemin tekrar kurulmasına kadar sorunları getirebilir.

Beklenmeyen kapanmalar neler olabilir? Bir sistem bileşeni yüzünden sistemin tekrar başlaması (veya sadece kapanması), güç kaybı sebebiyle sistemin kapanması.

last komutuyla sistemdeki açılış ve kapanış olaylarının listesini alabiliriz. Şu kaynakta belirtildiği gibi

$ last -Fxn2 shutdown reboot

-F : Uzun tarih formatı kullan
-x : Kapanma ve çalışma seviyesi değişikliklerini de göster
-n2: En son 2 olayı listele

komudu normal şartlar altında bir kapanışı takip eden bir açılış listelemeli. Eğer normal bir kapanma yoksa iki açılış olayı ile karşı karşıya kalabiliriz. Aşağıdaki çıktıya bakarsak

reboot   system boot  5.9.10-200.fc33. Sun Nov 29 17:09:18 2020   still running
reboot   system boot  5.9.10-200.fc33. Sun Nov 29 16:10:37 2020   still running

16:10'daki açılış da hala "still running" olarak gözüküyor, 17:09'daki açılış da. Bu arada bir beklenmeyen bir kapanış olmuş. Normal bir kapanış ve açılış sonrasında şu gözükür:

reboot   system boot  5.9.10-200.fc33. Sun Nov 29 16:10:37 2020   still running
shutdown system down  5.9.10-200.fc33. Sun Nov 29 01:58:33 2020 - Sun Nov 29 16:10:37 2020  (14:12)

Aynı kaynakta alternatif olarak ausearch aracının kullanılmasından da bahsedilmiş. Örneğin

$ sudo ausearch -i -m system_boot,system_shutdown | tail -4

benim normal olarak kapanmayan sistemimde şu sonuçları verdi:

Option ENRICHED not found - line 9
NOTE - using built-in logs: /var/log/audit/audit.log
----
type=SYSTEM_BOOT msg=audit(29-11-2020 13:10:59.324:106) : pid=778 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success'
----
type=SYSTEM_BOOT msg=audit(29-11-2020 14:09:31.491:105) : pid=793 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success'

Normal bir kapanış sonrası ise şuna benzer bir kapanış ve sonrasında bir açılış içeren bir çıktı üretmesi gerekirdi:

---
type=SYSTEM_SHUTDOWN msg=audit(29-11-2020 01:58:33.647:1688) : pid=19324 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success'
----
type=SYSTEM_BOOT msg=audit(29-11-2020 13:10:59.324:106) : pid=778 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success'

Bu iki durumda da 2 değil 1 satır veri görünüyorsa loglarda bir kalıcılık yoktur, ya da yeterli süre açık kalan bir sistemin son açılış sonrasındaki kayıtları silinmiş olabilir.

2026-07-07 Ek: Başka bir durumda kapanıp tekrar açılmış bir sistemin last reboot kayıtlarında şöyle bir satır vardı:

reboot   system boot  6.9.12-200.fc40* Mon Feb 23 11:27:20 2026 - Tue Apr 28 10:22:17 2026 (63+22:54)
reboot   system boot  6.9.12-200.fc40. Sat Feb 21 14:18:35 2026 - crash                    (1+21:08)

Burada beklenmeyen bir kapanma olmuş gibi gözüküyor. Gerçekten de journalctl kayıtlarında ani bir kesinti var mı diye kontrol etmek istedim. Paralel olarak journalctl --list-boots ile kapanan boot işleminin -8. işlem olduğunu gördüm. Ayrıca bu kayıtlarda sistemin en son 11:21:50'ye kadar kayıt düştüğü de gözüküyor. Bundan 5 dakika sonra sistem yeniden başlamış. Ne olduysa buranın önünde arkasında bir yerlerde olmuş.

-8 c4e13d6cdabd47e7a3c15dcc2c32d482 Sat 2026-02-21 14:18:28 +03 Mon 2026-02-23 11:21:50 +03 
-7 cd47afdf66524011a81b7654a28c21bf Mon 2026-02-23 11:27:14 +03 Tue 2026-04-28 10:22:19 +03 

ve --until parameteresini bir sonraki açılıştan birkaç dakika sonrası seçtim. -8 boot işlemi için 11:27'den sonra bir işlem yok, bunlar -7 boot işlemi için. Onun için bu güvenli, bir sonraki boot işleminin kayıtları araya girmeyecek.

journalctl -b -9 --since "2026-02-23 11:00" --until "2026-02-23 11:30"

Burada sadece sysstat-collect.service hizmetine ait devreye girdi ve devreden çıktı gibi kayıtlar var, kapanışa ait bir şey yok ve kayıtlar çok keskin bir şekilde son bulmuş. 

Daha ilginç bir çözüm olarak bizim sistemimize özel bir hizmet birimi oluşturmamız önerilmiş. Sadece kapanışta çalılştırılacak /etc/systemd/system/set_gracefulshutdown.service dosyasının içeriği olarak şunlar önerilmiş:

[Unit]
Description=Set flag for graceful shutdown 
DefaultDependencies=no 
RefuseManualStart=true 
Before=shutdown.target
 
[Service] 
Type=oneshot 
ExecStart=/bin/touch /root/graceful_shutdown
 
[Install]
WantedBy=shutdown.target

Daha sonra

$ sudo systemctl daemon-reload
$ sudo systemctl enable set_gracefulshutdown

ile bunlar etkinleştirilmiş. Bu hizmet birimi kapanışta /root/graceful_shutdown dosyası oluşturacak. Ayrıca bir de açılışlarda çalışacak /etc/systemd/system/check_graceful.service hizmet birimi önerilmiş:

[Unit]
Description=Check if previous system shutdown was graceful 
ConditionPathExists=/root/graceful_shutdown 
RefuseManualStart=true 
RefuseManualStop=true
 
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/rm /root/graceful_shutdown
 
[Install]
WantedBy=multi-user.target
 
Bu hizmet birimi de açılışta çalışacak ama /root/graceful_shutdown dosyasının varlığı önkoşulu ile. Eğer o dosya yoksa çalışmayacağı için anlayabiliriz ki bir önceki kapanma beklenen bir kapanma değildi. Ayrıca dosya sonraki adımda bu dosyayı sileceği için kapanışta bir dosyanın tekrar oluşturulmasına izin veriyor. Bunu da benzer şekilde enable etmeliyiz:
 
$ sudo systemctl daemon-reload
$ sudo systemctl enable check_graceful

Tereddüt ettiğimizde
 
$ systemctl is-active check_graceful

komutu ile önceki kapanmanın normal olup olmadığını anlayuabiliriz.

En son olarak bir de journalctl ile durum kontorlü önerilmiş.

$ sudo journalctl -b -1 -n

Bu komut, bir önceki boot'ta (-b -1) en son 10 (-n parametresinin varsayılanı 10) kaydını görüntüler. Normal olarak aşağıdakine benzer bir çıktı olması gerekir:

Eyl 06 13:46:58 archbox systemd[1]: Reached target System Shutdown.
Eyl 06 13:46:58 archbox systemd[1]: Reached target Late Shutdown Services.
Eyl 06 13:46:58 archbox systemd[1]: systemd-reboot.service: Deactivated successfully.
Eyl 06 13:46:58 archbox systemd[1]: Finished System Reboot.
Eyl 06 13:46:58 archbox systemd[1]: Shutting down.
Eyl 06 13:46:58 archbox systemd-shutdown[1]: Syncing filesystems and block devices.
Eyl 06 13:46:58 archbox systemd-shutdown[1]: Sending SIGTERM to remaining processes...
Eyl 06 13:46:58 archbox systemd-journald[218]: Received SIGTERM from PID 1 (systemd-shutdow).
Eyl 06 13:46:58 archbox systemd-journald[218]: Journal stopped

Böyle bir durumda aranılan satırlar başarılı olarak kapanmanın ve gerçekleştiğini gösteren (Reached System Shutdown, Finished System Reboot/Shutdown gibi) satırlar ve Journal stopped mesajları olabilir. Benim sistemimde bol hatalı bir çıktı üretti, beklenmeyen kapanmanın göstergesi olarak.

2020-10-26

Medya tuşlarıyla videoları kontrol etmemizi sağlayan tarayıcı: Firefox!

Ardı ardına birbirinden güzel özellikler ile karşımıza çıkan açık kaynak kodlu internet tarayıcımız, canımız ciğerimiz: Firefox!

En yeni özelliği yine çok hoşuma gitti. Klavyelerimizde ve kulaklıklarımızda yer alan medya kontrol tuşları ile artık Youtube videolarımızı ve tarayıcımızda oynatılan diğer medyaları kontrol edebiliyoruz.

Yazılanlara göre Windows 7'yi desteklemiyor. Linux'ta gtk tabanlı masaüstlerinde destekleniyor denmiş, ama 3.36.7 gnome-shell'e sahip Fedora 32'de de çalışmadı. Olsun, yakında çalışır.

Olur da bu özelliği kapatmak istersek adres satırına about:config yazdıktan sonra 

media.hardwaremediakeys.enabled = false

yapmamız gerek. Ayrıca bir de timeout özelliğinden bahsedilmiş:

media.mediacontrol.stopcontrol.timer

Bunu etkinleştirdikten ve varsayılan olarak 60 saniyelik bir süre geliyor (onu da media.mediacontrol.stopcontrol.timer.ms ile değiştirebiliyoruz). Firefox'ta çalan medyayı durdurduktan 60 saniye sonra medya kontrol dışına çıkacak.

---

[1] https://docs.google.com/document/d/1c4FivJpvAjjw9Uw-jn7X1UjGOoWkANXOulNyqDWs83w/edit#heading=h.wsp2v2xk20e

2020-10-13

Linux'ta standart akışları yönlendirme

Bir komut satırı uygulaması için 3 temel akış (stream) söz konusu:

0: Giriş için stdin, 
1: çıkış için stdout,
2: hata çıktıları için stderr.

Bir uygulamanın konsolda çalıştığı düşünülerek, bazı giriş ve çıkışlarının olduğu varsayılır. stdin olarak isimlendirilen giriş, klavyedir. Program çıktısını stdout'a yani ekrana basar. Olası hata durumlarında ekrana bazı hata mesajları da basılır ama genellikle hata mesajlarının normal stdout çıktısınıdan ayrı olması istendiği için onun akışı ayrıdır, stderr olarak adlandırılır.

stdin yönlendirmesi

Tüm girişin klavyeden değil de, bir programın çıktısından ya da bir dosyadan olması gereken durumlarda kullanılır.

$ head < dosya.txt

Yönlendirme operatörü olarak "<"  kullanıldı.

stdout yönlendirmesi

Genelde bir komutun planlanan tüm çıktılarını başka bir komuta veya dosyaya yönlendirmek için kullanılır.

$ ls -ld */  | wc -l # tüm klasörleri say

$ last -s 2019-05-01 | awk '{print $1}' #tarihten sonra yeniden başlatan kullanıcıları isimleri

$ find . -iname "*.txt" -newermt "5 months ago" -ls > new_txt_files.log # son 5 ayda değiştirilen dosyaların listesini new_txt_files.log'a yaz

Burada > operatörünün yanında boru "|" operatörünün de kullanıldığına dikkat.

stderr yönlendirmesi

3 klasörün içeriklerini bir dosyaya yazdığımızı düşünelim. Ama üçüncü klasör var olmayan bir yolu gösteriyor.

$ ls Belgeler Resimler Filmler > dosya_listesi.txt

Bu komut Belgeler ve Resimler klasörünün çıktısını belirtilen dosya_listesi.txt dosyasına yazarken, Filmler klasorunun var olmadığına dair hatayı sadece ekrana yazar ve dosya_listesi.txt dosyasına bir mesaj yazmaz. Bunun sebebi, hata çıktısının farklı bir kanaldan gönderilmesidir. İki seçeneğimiz var. Ya stderr akışını hata.txt dosyası gibi bir dosyaya yazabilir, ya da hataları da dosya_listesi.txt dosyasına atabiliriz. İlk durum için komudumuz şöyle olabilir:

$ ls Belgeler Resimler Filmler 2> hata.txt 1> dosya_listesi.txt

İkinci durum için komudumuz şöyle olabilirdi:

$ ls Belgeler Resimler Filmler > dosya_listesi.txt 2>&1

Burada sonda yer alan 2 numaralı stderr akışını &1'e yönlendirme bölümünün stdout'un dosya_listesi.txt'ye yönlendirmesi (> dosya_listesi.tx) bölümünden sonra olması önemli. Eğer sıra değişirse sadede stdout dosyaya yazılır (çünkü stderr > stdout yönlendirmesi sırasında stdout dosyaya yönlendirilmemiş olur)

Alternatif olarak bash'e özel &> kısaltması (2>&1 ile aynı anlama gelir) kullanılabilir:

$  ls Belgeler Resimler Filmler &> dosya_listesi.txt

---

[1] https://linuxize.com/post/bash-redirect-stderr-stdout/

[2] https://linuxhandbook.com/redirection-linux/

[3] https://www.guru99.com/linux-redirection.html

[4] https://www.putorius.net/linux-io-file-descriptors-and-redirection.html

[5] https://linuxconfig.org/bash-scripting-tutorial-for-beginners