AWS CloudFront ve S3 Veri Güvenliği 1: Signed URLs

Ugur Cem Ozturk
Devops Türkiye☁️ 🐧 🐳 ☸️
3 min readAug 17, 2020
Credits: https://undraw.co/illustrations

AWS S3'deki dosyalarınızı bir CDN(AWS CloudFront) üzerinden paylaşırken, iş mantığı, dosya URL’lerinin public ama herkes tarafından erişilemez olmasını gerektirebilir, ironi değil. AWS CloudFront, bunun için CloudFront Signed URL adını verdiği, imza ve expire temelli bir güvenlik yöntemi sunuyor.

Hikaye kısa ve öz olsun;

  • Bir S3 bucketında fotoğraflarım var. Bucket, public olarak erişilemez olmalı. Onun yerine, fotoğraflarım CDN(AWS CloudFront) üzerinden servis edilmeli.
  • CDN’e foto.com domaini üzerinden eriştiğimizi varsayalım. Fotoğraflarımın güvenliğini sağlamak için, adresimi bilen herkesin foto.com/mezuniyet.png üzerinden resimlere ulaşmamasını istiyorum.

AWS CloudFormation, Signed URL ile bunu çözüyor. Bu çözüm basitçe, bir lambda ile istenen fotoğraf URL’ni imzalıyor ve yeniden kullanımı kısıtlamak için son kullanım tarihi atıyor.

Çözümün mimarisi basit ve öz olsun;

  1. AWS Lambda: İstenen dosya eğer varsa, URL’ini imzalayıp (Signed URL) geri döndüren servis.
  2. API Gateway: Lambda fonksiyonunun API isteklerini karşılayan, fonksiyonu çağıran/yönlendiren servis.
  3. S3: Resimlerimizin tutulduğu servis.
  4. CloudFront: Resimlere, istek atılan konuma bağlı en yakın CDN’den cevap dönen servis. Not: Lambda ve Cloudfront, Lambda@Edge servisiyle beraber mucizeler çıkarıyor.

Gereksinimler

  1. S3 bucket
  2. Bu bucket’dan dosya erişimi sağlayan CDN/Cloudfront Distribution
  3. Root account ile yaratılmış Cloudfront Key-Pair (Böyle)

Teori

Diyelim ki siz pullara meraklı bir aşçısınız.

Bir kasanın içinde pullar var. Kasa şifreli ve bu şifre bu her gün değişiyor. Kasayı açmak için gerekli olan şifreyi ise, sizi tanıyan bir bahçıvan var, ondan öğrenebilirsiniz. Aşçı, pulları görmek için, her gün bu bahçıvana şifreyi sormalı. Aşçıya bahçıvanın yerini söyleyen ve eşlik eden bir de uşak var.

Uşak = API Gateway — HTTP isteklerini ile lambda fonksiyonunu çalıştıran servis
Bahçıvan = Lambda — Resmin signed URL’ini dönen Lambda fonksiyonu
Aşçı = Client — Uygulamamıza resim alma isteği atan client
Kasa = S3 — Resimleri sakladığımız S3 Bucket
Pullar = S3 Objects — Resimler
Şifre = Signed URL

Teknik Açıklama

myApp.cloudfront.net üzerinden resim.png dosyasıni almak için, CDN dosyanın URI’sine ek olarak query string’de signed URL’e ait parametreler beklemektedir.

Tüm URL’in omurgası: (Resmi dökümandan)

