4 puan yazan GN⁺ 2024-06-24 | 1 yorum | WhatsApp'ta paylaş
  • sudo ve doas, setuid ikili dosyalarına ve yetki yükseltmeye dayandığı için, root komutlarının çalıştırılmasını yerel bir sshd’yi Unix domain socket’e bağlayarak üstlendiren bir deney
  • Amaç, yalnızca izin verilen kullanıcıların root komutları çalıştırabilmesini sağlarken, kullanıcı oturumunun tamamında yetki yükseltme kabiliyeti bırakmamak
  • Uygulama; root’a özel SSH anahtar dosyası, erişimi kısıtlanmış /run/sshd/sshd.sock, AuthorizedKeysFile ve PermitRootLogin=yes seçeneklerine sahip ayrı bir sshd instance’ından oluşuyor
  • ssh’te mevcut bir socket’i doğrudan aktaran bir seçenek olmadığı için başlangıçta ProxyCommand ve socat kullanılmış; daha sonra ProxyUseFdpass ve kısa bir Python betiğiyle socket dosya tanımlayıcısı aktarılmış
  • Bu yöntem çalışıyor ve güvenlik işlemleri ağırlıklı olarak OpenSSH’e dayanıyor; ancak günlük kullanım için passfd.py’yi küçük bir çalıştırılabilir dosyaya dönüştürmek ve tüm ssh komutunu bir wrapper ile sarmak daha uygun

sudo ve doasın yapısal kısıtları

  • sudo ve doas, komutları root olarak çalıştırmak için setuid ikili dosyalarına ve yetki yükseltmeye dayanır
  • Bu tasarımın birkaç sınırı vardır
    • Kullanıcı oturumunun tamamının yetki yükseltme gerçekleştirebilme kabiliyetini koruması gerekir
    • Tüm kullanıcı oturumu kısıtlı bir kullanıcı ad alanı içinde çalıştırıldığında işlemez
    • setuid ikili dosyaları sistem genelindeki güvenlik yapılandırmasına kısıtlar getirir
  • s6-sudod, programı yetkili bir sunucu ve yetkisiz bir istemci olarak ayıran bir alternatiftir

Deneyin hedefi

  • Yerelde ssh kullanarak sudo ile aynı rolü yerine getirmek; ancak ilgili sshd instance’ını ağa açmamak
  • Temel koşul iki tanedir
    • Yalnızca izin verilen kullanıcılar root olarak komut çalıştırabilir
    • Yetki yükseltme kullanılmaz

Yerel root girişi için sshd yapılandırması

  • Yalnızca root kimlik doğrulamasında kullanılacak özel bir SSH anahtarı oluşturulur ve genel authorized_keys yerine /root/.ssh/local_keys içinde saklanır
  • Ayrı bir sshd instance’ı Unix domain socket’e bind edilir ve izin verilmeyen kullanıcıların socket’e erişememesi için /run/sshd/ izinleri kısıtlanır
  • Çalıştırma örneği, s6-ipcserver ile /run/sshd/sshd.sock oluşturup sshd’ye şu seçenekleri aktarma biçimindedir
    • AuthorizedKeysFile=/root/.ssh/local_keys
    • PermitRootLogin=yes
  • /etc/ssh/sshd_config değiştirilmez
    • Çünkü ağa bind edilmiş mevcut sshd üzerinde root girişine izin verilmek istenmez
    • Mevcut ayarda PermitRootLogin no korunur

Root hesabı kilidi ve parola ile girişin işlenmesi

  • Root hesabı hiçbir şekilde giriş yapılamayacak biçimde kilitliydi; bu, parola hash’inin başına ! eklenerek yapılmıştı
  • sshd, bu ! önekini hesap kilidi olarak yorumlar ve root girişine izin vermez
  • /etc/passwd içindeki root parola değeri değiştirilerek ! yerine * konur
    • sshd, * karakterini özel bir kilit değeri olarak yorumlamaz
    • * hiçbir parola hash’iyle eşleşmediği için parola ile giriş fiilen devre dışı kalmış olur
  • Ayrıca sshd_config içinde PasswordAuthentication no ayarlıdır; sshd üzerinde parola tabanlı kimlik doğrulamayı kapatan yapılandırma güvenlidir

