1. 기본 환경 구성
이번 실습은 IAM 사용자 계정을 통해 관리 콘솔에 접근하고 액세스 키를 활용해 awscli 도구를 사용합니다.
해당 작업을 수행하지 않았다면 아래 토글을 확장해 작업을 선행하고 본격적인 실습에 들어갑니다.
IAM 사용자 생성 및 액세스 키 생성
1.1. Terraform을 통한 기본 인프라 배포
Terraform을 통한 기본 인프라 배포에 앞서 SSH 키 페어, IAM User Access Key ID, IAM User Secret Access Key를 미리 확인하고 메모해 둡니다.
Terraform으로 기본 인프라 배포
cd cnasg_class_tf/Section10
Bash
복사
# 실습 코드 경로 진입
export TF_VAR_KeyName=[각자 ssh keypair]
export TF_VAR_NickName=[각자 닉네임]
export TF_VAR_MyIamUserAccessKeyID=[각자 iam 사용자의 access key id]
export TF_VAR_MyIamUserSecretAccessKey=[각자 iam 사용자의 secret access key]
export TF_VAR_SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32
Bash
복사
# Terraform 환경 변수 저장
terraform init
terraform plan
Bash
복사
# Terraform 배포
nohup sh -c "terraform apply -auto-approve" > create.log 2>&1 &
Bash
복사
Note:
Terraform 배포가 완료되면(약 5분 정도 대기) 정상적으로 자원 생성이 되었는지 확인을 합니다.(cat create.log)
1.2. 기본 정보 확인 및 설정
Terraform 배포가 완료 후 출력되는 Outputs 정보에서 lab_admin_ip과 lab_user_ip의 퍼블릭 IP를 확인합니다.
대상 IP로 인스턴스에 SSH로 접속하고 아래 명령어를 통해 정보를 확인합니다.
LAB-ADMIN
echo ${AWS_DEFAULT_REGION}
echo ${NICKNAME}
echo ${ACCOUNT_ID}
Bash
복사
# 변수 확인
aws sts get-caller-identity
Bash
복사
# caller id 확인
Note:
인스턴스가 생성되고 너무 빠르게 접속하면 aws 자격 증명이 완료되지 않을 수 있습니다.
그럴 경우에는 약간의 대기 후 다시 접속해 주세요.
2. Amazon S3 보안 구성 - 접근 통제와 보안 탐지
2.1. IAM 정책과 버킷 정책 확인
Amazon S3 서비스에 대한 IAM 정책과 버킷 정책을 생성하고 차이를 이해합니다.
LAB-ADMIN
실습용 버킷 생성 및 ACL 확인
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-access \
--create-bucket-configuration LocationConstraint=${AWS_DEFAULT_REGION} | jq
Bash
복사
# S3 버킷 생성
aws s3api get-bucket-ownership-controls \
--bucket cnasg-${NICKNAME}-access | jq
Bash
복사
# ACL 설정 확인
echo "hello cnasg" > hello.txt
aws s3api put-object \
--bucket cnasg-${NICKNAME}-access \
--key hello.txt \
--body hello.txt
Bash
복사
# 테스트 버킷에 객체 업로드
aws s3api list-objects-v2 \
--bucket cnasg-${NICKNAME}-access | jq
Bash
복사
# 업로드된 객체 확인
aws s3api put-object-acl \
--bucket cnasg-${NICKNAME}-access \
--key hello.txt \
--acl private
Bash
복사
# private라는 ACL 적용 (객체 소유자만 Full Control)
실습용 IAM 사용자 생성
aws iam create-user \
--user-name lab-s3-user
aws iam create-access-key \
--user-name lab-s3-user
Bash
복사
# 실습용 IAM 사용자 생성 및 액세스 키 생성
LAB-USER
자격 증명 설정
aws sts get-caller-identity
Bash
복사
# caller id 확인
aws configure
Bash
복사
# lab-s3-user에 대한 자격 증명 설정
aws sts get-caller-identity
Bash
복사
# caller id 확인
aws s3api list-objects-v2 \
--bucket cnasg-${NICKNAME}-access
Bash
복사
# 실습용 버킷 정보 조회
aws s3api get-object \
--bucket cnasg-${NICKNAME}-access \
--key hello.txt \
/tmp/hello.txt
Bash
복사
LAB-ADMIN
IAM 정책 생성 및 부여
cat > iam-s3-allow.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-access"
},
{
"Sid": "AllowGetObject",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-access/*"
}
]
}
EOF
Bash
복사
# IAM 정책 파일 생성
aws iam put-user-policy \
--user-name lab-s3-user \
--policy-name AllowS3Read-Test1 \
--policy-document file://iam-s3-allow.json
Bash
복사
# 실습용 IAM 사용자에 IAM 정책 연결
aws s3api get-bucket-policy \
--bucket cnasg-${NICKNAME}-access | jq
Bash
복사
# 버킷 정책 확인
LAB-USER
S3 접근 확인
aws s3api list-objects-v2 \
--bucket cnasg-${NICKNAME}-access | jq
Bash
복사
# 테스트 버킷에서 객체조회
aws s3api get-object \
--bucket cnasg-${NICKNAME}-access \
--key hello.txt \
/tmp/hello.txt
cat /tmp/hello.txt
Bash
복사
# 테스트 버킷에 객체 파일 다운로드
echo "hello2 cnasg" > hello2.txt
aws s3api put-object \
--bucket cnasg-${NICKNAME}-access \
--key hello2.txt \
--body hello2.txt
Bash
복사
# 테스트 버킷에 객체 업로드
LAB-ADMIN
버킷 정책 생성 및 부여
cat > bucket-policy-deny-get.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListBucket",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${ACCOUNT_ID}:user/lab-s3-user"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-access"
},
{
"Sid": "DenyGetObject",
"Effect": "Deny",
"Principal": {
"AWS": "arn:aws:iam::${ACCOUNT_ID}:user/lab-s3-user"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-access/*"
}
]
}
EOF
Bash
복사
# 버킷 정책 파일 생성
aws s3api put-bucket-policy \
--bucket cnasg-${NICKNAME}-access \
--policy file://bucket-policy-deny-get.json
Bash
복사
# 버킷 정책을 테스트 버킷에 연결
aws s3api get-bucket-policy \
--bucket cnasg-${NICKNAME}-access \
| jq -r '.Policy | fromjson'
Bash
복사
# 테스트 버킷의 버킷 정책 확인 (JSON 형태 파싱)
LAB-USER
S3 접근 확인
aws s3api list-objects-v2 \
--bucket cnasg-${NICKNAME}-access | jq
Bash
복사
# 테스트 버킷 조회
aws s3api get-object \
--bucket cnasg-${NICKNAME}-access \
--key hello.txt \
/tmp/hello.txt
Bash
복사
# 테스트 버킷에 객체 파일 다운로드
2.2. PrivateLink for S3 - 통신 경로 확인
Amazon S3 서비스와 통신을 위한 PrivateLink를 구성하고 통신 흐름을 이해합니다.
LAB-ADMIN
S3 버킷 대상 주소의 IP 확인
dig +short cnasg-${NICKNAME}-access.s3.ap-northeast-2.amazonaws.com
Bash
복사
# S3 버킷의 도메인에 대한 IP 주소 확인 (dig)
대상 S3 버킷에 접근하기 위한 주소를 사전에 정의된 범위 내에서 사용합니다.
curl -O https://ip-ranges.amazonaws.com/ip-ranges.json
Bash
복사
# AWS 서비스 용도의 IP 대역을 정의한 파일 다운로드
jq '.prefixes[0:2]' ip-ranges.json
Bash
복사
# 샘플 구조 확인 (2개만)
jq '.prefixes
| map(select(.service=="S3"))
| .[0:2]' ip-ranges.json
Bash
복사
jq -r '.prefixes[]
| select(.region=="ap-northeast-2" and .service=="S3")
| .ip_prefix' ip-ranges.json
Bash
복사
# 서울 리전의 S3 서비스만 필터링
[PrivateLink 구성 전] 라우팅 테이블 확인
VPC_ID=$(aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values=cnasg-VPC" \
--query 'Vpcs[0].VpcId' --output text)
RTB_ID=$(aws ec2 describe-route-tables \
--filters "Name=tag:Name,Values=cnasg-VPC-public" \
--query 'RouteTables[0].RouteTableId' \
--output text)
Bash
복사
# 변수 선언
echo "export VPC_ID=${VPC_ID}" >> /etc/profile
echo "export RTB_ID=${RTB_ID}" >> /etc/profile
echo "VPC_ID=${VPC_ID}"
echo "RTB_ID=${RTB_ID}"
Bash
복사
# 전역 변수 선언 및 확인
aws ec2 describe-route-tables \
--route-table-ids $RTB_ID \
--query 'RouteTables[0].Routes' \
--output table
Bash
복사
# 라우팅 테이블 확인
[PrivateLink 구성] VPC Gateway Endpoint for S3 생성
aws ec2 create-vpc-endpoint \
--vpc-id $VPC_ID \
--service-name com.amazonaws.ap-northeast-2.s3 \
--route-table-ids $RTB_ID \
--vpc-endpoint-type Gateway
Bash
복사
# S3 용 VPC Gateway Endpoint 생성
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=$VPC_ID" \
--query 'VpcEndpoints[].{ID:VpcEndpointId,Service:ServiceName,State:State}' \
--output table
Bash
복사
# VPCE 생성 확인
[PrivateLink 구성 후] 라우팅 테이블 확인
aws ec2 describe-route-tables \
--route-table-ids $RTB_ID \
--query 'RouteTables[0].Routes' \
--output table
Bash
복사
# 라우팅 테이블 확인
aws ec2 describe-managed-prefix-lists \
--query 'PrefixLists[?PrefixListName==`com.amazonaws.ap-northeast-2.s3`].[PrefixListId,PrefixListName]' \
--output table
PL_S3_ID=$(aws ec2 describe-managed-prefix-lists \
--query 'PrefixLists[?PrefixListName==`com.amazonaws.ap-northeast-2.s3`].PrefixListId' \
--output text)
echo ${PL_S3_ID}
Bash
복사
# 대상 Prefix-Lists 확인하고 이름을 변수 선언
aws ec2 get-managed-prefix-list-entries \
--prefix-list-id ${PL_S3_ID} \
--query 'Entries[].Cidr' \
--output table
Bash
복사
# 대상 Prefix-Lists에 연결된 CIDR 확인
2.3. Pre-signed URL for S3 - 임시 접근
Amazon S3 버킷 내 객체 접근을 위한 Pre-signed URL을 생성해서 임시적인 접근 권한을 실습합니다.
LAB-ADMIN
버킷 정책 생성 및 객체 업로드
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-presign \
--create-bucket-configuration LocationConstraint=${AWS_DEFAULT_REGION} | jq
Bash
복사
# S3 버킷 생성
aws s3api head-bucket \
--bucket cnasg-${NICKNAME}-presign | jq
Bash
복사
# S3 버킷 생성 확인
echo "cnasg presign test" > presign-test.txt
aws s3api put-object \
--bucket cnasg-${NICKNAME}-presign \
--key presign-test.txt \
--body presign-test.txt \
--content-type text/plain
Bash
복사
# 테스트 파일 생성 + 객체 업로드
aws s3api list-objects-v2 \
--bucket cnasg-${NICKNAME}-presign | jq
Bash
복사
# 객체 업로드 확인
LAB-USER
S3 버킷에 직접 접근
curl -I https://cnasg-${NICKNAME}-presign.s3.ap-northeast-2.amazonaws.com/presign-test.txt
Bash
복사
# 대상 S3 버킷의 객체로 직접 접근 (curl & lynx)
lynx --dump https://cnasg-${NICKNAME}-presign.s3.ap-northeast-2.amazonaws.com/presign-test.txt
Bash
복사
사용자 PC의 브라우저에서도 접근을 시도합니다.
echo "https://cnasg-${NICKNAME}-presign.s3.ap-northeast-2.amazonaws.com/presign-test.txt"
Bash
복사
LAB-ADMIN
Pre-signed URL 생성 (5분)
aws s3 presign s3://cnasg-${NICKNAME}-presign/presign-test.txt \
--expires-in 300
Bash
복사
# 대상 객체에 대한 Pre-signed URL 생성 (5분)
LAB-USER
S3 버킷에 Pre-signed URL로 접근
URL='<출력된 presigned URL 붙여넣기>'
Bash
복사
# Pre-signed URL을 변수에 선언 및 확인
echo $URL
Bash
복사
lynx --dump $URL
Bash
복사
# Pre-signed URL 접근 확인
사용자 PC의 브라우저에서도 Pre-signed URL로 접근을 시도합니다.
Pre-signed URL에 정의한 만료 시간 5분이 지난 후 다시 접근을 해 봅니다.
2.4. AWS Config - S3 퍼블릭 접근 설정 탐지
AWS Config를 통해 리소스를 기록하고 S3 퍼블릭 버킷 규칙에 따라 위반 대상을 탐지합니다.
LAB-ADMIN
퍼블릭 버킷 생성 및 객체 업로드
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-public \
--create-bucket-configuration LocationConstraint=${AWS_DEFAULT_REGION}
Bash
복사
# S3 버킷 생성
aws s3api get-public-access-block \
--bucket cnasg-${NICKNAME}-public | jq
Bash
복사
# 대상 버킷에 퍼블릭 접근 확인
aws s3api put-public-access-block \
--bucket cnasg-${NICKNAME}-public \
--public-access-block-configuration \
'BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false'
aws s3api get-public-access-block \
--bucket cnasg-${NICKNAME}-public | jq
Bash
복사
# 대상 버킷에 퍼블릭 접근 차단을 해제(퍼블릭 접근 가능)
echo "cnasg public test" > public-test.txt
aws s3api put-object \
--bucket cnasg-${NICKNAME}-public \
--key public-test.txt \
--body public-test.txt \
--content-type text/plain
Bash
복사
# 테스트 파일 생성 + 객체 업로드
aws s3api list-objects-v2 \
--bucket cnasg-${NICKNAME}-public | jq
Bash
복사
# 객체 업로드 확인
cat << EOF > bucket-policy-public-read.json
{
"Version":"2012-10-17",
"Statement":[
{
"Sid":"AllowPublicReadObject",
"Effect":"Allow",
"Principal":"*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::cnasg-${NICKNAME}-public/*"]
}
]
}
EOF
aws s3api put-bucket-policy \
--bucket cnasg-${NICKNAME}-public \
--policy file://bucket-policy-public-read.json
Bash
복사
# 버킷 정책에서 익명의 GetObject 허용
LAB-USER
S3 객체 주소로 접근 확인
curl -I -s https://cnasg-${NICKNAME}-public.s3.ap-northeast-2.amazonaws.com/public-test.txt | head
Bash
복사
# 대상 S3 버킷의 객체로 직접 접근 (curl & lynx)
lynx --dump https://cnasg-${NICKNAME}-public.s3.ap-northeast-2.amazonaws.com/public-test.txt
Bash
복사
사용자 PC의 브라우저에서도 접근을 시도합니다.
echo "https://cnasg-${NICKNAME}-public.s3.ap-northeast-2.amazonaws.com/public-test.txt"
Bash
복사
LAB-ADMIN
[AWS Config] Config 용도의 버킷 생성
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-config \
--create-bucket-configuration LocationConstraint=ap-northeast-2 | jq
Bash
복사
# Config 용도 버킷 생성
cat > cnasg-config-bucket-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSConfigBucketPermissionsCheck",
"Effect": "Allow",
"Principal": { "Service": "config.amazonaws.com" },
"Action": [
"s3:GetBucketAcl",
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-config"
},
{
"Sid": "AWSConfigBucketDelivery",
"Effect": "Allow",
"Principal": { "Service": "config.amazonaws.com" },
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-config/AWSLogs/${ACCOUNT_ID}/Config/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
EOF
aws s3api put-bucket-policy \
--bucket cnasg-${NICKNAME}-config \
--policy file://cnasg-config-bucket-policy.json
Bash
복사
# 버킷 정책에 Delivery Policy 추가
[AWS Config] Config 용도의 IAM 생성
cat > config-trust.json << 'EOF'
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Principal":{"Service":"config.amazonaws.com"},
"Action":"sts:AssumeRole"
}
]
}
EOF
aws iam create-role \
--role-name CNASG-ConfigRole \
--assume-role-policy-document file://config-trust.json
Bash
복사
# 신뢰 정책 파일 생성 및 IAM 역할 생성
aws iam attach-role-policy \
--role-name CNASG-ConfigRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AWS_ConfigRole
Bash
복사
# AWS 관리형 IAM 정책을 IAM 역할에 연결
[AWS Config] Recorder 생성
CONFIG_ROLE_ARN=$(aws iam get-role \
--role-name CNASG-ConfigRole \
--query 'Role.Arn' \
--output text)
cat > recorder.json <<EOF
{
"name": "cnasg-recorder",
"roleARN": "${CONFIG_ROLE_ARN}",
"recordingGroup": {
"allSupported": false,
"includeGlobalResourceTypes": false,
"resourceTypes": ["AWS::S3::Bucket"]
}
}
EOF
aws configservice put-configuration-recorder \
--configuration-recorder file://recorder.json
Bash
복사
# IAM 역할의 ARN 값 변수 선언 및 Recorder 생성 (S3 서비스 리소스 기록)
aws configservice describe-configuration-recorder-status | jq
Bash
복사
# Recorder 상태 확인
[AWS Config] Delivery Channel 생성
aws configservice put-delivery-channel \
--delivery-channel \
name=cnasg-channel,s3BucketName=cnasg-${NICKNAME}-config
Bash
복사
# Delivery Channel 생성 (Config 용도의 S3 버킷)
aws configservice describe-delivery-channels | jq
Bash
복사
# Delivery Channel 설정 확인
[AWS Config] Recorder 시작
aws configservice start-configuration-recorder \
--configuration-recorder-name cnasg-recorder
Bash
복사
# Recorder 시작
aws configservice describe-configuration-recorder-status | jq
Bash
복사
# Recorder 상태 확인
[AWS Config] 관리형 규칙 추가 (S3 Bucket - Public Read/Write)
cat > cnasg-s3-public-read-prohibited.json <<'EOF'
{
"ConfigRuleName": "cnasg-s3-public-read-prohibited",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
}
}
EOF
aws configservice put-config-rule \
--config-rule file://cnasg-s3-public-read-prohibited.json
cat > cnasg-s3-public-write-prohibited.json <<'EOF'
{
"ConfigRuleName": "cnasg-s3-public-write-prohibited",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
}
}
EOF
aws configservice put-config-rule \
--config-rule file://cnasg-s3-public-write-prohibited.json
Bash
복사
# 관리형 규칙 2개 생성 (퍼블릭 S3 버킷의 Read/Write 규칙)
aws configservice describe-config-rules \
--config-rule-names cnasg-s3-public-read-prohibited cnasg-s3-public-write-prohibited | jq
Bash
복사
# 생성한 관리형 규칙 상태 확인
[AWS Config] Config 즉시 평가 및 결과 확인
aws configservice start-config-rules-evaluation \
--config-rule-names cnasg-s3-public-read-prohibited cnasg-s3-public-write-prohibited
Bash
복사
# 즉시 평가 트리거
aws configservice describe-compliance-by-config-rule \
--config-rule-names cnasg-s3-public-read-prohibited cnasg-s3-public-write-prohibited | jq
Bash
복사
# S3 버킷 퍼블릭 접근 Read/Write 규칙 결과 확인
S3 버킷 퍼블릭 접근에 대한 Read 규칙만 문제 있음을 확인할 수 있어, 대상 규칙에 대한 상세 정보만 확인합니다.
aws configservice get-compliance-details-by-config-rule \
--config-rule-name cnasg-s3-public-read-prohibited | jq
Bash
복사
# S3 버킷 퍼블릭 접근 Read 규칙 상세 결과 (위: 전체, 아래: NON_COMPLIANT Only)
aws configservice get-compliance-details-by-config-rule \
--config-rule-name cnasg-s3-public-read-prohibited \
| jq '.EvaluationResults[]
| select(.ComplianceType=="NON_COMPLIANT")'
Bash
복사
S3 퍼블릭 버킷 감지를 위한 AWS Config가 구성된 상태에서 신규 S3 퍼블릭 버킷이 생성되면, 자동으로 감지합니다. (약간의 대기가 필요)
[OPTIONAL] S3 퍼블릭 버킷 생성이 감지되면 Slack 웹 훅으로 메시지 전달하기
AWS Config 실습 자원 삭제
AWS Config Recording 동작으로 미세하지만 과금이 발생할 수 있어, 이번 단위 실습에 대한 자원만 삭제합니다.
aws configservice delete-config-rule \
--config-rule-name cnasg-s3-public-read-prohibited
aws configservice delete-config-rule \
--config-rule-name cnasg-s3-public-write-prohibited
Bash
복사
# Config Rule 삭제
aws configservice stop-configuration-recorder \
--configuration-recorder-name cnasg-recorder
Bash
복사
# Recorder 중지
aws configservice delete-configuration-recorder \
--configuration-recorder-name cnasg-recorder
Bash
복사
# Recorder 삭제
aws configservice delete-delivery-channel \
--delivery-channel-name cnasg-channel
Bash
복사
# Delivery Channel 삭제
aws iam detach-role-policy \
--role-name CNASG-ConfigRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AWS_ConfigRole
aws iam delete-role \
--role-name CNASG-ConfigRole
Bash
복사
# AWS Config 용도의 IAM 삭제
aws s3 rb s3://cnasg-${NICKNAME}-public --force
aws s3 rb s3://cnasg-${NICKNAME}-config --force
Bash
복사
# 실습용 S3 버킷 삭제
2.5. Amazon Macie - 민감 데이터 노출 탐지
Amazon Macie를 통해 S3 버킷 내 구성된 객체 파일의 민감 데이터를 탐지하고 대응합니다.
LAB-ADMIN
실습 용도의 버킷 생성
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-customer-data \
--create-bucket-configuration LocationConstraint=${AWS_DEFAULT_REGION}
Bash
복사
# S3 버킷 생성
실습 용도의 파일 생성 및 S3 버킷 업로드
python3 gen-macie-csv.py
ls -l *.csv *.env
Bash
복사
# 실습 용도의 파일을 생성하는 스크립트 실행
스크립트를 통해 생성하는 파일은 랜덤하게 생성되는 더미 파일로, FAKE 데이터입니다.
column -s, -t customer-data.csv | head -n 6
Bash
복사
# 생성된 파일 정보 확인
column -s, -t customer-data-safe.csv | head -n 6
Bash
복사
cat config.env
Bash
복사
aws s3 cp customer-data.csv s3://cnasg-${NICKNAME}-customer-data/customer-data.csv
aws s3 cp customer-data-safe.csv s3://cnasg-${NICKNAME}-customer-data/customer-data-safe.csv
aws s3 cp config.env s3://cnasg-${NICKNAME}-customer-data/config.env
Bash
복사
# 대상 S3 버킷에 객체 파일 업로드
aws s3 ls s3://cnasg-${NICKNAME}-customer-data/
Bash
복사
# 대상 S3 버킷에 객체 정보 확인
[Amazon Macie] Macie 활성화
[관리 콘솔 확인: Amazon Macie→ 시작하기]
aws macie2 get-macie-session
Bash
복사
# Amazon Macie 상태 확인
aws macie2 enable-macie
Bash
복사
# Amazon Macie 활성화
[Amazon Macie] 사용자 지정 데이터 식별자 생성 (주민 등록 번호)
[관리 콘솔 확인: Amazon Macie→ 설정 → 사용자 지정 데이터 식별자]
aws macie2 create-custom-data-identifier \
--name "KoreanResidentNumberLike" \
--description "RRN-like pattern for lab" \
--regex "\\b\\d{6}-[1-4]\\d{6}\\b" \
--maximum-match-distance 50
Bash
복사
# 주민 등록 번호를 검출하는 사용자 지정 데이터 식별자 생성
aws macie2 list-custom-data-identifiers
Bash
복사
# 생성된 사용자 지정 데이터 식별자 확인
RRN_CDI_ID=$(aws macie2 list-custom-data-identifiers \
--query "items[?name=='KoreanResidentNumberLike'].id | [0]" \
--output text)
echo "export RRN_CDI_ID=$RRN_CDI_ID" >> /etc/profile
echo $RRN_CDI_ID
Bash
복사
# 생성된 사용자 지정 데이터 식별자 ID를 변수로 선언
[Amazon Macie] 일회성 작업 생성 - 민감 데이터 검토
[관리 콘솔 확인: Amazon Macie→ 작업]
cat > s3-job.json <<EOF
{
"bucketDefinitions": [
{
"accountId": "${ACCOUNT_ID}",
"buckets": ["cnasg-${NICKNAME}-customer-data"]
}
]
}
EOF
Bash
복사
# 작업을 수행할 S3 버킷 지정
aws macie2 create-classification-job \
--job-type ONE_TIME \
--name "cnasg-macie-lab-job" \
--s3-job-definition file://s3-job.json \
--custom-data-identifier-ids ${RRN_CDI_ID} \
--managed-data-identifier-selector RECOMMENDED \
--sampling-percentage 100
Bash
복사
# 일회성 작업 생성 - S3 버킷 지정, 사용자 지정 데이터 식별자 선택, 관리형 식별자 추천 대상 선택
aws macie2 list-classification-jobs | jq
Bash
복사
# 생성된 작업 확인
JOB_ID=$(aws macie2 list-classification-jobs \
--query "items[?name=='cnasg-macie-lab-job'].jobId" \
--output text)
echo "export JOB_ID=$JOB_ID" >> /etc/profile
echo $JOB_ID
Bash
복사
# 대상 일회성 작업의 ID를 변수로 선언
aws macie2 describe-classification-job \
--job-id ${JOB_ID} \
--query jobStatus \
--output text
Bash
복사
# 일회성 작업의 상태 확인
watch -d "aws macie2 describe-classification-job \
--job-id ${JOB_ID} \
--query jobStatus \
--output text"
Bash
복사
# 일회성 작업의 상태 모니터링
RUNNING 상태는 활성(실행 중) 상태로 민감 데이터 검토 작업을 수행 중인 상태입니다.
약 12분 정도 시간이 지나면 COMPLETE 상태로 완료되니 이후에 결과를 확인합니다.
[Amazon Macie] 일회성 작업 결과 확인 - 민감 데이터 검토
FINDING_IDS=$(aws macie2 list-findings \
--finding-criteria '{"criterion":{"classificationDetails.jobId":{"eq":["'"${JOB_ID}"'"]}}}' \
--query 'findingIds[]' \
--output text)
echo "$FINDING_IDS"
Bash
복사
# 결과 ID 변수 선언
aws macie2 get-findings \
--finding-ids ${FINDING_IDS} \
--output json | jq '
.findings[] | {
id,
type,
severity: .severity.description,
object: .resourcesAffected.s3Object.key,
updatedAt
}'
Bash
복사
# 결과 요약 View
aws macie2 get-findings \
--finding-ids ${FINDING_IDS} \
--output json |
jq '
.findings[]
| select(.type=="SensitiveData:S3Object/Credentials")
'
Bash
복사
# 첫 번째 결과 확인
grep -n 'AWS_ACCESS_KEY_ID\|AWS_SECRET_ACCESS_KEY' config.env
Bash
복사
# [신규 터미널] config.env에서 필요한 부분만 확인
grep -n 'BEGIN OPENSSH PRIVATE KEY\|END OPENSSH PRIVATE KEY' -n config.env
Bash
복사
aws macie2 get-findings \
--finding-ids ${FINDING_IDS} \
--output json |
jq '
.findings[]
| select(.type=="SensitiveData:S3Object/Multiple")
'
Bash
복사
# 두 번째 결과 확인
nl -ba customer-data.csv | column -s, -t -n ' '
Bash
복사
# [신규 터미널] customer-data.csv 파일 확인 (라인 넘버링)
3. Amazon S3 보안 구성 - 데이터 보호와 감사 및 로깅 분석
3.1. HTTPS 사용 강제화
Amazon S3 버킷의 객체를 접근할 때 HTTPS만 접근할 수 있도록 보안 설정을 구성하여 통신 구간의 암호화를 처리합니다.
LAB-ADMIN
버킷 정책 생성 및 객체 업로드
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-protect \
--create-bucket-configuration LocationConstraint=${AWS_DEFAULT_REGION}
Bash
복사
# S3 버킷 생성
aws s3api head-bucket \
--bucket cnasg-${NICKNAME}-protect | jq
Bash
복사
# S3 버킷 생성 확인
echo "cnasg protect test" > protect-test.txt
aws s3api put-object \
--bucket cnasg-${NICKNAME}-protect \
--key protect-test.txt \
--body protect-test.txt \
--content-type text/plain
Bash
복사
# 테스트 파일 생성 + 객체 업로드
aws s3api list-objects-v2 \
--bucket cnasg-${NICKNAME}-protect | jq
Bash
복사
# 객체 업로드 확인
aws s3api put-public-access-block \
--bucket cnasg-${NICKNAME}-protect \
--public-access-block-configuration \
'BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false'
Bash
복사
# Public Access Block 설정 해제
cat << EOF > bucket-policy-protect-read.json
{
"Version":"2012-10-17",
"Statement":[
{
"Sid":"AllowPublicReadObject",
"Effect":"Allow",
"Principal":"*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::cnasg-${NICKNAME}-protect/*"]
}
]
}
EOF
aws s3api put-bucket-policy \
--bucket cnasg-${NICKNAME}-protect \
--policy file://bucket-policy-protect-read.json
Bash
복사
# 버킷 정책에서 익명의 GetObject 허용
LAB-USER
객체 주소로 접근 확인
lynx --dump https://cnasg-${NICKNAME}-protect.s3.ap-northeast-2.amazonaws.com/protect-test.txt
Bash
복사
# 대상 S3 버킷의 객체로 직접 접근 (http & https)
lynx --dump http://cnasg-${NICKNAME}-protect.s3.ap-northeast-2.amazonaws.com/protect-test.txt
Bash
복사
HTTP와 HTTPS 접근 모두 가능한 것을 확인할 수 있습니다.
LAB-ADMIN
HTTPS 강제 정책 적용
cat << EOF > bucket-policy-enforce-https.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid":"AllowPublicReadObject",
"Effect":"Allow",
"Principal":"*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::cnasg-${NICKNAME}-protect/*"]
},
{
"Sid":"EnforceHTTPS",
"Effect":"Deny",
"Principal":"*",
"Action":"s3:*",
"Resource":[
"arn:aws:s3:::cnasg-${NICKNAME}-protect",
"arn:aws:s3:::cnasg-${NICKNAME}-protect/*"
],
"Condition":{
"Bool":{
"aws:SecureTransport":"false"
}
}
}
]
}
EOF
aws s3api put-bucket-policy \
--bucket cnasg-${NICKNAME}-protect \
--policy file://bucket-policy-enforce-https.json
Bash
복사
# HTTPS 강제 적용을 포함한 버킷 정책 적용
LAB-USER
객체 주소로 접근 확인
lynx --dump https://cnasg-${NICKNAME}-protect.s3.ap-northeast-2.amazonaws.com/protect-test.txt
Bash
복사
# 대상 S3 버킷의 객체로 직접 접근 (http & https)
lynx --dump http://cnasg-${NICKNAME}-protect.s3.ap-northeast-2.amazonaws.com/protect-test.txt
Bash
복사
curl -I http://cnasg-${NICKNAME}-protect.s3.${AWS_DEFAULT_REGION}.amazonaws.com/protect-test.txt
Bash
복사
# HTTP 접근에 대한 HEAD 요청 확인
3.2. SSE-KMS 암호화
Amazon S3 객체를 AWS KMS 키를 통해 서버 측에서 암호화하고, 적용 전과 적용 후의 암호화 상태를 확인합니다.
LAB-ADMIN
기존 객체 암호화 상태 확인
aws s3api head-object \
--bucket cnasg-${NICKNAME}-protect \
--key protect-test.txt | jq
Bash
복사
# 기존 객체 메타데이터 확인 (암호화 방식 확인)
"ServerSideEncryption": "AES256"
⇒ 기본적으로 SSE-S3 방식으로 암호화된 것을 확인할 수 있습니다.
사전 준비 사항
START_TIME=$(date -u -d '5 minutes ago' +"%Y-%m-%dT%H:%M:%SZ")
echo "export START_TIME=${START_TIME}" >> /etc/profile
echo $START_TIME
Bash
복사
# 실습 시작 시간 변수 선언 (5분 전 시간)
CloudTrail 로그에서 KMS 로그 정보를 확인하는 시점을 정의하기 위해 변수를 선언합니다.
KMS 키 생성 (CreateKey)
CALLER_ARN=$(aws sts get-caller-identity --query Arn --output text)
cat > kms-key-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAdminUser",
"Effect": "Allow",
"Principal": {
"AWS": "${CALLER_ARN}"
},
"Action": "kms:*",
"Resource": "*"
}
]
}
EOF
Bash
복사
# KMS 키 정책 파일 생성
KMS_KEY_ID=$(aws kms create-key \
--description "cnasg s3 kms key" \
--policy file://kms-key-policy.json \
--query 'KeyMetadata.KeyId' \
--output text)
echo "export KMS_KEY_ID=${KMS_KEY_ID}" >> /etc/profile
echo $KMS_KEY_ID
Bash
복사
# KMS 키 생성 및 ID 변수 선언
watch -d "aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventSource,AttributeValue=kms.amazonaws.com \
--start-time \"$START_TIME\" \
--query \"sort_by(Events[?contains(CloudTrailEvent, '${KMS_KEY_ID}')], &EventTime)[].[EventTime,EventName]\" \
--output table"
Bash
복사
# [신규 터미널] CloudTrail - KMS 이벤트 메시지 정렬
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventSource,AttributeValue=kms.amazonaws.com \
--start-time "$START_TIME" \
--query "Events[?EventName=='CreateKey' && Resources[?ResourceName=='${KMS_KEY_ID}']]" \
--output json | jq
Bash
복사
# CloudTrail에서 KMS 동작 확인 (CreateKey만 필터링)
CloudTrail 로그에 반영될 때 까지 약간의 시간이 필요합니다.
KMS 키 정보 확인 (DescribeKey & GetKeyPolicy)
aws kms describe-key \
--key-id $KMS_KEY_ID \
--output json | jq
Bash
복사
# KMS 키 메타데이터 확인
aws kms get-key-policy \
--key-id $KMS_KEY_ID \
--policy-name default \
--query 'Policy' \
--output text | jq
Bash
복사
# KMS 키 정책 확인
[SSE-KMS] 객체 업로드 및 확인
echo "cnasg kms test" > kms-test.txt
aws s3api put-object \
--bucket cnasg-${NICKNAME}-protect \
--key kms-test.txt \
--body kms-test.txt \
--server-side-encryption aws:kms \
--ssekms-key-id $KMS_KEY_ID
Bash
복사
# SSE-KMS 적용한 객체 업로드
aws s3api head-object \
--bucket cnasg-${NICKNAME}-protect \
--key kms-test.txt | jq
Bash
복사
# 업로드 객체 메타데이터 확인
"ServerSideEncryption": "aws:kms"
⇒ SSE-KMS 방식으로 암호화된 것을 확인할 수 있습니다.
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventSource,AttributeValue=kms.amazonaws.com \
--start-time "$START_TIME" \
--query "Events[? (EventName=='GenerateDataKey') && Resources[?ResourceName=='${KMS_KEY_ID}'] ]" \
--output json | jq
Bash
복사
# CloudTrail에서 KMS 동작 확인 (GenerateDataKey만 필터링)
[SSE-KMS] 객체 다운로드 및 확인
aws s3 cp s3://cnasg-${NICKNAME}-protect/kms-test.txt .
Bash
복사
# 객체 다운로드
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventSource,AttributeValue=kms.amazonaws.com \
--start-time "$START_TIME" \
--query "Events[?EventName=='Decrypt' && contains(CloudTrailEvent, '${KMS_KEY_ID}')]" \
--output json | jq
Bash
복사
# CloudTrail에서 KMS 동작 확인 (Decrypt만 필터링)
KMS 키 삭제
aws s3 rm s3://cnasg-ongja-protect/kms-test.txt
Bash
복사
# 실습용 객체 삭제
aws kms schedule-key-deletion \
--key-id $KMS_KEY_ID \
--pending-window-in-days 7
Bash
복사
# KMS 키 삭제 예약
KMS 키 삭제는 즉각 이루어지지 않고 삭제 예약 후 7일 ~ 30일 대기 시간이 필요합니다.
별도의 과금이 이루어지지 않으며, 알아서 삭제되니 예약만 걸어두면 됩니다.
3.3. CloudTrail - S3 API 호출 감사
Amazon S3 버킷에 대한 API 호출 내역을 기록해서 누가, 언제, 어떤 작업을 수행했는지 추적합니다.
LAB-ADMIN
실습용 버킷 생성 및 정책 수립
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-audit \
--create-bucket-configuration LocationConstraint=${AWS_DEFAULT_REGION}
Bash
복사
# 실습용 S3 버킷 생성
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-audit-trail \
--create-bucket-configuration LocationConstraint=${AWS_DEFAULT_REGION}
Bash
복사
# CloudTrail 저장용 버킷 생성
cat <<EOF > ct-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-audit-trail"
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-audit-trail/AWSLogs/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
EOF
aws s3api put-bucket-policy \
--bucket cnasg-${NICKNAME}-audit-trail \
--policy file://ct-policy.json
Bash
복사
# CloudTrail 저장용 버킷의 정책 적용
[CloudTrail] Trail 생성 및 시작
aws cloudtrail create-trail \
--name cnasg-trail \
--s3-bucket-name cnasg-${NICKNAME}-audit-trail \
--is-multi-region-trail
Bash
복사
# CloudTrail - Trail 생성
aws cloudtrail start-logging \
--name cnasg-trail
Bash
복사
# CloudTrail - Trail 시작
aws cloudtrail put-event-selectors \
--trail-name cnasg-trail \
--event-selectors '[
{
"ReadWriteType": "All",
"IncludeManagementEvents": false,
"DataResources": [
{
"Type": "AWS::S3::Object",
"Values": ["arn:aws:s3:::cnasg-'${NICKNAME}'-audit/"]
}
]
}
]'
Bash
복사
# CloudTrail - S3 Data Event 활성화
객체 업로드 및 다운로드
for i in {1..5}; do
echo "test $i" > ct-test-$i.txt
aws s3api put-object \
--bucket cnasg-${NICKNAME}-audit \
--key ct-test-$i.txt \
--body ct-test-$i.txt
done
aws s3 ls s3://cnasg-${NICKNAME}-audit
Bash
복사
# 객체 업로드 (다른 파일로 5회)
for i in {1..5}; do
aws s3api get-object \
--bucket cnasg-${NICKNAME}-audit \
--key ct-test-$i.txt \
ct-test-$i-download.txt
done
ls -al *download.txt
Bash
복사
# 객체 다운로드 (다른 파일로 5회)
[CloudTrail] Trail 로그 파일 확인 및 다운로드
watch aws s3 ls s3://cnasg-${NICKNAME}-audit-trail/ --recursive
Bash
복사
# CloudTrail 로그 파일 확인
aws s3 ls s3://cnasg-${NICKNAME}-audit-trail/ --recursive
Bash
복사
CloudTrail 로그 파일 생성까지 약간의 대기가 필요합니다.
CT_LOG_FILE=<로그파일 이름>
Bash
복사
# 로그 파일 다운로드
aws s3 cp s3://cnasg-${NICKNAME}-audit-trail/${CT_LOG_FILE} ./trail/
Bash
복사
gunzip trail/*.gz
Bash
복사
# 압축 해제
cat trail/*.json | jq .
Bash
복사
# CloudTrail 로그 확인
cat trail/*.json | jq '.Records[] | select(.eventName=="GetObject")'
Bash
복사
# CloudTrail 로그 확인 - GetObject만 필터링
cat trail/*.json | jq '.Records[] | select(.eventName=="PutObject")'
Bash
복사
# CloudTrail 로그 확인 - PutObject만 필터링
3.4. S3 액세스 로깅 - 접근 패턴 분석
Amazon S3 버킷에 대한 접근 요청 로그를 저장하고, Python 기반 분석 도구를 통해 접근 패탄과 사용 형태를 분석합니다.
LAB-ADMIN
실습용 버킷 생성 및 정책 수립 (& 액세스 로깅 활성화)
aws s3api create-bucket \
--bucket cnasg-${NICKNAME}-audit-log \
--create-bucket-configuration LocationConstraint=${AWS_DEFAULT_REGION}
Bash
복사
# S3 로그 저장용 버킷 생성
cat <<EOF > s3-log-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3ServerAccessLogsPolicy",
"Effect": "Allow",
"Principal": {
"Service": "logging.s3.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-audit-log/*"
}
]
}
EOF
aws s3api put-bucket-policy \
--bucket cnasg-${NICKNAME}-audit-log \
--policy file://s3-log-policy.json
Bash
복사
# S3 로그 저장용 버킷의 정책 적용
aws s3api put-bucket-logging \
--bucket cnasg-${NICKNAME}-audit \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "cnasg-'${NICKNAME}'-audit-log",
"TargetPrefix": "access-log/"
}
}'
Bash
복사
# 실습용 버킷에 Access Logging 활성화
사전 준비 작업
for i in {1..3}
do
echo "test-$i" > file-$i.txt
aws s3 cp file-$i.txt s3://cnasg-${NICKNAME}-audit/
done
Bash
복사
# 테스트 객체 생성 (3개)
cat > iam-s3-audit-allow.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-audit"
},
{
"Sid": "AllowGetObject",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::cnasg-${NICKNAME}-audit/*"
}
]
}
EOF
Bash
복사
# LAB-USER(lab-s3-user)의 권한을 위한 IAM 정책 파일 생성
aws iam put-user-policy \
--user-name lab-s3-user \
--policy-name AllowS3Read-Test2 \
--policy-document file://iam-s3-audit-allow.json
Bash
복사
# lab-s3-user 사용자에 IAM 정책 연결
접근 패턴 스크립트 수행
LAB-ADMIN
for i in {1..100}
do
r=$((RANDOM % 3 + 1))
aws s3 cp s3://cnasg-${NICKNAME}-audit/file-$r.txt /dev/null
done
Bash
복사
# LAB-ADMIN (랜덤 정상 트래픽)
LAB-USER
for i in {1..50}
do
aws s3 cp s3://cnasg-${NICKNAME}-audit/not-exist.txt /dev/null
done
Bash
복사
# LAB-USER (이상 접근 - 실패 트래픽)
CloudShell
NICKNAME=<각자 닉네임>
Bash
복사
# CloudShell (특정 객체 집중 요청)
for i in {1..80}
do
aws s3 cp s3://cnasg-${NICKNAME}-audit/file-1.txt tmp.txt
rm -f tmp.txt
done
Bash
복사
관리 콘솔에서 CloudShell에 접근 후 명령어를 수행합니다.
이로써 이번 실습의 접근 대상은 LAB-ADMIN, LAB-USER, CloudShell 입니다.
로그 파일 확인 및 수집
LAB-ADMIN
watch aws s3 ls s3://cnasg-${NICKNAME}-audit-log/ --recursive
Bash
복사
# 로그 생성 대기 (5~10분)
aws s3 cp s3://cnasg-${NICKNAME}-audit-log/ ./logs --recursive
Bash
복사
# 로그 수집
FILE=$(ls logs/access-log | head -n 1)
Bash
복사
# 액세스 로그 파일 1개 확인
cat logs/access-log/$FILE
Bash
복사
sed -E 's/ \[/\n\[/g; s/ "GET/\n"GET/g; s/ [0-9]{3} /\n&/g' \
logs/access-log/$FILE
Bash
복사
Python을 통한 분석 코드 생성 및 확인
yum install -y python3-pip
Bash
복사
# 실습을 위한 Python 도구 설치
python3 -m venv venv
source venv/bin/activate
Bash
복사
# 가상환경 생성 및 활성화
pip install pandas matplotlib
Bash
복사
# 분석 라이브러리 설치 (가상환경 내부)
cat << 'EOF' > analyze.py
import pandas as pd
import matplotlib.pyplot as plt
import glob
import re
records = []
pattern = re.compile(
r'(\S+) (\S+) \[(.*?)\] (\S+) (\S+) (\S+) (\S+) (\S+) "(.*?)" (\d{3})'
)
# -------------------------------
# 로그 파싱
# -------------------------------
for file in glob.glob("logs/access-log/*"):
with open(file) as f:
for line in f:
match = pattern.match(line)
if not match:
continue
ip = match.group(4)
operation = match.group(7)
key = match.group(8)
status = match.group(10)
records.append([ip, operation, key, status])
df = pd.DataFrame(records, columns=["ip","operation","object","status"])
# -------------------------------
# 분석 기준
# -------------------------------
# 정상 접근 (실제 다운로드)
df_normal = df[df["operation"] == "REST.GET.OBJECT"]
# 이상 접근 (실패 요청 기준)
df_abnormal = df[df["status"] == "404"]
# -------------------------------
# 출력
# -------------------------------
print("\n==============================")
print("[전체 요청 수]")
print(len(df))
print("\n[HTTP Status Code]")
print(df["status"].value_counts())
print("\n[정상 접근 IP]")
print(df_normal["ip"].value_counts())
print("\n[이상 접근 IP]")
print(df_abnormal["ip"].value_counts())
print("\n[정상 접근 대상 객체]")
print(df_normal["object"].value_counts().head())
print("\n[이상 접근 대상 객체]")
print(df_abnormal["object"].value_counts().head())
# -------------------------------
# 그래프
# -------------------------------
# 1. 정상 접근 IP
plt.figure()
df_normal["ip"].value_counts().head(5).plot(kind="bar")
plt.title("Normal Access IP")
plt.tight_layout()
plt.savefig("normal_ip.png")
# 2. 이상 접근 IP
if not df_abnormal.empty:
plt.figure()
df_abnormal["ip"].value_counts().head(5).plot(kind="bar")
plt.title("Abnormal Access IP")
plt.tight_layout()
plt.savefig("abnormal_ip.png")
# 3. HTTP Status - Pie
plt.figure()
df["status"].value_counts().plot(
kind="pie",
autopct="%1.1f%%",
startangle=90
)
plt.title("HTTP Status Code Ratio")
plt.ylabel("") # 불필요한 y축 제거
plt.tight_layout()
plt.savefig("status.png")
# 4. 정상 접근 객체 - Pie
plt.figure()
df_normal["object"].value_counts().head(5).plot(
kind="pie",
autopct="%1.1f%%",
startangle=90
)
plt.title("Normal Access Object Ratio")
plt.ylabel("")
plt.tight_layout()
plt.savefig("normal_object.png")
# 5. 이상 접근 객체
if not df_abnormal.empty:
plt.figure()
df_abnormal["object"].value_counts().head(5).plot(kind="bar")
plt.title("Abnormal Access Object")
plt.tight_layout()
plt.savefig("abnormal_object.png")
print("\n[완료] 분석 완료")
EOF
Bash
복사
# Python 코드 파일 구성
python3 analyze.py
Bash
복사
# Python 실행으로 액세스 로그 분석 실행
ls *.png
Bash
복사
# 생성된 그래프 이미지 확인
mv *.png /home/ec2-user/
chown ec2-user:ec2-user /home/ec2-user/*.png
sed -i '/sudo su -/d' /home/ec2-user/.bashrc
Bash
복사
# 그래프 이미지 파일 이동
scp 동작에 영향을 줄 수 있는 .bashrc의 자동 실행 명령을 제거한 것입니다.
sed -i '/sudo su -/d' /home/ec2-user/.bashrc
각자 사용자 PC
scp -i <각자 SSH 키페어.pem> "ec2-user@<LAB-ADMIN 퍼블릭 IP>:/home/ec2-user/*.png" .
Bash
복사
다운로드한 그래프 이미지 파일을 실행해서 확인합니다.
4. 실습 환경 삭제
S3 버킷 및 객체 삭제
aws s3 rb s3://cnasg-${NICKNAME}-access --force
aws s3 rb s3://cnasg-${NICKNAME}-presign --force
aws s3 rb s3://cnasg-${NICKNAME}-protect --force
aws s3 rb s3://cnasg-${NICKNAME}-audit --force
aws s3 rb s3://cnasg-${NICKNAME}-audit-log --force
Bash
복사
# 실습용 버킷과 객체 삭제
VPC Gateway Endpoint for S3 삭제
VPCE_S3_ID=$(aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=${VPC_ID}" "Name=service-name,Values=com.amazonaws.ap-northeast-2.s3" \
--query 'VpcEndpoints[0].VpcEndpointId' --output text)
echo "VPCE_S3_ID=${VPCE_S3_ID}"
Bash
복사
# VPC Endpoint ID 변수 선언
aws ec2 delete-vpc-endpoints \
--vpc-endpoint-ids ${VPCE_S3_ID}
Bash
복사
# VPC Endpoint 삭제
aws ec2 describe-route-tables \
--route-table-ids ${RTB_ID} \
--query 'RouteTables[0].Routes' \
--output table
Bash
복사
# 대상 라우팅 테이블 확인 (Prefix-Lists 경로 삭제 확인)
테스트용 IAM 사용자 삭제
aws iam delete-user-policy \
--user-name lab-s3-user \
--policy-name AllowS3Read-Test1
aws iam delete-user-policy \
--user-name lab-s3-user \
--policy-name AllowS3Read-Test2
Bash
복사
# 테스트용 IAM 사용자에 연결된 정책 제거
ACCESS_KEY_ID=$(aws iam list-access-keys \
--user-name lab-s3-user \
--query 'AccessKeyMetadata[*].AccessKeyId' \
--output text)
echo ${ACCESS_KEY_ID}
aws iam delete-access-key \
--user-name lab-s3-user \
--access-key-id ${ACCESS_KEY_ID}
Bash
복사
# 테스트용 IAM 사용자에 액세스 키 삭제
aws iam delete-user \
--user-name lab-s3-user
Bash
복사
# 테스트용 IAM 사용자 삭제
Amazon Macie 비활성화
aws macie2 disable-macie
Bash
복사
# Amazon Macie 비활성화
aws macie2 get-macie-session
Bash
복사
# Amazon Macie 상태 확인
aws s3 rb s3://cnasg-${NICKNAME}-customer-data --force
Bash
복사
# Amazon Macie 실습 용도의 S3 버킷 삭제
aws logs delete-log-group \
--log-group-name /aws/macie/classificationjobs
Bash
복사
# Amazon Macie를 위한 CloudWatch 로그 그룹 삭제
AWS CloudTrail 삭제
aws cloudtrail stop-logging \
--name cnasg-trail
Bash
복사
# CloudTrail 중지
aws cloudtrail delete-trail \
--name cnasg-trail
Bash
복사
# CloudTrail 삭제
aws s3 rb s3://cnasg-${NICKNAME}-audit-trail --force
Bash
복사
# Trail 로그 버킷 삭제
Terraform 자원 삭제
nohup sh -c "terraform destroy -auto-approve" > delete.log 2>&1 &
Bash
복사
# terraform 자원 삭제
Note:
Terraform 자원 삭제가 완료되면(약 3분 정도 대기) 정상적으로 자원 삭제가 되었는지 확인을 합니다.(cat delete.log)
여기까지 섹션 10 실습 - Amazon S3 보안 구성을 마칩니다.
수고하셨습니다 :)


