Türkçe
  • saas
  • çok kiracılı
  • postgres
  • satır düzeyinde güvenlik
  • rls
  • tetikleyici fonksiyon
  • çok kiracılı mimari
  • tek kiracılı mimari

PostgreSQL ile Çok Kiracılı Uygulama: Basit Bir Gerçek Dünya Örneği Üzerinden Öğrenin

PostgreSQL Satır Düzeyinde Güvenlik (RLS) ve veritabanı rolleri ile çok kiracılı mimari nasıl uygulanır öğrenin. Kiracılar arasında güvenli veri izolasyonu sağlamak için gerçek dünya örneği.

Yijun
Yijun
Developer

Önceki makalelerimizin bazılarında, çok kiracılı kavramını ve ürünler ile gerçek dünya iş senaryolarındaki uygulamalarını derinlemesine inceledik.

Bu makalede, uygulamanız için çok kiracılı bir mimariyi PostgreSQL kullanarak teknik açıdan nasıl uygulayabileceğimizi keşfedeceğiz.

Tek kiracılı mimari nedir?

Tek kiracılı mimari, her müşterinin uygulamanın ve veritabanının kendi özel örneğine sahip olduğu bir yazılım mimarisi türüdür.

Bu mimaride, her kiracının verileri ve kaynakları diğer kiracılardan tamamen izole edilmiştir.

Tek kiracılı yapı

Çok kiracılı mimari nedir?

Çok kiracılı mimari, birden fazla müşterinin (kiracının) aynı uygulama örneğini ve altyapıyı paylaşırken veri izolasyonu sağladığı bir yazılım mimarisi türüdür. Bu mimaride, yazılımın tek bir örneği birden fazla kiracıya hizmet eder ve her kiracının verileri çeşitli izolasyon mekanizmalarıyla başkalarından ayrı tutulur.

Çok kiracılı yapı

Tek kiracılı mimari vs çok kiracılı mimari

Tek kiracılı mimari ve çok kiracılı mimari, veri izolasyonu, kaynak kullanımı, ölçeklenebilirlik, yönetim ve bakım ile güvenlik gibi yönlerden farklılık gösterir.

Tek kiracılı mimaride, her müşteri bağımsız bir veri alanına sahiptir ve bu da kaynak kullanımını düşürürken özelleştirmeyi nispeten daha basit hale getirir. Genellikle, tek kiracılı yazılımlar, belirli müşteri ihtiyaçlarına göre özelleştirilir; örneğin, belirli bir kumaş tedarikçisi için envanter sistemleri veya kişisel bir blog web uygulaması gibi. Bu yazılımlar arasında ortak olan şey, her müşterinin uygulama hizmetinin ayrı bir örneğini kullanmasıdır ve bu da özelleştirmenin belirli gereksinimleri karşılamasını kolaylaştırır.

Çok kiracılı bir mimaride, birden fazla kiracı aynı temel kaynakları paylaşır ve bu da daha yüksek kaynak kullanımına yol açar. Ancak, veri izolasyonu ve güvenliğinin sağlanması kritik önem taşır.

Hizmet sağlayıcıların farklı müşterilere standart hizmetler sunduğu zamanlarda çok kiracılı mimari genellikle tercih edilen yazılım mimarisidir. Bu hizmetlerde özelleştirme seviyesi düşük olur ve tüm müşteriler aynı uygulama örneğini paylaşır. Uygulama güncelleme gerektirdiğinde, tek bir uygulama örneğinin güncellenmesi, tüm müşteriler için uygulamanın güncellenmesi anlamına gelir. Örneğin, CRM (Müşteri İlişkileri Yönetimi) standart bir gereksinimdir. Bu sistemler genellikle aynı hizmeti tüm kiracılara sağlamak için çok kiracılı bir mimari kullanır.

Çok kiracılı mimaride kiracı veri izolasyonu stratejileri

Çok kiracılı bir mimaride, tüm kiracılar aynı temel kaynakları paylaşır ve bu da kiracılar arasındaki kaynakların izolasyonunu önemli hale getirir. Bu izolasyon fiziksel olmak zorunda değildir; sadece kiracılar arasındaki kaynakların birbirine görünmez olmasını sağlamayı gerektirir.

Mimarinin tasarımında, kiracılar arasında çeşitli derecelerde kaynak izolasyonu elde edilebilir:

Izole edilen paylaşılan

Genel olarak, kiracılar arasında paylaşılan kaynak ne kadar fazla olursa, sistem yineleme ve bakım maliyeti o kadar düşer. Tam tersi durumda, paylaşılan kaynak ne kadar az olursa, maliyet o kadar yüksek olur.

Gerçek bir dünyada örnekle çok kiracılı uygulamaya başlama

Bu makalede, bir CRM sistemini örnek alarak basit ama pratik bir çok kiracılı mimariyi tanıtacağız.

Tüm kiracıların aynı standart hizmetleri kullandığını fark ettik, bu nedenle tüm kiracıların aynı temel kaynakları paylaşmasına karar verdik ve PostgreSQL'in Satır Düzeyinde Güvenlik özelliğini kullanarak farklı kiracılar arasında veri izolasyonu uygulayacağız.

Ayrıca, her kiracının izinlerini daha iyi yönetebilmek için ayrı bir veri bağlantısı oluşturacağız.

Ardından, bu çok kiracılı mimariyi nasıl uygulayacağımızı ele alacağız.

PostgreSQL ile çok kiracılı yapı nasıl uygulanır

Tüm kaynaklar için kiracı tanımlayıcı ekleme

Bir CRM sisteminde, birçok kaynağımız olacak ve bunlar farklı tablolarda depolanacaktır. Örneğin, müşteri bilgileri customers tablosunda depolanır.

Çok kiracılığı uygulamadan önce, bu kaynaklar herhangi bir kiracı ile ilişkilendirilmez:

Farklı kaynaklara sahip kiracıları ayırt etmek için bir tenants tablosu tanıtarak kiracı bilgilerini depolayacağız (burada db_user ve db_user_password her kiracı için veritabanı bağlantı bilgilerini depolamak için kullanılır, bağlı olarak detaylandırılacaktır). Ek olarak, her kaynağa hangi kiracıya ait olduğunu belirlemek için bir tenant_id alanı ekleyeceğiz:

Artık her kaynak bir tenant_id ile ilişkilendirilmiştir, teorik olarak, her kiracı için kaynaklara erişimi sınırlamak için tüm sorgulara bir where cümlesi ekleyebiliriz:

İlk bakışta, bu basit ve uygulanabilir görünüyor. Ancak, aşağıdaki sorunlar ortaya çıkacaktır:

  • Hemen hemen her sorgu bu where cümlesini içerecek ve bu da kodun dağınık hale gelmesine ve bakımı zorlaştırmasına neden olacak, özellikle karmaşık birleştirme ifadeleri yazarken.
  • Kod tabanına yeni katılanlar bu where cümlesini eklemeyi kolaylıkla unutabilirler.
  • Farklı kiracılar arasındaki veri gerçekten izole değildir çünkü her kiracı hala diğer kiracılara ait verilere erişim yetkisine sahiptir.

Bu nedenle, bu yaklaşımı benimsemeyeceğiz. Bunun yerine, PostgreSQL'in Satır Düzeyinde Güvenliğini bu endişeleri ele almak için kullanacağız. Ancak, devam etmeden önce, bu paylaşılan verilere erişim sağlamak için her kiracıya özel bir veritabanı hesabı oluşturacağız.

Kiracılar için veritabanı rolleri oluşturma

Veritabanı rollerini her bir veritabanına bağlanabilen kullanıcıya atamak iyi bir uygulamadır. Bu, her kullanıcının veritabanına erişimini daha iyi kontrol etmeyi sağlar, farklı kullanıcılar arasındaki işlemlerin izolasyonunu kolaylaştırır ve sistem kararlılığını ve güvenliğini artırır.

Tüm kiracıların aynı veritabanı işlem izinlerine sahip olduğu için, bu izinleri yönetmek için bir temel rol oluşturabiliriz:

Ardından, her kiracı rolünü farklılaştırmak için, oluşturulan her kiracıya temel rolden türemiş bir rol atanır:

Sonraki adımda, her kiracının veritabanı bağlantı bilgileri tenants tablosunda saklanacaktır:

iddb_userdb_user_password
x2euiccrm_tenant_x2euicpa55w0rd

Bu mekanizma her kiracıya kendi veritabanı rolünü sağlar ve bu roller crm_tenant rolüne verilen izinleri paylaşır.

Kiracılar için izin kapsamını crm_tenant rolü ile tanımlayabiliriz:

  • Kiracılar, tüm CRM sistem kaynağı tablolarına CRUD erişimine sahip olmalıdır.
  • CRM sistemi kaynaklarıyla ilgili olmayan tablolar kiracılara görünmez olmalıdır (yalnızca systems tablosu varsayılarak).
  • Kiracılar, tenants tablosunu değiştirememelidir ve yalnızca id ve db_user alanları sorgulama yaparken kendi kiracı kimliklerini almak için görünür olmalıdır.

