5.07.2024

Zamanlanmış görevler ile yeniden başlatma ayarlamak

Daha önce şu yazımda tek satırlık powershell komutları ile bir sunucuyu yeniden başlatma fikirleri paylaşmıştım.

Daha iyi bir fikrim var.

Yeniden başlatma zamanlamasını kendi bilgisayarımdan yapmak istiyorum. Tek tek yeniden başlatılacak bilgisayarlara uzak masaüstü veya uzak PSSsession bağlantıları ile değil. Aşağıdaki gibi schedule-restart.ps1 dosyasını oluşturuyorum.

param(    
    [string]$Computername,
    [string]$ScheduledTime="23:00"
)
Invoke-Command -Computername $Computername -ScriptBlock {
    $trig1 = New-JobTrigger -At $ScheduledTime -Once
    $obj1 = Register-ScheduledJob -Trigger $trig1 -Name "YenidenBaslatma" -ScriptBlock {Restart-Computer -Force}
    "Görev oluşturuldu: $($obj1.Name)"
    "Zaman : $($trig1.At)"
}

Bu betikle uzaktaki bir bilgisayar için yeniden başlatma görevi oluşturmak istediğimde şu şekilde bir kullanım yeterli:

.\schedule-restart.ps1 -ComputerName uzakpc

Varsayılan yeniden başlatma zamanı saat 23:00. Eğer değer vermezsek bu saatte, -ScheduledTime ile değer verirsek başka bir saatte yeniden başlatılacak. Zamanlama bir kez, bugün için geçerli olacak. Yani tekrarlama yok.

Peki bu isimde bir görev daha varsa ne olur? Bir önceki seferden oluşturduğum bir zamanlanmış görev varsa bir tane daha oluşturmayacaktır. İsmini farklı seçerek bir görev daha oluşturabilirim, ama bu durumda gereksiz görev kalabalığı olur. Aşağıda kırmızı ile vurguladığım bölüm, önce aynı isimle bir görev olup olmadığını kontrol ediyor, sonra bunu silmeyi öneriyor.

param(    
    [string]$Computername,
    [string]$ScheduledTime="23:00"
)
$bSilme = $false 
Invoke-Command -Computername $Computername -ScriptBlock {
$prev1 = Get-ScheduledJob -Name "YenidenBaslatma" -EA Silent
    if ($prev1) {
$resp1 = Read-Host "Aynı isimde bir görev daha var. Silinsin mi (N/y)?"
if ($resp1 -eq "Y" -or $resp1 -eq "y") {
try {
Unregister-ScheduledJob -InputObject $prev1
}
catch {
$_.ErrorDetails.Message
}
}
else {
"Silmemeyi sectigin icin yeni gorev olusturulmayacak"
$bSilme = $true
}
    }
else {

        $trig1 = New-JobTrigger -At $ScheduledTime -Once
        $job1 = Register-ScheduledJob -Trigger $trig1 -Name "YenidenBaslatma" -ScriptBlock {Restart-Computer -Force}
        "Görev oluşturuldu: $($job1.Name)"
        "Zaman : $($trig1.At)"
    }
}

Takıntılı olanlar, bu görevi daha karmaşık sonuçlar yaratmadan silmek isteyebilir. Bunun için de bir delete-restartjob.ps1 betiği oluşturdum.

param(    
    [string]$Computername
)
Invoke-Command -Computername $Computername -ScriptBlock {
    $job1 = Get-ScheduledJob -Name "YenidenBaslatma" -EA Silent
    if ($job1) {
        $trig1 = $job1 | Get-JobTrigger
        "Bulunan gorev: $($job1.Name)"
        "Zamanlamasi : $($trig1.At)"
        $resp1 = Read-Host "Bu gorev silinsin mi (N/y)?"
        if ($resp1 -eq "Y" -or $resp1 -eq "y") {
            $job1 | Unregister-Scheduled 
        }
        else {
            "Silinme iptal edildi." 
        }
    
    else {
        "Silinecek yeniden baslatma gorevi bulunamadi" 
    
İyileştirmenin sonu yok. Daha neler eklenebilir, neler...

5.06.2024

4 rakamdan oluşan pin seçmek gerektiğinde

Kredi kartları, banka kartları ve SIM kartlarda hala 4 haneli pinler kullanıyoruz. Çoğumuz ilk deneyimimizde belirlediğimiz pinleri hayatımızın sonuna kadar kullanıyor. Ve daha da büyük bir çoğunluk "unutmamak için" bu pinleri kendi doğum tarihleri, eşlerinin veya çocuklarının doğum tarihlerinden seçiyor. Zaman içinde farklı yerlerde ve zamanlarda sızan pinler üzerinden yapılan istatistiksel bir çalışmada olası pinlerin kullanılma oranları görselleştirilmiş. İşte bu çalışmanın sonucu olan görsel:

 
Bazı önemli noktaları vurgulamak lazım:
  • Açık renk en fazla kullanımı, koyu renk en az kullanımı gösteriyor.
  • 19xx veya 20xx gibi (muhtemelen) doğum tarihleri çokça kullanılmış.
  • Belli tarihlerin ay/yıl(son iki hane) veya yıl/ay şeklinde ifade edildiği pinler de çok kullanılmış.
  • Köşegen üzerinde 1111 veya 1212 gibi pinler de çok kullanılmış.
  • Araya sıkışan koyu renk noktalar ilginç bir şekilde en az kullanılan pinler.
  • Aşağıdaki pinler, tüm araştırmaya konu 3,4 milyon içinde %27'lik bir oranlar kabaca 918.000 kişi tarafından tercih edilmiş:
    • 1234, 0000, 7777, 2000, 2222, 5555, 1122, 8888, 2001, 1111, 1212, 1004, 4444, 6969, 3333, 6666, 1313, 4321, 1010
  • Aşağıdaki pinler ise en az kullanılanlar olarak listelenmiş:
    •  8557, 8438, 9539, 7063, 6827, 0859, 6793, 0738, 6835, 8093, 9047, 0439, 8196, 6093, 7394, 9480, 8398, 7637, 9629, 8068 

Benzer bir paylaşım Reddit'te de var.

---

https://www.howtogeek.com/125378/the-most-common-and-least-used-4-digit-pin-numbers-security-analysis-report/

3.06.2024

Lockout policies and cached credentials

İlginç bir olay. Kullanıcı bir bilgisayar oturum açmayı deniyor ama başka yerde kullandığı şifresini giriyor (yeterli sayıda) ve hesabı lockout oluyor. Domain controller üzerinde 4740 olayı oluşuyor, Security eventlog'da. Kullanıcıya da belli sayıda yanlış parola denemesinden sonra hesabının kilitlendiği bilgisi gösteriliyor. Kullanıcı da mevcut bilgisayarını yeniden başlatıyor. Yeniden başladıktan sonra şifresini girmesi gereken doğru şifreyi hatırlıyor ve giriyor. Bu durumda ne olması gerekirdi? Ben domain controller'a danışarak oturum açmayı reddetmesini beklerdim. Ama öyle olmamış. Oturum açma işlemi başarılı olmuş. Söz konusu bilgisayar üzerinde o tarih ve saatteki 4624 eventlog'una baktığımda Logon Type olarak 11 gördüm. 11 neydi? Şifre doğrulaması için domain controller'a sorma, yerel bilgisayardaki cache'lenmiş şifrelere bak. Bu bilgisayarda da en son başarılı şifre girişine ait bir cache'lenmiş şifre olduğu için bilgisayar girişe izin veriyor. Ve kullanıcı oturum açabiliyor.

Kullanıcının hesabının unlock olmasına ait kayıt 4767, domain contorller üzerinde security eventlog'da tutuluyor. Ama bu olayda bu event yok, çünkü bu event sadece bir yetkili kullanıcının hesabı unlock etmesi sırasında oluşuyor. Lockout süresinin geçmesi sonrasında otomatik olarak unlock olması durumunda oluşmuyor.

Bu nahoş bir durum. Ama çözümünü bulamadım. Cached credentials'ı tümden engelleyebiliriz. Ama bu mobil bilgisayarlarını taşıyan ve yerel ağın dışında kullanan kullanıcılar için soruna sebep olur.

Önerilere açığım.

30.05.2024

Powershell ile hava tahminleri

Powershell terminal penceresinden hava tahminlerine ulaşmak kolay, Igor Chubin tarafından sağlanan wttr.in hizmeti sayesinde. Bir web tarayıcıdan girildiğinde HTML olarak sunulan veriler, Powershell veya bash gibi terminallerle yapıldığında ortama uygun şekilde (HTML etiketleri olmadan, ama terminalin desteklediği renklendirme ile-terminal sequences) sunuluyor.

Şehir seçmek için "/" işaretinden sonra şehir adını yazmak yeterli. Sadece şehir değil, hava tahminlerinin yapıldığı Türkçe karakterler de destekleniyor. Örneğin

irm wttr.in/Bursa

Hangi hava durumu hizmetini kullandığı bilgisine ulaşamadım.

wttr.in/:help ile yardıma ulaşılabiliyor. Buradan farkettim ki Türkçeleştirme seçeneğimiz var:

irm wttr.in/Bursa?lang=tr

Help'te daha bir sürü özellikten bahsedilmiş, "~" karakter ile bilen bir yerin yakınında arama yapma, "@" karakteri ile alan adının (IP adresi coğrafi konum) konumuna göre /moon ile ayın evresi hakkında bilgiye ulaşmak gibi. Aşağıdaki ise

irm wttr.in/Bursa?Fnq
F: Follow satırını gösterme
n: Dar görünüş ver
q: sessiz mod

ile gösterir. Bunu bir ps1 dosyasına ya da fonksiyon içine koyup hızlı durum bilgisi almak için kullanabiliriz:

function hava {
irm "wttr.in/Bursa?FTQ0"
}

Bunların tümü linux'ta curl ile de yapılabilir.

28.05.2024

Kapanan sunucudan haberdar olmak

Sunucular önemli. Onlar ne zaman yeniden başlamış, sebebi neymiş gibi sorular hep var. Bu soruların cevaplarını ben daha sormadan bulacak bir yöntemim olmasını istedim. Söz konusu sunucular Windows. Linux için de benzer bir yöntem bulunabilir elbet, önemli olan mantık.

Windows'da bir zamanlanmış göre oluşturayım. D:\bildir.ps1 konumundaki powershell betiğini çalıştırsın.

$act1 = New-ScheduledTaskAction -Execute powershell.exe 
-Argument "-ExecutionPolicy Bypass -NoProfile -File D:\bildir.ps1"
-WorkingDirectory "D:\"

Tetikleyici olarak da Sistem açılışını seçeyim.

$trig1 = New-ScheduledTaskTrigger -AtStartup 

Bu işi bir kullanıcının hesabı ile değil, SYSTEM hesabı ile yapacağım. Yüksek yetkilere ihtiyacım yok, standart yetkiler yeterli.

$prin1 = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Limited 

Önce görev nesnesini oluşturup sonrasında kaydetmek istiyorum. Bu işi iki adımda yapacağım.

$tsk1  = New-ScheduledTask -Action $act1 -Principal $prin1 -Trigger $trig1
$tsk1 | Register-ScheduledTask -TaskName "YenidenbaslatmaBildirimi" -TaskPath "\Firma"

Sıra geldi D:\bildir.ps1 betiğini oluşturmaya. Bu aşamaya kadar bilgisayar çoktan yeniden başladı. Betik bize açılıştan sonra bildirim verecek. Bu amaca yönelik aşağıdaki içerik yeterli:

$tarih = Get-Date -Format "yyyy-MM-ddTHH-mm-ss"
$sunucu_adi=$env:COMPUTERNAME
$smtpParams = @{
   From = "$sunucu_adi@firma.com"
   To = "admin@firma.com"
   Body = "$tarih - $sunucu_adi yeniden basladi"
   Subject = "$sunucu_adi yeniden basladi"
   SmtpServer = "smtp.firma.com"
}
Send-MailMessage @smtpParams
Add-Content -Value "$tarih - $sunucu_adi" -Path D:\bildir.log

Bu kısmı sadece sunucunun ne zaman başladığını bildirdi. Biraz da nedene olaklanalım. Windows'da nedeni bulmak için birkaç farklı yere bakmak gerekebilir. Olası sebepler Windows Update, güç kaybı, operatör isteği veya kritik bir uygulamanın hata vermesi olabilir. Bütün bu olayları içine alan bir olay kaydı günlüğünü inceleyelim:

$olaylar = Get-WinEvent -FilterHashTable @{LogName='System';`
StartTime=(Get-Date).AddHours(-1);`
ID=19,20,21,22,26,27,41,109,1000,1001,1002,1074,1076,6005,6006,6008} -ea silent

Bazen hiçbir olay dönmeyebilir. Bu gibi durumlarda, hata vermemesi sonuna için -ea silent (-ErrorAction SilentlyContinue kısaltması) ekledim. Daha sonra bu olayların içeriklerini gönderilecek mesajın gövdesine ekleyelim. Yukarıdaki betikte mesajın gövdesi $smtpParams içinde kısa bir şekilde oluşturulmuştu. Örneği bozmamak için aşağıdaki kodu bu splat değişkeninin ardından, Send-MailMessage cmdlet'inden önce olmasını düşündüm.

foreach ($olay in $olaylar) {
    $Body += "$($olay.TimeCreated) - $($olay.Id) - $($olay.Message)"
}

Bu şekilde en üstte tarih ve saatin yanında sunucu adı ve "yeniden basladi" görünecek. Ardından son 1 saatte konuyla ilgili olabilecek tüm olay kayıtları listelenecek. Bu elbette iyileştirmeye açık.

15.05.2024

Tarayıcı savaşları

 Bugün Reddit'te aşağıdaki ekran görüntüsünü görünce şaşırdım.


Bu "kullanıcı iradesine müdahale" delilini gördükten sonra önce Linux üzerinden Bing'de yaptığım aramada benzer bir sonuç ile karşılaşmadım. Sonra Windows'da Edge ile yaptığım aramada ben de aynı şeyi gördüm:


Bu bir arama motoru. Arama terimlerine karşılık gelen en yakın sonuçları listelemesi gerek. Ama o sonuçları amacına göre değiştiriyor. İlk sıraya reklam görünümlü mesajı koyuyor: "Yeni bir web tarayıcısı indirmenize gerek yok".

İlk adımı bunun gibi görece masum minik müdahaleler sonraki adımlarda hayatımızda daha büyük etkiye sahip, daha rahatsız edici şekillerde kullanılıyor. Bakınız, Cambridge Analytica olayı. "Kullanıcı davranışında belli belirsiz bir değişikliğe sebep olabiliyorsak başarılı sayılırız" denmişti. Kullanıcı verileri kullanılmıyor gibi gelebilir belki ama Linux'ta aynı sonucu vermediğinden yola çıkarak benim işletim sistemimin Windows olmadığı bilgisini kullandı bile.

---

https://www.reddit.com/r/browsers/comments/1crs3d1/who_else_does_this/
https://www.reddit.com/r/Windows11/comments/17b3bu5/microsoft_stop_making_me_feel_bad_for_downloading/
https://www.reddit.com/r/assholedesign/comments/zz8t4i/microsoft_really_does_not_want_you_to_download/
https://www.reddit.com/r/windows/comments/12skbx9/edge_stopping_me_from_downloading_chrome/
https://www.reddit.com/r/shitposting/comments/199sxv9/edging_to_this_rn/
https://www.reddit.com/r/pcmasterrace/comments/199vks2/chat_is_this_real/
https://www.reddit.com/r/firefox/comments/11cpa98/forgot_how_pushy_edge_is_after_a_clean_install/

8.05.2024

Powershell ve Windows Firewall

Windows Firewall, Windows Advanced Firewall veya Windows Defender Firewall. Microsoft'un ürünlerine verdiği isimler bana hep garip gelmiştir. Bir müzik ve video oynatıcısı yapıp adına media player demek, mesajlaşma uygulamasına messanger, interent tarayıcılarına Internet Explorer demek... İşletim sisteminin adı zaten Windows.

Konuyu dağıtmayalım. Bugün baktığımda kullandığım bilgisayarın güvenlik duvarımda 700'den fazla kural var. En fazla 2 tanesini ben oluşturmuşumdur -belki. Üstüne ek yazılımlar kurduğumuzda daha da fazlası geliyor. Bunlar ile uğraşmak hiç kolay değil. Evet, belli kolaylıklar sağlanmış, gruplanmış. Ağlara göre sınıflandırılmış (etki alanı, özel ağ, genel ağ falan) ama yine de belli bir portu açmak için yeni bir kural mı oluştursam, yoksa var olan kurallardan biri işimi görür mü diye düşündüğüm durumlar hiç az değil. Üstüne üstlük "Gelişmiş Güvenlik Özellikli Windows Defender Güvenlik Duvarı" uygulaması da pek kullanışlı değil. İsme, adrese veya porta göre arama, sıralama, süzme yok. Ama iyi ki Powershell var!

Yeni bir bilgisayara oturduk. Tüm önyargılara rağmen ilk yapılacak iş güvenlik duvarını devreye almak. Güvenlik duvarı, bağlı olunan ağ profiline göre kuralları etkinleştiriyor. Mevcut ağ profilimize bakalım:

Get-NetConnectionProfile

Bakılacak alan NetworkCategory. Private / Public arasında geçiş yapmak için

Set-NetConnectionProfile -NetworkCategory Private

kullanabiliriz. Bu profile uyan güvenlik duvarı profilimizi görmek için

Get-NetFirewallProfile -Name Private | Select-Object Name, Enabled, DefaultInboundAction

kullanabilir, Enabled alanından etkin mi değil mi görebilir, DefaultInboundAction'dan kurala uymayan gelen trafiğe karşı varsayılan eylemini (Block olmalı) tespit edebiliriz. Genel eğilim, güvenlik duvarını etkinleştirirken bütün profilleri etkinleştirmek. Eğer yapmak istediğimiz buysa

Set-NetFirewallProfile -All -Enabled True

Burada garip olan, True değerinin bir boolean değer değil de GpoBoolean olması. Şu sayfa da dahil her yerde $True yerine True yazılmış. Tam olarak aynı şey mi, emin değilim ama şöyle birşey buldum:

$a = [Microsoft.PowerShell.Cmdletization.GeneratedTypes.NetSecurity.GpoBoolean]::True
$a -eq $True
True

yani sanki aynı şey. Bu adımlardan sonra ikinci yaptığım şey genelde bazı temel kuralları etkinleştirmek veya kontrol etmek. Örneğin söz konusu bilgisayara uzak masaüstü yapılmasını istiyorsam ilgili kuralın devrede olup olmadığını kontrol ederim. Bunun için içinde "Uzak Masaüstü*" (İngilizce bilgisayarlarda "Remote Desktop*") geçen kuralları  geçenleri ararım:

Get-NetFirewallRule -DisplayName "Uzak Masaüstü*" | ft Name, DisplayName, Enabled

Bunu dahil oldukları gruba göre arayarak da yapabilirim:

Get-NetFirewallRule -DisplayGroup "Uzak Masaüstü" | ft Name, DisplayName, Enabled

Bu kuralların hangi uygulamaya, hangi porta ve hangi uzak IP grubuna izin/engelleme verdiğini görmek için ise şu sorgu mükemmel:

Get-NetFirewallRule -DisplayGroup "Uzak Masaüstü" | 
Format-Table DisplayName,
@{N="Port";E={($_|Get-NetFirewallPortFilter).LocalPort}},
@{N="Application";E={($_|Get-NetFirewallApplicationFilter).Program}},
@{N="Address";E={($_|Get-NetFirewallAddressFilter).RemoteAddress}} -AutoSize

Benzer şekilde IIS yüklü bir bilgisayarda 80 numaralı port üzerinden HTTP hizmeti vermek için güvenlik duvarındaki kuralları kontrol etmek istersem

Get-NetFirewallRule -DisplayGroup "*World Wide Web Services*" | Ft DisplayGroup, DisplayName, Enabled

Bilgisayarımız ping'lere cevap veriyor mu diye bakmak için (İngilizce bilgisayarlarda):

Get-NetFirewallRule -DisplayName "Core Networking Diagnostics - ICMP Echo Request (ICMPv4-In)" | 
Format-Table DisplayGroup, Displayname, Profile, Enabled

ya da grup bazlı

Get-NetFirewallRule -DisplayGroup "Core Networking Diagnostics" | 
Format-Table DisplayGroup, Displayname, Profile, Enabled

Türkçe bilgisayarlarda aynısını yapmak için ise

Get-NetFirewallRule -DisplayName "Çekirdek Ağ Tanılama - ICMP Yankı İsteği (ICMPv4-Gelen)" | 
Format-Table DisplayGroup, Displayname, Profile, Enabled

grup bazlı olarak da

Get-NetFirewallRule -DisplayGroup "Çekirdek Ağ Tanılama" | 
ft DisplayGroup, DisplayName, Profile, Enabled -AutoSize

gibi birşey olabilir. Bunlardan etkinleştirmek istediklerimizi pipe ile

Get-NetFirewallRule -DisplayName "Çekirdek Ağ Tanılama - ICMP Yankı İsteği (ICMPv4-Gelen)" | 
Enable-NetFirewallRule

ya da devre dışı bırakmak için Disable-NetFirewallRule yapabiliriz. Dosya ve yazıcı paylaşımı kural grubu için sırasıyla Türkçe ve İngilizce bilgisayarlarda komudumuz şöyle olabilir:

Get-NetFirewallRule -DisplayGroup "Dosya ve Yazıcı Paylaşımı" | 
ft Displayname, DisplayGroup, Enabled, Profile -AutoSize

Get-NetFirewallRule -DisplayGroup "File and Printer Sharing" | 
ft Displayname, DisplayGroup, Enabled, Profile -AutoSize

Bütün dosya paylaşımlarını devre dışı bırakmak için

Get-NetFirewallRule -DisplayGroup "Dosya ve Yazıcı Paylaşımı" | Disable-NetFirewallRule

kullanılabilir.

Asıl kilit nokta, herhangi bir porta komuta eden bir kural var mı yok mu aramak istersek

Get-NetFirewallRule | where {($_ | Get-NetFirewallPortFilter).LocalPort -eq 9401} | select DisplayName

Kurallar etki alanı (domain), özel ağlar (private) ve genel ağlar (public) olarak kategorize edildiği için bu profile bilgisine dikkat etmek çok önemli. Özel bir ağda çalışırken genel ağ kuralı üzerinde işlem yaparsak etkili olmayacaktır.

Bunları Get-* komutları ile mevcut durumu görmek üzerine komutlardı. Bir de kurallarda toplu değişiklik yapalım. Örneğin DisplayGroup özelliği "Dosya ve Yazıcı Paylaşımı" olan kural gruplarını sadece belli IP'lerden erişilebilecek şekilde ayarlayalım. Önce "Dosya ve Yazıcı Paylaşımı" DisplayGroup'una dahil kuralların IP adresi kısıtlaması var mı, bakalım:

Get-NetFirewallRule -DisplayGroup "Dosya ve Yazıcı Paylaşımı" | 
Format-Table Enabled, DisplayName, Profile, 
@{N="Scope";E={($_|Get-NetFirewallAddressFilter).RemoteAddress}} -Autosize

Bu şekilde hedef kural grubumun tam olarka istediğim kurallar olduğundan emin olabilirim. Hedef kural grubum basit bir şekilde DisplayName veya DisplayGroup ile ifade edilebilecek grup ise basitçe

Set-NetFirewallRule -DisplayGruop "Dosya ve Yazıcı Paylaşımı" -RemoteAddress 192.168.1.28

gibi bir komutla bu kurala uzaktan erişebilecek bilgisayarların IP adreslerini seçebilirim.

Bu arada LocalSubnet bilgisayarın yerel ağındaki IPv4 ve IPv6 altağını kapsar. Sadece IPv4 için LocalSubnet4, veya sadece IPv6 için LocalSubnet6 kullanılabilir:
Set-NetFirewallRule -DisplayGruop "Dosya ve Yazıcı Paylaşımı" -RemoteAddress LocalSubnet4

Daha karmaşık bir senaryomuz varsa, örneğin domain profilinde ve sadece etkinleştirilmiş kural alt grubununa uzaktan erişebilecek bilgisayarın IP adresini kısıtlamak için

Get-NetFirewallRule -DisplayGroup "Dosya ve Yazıcı Paylaşımı" | 
where {$_.Enabled -eq $true -and $_.Profile -eq "Domain"} | 
Set-NetFirewallRule -RemoteAddress 192.168.1.28