Eğer ntfy ile tanışmadıysan, anlatımı tek cümle: bir konu adı seç, ona POST et, abone ol, bitti. Hesap yok, kurulum yok, SDK yok. Bu paragrafı okuma süresinde bir shell script'ten telefonuna bildirim gönderebilirsin. Onu sürekli kullanıyorum, ve bu zahmetsizlik tam da meselenin kendisi. Ama basitliğin içinde gizli bir tuzak var, ve onu bir kez görünce artık göremez olamıyorsun: verini koruyan tek şey konu adının ta kendisi. Adı bilen ya da tahmin eden herkes konuyu okuyup yazabilir. "Bu kanal şu iki tarafa ait, başka kimseye değil" diye bir kavram yok. Uyarılar için bu sorun değil. Bir yabancının okumasından rahatsız olacağın herhangi bir şey için ise sorun. doublethink, ntfy'ın kolaylığını korumaya ve ntfy'ın bilerek dışarıda bıraktığı tam o şeyi eklemeye dair girişimim: gerçekten özel kanallar.
İlk karar hiçbir şey yapıp yapmamaktı. Birçok güvenli mesaj aracısı var. Bu yüzden tek satır kod yazmadan önce dürüst ön araştırmayı yaptım: istediğim tam kesişimde, yani ntfy düzeyinde kurulum zahmeti ve gerçek gizlilik noktasında, hâlihazırda oturan bir şey var mı? ntfy'ın kendi erişim kontrolüne, ACL'li MQTT aracılarına, hesapları ve imzalı JWT'leriyle NATS'e, Pusher ve Ably gibi yönetilen hizmetlere, ve uçtan uca şifreli bir altyapı olarak Matrix'e baktım. Bulgu netti ve biraz şaşırtıcıydı: mevcut her kendi-sunucunda-barındırılabilir araçta bu iki özellik birbirleriyle ödünleşiyor. ntfy kadar kolay olanlar varsayılan olarak açık. Gerçek kanal-başına yetkilendirmesi olanlar, özellikle NATS, bunda gerçekten iyi ama ntfy'dan çok daha fazla kurulum töreni gerektiriyor. Ve kendi-sunucunda-barındırılabilir olanların neredeyse hiçbiri operatörün mesajları okuyamayacağı şekilde şifrelemiyor. Uçtan uca şifreleme sunan yönetilen hizmetler ise kapalı ve yalnızca barındırılan hizmet. Yani boşluk gerçekti: ntfy kadar kolay ve operatörü özel trafiğinin dışında tutan, kendi sunucunda barındırılabilir bir aracı yoktu. doublethink'in doldurduğu hücre işte bu.
İlk tasarımım yanlıştı, ve nedenini söylemeye değer, çünkü bu yaygın bir hata. Ağır, "besbelli güvenli" makineye uzandım: her taraf kriptografik bir cihaz anahtar çiftine sahip olacak, aracı bağlantıları imzalı bir challenge ile doğrulayacak, ve iki tarafı eşleştirmek tek kullanımlık bir davet kodu artı her iki insanın da bir ortadaki-adam saldırısını elemek için kanal dışında karşılaştırdığı kısa bir doğrulama dizesi içerecekti. Kâğıt üstünde güçlü tasarım buydu. Onu yaptım. Sonra kullanmayı denedim ve tüm önermeye sessizce ihanet ettiğimi fark ettim. Her özel kanal artık döngüde bir operatör olan çok adımlı bir tören gerektiriyordu. ntfy'ı taklit etmeye değer kılan şey, onu dakikalar içinde kurup üstüne bir şeyler yöneltebilmen, gitmişti. Artık ntfy kadar kolay olmayan güvenli bir aracı yapmıştım, yani zaten var olan şeylerden birini.
Bu yüzden onu sildim. Yerine geçen şey neredeyse utandırıcı derecede daha basit, ve bu basitlik özelliğin ta kendisi. Bir özel kanal tek bir yüksek entropili paylaşılan gizli anahtar ile korunur. Tek bir istekle bir kanal oluşturursun ve gizli anahtarı geri alırsın. O gizli anahtarı, zaten sahip olduğun güvenilir herhangi bir yoldan diğer tarafa verirsin. Gizli anahtara sahip olan kanala katılabilir; başka kimse katılamaz. Bu tam olarak ntfy'ın zihinsel modeli, bir şey seç ve paylaş, yalnızca o şey insanca okunabilir bir ad yerine tahmin edilemez bir gizli anahtar, ve işte bu tek değişiklik deliği kapatan şey.
Asıl önemli kısım gizli anahtarın nasıl kullanıldığı, çünkü aracıya üçüncü bir iş yaptırmadan iki farklı işi yapmak zorunda. Gizli anahtardan, her taraf bir anahtar türetme fonksiyonuyla iki bağımsız anahtar türetir. Bir anahtar seni aracıya karşı doğrular: aracı onu saklar ve seni bir challenge-response ile kabul eder, böylece gizli anahtar hiç tel üzerinden geçmeden senin onu tuttuğunu doğrulayabilir. Diğer anahtar mesajlarını şifreler, ve farklı bir etiketle türetilir, böylece ilk anahtara sahip aracı ikincisini hesaplayamaz. Yani aracı kanalda izinli olduğunu bilir ve mesajlarını sadakatle aktarır, ama mesajların kendisi gizli anahtarı paylaşan taraflar arasında uçtan uca şifrelidir. Aracının operatörü, yani ben, kendi sunucumda, onları okuyamam. Bu özellik, operatörün özel trafiği okuyamaması, projenin var olmasının bütün sebebi, ve kriptoda kendi ilk içgüdüme güvenmek yerine onu özenle çapraz kontrol ettirdim.
Bunun ne sağlayıp ne sağlamadığı konusunda kesin olmak istiyorum, çünkü şifrelemeyi olduğundan fazla satmak da kendine has bir tür dürüstsüzlüktür. doublethink yük-kör'dür, meta-veri-kör değil. Aracı mesaj içeriklerini okuyamaz, ama kanal kimliğini, mesaj zamanlamasını ve boyutları görür. Model ayrıca simetriktir: gizli anahtarın iki sahibi de gönderip okuyabilir, yani bir mesaj onu bir sahibin gönderdiğini kanıtlar, hangisinin olduğunu değil. Bunlar bilinçli, belgelenmiş sınırlar, bir dipnotta gizlediğim şeyler değil. "Birbirine zaten güvenen ve aralarında özel bir hat isteyen iki taraf" için, ki gerçek kullanım durumu bu, doğru ödünleşim bunlar.
İşte burada teori olmaktan çıkıyor. doublethink'i kullandığım ilk şey Android derlemelerini debug etmek. Yalnızca gerçek bir cihazda, dengesiz bir ağda, debugger'dan uzakta tekrarlanan bir buga peşinden koşmuş herkes acıyı bilir: adb logcat bağlı değildir, çökme gitmiştir, ve tahmin etmeye indirgenirsin. Bu yüzden geliştirme aşamasındaki APK'ma debug akışını, yapılandırılmış logları, bir şeyin ters gittiği andaki durumu, bir stack trace'i, özel bir doublethink kanalına POST ettiriyorum, ve o kanala laptopumdan abone oluyorum. Telefon, şehrin öteki ucunda mobil veride olabilir. Debug bilgileri ekranımda gerçek zamanlı belirir, uçtan uca şifrelidir, böylece cihazın bulguları ve içerdikleri her ne ise bir relay üstünde açık metin olarak durmaz, ve test derlemesinin tek ihtiyacı içine gömülü kanal gizli anahtarı, koca bir loglama backend'i değil. Bug tekrarlandığında, bir omuz silkme yerine kanıta sahip olurum.
İkinci kullanım araçları ve ajanları birbirine bağlamak. Şimdilerde yaptığım şeylerin çoğu birbiriyle konuşması gereken ayrı süreçler içeriyor: bir yerel ajan ve bir tarayıcı ön yüzü, ilerleme akıtan uzun süren bir görev, işi birbirine devretmek isteyen iki CLI. Kanal çift yönlü, eşzamansız ve akış destekli, böylece bir arka plan işi zaman içinde birçok mesaj çıkarabilir ve diğer taraf onları sonda tek seferde değil, geldikçe alır. Bu, ntfy ile aynı biçim, bir aracı oraya yöneltmenin hoş olmasının nedeni de tam bu, ama gerçek talimatları ve gerçek çıktıları, konu adının verim ile bir yabancı arasındaki tek şey olduğu o tedirgin hissi olmadan içinden geçirebilirim.
Herkese açık olmak bir tur daha dürüstlük dayattı. Bir aracının herkesçe erişilebilir olduğu an, "herkes bir kanal oluşturabilir" çekici bir ntfy özelliği olmaktan çıkar ve internete teslim ettiğin açık bir relay'e dönüşür. Bu yüzden en son sürüm, herkese açık bir örneğin gerçekten ihtiyaç duyduğu gösterişsiz makineyi ekliyor: kalıcılık isteyen herkes için bir API anahtarıyla hafif hesaplar, çevrimdışı kalan bir gegenstellenin yeniden bağlanıp tam olarak kaçırdığını yakalayabilmesi için isteğe bağlı mesaj saklama, eski mesajları eskiten bir yaşam süresi, kimsenin diski dolduramaması için kanal-başına ve hesap-başına depolama kotaları, ve gürültülü yollarda hız sınırları. Kalıcı olmayan kanallar anonim ve törensiz kalır, ntfy yolu; depolama maliyeti olan saklanan kanallar, maliyetin atfedilebilmesi için bir hesap gerektirir. Ayrıca bir ortam değişkeninde ayarlanan bir operatör anahtarı var, onunla önemsediğim bir kanalın limitlerini, kimseye içeriğini okuma yolu vermeden yükseltebiliyorum.
Sonra gerçek bir kullanıcı tasarımın henüz yapamadığı bir şey istedi, ve bu projeyi daha çok sevdiğim bir yere itti. Durum şu: küçük bir webring, ziyaretçilerin bir sayfadan kendi kalıcı topic'lerini oluşturmasına izin vermek istiyordu, aslında bir ziyaretçi defteri. Bundan iki sorun çıktı. Birincisi, asla sona ermeyen bir topic oluşturmak ayrıcalıklı bir eylemdir, internetteki herhangi birinin diskinde kalıcı depolama açmasını istemezsin, yani bir operatörün onayı gerekir. Ama ikincisi, operatör kanalın gizli anahtarını öğrenmemeli, yoksa gizlilik vaadi bir yalan olur. Cevap bir grant bileti. Admin anahtarını tutan operatör, sabit bir ad alanı altında kalıcı ve sınırlı bir kanalı yetkilendiren tek kullanımlık, süreli bir bilet düzenler. Kullanıcı bu bileti, kanalı kendi gizli anahtarıyla oluştururken kullanır. Böylece operatör kalıcı kanalı gizli anahtarı hiç görmeden yetkilendirir ve onu hâlâ okuyamaz; politika bileten gelir, asla istemciden değil, yani sızdırılmış bir bilet kendi yetkilerini genişletemez. Admin anahtarı yalnızca sunucuda yaşar, asla tarayıcıda değil, bu da bu grant'ları aracılayan minik ayrı bir hizmet kurmak demekti, anahtarın bulunmasına izin verilen tek yer.
Bunu yapmak, kullanıcının açıkça sorduğu daha keskin bir soruyu ortaya çıkardı: bir ziyaretçi defteri herkese açıktır, öyleyse neden uçtan uca şifreleme bedelini hiç ödeyesin? Haklıydı. Bu yüzden grant şifrelemeden bağımsız hâle geldi, ve seçim verinin sahibi olan kişiye taşındı, ait olduğu yere. Bileti bir anahtarla kullan, eskisi gibi şifreli kanalı alırsın. Anahtarsız kullan, anahtar töreni olmadan açık ntfy yolundan erişilebilen bir düz metin kalıcı topic alırsın: herkes gönderebilir, herkes okuyabilir, ve yeni bir ziyaretçi tüm geçmişi yakalar. Bu tür, "operatör onu okuyamaz" özelliğini bilerek bırakır, çünkü gerçekten herkese açık veri için bu özellik sana bir şey kazandırmaz ve sana anahtar yönetimi maliyeti çıkarır. Dürüst olan şey, bunu kullanıcının oluşturma anında yaptığı görünür, belgelenmiş bir seçim hâline getirmekti, onun yerine benim seçtiğim bir varsayılan değil. Bir yerlerde depolama motorunu da Redis'e çevirdim, çünkü yük yüksek hacimden çok yüksek verimli, ve sert bir çökmenin ne kadarını kaybedebileceğini (yaklaşık bir saniye) tam olarak yazdım, çünkü "kalıcı" verinin eskimemesi demek olmalı, diskin verdiğinden fazla dayanıklılık sözü vermem demek değil.
O son paragrafta heyecan verici hiçbir şey yok, ve sürekli yeniden öğrendiğim nokta da bu. doublethink'teki ilginç fikir, ntfy kolaylığı artı aracının çözemediği bir paylaşılan gizli anahtar, tek bir cümleye sığar. Onu açık internette gerçek trafiğe güvenebileceğin bir şeye dönüştüren iş, sıkıcı kısımlarda: bir yarış durumuna girmemesi için bir işlem içinde uygulanan kota, depolama sızdırmak yerine süre dolunca silen saklama, beni gerçek bir admin anahtarını test sabiti olarak herkese açık bir depoya commit etmek üzereyken yakalayan gizli anahtar taraması. doublethink açık kaynak, Docker'da ya da tek bir ikili dosya olarak çalışır, ve açık bir garanti reddi taşır, çünkü tek işi başka insanların özel trafiğini tutmak olan bir araç, denetlenmiş ama yanılmaz olmayan tek bir kişinin işi olduğu konusunda dürüst olmalı. Bir yabancının okumasını istemeyeceğin bir şey için ntfy'ın kolaylığını hiç istediysen, doldurmaya çalıştığı boşluk işte bu. Proje sayfasında tamamen tarayıcında çalışan canlı bir demo var: aracının ilettiği tam şifreli baytları, yalnızca gizli anahtarı tutanın geri kazanabildiği düz metnin yanında gösterir, böylece bana inanmak yerine gizliliğin çalıştığını kendi gözünle görebilirsin.