Kiracılar için roller ayarlandıktan sonra, bir kiracı hizmete erişim talep ettiğinde, o kiracıya karşılık gelen veritabanı rolü ile etkileşimde bulunabiliriz:

PostgreSQL Satır Düzeyinde Güvenliği kullanarak kiracı verilerini güvenli hale getirme

Şu ana kadar, kiracılar için karşılık gelen veritabanı rolleri oluşturduk, ancak bu, kiracılar arasında veri erişimini sınırlamaz. Gelecek adımda, PostgreSQL'in Row-Level Security (Satır Düzeyinde Güvenlik) özelliğini kullanarak her kiracının yalnızca kendi verilerine erişimini sınırlayacağız.

PostgreSQL'de, tablolar üzerinde sorgularla hangi satırların erişilebileceğini veya veri işleme komutlarıyla hangi satırların değiştirilebileceğini kontrol eden satır güvenliği politikaları olabilir. Bu özellik RLS (Satır Düzeyinde Güvenlik) olarak da bilinir.

Varsayılan olarak, tablolarda satır güvenliği politikaları yoktur. RLS'yi kullanabilmek için, tablo için etkinleştirmeniz ve tablo her erişildiğinde yürütülen güvenlik politikaları oluşturmanız gerekir.

CRM sistemindeki customers tablosunu ele alarak, RLS'yi etkinleştirecek ve her kiracının yalnızca kendi müşterilerinin verilerine erişimine izin veren bir güvenlik politikası oluşturacağız:

Güvenlik politikasını oluşturduğumuz ifadede:

  • for all (isteğe bağlı) bu erişim politikasının tablo üzerindeki select, insert, update ve delete işlemleri için kullanılacağını belirtir. Belirli işlemler için erişim politikası belirtmek için for ardından komut anahtar kelimesi ile kullanabilirsiniz.
  • to crm_tenant bu politikanın veritabanı rolü crm_tenant olan kullanıcılara, yani tüm kiracılar için geçerli olduğunu belirtir.
  • as restrictive politikanın uygulama modunu belirtir, erişimin katı bir şekilde sınırlanması gerektiğini belirtir. Varsayılan olarak, bir tablonun birden fazla politikası olabilir ve birden fazla permissive politikası OR ilişkisiyle birleştirilir. Bu senaryoda, bu politikayı restrictive olarak belirtiyoruz çünkü bu politika kontrolünün CRM sistem kiracılarına ait kullanıcılar için zorunlu olmasını istiyoruz.
  • using ifadesi, gerçek erişim şartlarını tanımlar ve mevcut sorgulayan veritabanı kullanıcısının yalnızca kendi kiracısına ait verileri görmesiyle kısıtlar. Bu kısıtlama bir komut (select, update veya delete) tarafından seçilen satırlara uygulanır.
  • with check ifadesi, veri satırlarını (insert veya update) değiştirme sırasında gerekli olan kısıtlamayı tanımlar ve kiracıların yalnızca kendileri için kayıt ekleyip güncelleme yapabilmelerini garanti eder.

Kiracıların erişimini kaynak tablolarımıza RLS kullanarak kısıtlamak birkaç fayda sağlar:

  • Bu politika, tüm sorgu işlemlerine (select, update veya delete) etkin bir şekilde where tenant_id = (select id from tenants where db_user = current_user) ekler. Örneğin, select * from customers komutunu yürüttüğünüzde, bu eşdeğer bir şekilde select * from customers where tenant_id = (select id from tenants where db_user = current_user) komutunu yürütmeye eşdeğerdir. Bu, uygulama kodunda açıkça where koşulları eklemeyi gereksiz hale getirir, böylece kodun sadeliğini artırır ve hata olasılığını azaltır.
  • Veritabanı düzeyinde farklı kiracılar arasında veri erişim izinlerini merkezi olarak kontrol eder, bu da uygulama içindeki güvenlik açıklarının veya tutarsızlıkların riskini azaltır ve sistem güvenliğini artırır.

