İçeriğe geç
academia.sh

Ders 09 / 13

Güvenli Kabuk

Anahtar tabanlı kimlik doğrulama ve sunucu sertleştirmesi kurulur; oturumun açılmamasının dokuz aday nedeni yedi sınamayla elenir ve kırk altı satırlık ayrıntılı istemci çıktısının beş, üç satırlık sunucu günlüğünün yedi aday elediği sayılır.

İçindekiler

Önceki konu bir arızanın izini aradı ve izin çoğu zaman geç bakana kalmadığını gösterdi: aralıklı arıza kayıt başlamadan önce başlar, düşürülen paket varsayılan olarak hiçbir satır yazmaz. Bu konu aynı soruyu erişimin kendisine sorar. Bir sunucuya bağlanmanın yolu kapandığında ortada tek bir satır vardır ve o satır genellikle “izin reddedildi” der.

Sorulacak soru şudur: o satır kaç aday nedeni eliyor. Kursun kuralı değişmez — bir sınamanın sayısı verdiği yanıt değil, elediği aday nedendir. Bu derste sınamalar bir araya geldiğinde bile eleyemedikleri bir çift kalacak ve o çifti ayıran şey bir araç değil, arızadan önce yapılmış bir ayar olacak.

Anahtar Çiftinin İki Ucu

Güvenli kabuk (secure shell) bağlantısında iki ayrı doğrulama üst üste durur ve karıştırılmaları tanıyı bozar. Birincisinde istemci sunucuyu doğrular: sunucunun konak anahtarı (host key) ilk bağlantıda kaydedilir, sonraki bağlantılarda kayıtla karşılaştırılır. İkincisinde sunucu kullanıcıyı doğrular: kullanıcının özel anahtarı (private key) istemcide durur, karşılığı olan açık anahtar (public key) sunucudaki yetkili anahtarlar dosyasında bulunur.

Anahtar çifti istemcide üretilir ve özel anahtar oradan hiç çıkmaz; sunucuya yalnız açık anahtar kopyalanır. Bu asimetri sertleştirmenin temelidir: sunucu ele geçse bile orada oturan şey kullanıcının kimliğini üretmeye yetmez. Bu derste gerçek bir anahtar, parmak izi ya da parola yazılmaz; anahtar dizgilerinin bulunduğu yerlerde köşeli ayraç içinde yer tutucu kullanılır.

Sunucu tarafındaki sertleştirme birkaç ayardan oluşur. Aşağıdaki döküm çalıştırılmamıştır; ayarların adlarını ve aldıkları değerleri göstermek için yazılmış bir örnektir:

# ornek dokum , calistirilmamistir
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
LogLevel VERBOSE

Bu satırların her biri aday neden kümesini değiştirir ve ölçümün asıl konusu budur. Parola doğrulamasını kapatmak, parolayla ilgili bütün aday nedenleri listeden çıkarır ve yerine bir tane koyar: anahtarı olmayan kullanıcının geri düşeceği bir yöntem kalmaz. Yetkili anahtarlar dosyasının yerini sabitlemek, dosyanın yanlış yerde aranması adayını çıkarır. Günlük ayrıntı düzeyini yükseltmek hiçbir adayı çıkarmaz, ama ayırt edilebilirliği artırır ve bu dersin son ölçüsü tam olarak onu sayar.

Bir ayar daha vardır ve sık gözden kaçar: yetkili anahtarlar dosyasının ve onu içeren dizinin izinleri fazla genişse sunucu dosyayı okumaz. Dosya yerindedir, içeriği doğrudur, anahtar eşleşecektir; sunucu yine de reddeder. Bu davranış listeye ayrı bir aday neden ekler ve ölçümde en can sıkıcı sonucu üreten aday odur.

Açık Anahtarın Karşı Tarafa Geçmesi

Anahtar çifti üretildikten sonra geriye tek bir iş kalır: açık anahtarın sunucudaki doğru kullanıcının yetkili anahtarlar dosyasına eklenmesi. Bu iş göründüğünden fazla aday neden üretir, çünkü dört ayrı yerde yanlış yapılabilir. Anahtar yanlış kullanıcının dosyasına eklenebilir; dosya doğru olup içindeki satır satır sonu eksikliğiyle bir öncekiyle birleşebilir; dizin sunucuda hiç yoksa ekleme sessizce başka bir yola düşebilir; kopyalama sırasında satır başına bir boşluk girip anahtarı geçersiz kılabilir. Dördünün ortak yanı, işlem çıktısının başarılı dönmesidir.

Bu yüzden anahtarın yerleştirildiği tek başına doğrulanmaz. Doğrulama, işlemin çıktısıyla değil, yeni bir oturum denemesiyle yapılır ve deneme mevcut oturum kapatılmadan yapılır. Bu ayrıntı sonraki derslerde kural yazımında aynen tekrarlanacak: değişikliği doğrulayan şey değişikliği yapan komutun döndürdüğü değer değil, ayrı bir kanaldan yapılan bir denemedir.

Anahtarın ömrü, düzenli olarak değiştirilmesi, merkezi bir kasada saklanması ve sertifikayla imzalanması bu kursun konusu değildir; bunlar Siber Güvenlik müfredatının anahtar yönetimi derslerinde ölçülmüştür. Burada ölçülen tek şey, tek bir makinenin yerel yapılandırmasının bir arıza karşısında ne kadarını ayırt edilebilir kıldığıdır.

Dokuz Aday Neden

Kurgu şudur: kullanıcı bağlanmayı deniyor ve oturum açılmıyor. Sağlam durumun tek bir alanını bozan dokuz aday neden var. Ortak tanımın sekiz ağ arızası bu listede tek bir adaya, ağın erişilemez oluşuna toplanmıştır; bu konu ağın üstündeki katmanı ölçer.

  • EF1 — Aday neden listesi dokuz maddedir ve tüketicidir; kurgu bu dokuz durumun dışına çıkmaz.
  • EF2 — Kâhin acik-anahtar-yetkisiz seçilmiştir: kullanıcının anahtarı vardır, sunucudaki yetkili anahtarlar dosyasında karşılığı yoktur.
  • EF3 — Yedi sınama tanımlıdır. Dördü dışarıdan, oturum açmadan yapılabilir; ikisi sunucuda çalışmayı gerektirir, biri ise sunucu günlüğünün temel düzeyidir.
  • EF4 — Her sınamaya bir satır sayısı verilmiştir. Bu sayı kurgunun parçasıdır, ölçülmüş bir değer değildir; amacı çıktı hacmi ile eleme gücünü yan yana koymaktır.
  • EF5 — Ayrıntılı istemci çıktısı 46, ayrıntılı sunucu günlüğü 3, temel sunucu günlüğü 1, dinleyen listesi 12, yerel anahtar denetimi 4, yöntem listesi 9, varsayılan istemci iletisi 1 satır sayılır.
  • EF6 — Ayrıntılı günlük düzeyi arızadan önce açılmış varsayılır; sonradan açmak geçmiş denemeyi geri getirmez.
  • EF7 — Anahtar izinlerinin genişliği ile açık anahtarın yetkisiz oluşu sunucuda aynı başarısızlık dalına düşer; bu, kurgunun değil gerçek davranışın yansımasıdır.
  • EF8 — Aday kümesi süpürmesi üç listeyle yapılır: sekiz, dokuz ve on aday.
  • EF9 — On adaylı listeye eklenen yontem-kapatildi, sunucunun anahtar yöntemini reddetmesi durumudur ve acik-anahtar-yetkisiz ile aynı yanıt desenini verir.
  • EF10 — Bütün sayılar tam sayımdır; rastgelelik ve tohum yoktur.
"""Guvenli kabuk oturumu acilmiyor: dokuz aday neden , yedi sinama.

Kahin = gercek neden (kurguyu biz sectik). Olculen sey , her sinamanin
kac aday nedeni eledigi ve geriye kac aday biraktigidir.
"""
ADAY = ("ag-erisilemez", "servis-dinlemiyor", "konak-anahtari-degisti",
        "ozel-anahtar-yok", "acik-anahtar-yetkisiz", "anahtar-izni-genis",
        "kullanici-yok", "hesap-kilitli", "parola-yontemi-kapali")
SAGLAM = {"tasima_kuruldu": True, "servis_dinliyor": True,
          "konak_anahtari_uyusuyor": True, "istemci_anahtar_sunar": True,
          "sunucu_kabul": True, "hesap_gecerli": True, "parola_acik": True}
BOZUK = {
    "ag-erisilemez": {"tasima_kuruldu": False},
    "servis-dinlemiyor": {"tasima_kuruldu": False, "servis_dinliyor": False},
    "konak-anahtari-degisti": {"konak_anahtari_uyusuyor": False},
    "ozel-anahtar-yok": {"istemci_anahtar_sunar": False, "sunucu_kabul": False},
    "acik-anahtar-yetkisiz": {"sunucu_kabul": False},
    "anahtar-izni-genis": {"sunucu_kabul": False},
    "kullanici-yok": {"sunucu_kabul": False, "hesap_gecerli": False},
    "hesap-kilitli": {"sunucu_kabul": False, "hesap_gecerli": False},
    "parola-yontemi-kapali": {"parola_acik": False},
}
SATIR = {"istemci-iletisi": 1, "ayrintili-istemci": 46, "yerel-anahtar": 4,
         "yontem-listesi": 9, "dinleyen": 12, "gunluk-temel": 1,
         "gunluk-ayrintili": 3}


