Tek Açılış Hatası, Beş Kök Neden
Bir müşterinin SUSE sunucusu açılmayı reddetti — ve onu geri getirmek beş katmanlı bir kazıya dönüştü: unutulmuş bir USB kaydı, kırık bir sertifika zinciri, bir pip sürprizi, tek harflik bir yazım hatası ve bir firmware tuzağı. Düzeltilen her katman bir sonrakini ortaya çıkardı.
[ TIME ] Timed out waiting for device /dev/disk/by-uuid/XXXX-XXXX. [DEPEND] Dependency failed for /mnt/usb. You are in emergency mode. After logging in, type "journalctl -xb" to view system logs, "systemctl reboot" to reboot. Give root password for maintenance (or press Control-D to continue):
Çağrı yeterince basitti: Bir müşterinin SAP HANA veritabanını barındıran, SUSE Linux Enterprise Server 15 çalıştıran HPE ProLiant DL380 Gen9, bir kapanışın ardından geri gelmiyordu. Konsolda oturum açma ekranı yerine bir acil durum kabuğu duruyordu. Makine gerçekten sağlığına kavuşana kadar, birbiriyle hiçbir ilgisi olmayan beş ayrı problem bulup düzeltmiştim. İşte yanlış dönüşler dahil hikâyenin tamamı — çünkü dersler tam da o yanlış dönüşlerde yaşıyor.
REQUESTS_CA_BUNDLE yerine REQUEST_CA_BUNDLE. Ortam değişkenleri sessizce başarısız olur.Hiçbir anlamı olmayan korkutucu mesajlar — ve her şeyi anlatan sessiz bir tane
Konsol beni gerçekten endişe verici bir metinle karşıladı: [Firmware Bug]: the BIOS has corrupted hw-PMU resources (MSR 38d is 330), artı MDS ve MMIO Stale Data hakkında bir duvar dolusu CPU güvenlik uyarısı. Bir sunucu açılmıyorken ekranda "Firmware Bug" yazıyorsa, önce onun peşine düşmemek disiplin ister. Not aldım (3. perdede önemli olacak) ve devam ettim — bu mesajların hiçbiri açılışın durma sebebi değildi. Sağlıklı ya da değil, bu makinenin her açılışında görünüyorlar.
İlk gerçek hipotezim bu donanım için bir klasikti: initrd'de — çekirdeğin gerçek diskler devreye girmeden önce kullandığı küçük başlangıç dosya sisteminde — HPE Smart Array P440ar RAID denetleyicisinin sürücüsünün eksik olması. ProLiant kasalarda bu tanıdık bir arıza. Ama tutmadı: Diskler acil durum kabuğundan görünüyordu ve kök dosya sisteminin UUID'si çekirdek komut satırının beklediğiyle eşleşiyordu. Bir numaralı yanlış dönüş, birkaç dakikada elendi — çünkü varsaymak yerine kontrol ettim.
Asıl ipucu neredeyse görünmezdi: zaman damgalarında bir boşluk. journalctl -xb çıktısında gezinirken günlük yaklaşık 89 saniye boyunca öylece… duruyordu. Hiçbir şey çökmedi, hiçbir hata düşmedi — sistem kımıldamadı. Ve 90 saniye, tam da systemd'nin bir aygıtı beklerken kullandığı varsayılan zaman aşımı.
# Ne başarısız oldu ve neden? journalctl -xb # bu açılışın tam günlüğü — zaman boşluklarına bak systemctl --failed # hangi birimler pes etti ls /dev/disk/by-uuid # şu anda gerçekten hangi diskler var cat /proc/cmdline # çekirdeğe hangi kök aygıtı söylendi
Sonra systemctl --failed her şeyi açıkça söyledi: Timed out waiting for device — ardından USB belleklerin kullandığı kısa FAT biçiminde bir UUID — ve Dependency failed for /mnt/usb. Biri bir zamanlar bir USB sürücü takmış, onu /etc/fstab'a (açılışta nelerin bağlanacağını listeleyen dosyaya) eklemiş ve sonra sürücüyü alıp gitmişti. nofail seçeneği olmadan systemd her fstab satırını bir söz olarak görür: Aygıt ortaya çıkmazsa açılış bozuk sayılır ve kendinizi acil durum kabuğunda bulursunuz. NEDEN 1
# Önce: bellek yoksa açılış 90 sn takılır, sonra başarısız olur UUID=XXXX-XXXX /mnt/usb vfat defaults 0 0 # Sonra: açılış onsuz devam eder ve yalnızca 5 saniye bekler UUID=XXXX-XXXX /mnt/usb vfat nofail,x-systemd.device-timeout=5s 0 0
Utandıran kısım: Ekrandaki ilk mesajlar suçluyu tam olarak adlandırmıştı. "Timed out waiting for device… Dependency failed for /mnt/usb" bir bilmece değil. Tekrar tekrar öğrendiğim ders: Teori kurmadan önce systemd'nin sözlerini kelimesi kelimesine okumak. Oradayken günlük, EFI açılış bölümünde FAT-fs (sda1): Volume was not properly unmounted de gösterdi — önceki sert kapanışlardan kalan yara dokusu — o da bir fsck.vfat temizliği aldı. Bir yeniden başlatma sonra sunucu ayaktaydı. Hikâye burada bitebilirdi. Bitmedi.
"Certificate verify failed" — her yerde
Makine açılır olunca müşterinin uygulaması bir Python paket güncellemesi istedi. pip reddetti: CERTIFICATE_VERIFY_FAILED. Can sıkıcı ama sıradan — genellikle uygulama düzeyinde bir sorundur. Ne var ki curl da aynı hosta karşı başarısız oldu (hata 60, sertifika hatası). Sonra zypper ref her bir depoda başarısız oldu. Bu tırmanış tanıyı tamamen değiştirdi: Bu bir uygulama sorunu değildi — işletim sisteminin tamamı internete güvenmeyi bırakmıştı.
Bir de tavuk-yumurta cilvesi vardı: Bariz çare olan "ca-certificates paketini yeniden kur", paketin indirilmesi de bozuk olan TLS'yi gerektirdiğinde işe yaramaz. (Tamamen köşeye sıkışırsanız: Tek seferlik, doğrulama kapalı bir RPM indirmek kabul edilebilir bir son çaredir, çünkü RPM paketleri kurulumda denetlenen kendi GPG imzalarını taşır. Ama asıl nedeni düzeltmek daha iyidir.)
Sistem çapındaki TLS arızalarında sabit bir merdiven inerim, en ucuz kontrol önce:
- Saat. Sertifikaların geçerlilik aralığı vardır; yanlış bir sistem tarihi hepsini birden kırar. (Burada sorun yoktu.)
- Güven deposu.
/etc/ssl/ca-bundle.pemvar mı ve sağlıklı ~140+ sertifika içeriyor mu? (Burada sorun yoktu.) - Sunucunun gerçekte ne gönderdiği.
openssl s_clientsertifika zincirini tam sunulduğu hâliyle gösterir — ve verify hatasının numarası kök nedene giden en hızlı yoldur.
echo | openssl s_client -connect pypi.org:443 -servername pypi.org 2>&1 | grep -E "s:|i:|verify" 0 s:CN = pypi.org ← leaf; imzalayan… i:CN = corp-SubCA ← …zincirin hiçbir yerinde olmayan bir ara sertifika verify error:num=2:unable to get issuer certificate
İki şey göze çarptı. Birincisi, pypi.org, corp-SubCA tarafından — yani müşterinin kendi iç CA'sı tarafından — imzalanmış bir sertifika sunuyordu; PyPI'ın asla kullanmayacağı bir şey. İkincisi, verify error num=2 "bunu kimin imzaladığını görüyorum ama imzalayanın sertifikasını bulamıyorum" demek. İkisi birlikte bütün hikâyeyi anlatıyordu.
TLS denetleyen bir proxy gerçekte ne yapar
Bu ağ, birçok kurumsal ağ gibi, şifreli trafiği denetleyen bir güvenlik cihazı çalıştırıyor. İnsanlar bağlantılarının, ortasında bir kontrol noktasıyla web sitesine ulaştığını hayal eder. Gerçekte ortada tek bir bağlantı yoktur — iki tane vardır:
sahte zincir
gerçek zincir
Makineniz gerçek web sitesinin sertifikasını hiç görmez. Proxy'nin bastığı, şirketin özel sertifika otoritesince imzalanmış bir taklidi görür. Bu kötü amaçlı yazılım değil — cihaz ilan edilmiş işini yapıyor — ama yalnızca her makine o özel CA'ya güveniyorsa ve proxy evrakların tamamını teslim ediyorsa çalışır. Bu da bizi "tamam" ne demek sorusuna getiriyor. Bir sertifika zincirinin üç halkası vardır: kök CA (makinenizde kurulu, güvenin çapası), ara sertifika (kök tarafından imzalanır, günlük imzalama işini yapar) ve leaf (asıl web sitesinin sertifikası). Sunucunun leaf artı ara sertifikayı göndermesi gerekir; kökü sizin makineniz sağlar.
imzalar
│
imzalar
│
Buradaki proxy sahte leaf'i ve tuhaf biçimde kökü sunuyordu — ama ara sertifika corp-SubCA'yı değil. Kırık zincir, doğrulama yok, TLS yok — hiçbir yerde. NEDEN 2
Peki binadaki her Windows dizüstü aynı proxy'ye karşı neden sorunsuz çalışıyordu? İki neden. Windows makineleri kurumsal CA'ları Grup İlkesi üzerinden otomatik alır ve Windows "AIA takibi" yapar — bir ara sertifika eksikse, sertifikanın içine gömülü bir URL'yi izleyip eksik halkayı kendi başına indirir. Linux'ta neredeyse her şeyin kullandığı OpenSSL bunu bilerek reddeder. Bu yüzden TLS'de "Windows'ta çalışıyor, Linux'ta başarısız" pratikte tam olarak bu durumun imzasıdır: eksik zincir.
cp corp-RootCA.crt corp-SubCA.crt /etc/pki/trust/anchors/ update-ca-certificates trust list | grep -c corp # ikisinin de depoya indiğini doğrula
Hem kök hem de eksik ara sertifika sistem güven deposundayken OpenSSL zinciri yerel olarak tamamlayabilir. curl çalıştı. zypper tüm depoları yeniledi. (Gerçekten doğru düzeltme, proxy'nin tam zincirini sunacak şekilde yapılandırılması — o, ağ ekibine açılacak bir kayıt; sunucu tarafındaki düzeltme sizi bugün önünüzü açar.)
Yirmi dakika sonra geri gelen problem
pip çalışıyordu — o yüzden doğal olarak ilk yapılan şey pip install --upgrade pip oldu. Ve TLS hataları anında geri döndü, yalnızca venv içinde. Sistemde hiçbir şey değişmemişti; pip'in kendisi değişmişti. SUSE, sistem güven deposunu okuyan yamalı bir pip dağıtır — az önce onardığım depoyu. Yükseltme onu, sistem deposunu tamamen yok sayan ve corp-RootCA'yı hiç duymamış kendi paketlenmiş sertifika koleksiyonunu (certifi adında bir paket) kullanan PyPI'daki upstream sürümle değiştirdi. NEDEN 3 Tek satır pip'i yazım hatasına dayanıklı ve kalıcı yapar:
pip config set global.cert /etc/ssl/ca-bundle.pem
Uygulamanın kendisi — Python tabanlı bir güvenlik-besleme toplayıcısı, özünde zafiyet duyuruları için bir RSS okuyucu — çalışma zamanında aynı şekilde, aynı certifi nedeniyle başarısız oldu. Ama hata deseni tanısal altındı: Yalnızca Microsoft ve Google/Feedburner beslemeleri çalışıyordu, geri kalan her şey başarısızdı. Bunlar tam olarak proxy'nin SSL denetimi muafiyet listesindeki alan adlarıydı — oraya giden trafik, gerçek ve herkesçe güvenilen sertifikalarla dokunulmadan geçiyordu; denetlenen her alan adı ise certifi'nin reddettiği kurumsal taklidi alıyordu. Bazı TLS hedefleri çalışıp bazıları çalışmıyorsa, çalışanların ağda ortak noktasını sorun, kodda değil. NEDEN 4
Bir saat süren beş dakikalık düzeltme
Python'un requests kütüphanesi için standart düzeltme, sistem paketini gösteren bir ortam değişkenidir. Ayarladım. Hiçbir şey değişmedi. Servisi, kabuk profilini, yeniden başlatma sırasını kontrol ettim — hiçbir şey. Utanç verici biçimde geç keşfedilen asıl problem:
REQUEST_CA_BUNDLE=/etc/ssl/ca-bundle.pem # benim yazdığım — sessizce yok sayıldı REQUESTS_CA_BUNDLE=/etc/ssl/ca-bundle.pem # kütüphanenin okuduğu — S harfine dikkat
Tek eksik harf. NEDEN 5 Ve ortam değişkenlerinin sessizce acımasız özelliği tam da bu: Yanlış yazılmış bir değişken hata değildir, sadece kimsenin okumadığı bir değişkendir. Uyarı yok, günlük satırı yok, hiçbir şey yok. O günden beri sessizce yanlış yazılamayan mekanizmaları tercih ediyorum — yukarıdaki gibi pip config, ya da varsayılanı doğrudan uygulamanın kendi kodunda os.environ.setdefault("REQUESTS_CA_BUNDLE", "/etc/ssl/ca-bundle.pem") ile ayarlamak; orada bir yazım hatası en azından gözden geçirilebilir tek bir yerde yaşar.
Bir katmanı düzelt, bir sonraki kendini gösterir. Bu şanssızlık değil — katmanlı sistemler böyle davranır.
Döngüyü kapatmak — ve bir düşürmeden kaçınmak
En baştaki [Firmware Bug] mesajını hatırlıyor musunuz? Her şey sağlıklıyken ona geri döndüm ve HPE'nin ProLiant Gen9 için son Service Pack'ini — 2022.08 SPP'yi, bu neslin alacağı son firmware paketini — SUM'un çevrimiçi moduyla, doğrudan çalışan işletim sisteminden uyguladım.
Tuzak da oradaydı. SUM birkaç bileşeni "Forced" anahtarıyla gösteriyordu — kulağa "ekstra titiz" gibi geliyor; gerçekte "sunduğum sürüm kuruludan daha yeni değil" demek. Sunucunun BIOS'u (P89 v3.30, 2023) ve iLO'su (2.82), 2022 SPP'sinin taşıdığından (P89 v2.92, iLO 2.81) zaten daha yeniydi; çünkü bu bileşenler son paket kesildiğinden sonra da tek tek güvenlik güncellemeleri almaya devam etmişti. "Hepsini dağıt" tıklaması, üretimdeki bir HANA sunucusunun anakart firmware'ini düşürecekti. Ömrünü tamamlamış donanımda en yeni resmî paket makinenizden eski olabilir — dağıtmadan önce daima "Installed" ve "Available" sütunlarını okuyun.
Dağıtım bu yüzden seçici oldu: NIC firmware'i, disk firmware'i (HPD4 → HPD6) ve yönetim araçları (ssacli ve arkadaşları) — gerçekten daha yeni olan parçalar. Sonra doğrulama turu:
sut -status # HPE ajanı memnun, bekleyen bir şey yok ssacli ctrl all show status # RAID denetleyici / önbellek / pil: OK journalctl -b -p err # mevcut açılış, yalnızca hatalar: sessiz
Gen9 makineleri yaşatan herkese bir not: Bir daha asla SPP gelmeyecek. Gelecekteki düzeltmeler tekil bileşen güncellemeleri olarak gelecek — iLO web arayüzünden yüklenerek ya da Linux .scexe/.rpm paketleri olarak kurularak. Bu donanım için tek büyük paket dönemi kapandı.
Yanımda götürdüklerim
- Tek belirti, çok neden. Görünen tek bir arıza, birden fazla bağımsız problemin üstünde oturabilir. Bir katmanı düzeltip yeni bir hata görmek başarısızlık değil — ilerlemedir.
- systemd'yi kelimesi kelimesine okuyun. "Timed out waiting for device X" suçluyu adlandırır. Teori kurmadan önce ona inanın.
- ~90 saniye bir imzadır. Bir buçuk dakikalık boşluk ya da takılma neredeyse her zaman systemd'nin bir aygıtı veya bağlama noktasını beklediği anlamına gelir. fstab'a bakın.
- Çıkarılabilir ve ağ bağlama noktalarına daima
nofailekleyin. Eksik bir USB bellek asla bir açılışa mal olmamalı. - TLS arızalarında verify hata numarasını alın.
openssl s_client'ınnum=2'si doğrudan eksik ara sertifikayı gösterdi. Çantadaki en hızlı alet. - TLS'de "Windows'ta çalışıyor, Linux'ta başarısız" neredeyse her zaman eksik zincir artı Windows'un onu örten AIA takibi demektir.
- Dağıtım yamalı araçlar upstream araçlar değildir. SUSE'nin pip'i sistem deposuna güveniyordu; yükseltme sessizce güvenmez oldu.
- Ortam değişkenleri sessizce başarısız olur. Önemli her şey için yapılandırma dosyalarını ya da kod içi varsayılanları tercih edin.
- "Forced" firmware'i asla körlemesine dağıtmayın. Ömrünü tamamlamış donanımda son resmî paket, kurulu sürümlerden eski olabilir.
Toplam hasar: bayat bir fstab satırı, tembel bir proxy, aceleci bir pip yükseltmesi, paketlenmiş bir güven deposu, eksik bir harf — ve geri gitmek isteyen bir firmware paketi. Hiçbiri egzotik değil. Hepsi tek bir makinede üst üste, tek bir belirtinin arkasında.
Sunucu o günden beri sıkıcı. Bu meslekte kupa, sıkıcılıktır.