Ancak, dikkate alınması gereken bazı noktalar vardır:

  • RLS politikaları her veri satırı için çalışır. RLS politikası içindeki sorgu koşulları çok karmaşıksa, sistem performansını önemli ölçüde etkileyebilir. Neyse ki, kiracı veri kontrol sorgumuz yeterince basit ve performansı etkilemeyecektir. İleride başka işlevler uygulamayı planlıyorsanız, RLS performansını optimize etmek için Supabase'in Satır Düzeyinde Güvenlik performans önerilerine başvurabilirsiniz.
  • RLS politikaları insert işlemleri sırasında tenant_id alanını otomatik olarak doldurmaz. Sadece kiracıların kendi verilerini ekleme olanağıyla kısıtlar. Bu, veri eklerken hala kiracı kimliğini sağlamamız gerektiği anlamına gelir, bu da sorgulama süreciyle uyumsuzdur ve geliştirme esnasında karışıklığa yol açabilir, hata yapma olasılığını artırır (bu duruma sonraki adımlar çözüme kavuşturacaktır).

customers tablosunun yanı sıra, tüm CRM sistem kaynağı tablolarına aynı işlemleri uygulamamız gerekecek (bu süreç biraz zahmetli olabilir, ancak tablo başlatma sırasında bir program yazarak bunu yapılandırabiliriz), böylece farklı kiracıların verilerini izole ederiz.

Veri ekleme için tetikleyici fonksiyon oluşturma

Daha önce bahsedildiği gibi, RLS (Satır Düzeyinde Güvenlik) sayesinde kiracı kimliğinin varlığından endişe etmeden sorgular yapabiliriz, çünkü bu süreç veritabanı tarafından otomatik olarak ele alınır. Ancak, insert işlemleri için, hala uygun tenant_id belirlememiz gerekmektedir.

Veri eklerken RLS'nin sunduğu kolaylığı elde etmek için, veritabanının veri ekleme sırasında tenant_id alanını otomatik olarak doldurmasını sağlamamız gerekiyor.

Bu, belirgin bir fayda sağlar: uygulama geliştirme seviyesinde, hangi kiracıya ait olduğunu düşünmemize gerek kalmaz ve bu da hata yapma olasılığını azaltır ve çok kiracılı uygulamalar geliştirirken zihinsel yükümüzü hafifletir.

Neyse ki, PostgreSQL güçlü tetikleyici işlevselliği sunar.

Tetikleyiciler, tablolara bağlı özel işlevlerdir ve tablo üzerinde yapılan belirli işlemler (örneğin insert, update veya delete) yapıldığında otomatik olarak belirli eylemleri (satır düzeyinde veya ifade düzeyinde) gerçekleştirir. Tetikleyicilerle, belirli veritabanı işlemlerinden önce veya sonra özel mantığı çalıştırabiliriz ve böylece hedefimize kolayca ulaşabiliriz.

Öncelikle, her veri ekleme işleminden önce çalışacak set_tenant_id tetikleyici fonksiyonunu oluşturacağız:

Ardından, bu tetikleyici fonksiyonunu customers tablosu için ekleme işlemleriyle ilişkilendireceğiz (bir tabloya RLS etkinleştirme gibi, bu tetikleyici fonksiyon ilgili tüm tablolarla ilişkilendirilmelidir):

Bu tetikleyici, eklenen verilerin doğru tenant_id içermesini sağlar. Eğer yeni veri zaten bir tenant_id içeriyorsa, tetikleyici fonksiyonu hiçbir şey yapmaz. Aksi takdirde, mevcut kullanıcının tenants tablosundaki bilgisine dayanarak tenant_id alanını otomatik olarak doldurur.

Bu şekilde, kiracılar veri eklerken tenant_id'nin veritabanı seviyesinde otomatik olarak ele alınmasını sağlamış oluyoruz.

Özet

Bu makalede, bir CRM sistemini örnek alarak, PostgreSQL veritabanını kullanarak pratik bir çözümü göstererek çok kiracılı mimarinin uygulamasına daldık.

Kiracılar arasında veri izolasyonunu sağlamak için veritabanı rolü yönetimi, erişim kontrolü ve PostgreSQL'in Satır Düzeyinde Güvenlik özelliğini tartıştık. Ayrıca, farklı kiracıları yönetirken geliştiricilerin bilişsel yükünü azaltmak için tetikleyici fonksiyonlar kullandık.

Bu makale burada sona eriyor. Çok kiracılı uygulamanızı kullanıcı erişim yönetimi ile daha da geliştirmek istiyorsanız, daha fazla içgörü için Logto organizasyonları ile çok kiracılı bir uygulamaya başlamak için kolay bir kılavuz başlığına başvurabilirsiniz.