Skip to content

[CHORE] File 도메인 prod 배포 선행 조건: AWS S3 인프라 구성과 환경변수 배선 #291

Description

@d1ng1724

작업 배경

File 도메인 Phase 1~4(#84 → #85, #86)의 코드 구현이 모두 Merge되어 Epic 완료 조건을 충족했다. 그러나 File 기능을 AWS S3 기반 prod Profile로 운영하려면 코드로 해결할 수 없는 인프라 선행 조건이 남아 있다. Epic #84에서 이 항목들을 분리한다.

상세 근거는 구현 명세 docs/file/file-domain-plan.md §14.1~§14.3.

현재 상태

정확한 출발점은 다음과 같다. 아직 prod Profile로 배포되는 경로 자체가 없다.

항목 현재
CD 트리거 .github/workflows/cd.yml:4 — push: branches: [develop] 전용. prod 배포 Job 없음
배포 Profile compose.yaml:121 — SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-develop}, CD가 값을 넘기지 않아 항상 develop
현재 Storage develop Profile은 MinIO 사용(compose.yaml:145 APP_FILE_STORAGE_ENDPOINT: http://minio:9000)
S3 설정 application-prod.yaml:23-38에만 존재. 현재 어떤 배포에서도 로드되지 않음

application-prod.yaml은 access-key/secret-key를 의도적으로 선언하지 않아 AWS SDK DefaultCredentialsProvider가 EC2 Instance Profile(IAM Role)에서 자격증명을 받는다. FILE_STORAGE_BUCKET·FILE_STORAGE_REGION은 기본값 없이 선언되어 미주입 시 기동을 거부한다. autoCreateBucket 기본값은 false라 운영 Bucket은 앱이 만들지 않는다 — 사람이 미리 만들어야 한다.

따라서 아래 항목 중 하나라도 빠지면 prod 배포 시 기동 실패 또는 모든 S3 호출 실패로 이어진다.

작업 범위

prod Profile 운영을 위한 AWS 인프라 구성과, 그 값을 배포 환경까지 전달하는 최소한의 저장소 배선.

애플리케이션 도메인 코드는 변경하지 않는다(#85 / #86에서 완료). 다만 아래 §4는 compose.yaml / cd.yml 변경이 필요하다.

세부 작업

실행 순서대로 수행한다. IAM 정책의 Resource가 Bucket 이름에 의존하므로 Bucket을 먼저 만든다.

1. S3 Bucket 생성 + 보안 설정

  • S3 Bucket 생성 (Region 결정 후 FILE_STORAGE_REGION과 반드시 일치시킨다)
  • Block Public Access 4개 항목 전부 활성화 — 구현 명세 §17/§42 "Bucket Public 금지"
  • 기본 암호화(SSE-S3 이상) 활성화 — 학생 지원 서류 등 개인정보가 저장된다

2. S3 최소 권한 IAM Role 생성

  • 아래 정책으로 Role 생성

    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
      "Resource": "arn:aws:s3:::<bucket>/*"
    }

    s3:ListBucket은 부여하지 않는다(Object Key를 항상 알고 접근한다). Bucket 생성·정책 변경 권한도 부여하지 않는다 — 운영에서 앱이 Bucket을 만들지 않는다(autoCreateBucket=false).

  • EC2 Instance에 위 Role을 Instance Profile로 연결

3. EC2 Metadata hop limit 상향

  • hop limit을 2로 올린다 — 앱이 Container 안에서 실행되어 IMDS까지 hop이 하나 더 필요하다. 기본값 1이면 DefaultCredentialsProvider가 자격증명을 얻지 못해 모든 S3 호출이 실패한다

    aws ec2 modify-instance-metadata-options \
      --instance-id <instance-id> \
      --http-put-response-hop-limit 2 \
      --http-tokens required

    재부팅 없이 즉시 적용된다. --http-tokens required(IMDSv2 강제)를 반드시 함께 준다.

4. 배포 환경에 값 주입 (저장소 변경 포함)

  • FILE_STORAGE_BUCKET 주입 — compose.yaml:155에 이미 전달 라인이 있으나 기본값이 geti-local이므로 운영 값으로 덮어써야 한다
  • FILE_STORAGE_REGION 전달 경로 추가 — 현재 compose.yaml·cd.yml 어디에도 이 변수를 전달하는 라인이 없다. prod Profile에서는 기본값이 없어 기동이 거부된다
  • prod 배포 시 SPRING_PROFILES_ACTIVE=prod가 실제로 전달되도록 배포 경로 확정 (현재는 항상 develop)
  • 실제 Bucket 이름·Region 값은 yaml이 아니라 배포 환경/Secret으로만 주입한다 (명세 §33 — 실제 Secret 값을 저장소에 두지 않는다)

5. 배포 후 검증

  • 기동 로그에 자격증명 오류가 없는지 확인
  • 업로드 → Presigned URL 다운로드 1회 수동 확인
  • 프로필 이미지 등록(PATCH /api/v1/me/profile, profileImageFileId) → 조회 시 실제 URL 확인

완료 조건

  • prod Profile로 애플리케이션이 정상 기동한다(환경변수 누락으로 인한 기동 거부 없음)
  • prod 환경에서 파일 업로드가 성공하고 Presigned URL로 다운로드가 성공한다
  • S3 Bucket이 Public으로 노출되지 않는다(Block Public Access 4개 항목 전부 ON)
  • Bucket 기본 암호화가 활성화되어 있다
  • IAM Role 권한이 PutObject/GetObject/DeleteObject 3개로 제한되어 있다
  • EC2 Metadata hop limit이 2, http-tokens가 required다
  • 프로필 이미지·기업 로고 조회 응답에 실제 Presigned URL이 내려온다

제외 범위

영향 및 위험

  • 이 작업 전까지 prod Profile에서 File 기능 전체가 동작하지 않는다. 업로드·다운로드뿐 아니라 프로필 이미지 등록(PATCH /api/v1/me/profile)과 기업 로고 등록도 실패한다.
  • hop limit 상향은 IMDS 접근 경로를 넓히므로 --http-tokens required(IMDSv2 강제)를 함께 적용해 SSRF를 통한 자격증명 탈취를 막는다. IMDSv1을 열어두면 안 된다.
  • Bucket에 학생 지원 서류 등 개인정보가 저장되므로 Block Public Access 누락은 곧 개인정보 유출이다.
  • Bucket Region과 FILE_STORAGE_REGION이 다르면 SDK가 잘못된 Endpoint로 요청해 실패한다.
  • Presigned URL 기본 TTL은 15분(FileStorageProperties.DEFAULT_PRESIGNED_URL_TTL_MINUTES)이다. 검증 중 URL이 만료되면 인프라 오류로 오인하기 쉽다.

검증 방법

CLI로 각 설정값을 확인한다.

# hop limit = 2, http-tokens = required
aws ec2 describe-instances --instance-ids <instance-id> \
  --query 'Reservations[].Instances[].MetadataOptions'

# Block Public Access 4개 항목 전부 true
aws s3api get-public-access-block --bucket <bucket>

# 기본 암호화 설정
aws s3api get-bucket-encryption --bucket <bucket>

# Bucket Region이 FILE_STORAGE_REGION과 일치하는지
aws s3api get-bucket-location --bucket <bucket>
  • 애플리케이션 기동 로그에 자격증명/Bucket 오류가 없는지 확인
  • prod 환경에서 업로드 → 다운로드 → 프로필 이미지 등록·조회 수동 확인

관련

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

🌿 size: m일반적인 크기의 기능 또는 개선 작업🏗️ infrastructure배포, 컨테이너, 네트워크 및 인프라 관련 작업📎 area: attachment첨부파일 업로드, 저장 및 다운로드 영역📝 ready요구사항이 정리되어 작업을 시작할 수 있는 상태🔒 security인증, 인가, 취약점 및 보안 관련 작업🚀 area: deployment배포, 운영 환경 및 릴리스 관련 영역🟠 priority: high다음 개발 주기에서 우선 처리해야 하는 중요 작업🧹 chore기능과 직접 관련 없는 유지보수 및 정리 작업

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions