Home
home

섹션 10 - Amazon S3 보안 구성

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_iplab_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 보안 구성을 마칩니다.
수고하셨습니다 :)