http://myApp.cloudfront.net/
?img=resim.png
Policy=eyANCiAgICEXAMPLEW1lbnQiOiBbeyANCiAgICAgICJSZXNvdXJjZSI6Imh0dHA 6Ly9kemJlc3FtN3VuMW0wLmNsb3VkZnJvbnQubmV0L2RlbW8ucGhwIiwgDQogICAgICAiQ 29uZGl0aW9uIjp7IA0KICAgICAgICAgIklwQWRkcmVzcyI6eyJBV1M6U291cmNlSXAiOiI yMDcuMTcxLjE4MC4xMDEvMzIifSwNCiAgICAgICAgICJEYXRlR3JlYXRlclRoYW4iOnsiQ VdTOkVwb2NoVGltZSI6MTI5Njg2MDE3Nn0sDQogICAgICAgICAiRGF0ZUxlc3NUaGFuIjp 7IkFXUzpFcG9jaFRpbWUiOjEyOTY4NjAyMjZ9DQogICAgICB9IA0KICAgfV0gDQp9DQo
&Signature=nitfHRCrtziwO2HwPfWw~yYDhUF5EwRunQA-j19DzZrvDh6hQ73lDx~ -ar3UocvvRQVw6EkC~GdpGQyyOSKQim-TxAnW7d8F5Kkai9HVx0FIu-5jcQb0UEmat EXAMPLE3ReXySpLSMj0yCd3ZAB4UcBCAqEijkytL6f3fVYNGQI6
&Key-Pair-Id=APKA9ONS7QCOWEXAMPLE

Parça parça:

  1. Base URL=Cloudfront ya da ona bağlı root domain name
  2. Object key=İstenilen S3 nesnesinin adı.
  3. Policy= Base64 kodlanmış, JSON formatında access policy. Bu policyler, imzalanmış bir URL’nin bir kullanıcıya verdiği erişimi kontrol eder: Bu erişim seçenekleri bayağı bir geniş;
    - Dosyanın URL’i,
    - Geçerliliğinin
    sona erme tarihi ve saati,
    - Geçerliiğinin
    başlama tarihi ve saati,
    - Erişime
    izin verilen IP veya IP aralığı.
  4. Signature= Hashlenmiş ve imzalanmış policy.
  5. Key-Pair-ID= Environment variable olarak kaydettiğimiz/kaydedeceğimiz Cloudfront Key-Pair ID.

Uygulama

Çalışır uygulama çeşitli AWS servisleri (Amazon Web Services servisleri, tezat değil mi?..) içeriyor. Kolayca deploy etmeniz için en sona bir Cloudformation template bırakıyorum. Template’i ve açıklamasını , odaktan kopmamak için en sonda anlatacağım.

Flow şu şekilde:

Query string (/?img=resim.png) ile istenen resmi, S3 üzerinden alıp, imzalanmış URL’ini dönen lambda fonksiyonumuz basit ve öz olsun:

Satır satır açıklama:

7- Query string’den istenen resim dosya/nesne adı(diyelim ki resim.png),
8- S3 bucket adı ile dosya adını birleştirip, resmin full path’i,
10- Environment variable olarak tuttuğumuz Cloudfront key-pair ID’si,
11- Dosya olarak tuttuğumuz Cloudfront key-pair private key(production ortamında dosya olarak tutmayın.) Bu private key ile URL imzalanacak ve gelen isteklerdeki URL’in doğruluğu kontrol edilecek,
12- Key-pair ID ve private key ile, AWS’in sunduğu signer objesi,
13- Environment variable olarak tuttuğumuz Cloudfront domain name ve S3 objesi için, expire’da belirtilen son kullanma süresi ile, bir signed Cloudfront URL yarattık.

Sonrasında başarılı ise 200 durum kodu ile URL döndük ya da hata oldu ise 400 ile hata döndük.

Bu kadar basit ve belki de ‘managed’ bir çözüm. İşlem developmentdan ziyade operasyonel bir yaklaşım ile çözülüyor.

Çözümü test edebilmeniz adına, kolayca deploy edebileceğiniz bir Cloudformation template bırakıyorum. Bu template ne içeriyor?

  • API Gateway: İstekleri karşılayıp, Lambda’ya iletmek için.(Varsa authentication, validation, custom response etc..)
  • Lambda: İstenen dosyanın Signed RL’ini yaratmak içim.
  • Origin Access Identity(OAI): S3 Bucket’ına, belirlenen AWS servisleri dışında hiçbir erişim olmaması için(Önemli. Bunun hakkında ayrı bir yazı yazacağım.)
  • S3 Bucket Policy: Bucket erişim için allow/deny policy.
  • Cloudfront Distribution: S3 Bucket için cache server/CDN servisi.

Umarım kısa, öz ve faydalı olmuştur.

Cem.

--

--