def durum(a=None):
    d = dict(SAGLAM)
    if a:
        d.update(BOZUK[a])
    return d


def t_istemci(d):
    if not d["tasima_kuruldu"]:
        return "baglanti-yok"
    if not d["konak_anahtari_uyusuyor"]:
        return "konak-anahtari-uyarisi"
    return "oturum-acildi" if d["sunucu_kabul"] else "izin-reddedildi"


def t_ayrintili(d):
    if not d["tasima_kuruldu"]:
        return "tasima-kurulamadi"
    if not d["konak_anahtari_uyusuyor"]:
        return "konak-anahtari-uyusmuyor"
    if not d["istemci_anahtar_sunar"]:
        return "anahtar-sunulmadi"
    return "oturum-acildi" if d["sunucu_kabul"] else "anahtar-reddedildi"


def t_gunluk_temel(d):
    if not d["tasima_kuruldu"]:
        return "satir-yok"
    return "kabul-edildi" if d["sunucu_kabul"] else "basarisiz"


def t_gunluk_ayrintili(d):
    if not d["tasima_kuruldu"]:
        return "satir-yok"
    if not d["hesap_gecerli"]:
        return "gecersiz-hesap"
    if not d["istemci_anahtar_sunar"]:
        return "yontem-sunulmadi"
    return "kabul-edildi" if d["sunucu_kabul"] else "anahtar-eslesmedi"


SINAMA = {
    "istemci-iletisi": t_istemci, "ayrintili-istemci": t_ayrintili,
    "yerel-anahtar": lambda d: ("anahtar-var" if d["istemci_anahtar_sunar"]
                                else "anahtar-yok"),
    "yontem-listesi": lambda d: ("parola-acik" if d["parola_acik"]
                                 else "parola-kapali"),
    "dinleyen": lambda d: "dinliyor" if d["servis_dinliyor"] else "dinlemiyor",
    "gunluk-temel": t_gunluk_temel, "gunluk-ayrintili": t_gunluk_ayrintili,
}
DISARIDAN = ["istemci-iletisi", "ayrintili-istemci", "yerel-anahtar",
             "yontem-listesi"]
SUNUCUDA = ["dinleyen", "gunluk-ayrintili"]
KAHIN = "acik-anahtar-yetkisiz"


def ele(adaylar, s, yanit):
    return tuple(a for a in adaylar if SINAMA[s](durum(a)) == yanit)


def obek(sinamalar, adaylar=ADAY):
    o = {}
    for a in adaylar:
        o.setdefault(tuple(SINAMA[s](durum(a)) for s in sinamalar), []).append(a)
    return [sorted(v) for v in o.values() if len(v) > 1]


print("aday neden:", len(ADAY), "| sinama:", len(SINAMA), "| kahin:", KAHIN)
print()
print("sinama              satir  ayri yanit  elenen  kalan  satir basina")
for s in SINAMA:
    kalan = len(ele(ADAY, s, SINAMA[s](durum(KAHIN))))
    el = len(ADAY) - kalan
    print(f"  {s:18s} {SATIR[s]:5d} {len({SINAMA[s](durum(a)) for a in ADAY}):11d}"
          f" {el:7d} {kalan:6d} {el / SATIR[s]:13.4f}")
print()
print("arac kumesi                   satir  ayrilamayan obek  en buyuk obek")
for ad, k in (("yalniz istemci iletisi", ["istemci-iletisi"]),
              ("yalniz ayrintili istemci", ["ayrintili-istemci"]),
              ("disaridan erisilebilen 4", DISARIDAN),
              ("disaridan 4 + temel gunluk", DISARIDAN + ["gunluk-temel"]),
              ("sunucuda calisan 2", SUNUCUDA),
              ("yedi sinama", list(SINAMA))):
    o = obek(k)
    print(f"  {ad:28s} {sum(SATIR[s] for s in k):5d} {len(o):17d}"
          f" {max((len(x) for x in o), default=0):14d}")
print()
print("aday kumesi supurmesi (kahin ayni)")
BOZUK["yontem-kapatildi"] = {"sunucu_kabul": False}
for ad, k in (("8 aday: hesap-kilitli cikarildi",
               tuple(a for a in ADAY if a != "hesap-kilitli")),
              ("9 aday: temel liste", ADAY),
              ("10 aday: yontem-kapatildi eklendi",
               ADAY + ("yontem-kapatildi",))):
    kalan = len(ele(k, "gunluk-ayrintili",
                    SINAMA["gunluk-ayrintili"](durum(KAHIN))))
    o = obek(list(SINAMA), k)
    print(f"  {ad:35s} ayrintili gunluk elenen {len(k) - kalan:2d}"
          f" kalan {kalan:2d} | ayrilamayan obek {len(o)}"
          f" en buyuk {max((len(x) for x in o), default=0)}")
print()
print("ayrilamayan obekler (yedi sinama):", obek(list(SINAMA)))
aday neden: 9 | sinama: 7 | kahin: acik-anahtar-yetkisiz

sinama              satir  ayri yanit  elenen  kalan  satir basina
  istemci-iletisi        1           4       4      5        4.0000
  ayrintili-istemci     46           5       5      4        0.1087
  yerel-anahtar          4           2       1      8        0.2500
  yontem-listesi         9           2       1      8        0.1111
  dinleyen              12           2       1      8        0.0833
  gunluk-temel           1           3       4      5        4.0000
  gunluk-ayrintili       3           5       7      2        2.3333

arac kumesi                   satir  ayrilamayan obek  en buyuk obek
  yalniz istemci iletisi           1                 2              5
  yalniz ayrintili istemci        46                 2              4
  disaridan erisilebilen 4        60                 2              4
  disaridan 4 + temel gunluk      61                 2              4
  sunucuda calisan 2              15                 3              2
  yedi sinama                     76                 2              2

aday kumesi supurmesi (kahin ayni)
  8 aday: hesap-kilitli cikarildi     ayrintili gunluk elenen  6 kalan  2 | ayrilamayan obek 1 en buyuk 2
  9 aday: temel liste                 ayrintili gunluk elenen  7 kalan  2 | ayrilamayan obek 2 en buyuk 2
  10 aday: yontem-kapatildi eklendi   ayrintili gunluk elenen  7 kalan  3 | ayrilamayan obek 2 en buyuk 3

ayrilamayan obekler (yedi sinama): [['acik-anahtar-yetkisiz', 'anahtar-izni-genis'], ['hesap-kilitli', 'kullanici-yok']]

Üç Sayı

Kâhin: gerçek neden acik-anahtar-yetkisiz — anahtar sunuluyor, sunucu tanımıyor. Sınama: varsayılan istemci iletisi tek satırda “izin reddedildi” diyor; ayrıntılı istemci çıktısı kırk altı satır basıyor ve “anahtar reddedildi” diyor; ayrıntılı sunucu günlüğü üç satırda “anahtar eşleşmedi” diyor. Elenen aday: varsayılan ileti 4, ayrıntılı istemci 5, ayrıntılı sunucu günlüğü 7.

Ortadaki karşılaştırma kursun ikinci iddiasının bu dersteki biçimidir. Ayrıntılı istemci çıktısı sunucu günlüğünün on beş katı satır üretiyor ve iki aday az eliyor. Satır başına eleme oranı 0,1087’ye karşı 2,3333; aradaki fark yirmi bir kat. Nedeni yapısaldır: ayrıntılı istemci çıktısı el sıkışmanın istemci tarafını ayrıntılandırır, oysa ayrılmayan adayların hepsi sunucu tarafındadır. Ayrıntı düzeyini yükseltmek görülen tarafı büyütür, görülmeyen tarafı açmaz.

Aynı tablonun en zayıf satırı dinleyen. On iki satır basıyor ve yalnız bir aday eliyor. Ortak tanımda dinleyen listesinin eleme gücü de birdi; erişim katmanında da bir. Servisin dinlediğini görmek, bağlanabildiğinizi göstermez ve bu iki cümle arasındaki mesafe bu kursun tamamıdır.

Ayrılamayan İkili

Alt tablo daha sert bir şey söylüyor. Yedi sınamanın hepsi birlikte kullanıldığında bile ayrılamayan iki öbek kalıyor ve ikisi de iki üyeli: acik-anahtar-yetkisiz ile anahtar-izni-genis, hesap-kilitli ile kullanici-yok. Yetmiş altı satır çıktı üretiliyor ve bu dört durum dörde ikiye bölünemiyor.

Birinci çift dersin sertleştirme bölümüne bağlanır. Yetkili anahtarlar dosyasının izinleri genişse sunucu dosyayı okumaz ve reddeder; açık anahtar dosyada yoksa da reddeder. İkisi de sunucunun bakış açısından “sunulan anahtar kabul edilebilir değil” dalıdır. Aradaki farkı gösterecek olan şey bir sınama değil, sunucunun o dalda ne yazdığıdır — ve ne yazacağı günlük ayrıntı düzeyine bağlıdır.

