AWS S3 Bucket Politikaları ile Erişim Kontrolü

S3 bucket’larınızı “herkese açık” bırakmak, sysadmin kariyerinizin en kötü anlarından birini yaşamanıza neden olabilir. Her yıl onlarca şirket, yanlış yapılandırılmış S3 politikaları yüzünden veri ihlali haberleriyle anılıyor. Bu yazıda, S3 bucket politikalarını sıfırdan anlayıp gerçek dünya senaryolarında nasıl uygulayacağınızı ele alacağız.

S3 Bucket Politikaları Nedir?

AWS S3, depolama erişimini kontrol etmek için birden fazla mekanizma sunar. Bunlar arasında IAM politikaları, bucket ACL’leri ve bucket politikaları bulunur. Bucket politikaları, JSON formatında yazılmış ve doğrudan bucket’a eklenen kaynak tabanlı politikalardır. IAM politikalarından farkı şudur: IAM politikaları bir kullanıcı veya role bağlıyken, bucket politikaları bucket’ın kendisine bağlıdır ve farklı AWS hesaplarından gelen erişimleri de kontrol edebilir.

Bir bucket politikası şu temel bileşenlerden oluşur:

  • Version: Politika dili versiyonu, genellikle “2012-10-17”
  • Statement: Bir veya daha fazla erişim kuralı içeren dizi
  • Effect: “Allow” veya “Deny” – erişime izin ver ya da reddet
  • Principal: Kimin erişeceği (AWS hesabı, IAM kullanıcısı, rol, servis)
  • Action: Hangi S3 işleminin yapılacağı (s3:GetObject, s3:PutObject vb.)
  • Resource: Hangi bucket veya obje üzerinde geçerli olduğu
  • Condition: İsteğe bağlı koşullar (IP kısıtlaması, MFA zorunluluğu vb.)

Temel Bucket Politikası Örnekleri

Herkese Salt Okunur Erişim (Statik Web Sitesi Senaryosu)

Şirketinizin statik web sitesini S3 üzerinden sunuyorsunuz. Bu durumda tüm dünyadan GET isteği kabul etmeniz gerekiyor. Ama dikkatli olun, bu politika bucket’ı gerçekten herkese açar.

aws s3api put-bucket-policy --bucket my-static-website --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-static-website/*"
    }
  ]
}'

Bu politikayı uygulamadan önce bucket’ın “Block Public Access” ayarlarını kontrol etmeyi unutmayın. AWS, 2023 itibarıyla yeni hesaplarda bu ayarları varsayılan olarak açık getiriyor.

Belirli Bir IP Aralığına Erişim Kısıtlaması

Ofis ağınız dışından S3’e erişimi engellemek istiyorsunuz. Muhasebe departmanının finansal raporlarını sakladığı bucket için bu kritik bir gereklilik.

aws s3api put-bucket-policy --bucket finance-reports-bucket --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyOutsideOfficeIP",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::finance-reports-bucket",
        "arn:aws:s3:::finance-reports-bucket/*"
      ],
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": [
            "203.0.113.0/24",
            "198.51.100.0/28"
          ]
        }
      }
    }
  ]
}'

Burada dikkat edilmesi gereken nokta: Deny politikaları her zaman Allow’dan önce gelir. Yani IAM politikanızda bu bucket’a erişim izni versek bile, Deny koşulu tetikleniyorsa erişim reddedilir.

Çapraz Hesap Erişimi (Cross-Account Access)

Gerçek dünyada sık karşılaşılan bir senaryo: Geliştirme hesabınızdaki bir Lambda fonksiyonu, production hesabındaki S3 bucket’ına yazması gerekiyor. Bu yapıyı güvenli şekilde kurmak için önce production bucket’ına cross-account politikası ekliyoruz.

# Production hesabında (hesap ID: 123456789012) çalıştırılır
aws s3api put-bucket-policy --bucket prod-data-bucket --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CrossAccountLambdaAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::987654321098:role/dev-lambda-execution-role"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::prod-data-bucket/dev-uploads/*"
    }
  ]
}'

Geliştirme hesabındaki Lambda rolüne de sts:AssumeRole veya doğrudan S3 erişimi için IAM politikası eklemeniz gerektiğini unutmayın.

Koşul (Condition) Operatörleri ile İleri Seviye Kontrol

HTTPS Zorunluluğu

Verinin şifreli iletilmesini zorlamak için tüm HTTP isteklerini reddedebilirsiniz. Bu özellikle uyumluluk gereksinimleriniz varsa (GDPR, HIPAA gibi) kritik önem taşır.

aws s3api put-bucket-policy --bucket sensitive-data-bucket --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyHTTPAccess",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::sensitive-data-bucket",
        "arn:aws:s3:::sensitive-data-bucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}'

Bu politikayı uyguladıktan sonra aws s3 cp ile test edebilirsiniz. HTTP endpoint’i kullanmaya çalışırsanız “Access Denied” alırsınız.

MFA Silme Koruması

Kritik verilerinizin silinmesini önlemek için MFA zorunluluğu ekleyebilirsiniz. Bu özellikle yedekleme bucket’ları için altın değerinde bir uygulamadır.

aws s3api put-bucket-policy --bucket critical-backups --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDeleteWithoutMFA",
      "Effect": "Deny",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:root"
      },
      "Action": [
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:DeleteBucket"
      ],
      "Resource": [
        "arn:aws:s3:::critical-backups",
        "arn:aws:s3:::critical-backups/*"
      ],
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}'

VPC Endpoint Kısıtlaması

Güvenlik açısından en güçlü yapılardan biri, S3 erişimini yalnızca belirli VPC’ler üzerinden izin vermektir. Bu sayede bucket’a internet üzerinden hiç erişilemez.

aws s3api put-bucket-policy --bucket internal-app-data --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RestrictToVPCEndpoint",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::internal-app-data",
        "arn:aws:s3:::internal-app-data/*"
      ],
      "Condition": {
        "StringNotEquals": {
          "aws:SourceVpce": "vpce-0a1b2c3d4e5f67890"
        }
      }
    }
  ]
}'

VPC endpoint ID’sini aws ec2 describe-vpc-endpoints komutuyla bulabilirsiniz.

Birden Fazla Koşulu Birleştirmek

Gerçek dünya senaryolarında genellikle birden fazla koşulu aynı anda uygulamanız gerekir. Aşağıdaki örnek, bir e-ticaret platformunun ürün resimlerini saklayan bucket için yazılmıştır. Yalnızca belirli IAM rollerine okuma/yazma izni verirken, CloudFront dağıtımına da okuma izni tanınıyor.

aws s3api put-bucket-policy --bucket ecommerce-product-images --policy '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontRead",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::ecommerce-product-images/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
        }
      }
    },
    {
      "Sid": "AllowAppRoleReadWrite",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::123456789012:role/product-upload-role",
          "arn:aws:iam::123456789012:role/product-admin-role"
        ]
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::ecommerce-product-images",
        "arn:aws:s3:::ecommerce-product-images/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "true"
        }
      }
    },
    {
      "Sid": "DenyAllOthers",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::ecommerce-product-images",
        "arn:aws:s3:::ecommerce-product-images/*"
      ],
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalArn": [
            "arn:aws:iam::123456789012:role/product-upload-role",
            "arn:aws:iam::123456789012:role/product-admin-role"
          ]
        },
        "StringNotEquals": {
          "aws:PrincipalServiceName": "cloudfront.amazonaws.com"
        }
      }
    }
  ]
}'

Politika Yönetimi için Pratik Komutlar

Günlük operasyonlarda sık kullandığım bazı komutlar:

# Mevcut bucket politikasını görüntüle
aws s3api get-bucket-policy --bucket my-bucket --output text | python3 -m json.tool

# Bucket politikasını dosyadan uygula
aws s3api put-bucket-policy --bucket my-bucket --policy file://bucket-policy.json

# Bucket politikasını sil
aws s3api delete-bucket-policy --bucket my-bucket

# Politika simülasyonu (erişim var mı yok mu test et)
aws iam simulate-resource-policy 
  --policy-source-arn "arn:aws:iam::123456789012:role/test-role" 
  --action-names "s3:GetObject" 
  --resource-arns "arn:aws:s3:::my-bucket/test-file.txt"

# Tüm bucket'ların public erişim durumunu kontrol et
aws s3api list-buckets --query 'Buckets[].Name' --output text | 
  tr 't' 'n' | 
  xargs -I {} aws s3api get-bucket-policy-status --bucket {} 2>/dev/null

Sık Yapılan Hatalar ve Çözümleri

Hata 1: Resource ARN Formatı

En sık gördüğüm hata, ListBucket işlemi için resource ARN’ını yanlış yazmak. s3:ListBucket için bucket ARN’ı (sonda / olmadan), obje işlemleri için ise / eklenmiş ARN kullanılmalı.

# YANLIS - ListBucket icin obje ARN kullanmak
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket/*"  # Bu calismaz

# DOGRU
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket"

"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"

Hata 2: Implicit Deny’ı Atlama

Bir bucket politikasında yalnızca Allow kuralı yazıp diğer herkesin zaten engellendiğini sanmak tehlikelidir. Aynı AWS hesabındaki IAM kullanıcıları ve roller, eğer IAM politikalarında gerekli izin varsa bucket politikasında Allow olmasa bile erişebilirler. Farklı hesaplardan erişim için ise bucket politikasında explicit Allow şart.

Hata 3: Condition Bloğundaki AND/OR Mantığı

Aynı Condition bloğu içindeki farklı operatörler AND mantığıyla çalışır. Aynı operatör içindeki değerler ise OR mantığıyla değerlendirilir.

# Bu politika: IP hem 203.0.113.0/24 ICINDE hem de
# SecureTransport true OLMALI sartini saglar (AND)
"Condition": {
  "IpAddress": {
    "aws:SourceIp": "203.0.113.0/24"
  },
  "Bool": {
    "aws:SecureTransport": "true"
  }
}

Politika Doğrulama ve Audit

Üretim ortamına geçmeden önce politikalarınızı test etmek için AWS Policy Simulator dışında şu yöntemleri kullanabilirsiniz:

# Bucket politikasının geçerli olup olmadığını kontrol et
aws s3api put-bucket-policy --bucket test-bucket --policy file://policy.json --dry-run

# AWS Config ile uyumluluk kontrolü
aws configservice describe-compliance-by-config-rule 
  --config-rule-names s3-bucket-public-read-prohibited

# CloudTrail ile erişim loglarını sorgula
aws logs filter-log-events 
  --log-group-name "CloudTrail/S3AccessLogs" 
  --filter-pattern '{ ($.eventSource = "s3.amazonaws.com") && ($.errorCode = "AccessDenied") }' 
  --start-time $(date -d "1 hour ago" +%s000)

Gerçek Dünya Güvenlik Checklist’i

Bir bucket’ı production’a almadan önce şu maddeleri mutlaka kontrol ediyorum:

  • Block Public Access: Gerekmedikçe tüm seçenekler açık olmalı
  • Encryption: SSE-S3 veya SSE-KMS aktif mi?
  • Versioning: Kritik veriler için versioning açık mı?
  • Logging: Server access logging başka bir bucket’a yazılıyor mu?
  • HTTPS zorunluluğu: aws:SecureTransport koşulu var mı?
  • Minimum yetki: Sadece gerekli action’lar mı veriliyor?
  • Principal kısıtlaması: Wildcard * Principal gerçekten gerekli mi?
  • Cross-account erişim: Hangi dış hesapların erişimi var, bunlar dokümante edildi mi?
  • Condition kullanımı: IP, VPC veya zaman kısıtlaması gerekiyor mu?
  • Deny kuralları: Hassas operasyonlar (silme, policy değişikliği) için explicit Deny var mı?

Sonuç

S3 bucket politikaları, ilk bakışta sade bir JSON yapısı gibi görünse de detayları anlamadan doğru yazmak oldukça zorlanabilir. Özellikle AND/OR mantığı, implicit vs. explicit deny farkı ve cross-account erişim senaryoları, deneyimli sysadminleri bile zaman zaman tökezletir.

Pratikte önerim şu: Her bucket için amacını ve kimin erişeceğini bir kağıda yazın, sonra politikayı oluşturun. “Bu bucket’a kim erişmeli, nasıl erişmeli, nereden erişmeli?” sorularını yanıtlamadan politika yazmaya başlamayın. Minimum yetki prensibini (least privilege) asla taviz vermeyin. İzin vermek kolaydır, kısıtlamak zordur; ama bir veri ihlali sonrası temizlik yapmak çok daha pahalıya gelir.

AWS Security Hub ve AWS Config kurallarını aktif tutun, düzenli olarak bucket politikalarınızı audit edin ve özellikle yeni ekip üyelerinin bucket oluşturma sürecinde bir review adımı koyun. Teknik kontroller kadar süreç kontrolleri de bu işin ayrılmaz bir parçası.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir