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.
Ö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.
Ç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.
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:
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:
id | db_user | db_user_password |
---|---|---|
x2euic | crm_tenant_x2euic | pa55w0rd |
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ızcaid
vedb_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 üzerindekiselect
,insert
,update
vedelete
işlemleri için kullanılacağını belirtir. Belirli işlemler için erişim politikası belirtmek içinfor
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 fazlapermissive
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
veyadelete
) tarafından seçilen satırlara uygulanır.with check
ifadesi, veri satırlarını (insert
veyaupdate
) 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
veyadelete
) etkin bir şekildewhere 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 şekildeselect * 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çawhere
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ındatenant_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.