ssh ile Unix socket’e bağlanmak

  • sshd mevcut bir socket’i aktarabilen -i bayrağına sahiptir, ancak ssh içinde aynı rolü üstlenen bir bayrak yoktur
  • Başlangıçta ProxyCommand ile socat kullanılarak /run/sshd/sshd.sock adresine bağlanılmıştır
  • Bağlantı komutu şu öğelerden oluşur
    • ProxyCommand='socat STDIO UNIX-CONNECT:/run/sshd/sshd.sock'
    • Özel root anahtarını -i .ssh/root-key.pub ile belirtme
    • root@root olarak bağlanma
    • Mevcut dizine geçtikten sonra login shell çalıştırma
  • Kullanılan SSH anahtarı donanıma bağlı anahtar olduğundan, bağlantıyı onaylamak için fiziksel cihaza dokunmak gerekir
  • Anahtarı yalnızca açık onaydan sonra açığa çıkaran ssh-agent, örneğin hissh-agent, bir alternatif olabilir

socat ek yükü ve ProxyUseFdpass

  • socat, ssh’in tüm girdisini okuyup ardından socket’e yazdığı için bağlantı ek yükü fiilen yinelenir
  • ProxyUseFdpass, komutun socket dosya tanımlayıcısını stdout üzerinden ssh’e göndermesini ve ssh’in bu socket üzerinden bağlanmasını sağlar
  • 2016 tarihli OpenSSH ProxyUseFdpass kullanım örneği temel alınarak Python betiği passfd.py yazılmıştır
    • Unix domain socket /run/sshd/sshd.sock adresine bağlanır
    • sendmsg ve SCM_RIGHTS ile dosya tanımlayıcısını aktarır
  • Sonrasında bağlantı komutu, ProxyCommand içinde passfd.py belirtilip ProxyUseFdpass=yes eklenen bir biçime dönüşmüştür
  • nc -FU /run/sshd/sshd.sock da mümkün görünüyor; ancak kılavuz -F seçeneğinin -U ile birlikte kullanılamayacağını açıkça belirtir

Sonuç ve pratik kullanım biçimi

  • Bu teknik çalışır ve hassas güvenlik işlemleri ağırlıklı olarak OpenSSH’e bırakılır
  • OpenSSH’in iyi bir geçmişi vardır ve donanım tabanlı SSH anahtarları dahil çeşitli kimlik doğrulama yöntemleri kullanılabilir
  • Yeni bir host üzerinde kurulum sürecinde karmaşık adımlar yoktur ve ipcserver komutu sistem servis yöneticisi aracılığıyla çalıştırılabilir
  • passfd.py, deneyi yürütmek için yapılmış hızlı bir hack’e daha yakındır
  • Günlük kullanımda aynı rolü üstlenen küçük bir çalıştırılabilir dosya oluşturup /usr/local/bin içine koymak ve tüm ssh komutunu da küçük bir wrapper ile sarmak daha uygundur

