Sunucudaki bir yönetim paneli neredeyse her zaman aynı duvara çarpar: web süreci, yetkisi olmayan bir şey yapmak zorundadır. Bir adresi engellemek, bir port açmak, günlüğü okumak. Akla gelen yanıt, sudoers dosyasında tek bir dar satır vermek ve başka hiçbir şey vermemektir:
www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client set * banip *
Satır «yalnızca engelleme» diye okunur. Gerçekte anlamı «root olarak her şey»dir. Aşağıda bunun neden böyle olduğunu, kendi sunucunuzda beş dakikada nasıl denetleyeceğinizi ve böyle kuralların neyle değiştirildiğini bulacaksınız.
* gerçekte ne yapar
Belirleyici ayrıntı: sudo argümanları tek tek değil, komut satırını bütün olarak karşılaştırır ve kalıptaki * boşlukların — yani argüman sınırlarının — üzerinden rahatça geçer. Kuralın ortasında duran değişmez banip bu nedenle hiçbir şeyi sınırlamaz: banip sözcüğünün komutta bir yerde geçmesi yeterlidir, öncesine de sonrasına da her şey aktarılabilir.
Ardından, kendisine çalıştırılacak bir komut verilebilen bir program aranır. Bizim durumumuzda bu, kuralın içinde duruyordu. fail2ban'da engelleme eylemi metin olarak tanımlanır ve bu metin çalışma sırasında yeniden tanımlanabilir:
fail2ban-client get portscan actions
sudo fail2ban-client set portscan action nftables-multiport actionban "<komut>" banip 192.0.2.10
sudo fail2ban-client set portscan banip 192.0.2.11
İlk çağrı gerçek bir eylemin metnini değiştirir, ikincisi jail'in tetiklenmesini sağlar — ve root olarak çalışan fail2ban, yerleştirileni yürütür. Bunu 4 Temmuz'da kendi sunucumuzda yaptık: /root içinde root yetkisiyle bir dosya oluştu, yani web süreci dar görünen bir kuraldan makine üzerinde tam yetki elde etti.
Yol boyunca, başkalarının anlatımlarında genellikle bulunmayan üç ayrıntı ortaya çıktı:
setönündeki-cseçeneğini sudo keser: kalıptasetdoğrudan çalıştırılabilir dosyanın adına yapışıktır ve öncesine hiçbir şey sokulamaz;banipsözcüğü olmadanaddactionveactionda geçmez — ama gereken her şey tek birset … banip …komutuna sığar, böylece kalıp sağlanmış olur;- eylemin adı gerçek olmalıdır, yoksa değiştirilecek bir şey yoktur:
fail2ban-client get <jail> actionsonu gösterir.
Aynı listede kimler daha var
fail2ban burada suçlu değil, tek örnek de değil. Tehlikeli olan, NOPASSWD ile yıldızın birlikteliğidir. Kendi sunucularımızda yanında ne bulduk:
journalctl *— günlük bir sayfalayıcı üzerinden açılır, sayfalayıcıdan ise bir kabuk başlatılır. Kural görünüşte günlük okumaya ilişkindir; uygulamada bir root kabuğudur. Çare daha dar bir kalıp değil,systemd-journalgrubudur: o zamanjournalctlsudo olmadan çalışır;grep * /var/log/fail2ban.log—grepkomutunun ilk argümanı kalıptır, ama yıldız ikinci bir yol verilmesine de izin verir ve grep root olarak çalışır./etc/shadowdosyasını bu kuralla okumak tek bir komuttur. Karşılığı aynıdır: günlükleri okumak içinadmgrubu;ufw --force *ve çıplakufw allow/deny/delete *— web süreci güvenlik duvarını tümüyle kapatabilir. Ayrıca can sıkıcı olan şu: iki durumda da panel bu kuralları hiç kullanmıyordu; kurulum programının erken sürümlerinden kalan ölü yetkilerdi;lynis-scan.sh *—"$@"içeriğini bir root sürecine geçiren bir sarmalayıcı, hiçbir sınırlaması olmayan bir kurala denktir.
Sizde ne olduğu nasıl görülür
Bakılacak olan dosya değil, belirli bir kullanıcının gerçek yetkileridir — PHP-FPM havuzunun gerçekten hangi kullanıcıyla çalıştığı (her zaman www-data değil; makinelerimizden birinde panel admin olarak çalışıyordu):
ps -o user= -C php-fpm | sort -u
sudo -l -U www-data
sudo -l -U admin
Sonra dosya listesinin kendisi; burada bize zamana mal olan iki tuzak var:
sudo grep -rn 'NOPASSWD' /etc/sudoers /etc/sudoers.d/
ls -la /etc/sudoers.d/
İlki: kurallar aynı anda iki yerde yaşar. Bizde tehlikeli satır hem /etc/sudoers.d/monitor dosyasında hem de ana /etc/sudoers dosyasındaydı — ana dosyada, kurulum programının farklı sürümlerinin biriktirdiği sekiz kadar kopya hâlinde. Bir yeri temizleyip rahatlamak, deliği açık bırakmanın alışılmış yoludur.
İkincisi: sudo, adında nokta bulunan dosyaları yok sayar. /etc/sudoers.d/ içindeki www-data.bak3 dosyası geçerli bir kural gibi görünür ve geçerli bir kural gibi okunur, ama etkisi yoktur. Bu iki yönde işler: «kural var, yetki yok» ve yapılandırma kopyasının «hemen yanında durduğu» yönündeki yanlış rahatlık.
Neyle değiştirilir
Kalıbı daraltmak boşunadır — satırın herhangi bir yerindeki bir yıldız, görevi başa döndürür. Yalnızca bir yaklaşım ayakta kalır: sabit konumlu argümanlar alan, onları kendisi doğrulayan ve programı hiçbir şey eklenemeyecek biçimde çağıran bir root sarmalayıcısı.
#!/bin/sh
# /usr/local/sbin/arciveo-f2b — ban|unban <jail> <ip>, başka hiçbir şey değil
set -eu
action="$1"; jail="$2"; ip="$3"
case "$action" in ban|unban) ;; *) echo "invalid action"; exit 1 ;; esac
case "$jail" in *[!a-zA-Z0-9_-]*|'') echo "invalid jail"; exit 1 ;; esac
printf '%s' "$ip" | grep -Eq '^[0-9a-fA-F:.]+$' || { echo "invalid ip"; exit 1; }
case "$action" in
ban) exec /usr/bin/fail2ban-client set "$jail" banip "$ip" ;;
unban) exec /usr/bin/fail2ban-client set "$jail" unbanip "$ip" ;;
esac
Sarmalayıcı root'a aittir, yetkileri 755 ve — bu seçimlik değildir — web tarafından yazılabilir olmamalıdır, yoksa bütün yapı anlamsızlaşır. sudoers dosyasında yalnızca o kalır:
www-data ALL=(root) NOPASSWD: /usr/local/sbin/arciveo-f2b
Argümanlar kuralda sıralanmaz: onları betiğin kendisi denetler ve içindeki konumlar sabittir, dolayısıyla bir eylem metni sokuşturulacak yer yoktur. Yalnızca okuyan kurallar — durumlar, ss, ipset list — ayrı satırlara, mümkün olan yerde yine yıldız olmadan yazılır.
Ayrıca sudo'nun gruplarla değiştirilmesi üzerine. Yaklaşım doğrudur: günlük için systemd-journal ve kayıtlar için adm, herhangi bir sudo kuralından daha ucuza gelir ve daha güvenlidir. Ama web kullanıcısını gruplardan körü körüne çıkarmak olmaz. Panelli bir sunucuda www-data kullanıcısını bir gruptan çıkardık ve bir anda bütün sitelerde 403 aldık: Apache orada www-data olarak çalışıyor, sitelerin dosyaları başka bir kullanıcıya ait ve okuma erişimini tam olarak o grup üyeliği veriyordu. Geri alma, Apache ve PHP-FPM'in tam olarak yeniden başlatılmasını gerektirdi — yeniden yükleme değil —, çünkü eski işçiler eski grup kümelerini korur ve «çalışıyor, sonra 403» durumunu üretir.
Değişiklikten sonra denetim
Bir sudoers dosyası değiştirilmeden önce söz diziminin denetlenmesi gerekir — yoksa tümüyle sudo'suz kalınabilir:
sudo visudo -cf /etc/sudoers.d/monitor
Sonrasında eski yolun öldüğü doğrulanmalıdır, hem de tam o kullanıcının adına:
sudo -k
sudo -u www-data sudo -n fail2ban-client set portscan banip 192.0.2.10
Yanıt a password is required olmalı; sarmalayıcıyı adres yerine saçma bir şeyle çağırmak ise invalid ip vermelidir. Burada kendine özgü bir tuzak pusuda: sudo -v onayı on beş dakika boyunca önbelleğe alır ve ondan sonra sudo -n ile yapılan her denetim «başarıyla» geçer. Bir keresinde sunucuda hiç bulunmayan bir kuralı tam olarak böyle doğrulamıştık. Bu yüzden denetimden önce sudo -k zorunludur.
İzlemek ne işe yarar
sudo kuralları seyrek değişir, ama sessizce değişir: kurulum programı ekler, barındırma paneli kendisininkini katar, kaldırılan bir paket satırlarını ardında bırakır. Bir kerelik gözden geçirme bugün var olanı kapatır ve bir sonraki güncellemenin ne getireceği konusunda hiçbir şey söylemez. Pratik sonuç yalındır: kimlerin root olabileceğinin listesi, sunucunun geri kalan durumunun yanında göz önünde durmalı; olaydan sonraki hatırlamada değil. Bütün bunların bir arada nasıl göründüğünü aşağıdaki tanıtım sayfaları gösteriyor.