Bu, kursun üçüncü iddiasının erişim katmanındaki biçimidir. Temel düzeyde tutulan günlük üç ayrı yanıt veriyor ve dört aday eliyor; ayrıntılı düzeyde beş yanıt veriyor ve yedi eliyor. Fark üç satırlık bir çıktıda saklı. Ancak düzey arızadan önce yükseltilmiş olmalıdır: sonradan açılan ayrıntı, geçmişte olan denemeyi geri getirmez. Kanıt beklemez.

Tablodaki bir satır daha bunu pekiştiriyor. Dışarıdan erişilebilen dört sınama altmış satır üretiyor ve en büyük ayrılamayan öbeği dört üyeli bırakıyor. Sunucuda çalışan iki sınama on beş satır üretiyor ve en büyük öbeği iki üyeli. Eleme gücünün tamamı sunucudadır; sorun ise sunucuya girmenin kapalı olmasıdır. Bunun cevabı, güvenli kabuğun yanında ikinci bir yol bırakmaktır: kaydedilebilen bir konsol ya da ayrı bir yönetim kanalı. O yol yoksa tanı dışarıdan erişilebilen dört sınamayla sınırlı kalır ve dört üyeli öbek olduğu gibi durur.

Aday Kümesi Süpürmesi

Bu kursta ikinci tohum yoktur, çünkü rastgelelik yoktur. Yerine aday listesi değiştirilir. Alt tablo üç listeyi yan yana koyuyor ve okunması gereken şey elenen sütunu değil, kalan sütunu.

Listeden hesap-kilitli çıkarıldığında ayrıntılı günlüğün elediği aday sayısı yediden altıya iniyor; kalan iki, ayrılamayan öbek sayısı ikiden bire düşüyor. Listeye yontem-kapatildi eklendiğinde elenen sayısı değişmiyor — yine yedi — ama kalan ikiden üçe çıkıyor ve ayrılamayan öbeğin en büyüğü iki üyeden üçe genişliyor. Aynı sınama, aynı yanıt, aynı eleme sayısı; tanı ise kötüleşti.

Buradan çıkan kural şudur: eleme sayısı tek başına bir ölçü değildir. Aday listesi büyüdükçe aynı sınama aynı sayıda adayı eler ve geriye daha fazlasını bırakır. Ölçülmesi gereken şey eleme değil, kalan belirsizliktir. Bu, kursun kuralının ikinci yarısıdır ve sonraki derslerde sıçrama sunucusu, zincirler ve adres dönüştürme aday listesini büyüttükçe tekrar tekrar karşımıza çıkacaktır.

Özet

  • Güvenli kabuk bağlantısında iki doğrulama üst üste durur: istemci konak anahtarıyla sunucuyu, sunucu açık anahtarla kullanıcıyı doğrular. Özel anahtar istemciden çıkmaz.
  • Sertleştirme ayarları aday neden listesini değiştirir: parola doğrulamasını kapatmak parolayla ilgili adayları çıkarır, yetkili anahtarlar dosyasının izinleri yeni bir aday ekler.
  • Dokuz aday neden ve yedi sınamada elenen sayıları eşit değil: ayrıntılı sunucu günlüğü üç satırda 7, ayrıntılı istemci çıktısı kırk altı satırda 5, dinleyen listesi on iki satırda 1 aday eliyor.
  • Yedi sınama birlikte kullanıldığında iki öbek ayrılamıyor; ayrımı yapan şey bir araç değil, arızadan önce yükseltilmiş günlük ayrıntı düzeyidir. Temel düzey 4, ayrıntılı düzey 7 aday eliyor.
  • Aday listesine bir madde eklendiğinde elenen sayısı sabit kalıp kalan 2’den 3’e çıkıyor; ölçülmesi gereken eleme değil, kalan belirsizliktir.

Sonraki Adım

Bu dersin en keskin sonucu, eleme gücünün sunucuda olması ve sunucuya girmenin kapalı olmasıydı. Dışarıdan erişilebilen dört sınama dört üyeli bir öbeği olduğu gibi bırakıyor. Sonraki ders bu tıkanmanın olağan çözümünü ele alır: doğrudan erişilemeyen bir makineye, erişilebilen bir başka makinenin üzerinden gitmek. Sıçrama sunucusu ve tünel, yolu ikiye böler ve tanıyı da ikiye böler — arıza artık iki makineden birinde olabilir. Ölçülecek soru, uçtan uca tek bir denemenin bu genişlemiş aday kümesinden kaçını elediğidir.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat