AWS EC2 Güvenlik Grupları Yapılandırması ve En İyi Uygulamalar

Bulut altyapısında güvenliği doğru kurmak, sonradan “keşke öyle yapsaydım” pişmanlığını önlemenin en etkili yolu. AWS EC2 instance’larınızı koruma altına almak söz konusu olduğunda, Security Group’lar yani Güvenlik Grupları ilk ve en kritik savunma hattınızı oluşturuyor. Stateful firewall olarak çalışan bu yapılar, hem inbound hem outbound trafiği kontrol etmenizi sağlıyor. Ama yanlış yapılandırıldığında da sizi bütün dünyaya açık bırakabiliyor. Bu yazıda, gerçek dünya senaryoları üzerinden AWS Security Group’ları nasıl doğru, güvenli ve yönetilebilir şekilde yapılandıracağınızı ele alacağız.

Security Group Nedir ve Nasıl Çalışır

Security Group, EC2 instance’larınız için sanal bir güvenlik duvarı görevi görür. AWS’in VPC (Virtual Private Cloud) altyapısı içinde, instance seviyesinde çalışır. Ağ ACL’lerinden (NACL) farklı olarak stateful yapıdadır; yani bir bağlantıya izin verdiyseniz, o bağlantının dönüş trafiği otomatik olarak geçer, ayrıca outbound kuralı eklemenize gerek kalmaz.

Birkaç temel kavramı kafaya yerleştirmek gerekiyor:

  • Inbound Rules: Dışarıdan instance’a gelen trafiği kontrol eder
  • Outbound Rules: Instance’dan dışarıya giden trafiği kontrol eder
  • Default davranış: Tüm inbound trafik engellenir, tüm outbound trafiğe izin verilir
  • Whitelist mantığı: Sadece izin verdiğiniz trafik geçer, geri kalanı düşer
  • Birden fazla SG: Bir instance’a maksimum 5 Security Group atanabilir
  • Limit: Her SG’de varsayılan olarak 60 inbound + 60 outbound kural tanımlanabilir

Security Group’lar deny kuralı desteklemez. Sadece allow kuralları yazarsınız. Bir trafiği engellemek istiyorsanız, basitçe o trafik için kural eklemezsiniz. Bu yaklaşım bazen kısıtlayıcı gelse de aslında konfigürasyonu oldukça sade tutar.

AWS CLI ile Security Group Oluşturma

Konsol üzerinden tıklayarak SG oluşturmak işin başlangıcı için iyidir, ama production ortamlarında her şeyi kod olarak yönetmek zorundasınız. AWS CLI ile temel işlemlere bakalım.

Önce bir VPC’nin ID’sini öğrenelim:

aws ec2 describe-vpcs 
  --query 'Vpcs[*].[VpcId,Tags[?Key==`Name`].Value|[0],CidrBlock]' 
  --output text

Yeni bir Security Group oluşturalım:

aws ec2 create-security-group 
  --group-name "web-tier-sg" 
  --description "Web katmani icin Security Group" 
  --vpc-id vpc-0abc123def456789 
  --tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=web-tier-sg},{Key=Environment,Value=production},{Key=Team,Value=platform}]'

Bu komut size bir GroupId döndürecek, örneğin sg-0123456789abcdef0. Bu ID’yi sonraki adımlarda kullanacaksınız.

Şimdi web sunucusu için tipik inbound kurallarını ekleyelim:

# HTTP trafiğine izin ver
aws ec2 authorize-security-group-ingress 
  --group-id sg-0123456789abcdef0 
  --protocol tcp 
  --port 80 
  --cidr 0.0.0.0/0

# HTTPS trafiğine izin ver
aws ec2 authorize-security-group-ingress 
  --group-id sg-0123456789abcdef0 
  --protocol tcp 
  --port 443 
  --cidr 0.0.0.0/0

# SSH erişimi sadece ofis IP'sinden
aws ec2 authorize-security-group-ingress 
  --group-id sg-0123456789abcdef0 
  --protocol tcp 
  --port 22 
  --cidr 203.0.113.0/24 
  --description "Ofis VPN IP blogu"

Katmanlı Mimari için Security Group Tasarımı

Gerçek production ortamlarında genellikle 3 katmanlı bir mimari kullanılır: Web, Application ve Database. Her katman için ayrı Security Group tanımlamak ve katmanlar arası iletişimi SG referansıyla kontrol etmek hem güvenli hem de yönetilebilir bir yaklaşım.

# Database SG oluştur
aws ec2 create-security-group 
  --group-name "db-tier-sg" 
  --description "Veritabani katmani icin Security Group" 
  --vpc-id vpc-0abc123def456789

# Application katmanından DB'ye MySQL erişimi
# Kaynak olarak IP değil, app-tier-sg kullanıyoruz
aws ec2 authorize-security-group-ingress 
  --group-id sg-db1234567890abcde 
  --protocol tcp 
  --port 3306 
  --source-group sg-app1234567890abc 
  --description "App tier MySQL erisimi"

Bu yaklaşımın gücü şurada: IP adresi yerine başka bir Security Group’u kaynak olarak gösterdiğinizde, o SG’ye atanmış herhangi bir instance otomatik olarak bu kurala dahil olur. Yeni bir app sunucusu eklediğinizde, sadece ona app-tier-sg atarsınız, başka bir şey yapmanıza gerek kalmaz.

Aşağıda 3 katmanlı mimarinin tam kural setini görebilirsiniz:

#!/bin/bash
# 3-tier-security-groups.sh
# Üretim ortamı için 3 katmanlı SG yapılandırması

VPC_ID="vpc-0abc123def456789"
OFFICE_CIDR="203.0.113.0/24"

echo "Web tier SG olusturuluyor..."
WEB_SG=$(aws ec2 create-security-group 
  --group-name "prod-web-sg" 
  --description "Production Web Tier" 
  --vpc-id $VPC_ID 
  --query 'GroupId' --output text)

echo "App tier SG olusturuluyor..."
APP_SG=$(aws ec2 create-security-group 
  --group-name "prod-app-sg" 
  --description "Production App Tier" 
  --vpc-id $VPC_ID 
  --query 'GroupId' --output text)

echo "DB tier SG olusturuluyor..."
DB_SG=$(aws ec2 create-security-group 
  --group-name "prod-db-sg" 
  --description "Production DB Tier" 
  --vpc-id $VPC_ID 
  --query 'GroupId' --output text)

# Web SG kuralları
aws ec2 authorize-security-group-ingress 
  --group-id $WEB_SG 
  --ip-permissions 
  '[{"IpProtocol":"tcp","FromPort":80,"ToPort":80,"IpRanges":[{"CidrIp":"0.0.0.0/0","Description":"HTTP public"}]},
    {"IpProtocol":"tcp","FromPort":443,"ToPort":443,"IpRanges":[{"CidrIp":"0.0.0.0/0","Description":"HTTPS public"}]},
    {"IpProtocol":"tcp","FromPort":22,"ToPort":22,"IpRanges":[{"CidrIp":"'"$OFFICE_CIDR"'","Description":"SSH ofis"}]}]'

# App SG: Sadece Web tier'dan gelen trafik
aws ec2 authorize-security-group-ingress 
  --group-id $APP_SG 
  --protocol tcp 
  --port 8080 
  --source-group $WEB_SG 
  --description "Web tier app port erisimi"

# DB SG: Sadece App tier'dan gelen trafik
aws ec2 authorize-security-group-ingress 
  --group-id $DB_SG 
  --protocol tcp 
  --port 5432 
  --source-group $APP_SG 
  --description "App tier PostgreSQL erisimi"

echo "Web SG: $WEB_SG"
echo "App SG: $APP_SG"
echo "DB SG: $DB_SG"
echo "Tamamlandi!"

Mevcut Security Group Kurallarını Sorgulama ve Denetleme

Ortamınızda hangi Security Group’ların ne kurallara sahip olduğunu düzenli olarak kontrol etmek güvenlik hijyeninin temel parçası. Özellikle büyük ekiplerde zaman içinde “kimin eklediği belli olmayan” kurallar birikmeye başlar.

# Belirli bir SG'nin tüm kurallarını göster
aws ec2 describe-security-groups 
  --group-ids sg-0123456789abcdef0 
  --query 'SecurityGroups[*].{Name:GroupName,InboundRules:IpPermissions,OutboundRules:IpPermissionsEgress}' 
  --output json

# TÜM açık 0.0.0.0/0 SSH kurallarını bul - güvenlik açığı taraması
aws ec2 describe-security-groups 
  --filters Name=ip-permission.from-port,Values=22 
             Name=ip-permission.cidr,Values='0.0.0.0/0' 
  --query 'SecurityGroups[*].[GroupId,GroupName,Description]' 
  --output table

# Kullanılmayan Security Group'ları bul
aws ec2 describe-security-groups 
  --query 'SecurityGroups[*].GroupId' 
  --output text | tr 't' 'n' > all_sgs.txt

aws ec2 describe-instances 
  --query 'Reservations[*].Instances[*].SecurityGroups[*].GroupId' 
  --output text | tr 't' 'n' | sort -u > used_sgs.txt

comm -23 <(sort all_sgs.txt) used_sgs.txt

Son komut size herhangi bir instance’a atanmamış Security Group’ların listesini verir. Bu SG’leri temizlemek hem karmaşıklığı azaltır hem de limit sorunlarını önler.

Gerçek Dünya Senaryosu: Bastion Host Yapılandırması

Klasik ama hâlâ geçerli bir senaryo: Private subnet’teki instance’lara erişim için Bastion Host (Jump Server) kullanımı. Bu yapıda SSH erişimi sadece Bastion üzerinden geçecek.

#!/bin/bash
# bastion-sg-setup.sh

VPC_ID="vpc-0abc123def456789"
OFFICE_IP="203.0.113.50/32"  # Tek bir IP, IP bloğu değil

# Bastion SG - Sadece ofis IP'sinden SSH
BASTION_SG=$(aws ec2 create-security-group 
  --group-name "bastion-sg" 
  --description "Bastion Host - Sadece ofis erisimi" 
  --vpc-id $VPC_ID 
  --query 'GroupId' --output text)

aws ec2 authorize-security-group-ingress 
  --group-id $BASTION_SG 
  --protocol tcp 
  --port 22 
  --cidr $OFFICE_IP 
  --description "Ofis statik IP - SSH"

# Private instance SG - Sadece Bastion'dan SSH
PRIVATE_SG=$(aws ec2 create-security-group 
  --group-name "private-instances-sg" 
  --description "Private subnet instance'lari" 
  --vpc-id $VPC_ID 
  --query 'GroupId' --output text)

aws ec2 authorize-security-group-ingress 
  --group-id $PRIVATE_SG 
  --protocol tcp 
  --port 22 
  --source-group $BASTION_SG 
  --description "Sadece Bastion'dan SSH erisimi"

# Outbound: Sadece gerekli trafiğe izin ver
# Önce default outbound'u kaldır
aws ec2 revoke-security-group-egress 
  --group-id $PRIVATE_SG 
  --protocol -1 
  --port -1 
  --cidr 0.0.0.0/0

# Sadece gerekli outbound kuralları ekle
aws ec2 authorize-security-group-egress 
  --group-id $PRIVATE_SG 
  --ip-permissions 
  '[{"IpProtocol":"tcp","FromPort":443,"ToPort":443,"IpRanges":[{"CidrIp":"0.0.0.0/0","Description":"HTTPS updates"}]},
    {"IpProtocol":"tcp","FromPort":53,"ToPort":53,"IpRanges":[{"CidrIp":"0.0.0.0/0","Description":"DNS TCP"}]},
    {"IpProtocol":"udp","FromPort":53,"ToPort":53,"IpRanges":[{"CidrIp":"0.0.0.0/0","Description":"DNS UDP"}]}]'

echo "Bastion SG: $BASTION_SG"
echo "Private SG: $PRIVATE_SG"

Bu yapıda private instance’lardan dışarıya giden trafik de kısıtlandı. Sadece HTTPS ve DNS’e izin verildi. Böylece instance’ın komuta-kontrol sunucusuna bağlanmaya çalıştığı bir senaryoda bile outbound engeli devreye giriyor.

Security Group Kurallarını Güncelleme ve Kaldırma

Yanlış bir kural eklediğinizde veya artık gerekmeyen bir kuralı temizlemeniz gerektiğinde:

# Belirli bir kuralı kaldır
aws ec2 revoke-security-group-ingress 
  --group-id sg-0123456789abcdef0 
  --protocol tcp 
  --port 8080 
  --cidr 0.0.0.0/0

# Kural ID'si ile kaldırma (daha kesin yöntem)
# Önce kural ID'lerini öğren
aws ec2 describe-security-group-rules 
  --filters Name=group-id,Values=sg-0123456789abcdef0 
  --query 'SecurityGroupRules[*].[SecurityGroupRuleId,IsEgress,IpProtocol,FromPort,CidrIpv4]' 
  --output table

# Sonra ID ile sil
aws ec2 revoke-security-group-ingress 
  --group-id sg-0123456789abcdef0 
  --security-group-rule-ids sgr-0abc123def456789a

# Açıklama güncelleme (kuralı silmeden)
aws ec2 update-security-group-rule-descriptions-ingress 
  --group-id sg-0123456789abcdef0 
  --ip-permissions '[{"IpProtocol":"tcp","FromPort":22,"ToPort":22,"IpRanges":[{"CidrIp":"203.0.113.0/24","Description":"IT ekibi VPN - 2024-01 guncellendi"}]}]'

Kural açıklamaları çok küçük ama kritik bir detay. Her kurala kim ekledi, ne zaman, neden sorusunun yanıtını açıklama alanına yazma alışkanlığı edinin. İnsan hafızası güvenilir değil, özellikle 6 ay sonra o kuralın neden orada olduğunu anlamaya çalışırken.

Terraform ile Security Group Yönetimi

Infrastructure as Code kullanmadan Security Group yönetimi bir noktada kontrolden çıkar. Özellikle birden fazla environment (dev, staging, prod) yönetiyorsanız, Terraform kullanmak hem tutarlılığı hem de takip edilebilirliği sağlar.

# main.tf içeriğini kontrol et ve plan çalıştır
terraform plan -var="environment=production" -out=sg-plan.tfplan

# Değişiklikleri uygula
terraform apply sg-plan.tfplan

# Mevcut state'i göster
terraform state show aws_security_group.web_tier

# State ile gerçek altyapıyı karşılaştır
terraform refresh
terraform plan  # Drift varsa burda görünür

Terraform state dosyasını S3’te remote backend olarak tutmak ve DynamoDB ile state locking kullanmak production için zorunlu. Aksi hâlde birden fazla kişinin aynı anda terraform apply çalıştırması felaket olur.

AWS Config ile Uyumluluk Kontrolü

Security Group konfigürasyonlarını sürekli izlemek ve politika ihlallerini otomatik olarak tespit etmek için AWS Config servisini kullanabilirsiniz. Yerleşik kurallar oldukça işe yarar:

# Mevcut AWS Config kurallarını listele
aws configservice describe-config-rules 
  --query 'ConfigRules[?Source.SourceIdentifier==`RESTRICTED_SSH` || Source.SourceIdentifier==`RESTRICTED_INCOMING_TRAFFIC`].[ConfigRuleName,ConfigRuleState]' 
  --output table

# Uyumsuz kaynakları göster
aws configservice get-compliance-details-by-config-rule 
  --config-rule-name restricted-ssh 
  --compliance-types NON_COMPLIANT 
  --query 'EvaluationResults[*].[EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType]' 
  --output table

# Belirli bir SG'nin Config geçmişini kontrol et
aws configservice get-resource-config-history 
  --resource-type AWS::EC2::SecurityGroup 
  --resource-id sg-0123456789abcdef0 
  --limit 5

AWS Config’in RESTRICTED_SSH kuralı, 0.0.0.0/0 veya ::/0 kaynağından gelen SSH trafiğine izin veren Security Group’ları otomatik olarak işaretler. Bu tür managed kuralları aktive etmek, güvenlik ekibinizin her değişikliği manuel inceleme zorunluluğunu ortadan kaldırır.

Sık Yapılan Hatalar ve Kaçınma Yolları

Production ortamında defalarca gördüğüm hatalar ve çözümleri:

0.0.0.0/0 SSH açma: En yaygın ve en tehlikeli hata. Sadece VPN IP’nizi veya Bastion SG’yi kaynak olarak kullanın. Eğer dinamik IP’niz varsa, AWS SSM Session Manager’a geçin. SSH portunu tamamen kapatabilirsiniz.

Tüm ICMP trafiğini engelleme: ICMP’yi tamamen engellemek MTU sorunlarına, path MTU discovery’nin çalışmamasına ve debug süreçlerinin zorlaşmasına neden olur. En azından VPC CIDR’inden gelen ICMP’ye izin verin.

Outbound trafiği görmezden gelme: “Stateful zaten, outbound’a gerek yok” diye düşünmek eksik güvenlik yaklaşımı. Outbound’u da kısıtlayarak exfiltration senaryolarını zorlaştırın.

Aynı SG’yi her yerde kullanma: “Tek SG ile halledelim” yaklaşımı başlangıçta kolay görünür, sonunda kim neye neden erişebiliyor anlamak imkânsızlaşır.

Description alanını boş bırakma: Kuralı kim ekledi, ne zaman, neden? Bu soruların yanıtı documentation’da değil, kural açıklamasında olmalı.

SG limitlerini gözetmeme: Varsayılan limit 60 inbound/outbound kural. Bunu aşmak için AWS’den limit artışı talep edebilirsiniz, ama önce kurallarınızı CIDR prefix list’lere taşıyıp sadeleştirmeyi deneyin.

CloudTrail ile Security Group Değişikliklerini İzleme

Kim hangi Security Group kuralını değiştirdi? Bu sorunun yanıtı CloudTrail’de:

# Son 24 saatteki SG değişikliklerini listele
aws cloudtrail lookup-events 
  --lookup-attributes AttributeKey=EventName,AttributeValue=AuthorizeSecurityGroupIngress 
  --start-time $(date -d '24 hours ago' --iso-8601=seconds) 
  --query 'Events[*].[EventTime,Username,CloudTrailEvent]' 
  --output text | while read time user event; do
    echo "Zaman: $time | Kullanici: $user"
  done

# Belirli bir SG üzerindeki tüm değişiklikleri göster
aws cloudtrail lookup-events 
  --lookup-attributes AttributeKey=ResourceName,AttributeValue=sg-0123456789abcdef0 
  --query 'Events[*].{Time:EventTime,User:Username,Action:EventName}' 
  --output table

Bu sorguyu bir cron job’a bağlayıp kritik SG’lerdeki değişiklikleri Slack veya e-posta ile bildirme sistemi kurabilirsiniz. Hafif bir incident response workflow’u için bile çok değerli.

Prefix List Kullanarak Kural Yönetimini Basitleştirme

Birden fazla Security Group’ta aynı IP bloklarına izin veriyorsanız, AWS Managed Prefix List kullanmak hayat kurtarır. Örneğin CloudFront IP adreslerinin güncel listesini her SG’ye tek tek eklemek yerine:

# AWS managed prefix list'leri göster
aws ec2 describe-managed-prefix-lists 
  --query 'PrefixLists[*].[PrefixListId,PrefixListName,MaxEntries]' 
  --output table

# CloudFront prefix list ID'sini bul
CLOUDFRONT_PL=$(aws ec2 describe-managed-prefix-lists 
  --filters Name=prefix-list-name,Values=com.amazonaws.global.cloudfront.origin-facing 
  --query 'PrefixLists[0].PrefixListId' 
  --output text)

# Prefix list'i SG kuralında kaynak olarak kullan
aws ec2 authorize-security-group-ingress 
  --group-id sg-0123456789abcdef0 
  --ip-permissions "[{"IpProtocol":"tcp","FromPort":443,"ToPort":443,"PrefixListIds":[{"PrefixListId":"$CLOUDFRONT_PL","Description":"CloudFront origin erisimi"}]}]"

Kendi prefix list’inizi de oluşturabilirsiniz. Kurumsal ofis IP bloklarını bir prefix list’te toplayıp tüm SG’lerde bu listeye referans verirseniz, yeni bir ofis açıldığında sadece prefix list’i güncellemeniz yeterli olur.

Sonuç

AWS Security Group’ları, doğru kullanıldığında güçlü ve esnek bir güvenlik katmanı sunar. Ama yapılandırmak bir kez yapılıp unutulan bir iş değil, sürekli dikkat ve bakım gerektiren bir sorumluluk. Özetlemek gerekirse:

  • Her katman için ayrı Security Group tanımlayın, her şeyi tek SG’ye tıkışturmayın
  • Kaynak olarak mümkün olduğunca CIDR yerine başka SG’leri kullanın
  • Her kurala açıklama ekleyin, bu alışkanlığı ekip standardı hâline getirin
  • Outbound trafiği de kısıtlayın, sadece inbound’a odaklanmayın
  • Tüm SG altyapısını Terraform gibi bir IaC aracıyla yönetin
  • AWS Config kurallarıyla sürekli uyumluluk kontrolü yapın
  • CloudTrail üzerinden değişiklikleri takip edin ve alertleme kurun
  • Düzenli aralıklarla kullanılmayan SG’leri ve gereksiz kuralları temizleyin

Güvenlik, bir konfigürasyon değil süreçtir. Security Group’larınızı doğru kurmanın yanı sıra, bu yapıların zaman içinde drift etmesini önleyecek otomasyon ve gözlemleme altyapısını kurmak uzun vadede sizi birçok baş ağrısından kurtaracak.

Bir yanıt yazın

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