AWS S3 Bucket Oluşturma ve Yapılandırma Rehberi
AWS’ye yeni başlayan birinin yaptığı en yaygın hata şudur: S3 bucket’ı açar, dosyaları yükler ve “tamam, oldu” der. Oysa S3, doğru yapılandırılmadığında hem güvenlik açığı hem de beklenmedik maliyet kaynağı haline gelebilir. Yıllar içinde gördüğüm onlarca vakada, kötü yapılandırılmış bir bucket ya veri sızıntısına yol açmış ya da faturayı patlatmıştır. Bu yazıda S3 bucket oluşturma sürecini baştan sona ele alacağız; sadece “nasıl açılır” değil, “nasıl doğru yapılandırılır” sorusunu da cevaplayacağız.
S3 Nedir ve Neden Önemlidir
Amazon S3 (Simple Storage Service), AWS’nin nesne depolama hizmetidir. Statik web sitesi barındırma, yedekleme, log arşivleme, uygulama medyası, data lake gibi onlarca farklı kullanım senaryosunda karşımıza çıkar. Pratik açıdan bakıldığında S3, sınırsız kapasiteli, yüksek dayanıklılıklı (%99.999999999, yani “11 dokuz”) bir depolama servisidir.
Ancak bu esneklik, beraberinde karmaşıklık da getiriyor. Bucket politikaları, erişim kontrol listeleri, şifreleme ayarları, versiyonlama, lifecycle kuralları… Tüm bunları anlamadan S3’ü production ortamında kullanmak, kilitsiz bir kapıyı internete açmak gibidir.
Ön Hazırlık: AWS CLI Kurulumu ve Kimlik Doğrulama
Önce araçlarımızı hazırlayalım. AWS Console üzerinden her şeyi yapabilirsiniz ama gerçek bir sysadmin CLI’dan yönetir. AWS CLI v2 kurulumu için:
# Linux için AWS CLI v2 kurulumu
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Kurulumu doğrula
aws --version
# aws-cli/2.x.x Python/3.x.x Linux/x.x.x
Kurulumun ardından kimlik bilgilerinizi yapılandırın. IAM konsolundan bir access key oluşturmuş olmanız gerekiyor:
aws configure
# AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name [None]: eu-west-1
# Default output format [None]: json
Birden fazla AWS hesabıyla çalışıyorsanız profil kullanın:
aws configure --profile production
aws configure --profile staging
# Profil ile komut çalıştırma
aws s3 ls --profile production
İlk Bucket’ı Oluşturmak
Bucket ismi seçimi kritiktir. İsimler DNS uyumlu olmalı, küçük harf ve tire kullanılabilir, nokta kullanımı SSL sertifika sorunlarına yol açabileceği için kaçının. En önemlisi: bucket isimleri AWS genelinde benzersiz olmalıdır.
# Temel bucket oluşturma (us-east-1 dışı için --create-bucket-configuration gerekir)
aws s3api create-bucket
--bucket mycompany-app-assets-prod
--region eu-west-1
--create-bucket-configuration LocationConstraint=eu-west-1
# Başarılı yanıt şöyle görünür:
# {
# "Location": "http://mycompany-app-assets-prod.s3.amazonaws.com/"
# }
Bucket oluşturuldu, ama henüz kullanıma hazır değil. Şimdi asıl yapılandırma kısmına geçiyoruz.
Public Access Engelini Yapılandırma
2018 yılında yaşanan büyük veri sızıntılarının büyük çoğunluğu yanlış yapılandırılmış public S3 bucket’larından kaynaklandı. AWS artık varsayılan olarak public erişimi engelliyor, ama bunu açıkça doğrulamak iyi bir alışkanlıktır:
# Public access'i tamamen engelle (production için önerilen)
aws s3api put-public-access-block
--bucket mycompany-app-assets-prod
--public-access-block-configuration
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
Bu dört parametrenin ne anlama geldiğini bilmek önemlidir:
- BlockPublicAcls: Yeni public ACL eklenmesini engeller
- IgnorePublicAcls: Mevcut public ACL’leri yok sayar
- BlockPublicPolicy: Public erişim veren bucket policy eklenmesini engeller
- RestrictPublicBuckets: Mevcut public policy’leri kısıtlar
Eğer statik web sitesi gibi gerçekten public erişime ihtiyacınız varsa, bu ayarları değiştirmek yerine CloudFront kullanmayı değerlendirin. CloudFront üzerinden origin access control ile bucket’ı private tutup içerik dağıtabilirsiniz.
Şifreleme Yapılandırması
S3’te server-side şifreleme artık varsayılan olarak etkin geliyor, ama kendi KMS anahtarınızı kullanmak çok daha iyi bir kontrol sağlar. Özellikle uyumluluk gerektiren (GDPR, HIPAA, PCI-DSS) ortamlarda bu kritiktir:
# Önce KMS anahtarı oluştur (ya da mevcut bir anahtarın ARN'ini kullan)
aws kms create-key
--description "S3 encryption key for mycompany production"
--region eu-west-1
# Anahtar alias'ı ekle
aws kms create-alias
--alias-name alias/mycompany-s3-prod
--target-key-id <key-id-buraya>
# Bucket şifrelemesini yapılandır
aws s3api put-bucket-encryption
--bucket mycompany-app-assets-prod
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "alias/mycompany-s3-prod"
},
"BucketKeyEnabled": true
}]
}'
BucketKeyEnabled parametresine dikkat edin. Bu özelliği aktif etmek, KMS API çağrı sayısını önemli ölçüde azaltır ve maliyetleri düşürür. Her nesne için ayrı KMS çağrısı yerine bucket seviyesinde anahtar türetilir.
Versiyonlamayı Aktif Etme
Versiyonlama, yanlışlıkla silinen ya da üzerine yazılan dosyaları kurtarmanızı sağlar. Özellikle kritik veriler için mutlaka açık olmalıdır:
# Versiyonlamayı aktif et
aws s3api put-bucket-versioning
--bucket mycompany-app-assets-prod
--versioning-configuration Status=Enabled
# Versiyonlama durumunu kontrol et
aws s3api get-bucket-versioning
--bucket mycompany-app-assets-prod
Versiyonlama açıldıktan sonra her dosya güncellemesi yeni bir versiyon oluşturur. Bu hem güvenlik hem de maliyet açısından önemlidir; eski versiyonlar yer kaplar ve maliyet oluşturur. Bu yüzden versiyonlamayla birlikte lifecycle policy de eklemek gerekir (ilerleyen bölümde ele alacağız).
Bucket Policy ile Erişim Kontrolü
Bucket policy, JSON formatında yazılan IAM tabanlı erişim kurallarıdır. Gerçek dünya senaryosuna bakalım: Uygulama sunucularınız EC2’de çalışıyor ve S3’e erişmesi gerekiyor. Doğru yol, sunuculara IAM role atamak ve bucket policy ile bunu sınırlandırmaktır:
# Bucket policy dosyası oluştur
cat > bucket-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAppServerAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/app-server-role"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::mycompany-app-assets-prod",
"arn:aws:s3:::mycompany-app-assets-prod/*"
]
},
{
"Sid": "DenyNonSecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::mycompany-app-assets-prod",
"arn:aws:s3:::mycompany-app-assets-prod/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
EOF
# Policy'yi uygula
aws s3api put-bucket-policy
--bucket mycompany-app-assets-prod
--policy file://bucket-policy.json
İkinci statement’a dikkat edin: DenyNonSecureTransport. Bu kural, HTTP üzerinden (yani şifresiz) yapılan tüm S3 erişimlerini reddeder. HTTPS zorunlu hale gelir. Bu, güvenlik açısından kritik bir önlemdir ve tüm bucket’larınızda bulunmalıdır.
Lifecycle Policy ile Maliyet Yönetimi
S3 maliyetlerini kontrol altına almanın en etkili yolu lifecycle policy’dir. Özellikle log ve yedekleme bucket’larında bu politika olmadan fatura hızla şişer.
Gerçek bir senaryo düşünelim: Uygulama logları S3’te birikiyor. 30 günden eski loglar daha az erişiliyor, 90 günden eski loglar neredeyse hiç açılmıyor, bir yıldan eski loglar sadece uyumluluk için tutuluyor:
cat > lifecycle-policy.json << 'EOF'
{
"Rules": [
{
"ID": "log-tiering-policy",
"Status": "Enabled",
"Filter": {
"Prefix": "logs/"
},
"Transitions": [
{
"Days": 30,
"StorageClass": "STANDARD_IA"
},
{
"Days": 90,
"StorageClass": "GLACIER_IR"
},
{
"Days": 365,
"StorageClass": "DEEP_ARCHIVE"
}
],
"Expiration": {
"Days": 2555
},
"NoncurrentVersionTransitions": [
{
"NoncurrentDays": 30,
"StorageClass": "STANDARD_IA"
}
],
"NoncurrentVersionExpiration": {
"NoncurrentDays": 90
}
}
]
}
EOF
aws s3api put-bucket-lifecycle-configuration
--bucket mycompany-app-assets-prod
--lifecycle-configuration file://lifecycle-policy.json
Depolama sınıflarını kısaca açıklayalım:
- STANDARD: Sık erişilen veriler için, en pahalı depolama
- STANDARD_IA: Seyrek erişilen ancak hızlı erişim gereken veriler, %40-50 daha ucuz
- GLACIER_IR: Arşiv verisi, dakikalar içinde erişim, çok daha ucuz
- DEEP_ARCHIVE: Uzun dönem arşiv, saatler içinde erişim, en ucuz seçenek
NoncurrentVersionExpiration kısmı da kritiktir. Versiyonlama aktifken eski versiyonlar birikerek maliyet yaratır. 90 günden eski eski versiyonları silmek mantıklı bir politikadır.
Access Logging ve Monitoring
Kimin hangi dosyaya ne zaman eriştiğini bilmek hem güvenlik hem de sorun giderme açısından hayat kurtarır. S3 server access logging şöyle aktif edilir:
# Önce loglar için ayrı bir bucket oluştur
aws s3api create-bucket
--bucket mycompany-s3-access-logs
--region eu-west-1
--create-bucket-configuration LocationConstraint=eu-west-1
# Log bucket'ına S3 servisinin yazabilmesi için ACL gerekiyor
aws s3api put-bucket-acl
--bucket mycompany-s3-access-logs
--grant-write URI=http://acs.amazonaws.com/groups/s3/LogDelivery
--grant-read-acp URI=http://acs.amazonaws.com/groups/s3/LogDelivery
# Ana bucket için logging aktif et
aws s3api put-bucket-logging
--bucket mycompany-app-assets-prod
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "mycompany-s3-access-logs",
"TargetPrefix": "mycompany-app-assets-prod/"
}
}'
Log bucket’ına da lifecycle policy ekleyin; aksi takdirde loglar birikerek hem yer hem de maliyet oluşturur.
CloudTrail ile API seviyesinde izleme yapabilirsiniz. Bu, bucket policy değişiklikleri, silme işlemleri gibi yönetimsel aksiyonları takip eder. Prodüksiyon ortamında CloudTrail S3 data events’i de aktif etmenizi öneririm.
CORS Yapılandırması
Web uygulamalarının doğrudan S3’e dosya yüklemesi gerekiyorsa CORS (Cross-Origin Resource Sharing) yapılandırması şarttır. Örneğin kullanıcı bir web formundan profil fotoğrafı yüklüyor ve bunu presigned URL ile doğrudan S3’e gönderiyor:
cat > cors-config.json << 'EOF'
{
"CORSRules": [
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST"],
"AllowedOrigins": [
"https://app.mycompany.com",
"https://www.mycompany.com"
],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]
}
EOF
aws s3api put-bucket-cors
--bucket mycompany-app-assets-prod
--cors-configuration file://cors-config.json
AllowedOrigins kısmında wildcard (*) kullanmayın, sadece gerçekten erişmesi gereken domainleri ekleyin. Bu basit bir güvenlik önlemi ama sıkça atlanıyor.
Presigned URL ile Güvenli Dosya Paylaşımı
S3’ü tamamen private tutup geçici erişim vermek için presigned URL kullanılır. Örneğin bir müşteriye 1 saatliğine indirme linki göndermek istiyorsunuz:
# Belirli bir süre için geçerli indirme URL'i oluştur
aws s3 presign s3://mycompany-app-assets-prod/reports/q3-2024.pdf
--expires-in 3600
--region eu-west-1
# Yükleme için presigned URL (PUT methodu)
aws s3 presign s3://mycompany-app-assets-prod/uploads/user-photo.jpg
--expires-in 300
--http-method PUT
--region eu-west-1
Presigned URL’ler, AWS credentials’larınızı açmadan geçici erişim sağlamanın en temiz yoludur. Uygulama kodunuzda bu URL’leri üretip kullanıcıya iletebilirsiniz; kullanıcı doğrudan S3 ile iletişim kurar, sunucunuz araya girmez.
Bucket’ı Doğrulama ve Sağlık Kontrolü
Tüm yapılandırmayı tamamladıktan sonra her şeyin doğru ayarlandığını kontrol edin:
# Genel bucket bilgilerini kontrol et
aws s3api get-bucket-location --bucket mycompany-app-assets-prod
aws s3api get-bucket-versioning --bucket mycompany-app-assets-prod
aws s3api get-bucket-encryption --bucket mycompany-app-assets-prod
aws s3api get-public-access-block --bucket mycompany-app-assets-prod
aws s3api get-bucket-policy --bucket mycompany-app-assets-prod
# Hepsini tek script ile kontrol et
for check in get-bucket-versioning get-bucket-encryption get-public-access-block; do
echo "=== $check ==="
aws s3api $check --bucket mycompany-app-assets-prod
echo ""
done
AWS Trusted Advisor veya Security Hub kullanıyorsanız, S3 güvenlik bulgularını otomatik olarak izleyebilirsiniz. Ayrıca AWS Config ile S3 yapılandırma değişikliklerini takip etmek ve uyumsuz yapılandırmaları otomatik düzeltmek mümkündür.
Terraform ile Infrastructure as Code
Production ortamında bucket’ları manuel oluşturmak yerine Terraform gibi bir IaC aracı kullanmak çok daha sağlıklıdır. Hem tekrar üretilebilirlik hem de versiyon kontrolü açısından büyük avantaj sağlar:
# main.tf içeriği (terraform dosyası olarak kaydedin)
cat > main.tf << 'EOF'
resource "aws_s3_bucket" "app_assets" {
bucket = "mycompany-app-assets-prod"
}
resource "aws_s3_bucket_versioning" "app_assets" {
bucket = aws_s3_bucket.app_assets.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_public_access_block" "app_assets" {
bucket = aws_s3_bucket.app_assets.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "app_assets" {
bucket = aws_s3_bucket.app_assets.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3_key.arn
}
bucket_key_enabled = true
}
}
EOF
# Terraform komutları
terraform init
terraform plan
terraform apply
Terraform ile yapılandırdığınız her bucket’ı Git’e pushlayın. Bir meslektaşınız yanlışlıkla bir bucket policy’yi değiştirirse, terraform plan ile farkı anında görürsünüz.
Yaygın Hatalar ve Çözümleri
Yıllar içinde karşılaştığım yaygın S3 hatalarını ve çözümlerini paylaşayım:
“Access Denied” hatası alıyorsunuz ama policy doğru görünüyor: IAM policy ile bucket policy’nin her ikisinin de izin vermesi gerekir. Bunlardan biri “Allow” demese bile erişim reddedilir. aws s3api get-bucket-policy ile policy’yi kontrol edin ve IAM policy simulator ile test yapın.
Fatura beklenmedik şekilde yükseldi: Büyük ihtimalle çok sayıda küçük nesne yüklüyorsunuzdur. S3 PUT/LIST/GET operasyonlarının her biri ücretlidir. Logları doğrudan S3’e küçük parçalar halinde yazıyorsanız, önce yerel olarak biriktirip toplu gönderin. Ayrıca S3 Storage Lens ile depolama analizini inceleyin.
Versiyonlama açık ama delete marker nedeniyle dosyalar “kayıp” görünüyor: Versiyonlama aktifken silinen bir nesne aslında silinmez, üstüne delete marker eklenir. aws s3api list-object-versions komutuyla tüm versiyonları görüp kurtarabilirsiniz.
Cross-region replication çalışmıyor: Hem kaynak hem hedef bucket’ta versiyonlama aktif olmalıdır. Ayrıca replication role’ün hem kaynak bucket okuma hem hedef bucket yazma iznine sahip olması gerekir.
Sonuç
S3 kurulumu beş dakika sürer ama doğru yapılandırması ciddi bir zaman ve düşünce gerektirir. Bu yazıda ele aldığımız konuları bir checklist olarak kullanabilirsiniz: public erişim engeli, şifreleme, versiyonlama, bucket policy (özellikle HTTPS zorunluluğu), lifecycle policy ve access logging. Bu altı madde doğru yapılandırıldığında S3 bucket’ınız hem güvenli hem de maliyet açısından optimize olacaktır.
Production ortamına çıkmadan önce mutlaka Terraform veya CloudFormation gibi bir IaC aracı kullanın. Manuel yapılandırmalar zamanla sürüklenir, takip edilemez hale gelir. Altyapınızı kod olarak yönetmek, S3 dahil tüm AWS kaynaklarında hayatınızı kolaylaştıracaktır. Son olarak, Security Hub ve AWS Config kurallarını devreye alarak yapılandırma uyumluluğunu sürekli izlemenizi öneririm; güvenlik tek seferlik bir iş değil, sürekli bir süreçtir.