1 yorum

 
GN⁺ 2024-06-24
Hacker News yorumları
  • Bu yaklaşıma yönelik en büyük itiraz nedeni karmaşıklığın artması. Bir yapılandırma dosyasını okuyup exec() çağıran tek bir suid ikilisi yerine, root olarak çalışan ve bir UNIX soketini dinleyen bir ikili ile o soketle konuşan bir ikili ortaya çıkıyor; üstelik ikisinin de asimetrik şifrelemeyi işlemesi gerekiyor.
    sudo/doas’a yönelik temel itiraz “tüm kullanıcıların erişebildiği bir suid ikilisi var ve bir hata olursa yetki yükseltmek için kötüye kullanılabilir” ise, aşağıdaki gibi yapılabilir:
    chgrp wheel /usr/bin/sudo
    chmod o-rwx /usr/bin/sudo
    sudo kullanıcılarını wheel grubuna koyarsanız, yalnızca sudo kullanabilen kullanıcılar dosya baytlarını diskten okuyabilir ve çalıştırabilir. UNIX soketini yalnızca wheel kullanıcılarının erişebileceği hâle getiren sshd yaklaşımıyla erişim denetimi açısından güvenlik neredeyse aynıdır, ama karmaşıklık çok daha düşüktür.
    Ayrıca sshd yaklaşımı, sudo gibi root erişimini belirli komutlarla sınırlayamaz; dolayısıyla sudo’nun komut kısıtlamalarını aşan bir hata olsa bile, sshd yaklaşımından daha fazla yetki vermez.
    Paket yöneticisinin /usr/bin/sudo izinlerini bozacağından endişeleniyorsanız, cron ile periyodik olarak düzelttirebilir ya da sudo’yu tamamen kaldırıp kaynaktan farklı bir konuma elle kurabilirsiniz. Elbette bunu yaparsanız bakım ve yükseltmeleri de kendiniz yapmanız gerekir.

    • Kişisel olarak /etc altındaki tüm değişiklikleri etckeeper ile izliyorum. Yazılım kurulumu/yükseltmesi olsun, bir kişinin yaptığı değişiklik olsun kaydediliyor; yeni bir sürüme yükseltirken de yerel değişiklikler için bir yama oluşturup temiz kuruluma uygulayarak çakışmaları 3-way merge ile kontrol edebilmek kullanışlı.
      https://etckeeper.branchable.com/
    • sudo kullanıcılarını wheel grubuna koyup yalnızca sudo kullanabilen kullanıcıların dosyayı okuyup çalıştırabilmesini sağlamak oldukça makul. Bunun neden her yerde varsayılan ayar olmadığını merak ediyorum.
    • Cehaletimi mazur görün. wheel grubunun ne olduğunu ve ne yaptığını açıklayan birini isterim. Bunun karmaşık bir tartışma açabileceğinin farkındayım.
    • /usr/bin/sudoyu immutable yapmak da mümkün olmaz mı? Bu, paket yöneticisinin ona dokunmasını engellemeye yardımcı olur gibi.
    • Root erişimini belirli komutlarla sınırlayamama kısmı için ForcedCommand altyapısı var.
  • Bu, şu anda systemd run0’ın yaptığı şey değil mi? systemd’de run0 adlı yeni bir araç var; aslında tamamen yeni bir araçtan ziyade, uzun süredir var olan systemd-runı run0 adlı sembolik bağlantı üzerinden çağırınca sudo alternatifi gibi davranması söz konusu.
    Temel fark, gerçekten SUID olmaması. Servis yöneticisinden hedef kullanıcının UID’siyle bir komut ya da kabuk çalıştırmasını istiyor, yeni bir PTY ayırıyor ve özgün TTY ile bu PTY arasında veri aktarıyor.
    Başka bir deyişle hedef komut, istemcinin bağlamını devralmıyor; PID 1’den yeni fork edilmiş yalıtılmış bir yürütme bağlamında çalışıyor. $TERM aktarılıyor, ama bu açık bir istisna; yani bir engelleme listesinden çok izin listesine yakın.
    Birçok açıdan run0ın davranışının sudodan çok sshe daha yakın olduğu söylenebilir.
    https://mastodon.social/@pid_eins/112353324518585654

    • systemd neden her şeyi yeniden yapmak zorunda?
  • Bir şeyi mi kaçırıyorum? root olarak ssh ile oturum açmanın, sudo kullanmaktan nasıl daha güvenli olduğunu anlamıyorum. Hep buna izin vermememiz gerektiği öğretildiği için gerçekte ne kadar tehlikeli olduğunu da pek bilmiyorum.
    Burada uzak kullanıcıları engellemeye yönelik bir düşünce var gibi, o yüzden o güvenlik yönünden bahsetmiyorum.
    Sudo’nun 3. sınırlamasıyla ilgili olabilir, ama en azından 1. maddeyle karşılaştırınca bir avantaj göremiyorum.
    Bunun bir deney olduğunu anlıyorum, ama sudo’dan daha az kırılgan değil, daha kırılgan olacakmış gibi geliyor. Açık soket proxy’si ortadaki adam saldırılarına açık görünüyor.
    Yine de eski araçlarla yapılabilecek birkaç teknik öğrendim; yeni bir şey göstermesi hoştu.

    • sudo ikilisi suid root ve ayrıcalıklı bir ikili olduğu için güvenilmeyen kullanıcılara doğrudan açılıyor. sudo içinde bir şey ters giderse kullanıcının tüm ortamı saldırı yüzeyi hâline gelip kötüye kullanılabilir.
      ssh yaklaşımı bir suid ikilisini açığa çıkarmaz, ssh ağ katmanını kullanır. Dolayısıyla ağ üzerinden ssh’e erişmekten daha güvensiz değildir; bu da epey güvenli kabul edilir.
    • sudo’nun büyük avantajlarından biri, sadece sudo bash gibi bir şey başlatmak yerine tek tek komutları sudo ile çalıştırarak denetlenebilirliği artırabilmesidir.
    • root olarak ssh ile oturum açmak derken, yazıda bir Unix soketini dinleyen ek SSH sunucusu kastediliyor. İnternete root oturumu açmayı açtığınızdaki genel tehdit modeli burada geçerli olmayabilir.
    • Bu yaklaşım, teorik bir yapılandırma hatası ya da var olabilecek veya olmayabilecek açıklarla, yeni bir daemon çalıştırmanın getirdiği yeni saldırı yüzeyini karşılaştırıyor.
      O daemon’da da yapılandırma hataları veya açıklar olabilir ve kullanıcı tabanlı birkaç yetkilendirme katmanını tek bir root seviyesine indirger.
      Buna rağmen daha güvenli sayılıyor gibi. Makul bir güvenlik bakış açısından daha güvenli sayılamaz; sadece farklı bir yöntemdir.
    • Bağlantı verilen yazının yaklaşımına şüpheyle bakıyorum, ancak “root olarak uzaktan doğrudan SSH girişi tehlikelidir” söyleminde korku, belirsizlik ve şüpheye kapılınmış bir yan var.
      Gerçekte, uzak SSH’de kullanıcı olarak oturum açıp sonra sudo kullanmaktansa, doğrudan root olarak SSH oturumu açmak kesin anlamda daha güvenlidir.
      user@home, root@servera ssh yaparsa root@server yalnızca user@home ele geçirildiğinde tehlikeye girer.
      Buna karşılık user@home, user@servera ssh yapıp ardından sudo ile root@server olursa, user@home ya da user@serverdan yalnızca biri ele geçirilse bile root tehlikeye girer. Özellikle user@server üzerinde daemon’lar veya cron işleri gibi başka yazılımlar sıkça çalışır.
      Bu yolla bulaşmayı başaran birine bedava root yetki yükseltmesi ve parola yeniden kullanımı nedeniyle sık görülen yatay hareket imkânı verilmemeli.
      Elbette sudo yalnızca izin listesine alınmış komutlar modunda kullanılıyorsa ve parola ya da uzak ana makineden tamamen erişilebilen kimlik bilgileri almıyorsa bu geçerli değildir.
  • Önyükleme sırasında ssh başlamazsa ne olur? Tipik yapılandırmada ağ bağlantısına bağlı olarak başladığını hatırlıyorum. O zaman failsafe konsoldan da giriş yapamazsınız.
    sudo veya su ile karşılaştırınca pratikte ne kazanıldığını bilmiyorum. setuid ikililerinden kaçınmak yerine, root yetkisiyle bir ağ servisi çalıştırmış oluyorsunuz; yalnızca sokete bağlı olsa bile.

    • Benzer bir yapılandırma kullanan biri olarak söyleyeyim: ana masaüstümde bunu yapıyorum. En kötü senaryonun da kötüsü olursa her şeyin yedeği var, sistemi yeniden kurarım.
      SSD ölürse ne olacak? O durumda da failsafe konsola giriş yapamazsınız.
      30 yıldır Linux kullanıyorum; sabit diskin öldüğü durumlar, sshd daemon’unun başlamadığı durumlardan katbekat fazla oldu; oran olarak bakarsak sıfıra bölmek gibi.
      İşletim sisteminin sshd daemon’u rastgele başlamıyorsa, bunu daha kararlı bir işletim sistemine geçme zamanının geldiğine dair bir işaret sayarım.
      sudo veya su’ya göre kazanç, yerel yetki yükseltme exploit’lerinin çok daha zorlaşmasıdır.
    • Linux konsolu, yani yerel ekranda veya uzak KVM’de görünen tty ile seri port ve IPMI SoL üzerindeki ttyS* aygıtları sudo veya su kullanmaz.
      Bu tür konsollar getty gibi programlar ya da pencere yöneticileri kullanır; bunlar root olarak başlatılan, suid olmayan programlardır.
      Konsol girişi için bir root parolası ayarlı tutmak doğrudur.
    • Benim durumumda setuid/sudo’yu denetim günlükleri için kullanıyorum. Artık çok kullanıcılı, çok servisli kutular neredeyse hiç işletmiyorum; çok kiracılı olanların çoğu k8s, bu yüzden ssh yerine kubectl uç noktasını kullanmak yeterli.
      Giriş yapabiliyorsanız root’a setuid yapabilecek şekilde ayarlıyorum. Bu bir k8s kutusuysa bu, platform altyapı ekibinin işidir; üzerindeki servislere erişim ise k8s yetki sağlayıcısı üzerinden olur.
      Platform altyapı ekibi açısından yalnızca metrik ve log gerekiyorsa bunlar zaten kutunun dışındadır; bir iş ya da iş akışı tetiklemek gerekiyorsa pipeline kullanılır.
      Yine de biri giriş yapıp root işi yapıyorsa denetim günlüğü bırakmak isterim.
      Sahip olduğum kutular arasında, giriş yetkisi olan birinin tam root yetkisine sahip olmadığı bir örnek aklıma gelmiyor.
      Elbette servislerin setuid yaptığı durumları anlıyorum; ama servislerde genelde systemd yetkiyi yükseltmek için değil, düşürmek için setuid uygular.
    • Önyükleyiciye erişiminiz varsa hâlâ systems.unit=emergency.target, init=/bin/bash, rd.break=pre-pivot ayarlayabilir veya live CD ortamından önyükleme yapabilirsiniz. Alışıldık acil kurtarma seçeneklerinin hepsi çalışır.
      Daha az ölümcül acil durumlarda ise bu sshd örneğinin ağa bağlı olması için bir neden de görmüyorum.
  • Bu yaklaşımın tüm dezavantajlarını içermemesi biraz üzücü. sudo, hangi grubun hangi komutları çalıştırabileceğini, o komutların hangi argümanları alabileceğini, alt kabuk oluşturulmasına izin verilip verilmeyeceğini vb. denetleyebilir.
    Bu yöntem bu tür ince ayarlı denetimin çoğunu kaybediyor ve sudoers dosyasını düzenlemekten daha zor yönetilen güvenilir anahtarlara dayanıyor.
    sudo’nun yapabildiği şaşırtıcı şeyleri görmek için Sudo Mastery kitabını gerçekten öneririm.

    • SSH de ForceCommand ile bunların bir kısmını yapabilir, ama o kadar esnek ya da hassas olmadığına katılıyorum.
  • Bu, Systemd’nin run0’ına benzer bir fikir: https://news.itsfoss.com/systemd-run0/

    • Ayrıca run0, elde yapılmış geçici bir çözüm değil. Denetlendi ve muhtemelen kendi yaptığınızdan daha iyi olacaktır.
  • ssh’nin sorunlarından biri, süreç oluşturmanın protokolün bir parçası olmaması. Ayrıca uzak protokol olduğu için yerel kaynakları çocuk sürece aktaramazsınız.
    Bu yüzden null ile ayrılmış argüman dizisi geçemez, ek dosya tanıtıcıları aktaramaz veya çalıştırılacak dosyayı belirtemezsiniz.
    Bunun yerine sunucuda ayarlı kabuğa tek bir string gönderirsiniz. Bu da kabuk kaçışlaması gerektirir ve sunucu tarafında hangi kabuğun çalıştığını bilmeniz gerekir.
    SSH’yi doğru dürüst bir sudo alternatifi olarak kullanmak için, eklenti olarak posix_spawn’a yakın bir işlevsellik gerekir.

  • Tamamen katılıyorum. Ben de benzer bir şey yapıyorum ve daha önce HN yorumlarında da anlatmıştım.
    Benim yöntemim biraz farklı. Fiziksel SSH konsolu olarak adanmış bir makine kullanıyorum; bu makine evdeki diğer makinelerden ayrılmış özel bir LAN’da. Yönetilebilir switch değil, sıradan switch; Ethernet kablosu kullanıyor ve trunk yok.
    Giriş yalnızca SSH ile mümkün ve buna Yubikey bağladım.
    Masaüstü PC’nin kendi güvenlik duvarı var ve yalnızca bu SSH konsolunun IP/MAC adresinden gelen SSH trafiğine izin veriyor. Bu sadece ikisinin paylaştığı özel LAN için geçerli; başka bir fiziksel LAN üzerinden masaüstü internete erişebiliyor.
    sshd daemon’u yalnızca açık/özel anahtar ile girişi kabul ediyor, parola ile girişi engelliyor.
    Root gerekiyorsa “SSH konsolu”nu açıyorum. Neredeyse hiçbir şeyi olmayan bir makine olduğu için çok hızlı açılıyor. Giriş yapıp yukarı okla ssh root@... satırını geri çağırıyorum, Enter’a basıyorum ve Yubikey’e dokunuyorum.
    O SSH konsolu ve klavye masanın üzerinde, her zaman elimin altında.
    Özel LAN’da, üstelik diğer özel LAN’lardan fiziksel olarak ayrılmış bir yerdeki iptables/nftables + sshd’nin sudo ikilisi veya su’dan daha güvenli mi daha güvensiz mi olduğuna herkes kendi karar versin.
    “Neden?” diye sorarsanız “çünkü yapabiliyorum” derim. O kadar uzun zaman önce kurdum ki ne zaman yaptığımı bile hatırlamıyorum. Muhtemelen yaklaşık 2 yıl önce bu fikirle oynamaya başladım ve o zamandan beri kullanıyorum. Hiç sorun olmadı.

    • Kurulumunuz birebir aynı olmasa da bastion host kavramına benziyor.
  • Bu sorun için zarif bir çözüm. Kullanıcılara çocuk muamelesi yapmak gerekmez, ama aynı zamanda makul varsayılanlarla olası hatalardan kaçınmak gerekir.
    Root gerekiyorsa konsoldan root olarak giriş yapılabilir; bu yüzden su bile gerekmez diye düşünüyorum. Bu yöntem, konsol tty’sinden root olarak giriş yapmaya olabildiğince yakın.

    • Root gerekiyorsa konsoldan root olarak giriş yapın yaklaşımının iki sorunu var.
      Birincisi, sudo’nun aksine tüm kullanıcıların root parolasını bilmesi gerekir.
      İkincisi, herkes doğrudan root olarak giriş yaparsa gerçekte kimin giriş yapıp ne yaptığını denetlemenin yolu yoktur.
  • 10 yıl önce benzer bir şey denemiştim. UNIX soket kısmı yoktu; yalnızca localhost üzerinde dinleyen ayrı bir sshd çalıştırmıştım ve SCM_RIGHTS ile uğraşmam da gerekmemişti
    Özellikle iyi ya da kötü bir sonuç olmadı; sadece ilgimi kaybettiğim için ayarı bir sonraki makineye taşımadım