Skip to content

(문제해결) AWS S3 KEY정보 없이 보안 챙기며 Spring에 연동하기

김도선 edited this page Oct 2, 2024 · 1 revision

Spring에 S3적용할 때, IAM으로 보안 곁들이기.

AWS S3란?

Amazon Simple Storage Service(Amazon S3)는 AWS에서 제공하는 객체 스토리지 서비스이다.

S3는 대규모 데이터를 안전하게 저장하고 관리할 수 있다. 주로 파일 저장소, 백업, 데이터 아카이빙, 로그 저장, 정적 호스팅에 사용된다.

대부분의 S3를 Spring에 적용하는 방법

혹시 S3를 스프링 프로젝트에 적용할 때, 아래와 같은 코드를 사용하고 있지는 않은가?

spring:
  cloud:
    aws:
      credentials:
        accessKey: <YOUR_ACCESS_KEY>
        secretKey: <YOUR_SECRET_KEY>
      s3:
        bucket: <BUCKET_NAME>
      region:
        static: <REGION>
      stack:
        auto: false

대부분의 S3를 처음 사용해보는 개발자는 이렇게 yml에 s3의 key정보를 넣고 사용하게 된다. 하지만 이러한 방법은 문제점이 있다.

위 S3사용법의 보안 문제점

accessKey와 scretKey는 AWS 계정에 대한 자격증명을 위한 키이다. 이러한 키가 public github나 인터넷에 올라가게 되면 나의 AWS 서비스는 침범 당할 수 있다. 그러면 해당 설정 YML파일을 .gitignore에 추가해서 노출 안되게 하면 되지 않나? 라는 생각을 할 수도 있다. 하지만 그런 생각은 개인 프로젝트까지 맞는 말이다. 팀원과 모든 내용을 공유한다고 해도 직접 작업한 사람이 아니면 쉽게 잊게 된다. 해당 설정이 담긴 yml을 이름만 바꿔도 .gitignore에서 제외될 뿐더러, 어떠한 방법으로도 노출되려면 노출될 수 있다. 그렇기 때문에 실제 회사를 가게 되면, 애초에 개발자에게 key정보를 전혀 주지 않을 가능성이 높다. 그래서 애초에 key정보를 모르는 상태에서 spring에서 s3로 접속하는 방법을 알아보자

S3, Spring 다이어그램

오늘 우리가 만들 구조를 다이어그램으로 알아보자 image1

  1. s3 버킷에 사진을 요청해서 받아올수 있는 역할을 생성
  2. EC2 인스턴스에 해당 역할을 부여
  3. Spring Application 실행 시, 해당 Application이 실행된 EC2로부터 역할을 찾는다
  4. S3에 접근 가능한 역할로 인증받고 S3에 접근한다. 위에서 설명한 단계에서 반복적으로 나오는 단어가 있다. 그것은 역할이다. 미리 EC2 인스턴스에 특정 행동을 할 수 있는 역할을 부여하고, 해당 역할로 Application실행시, S3에 접근하고 있다. 그러한 역할을 부여할 수 있는 AWS 서비스가 바로 IAM이다.

AWS IAM이란?

**IAM(Identity and Access Management)**는 AWS에서 사용자가 누가 무엇에 접근할 수 있는지를 관리하는 서비스이다. 쉽게 말해, **“누구에게 어떤 권한을 줄 것인가"**를 결정하고 관리하는 시스템입니다. 위에서 설명한 그림에서는 그 ‘누구’가 ec2가 된 케이스이다. IAM에서는 ‘누구’를 세가지로 분류가능하다. 각 ‘누구’에 다른 정책을 부여할 수 있다.

IAM 주요 개념

  1. 사용자(User):
    • AWS에 로그인해서 서비스를 사용할 수 있는 사람. 각 사용자마다 개별 계정이 있고, 각자의 권한이 다를 수 있다.
  2. 그룹(Group):
    • 여러 사용자를 하나로 묶어, 한 번에 같은 권한을 부여할 수 있는 방법. 예를 들어, "개발자 그룹", "관리자 그룹"처럼 구성할 수 있다.
  3. 역할(Role):
    • 사람이 아닌, AWS 서비스나 애플리케이션이 사용할 수 있는 권한. 예를 들어, EC2 인스턴스가 S3 버킷에 접근하려면 IAM 역할을 할당해 줄 수 있다. 역할을 이용하면, 비밀번호나 키 없이도 안전하게 AWS 리소스에 접근할 수 있다.
  4. 정책(Policy):
    • 누가 무엇을 할 수 있는지를 구체적으로 정의한 규칙이다. 예를 들어, "S3 버킷에서 파일을 읽을 수 있지만 쓸 수는 없다"는 규칙을 정책으로 정의할 수 있다. 정책을 사용자, 그룹, 또는 역할에 부여하여 권한을 설정할 수 있다. 위 주요 개념을 읽었다면, 위에서 그렇게 역할 이라는 단어를 자주 꺼낸 이유를 알 수 있을 것이다. 바로 EC2(AWS 서비스)에 S3에 접근할 수 있는 권한을 줄 것이기 때문에, 역할을 생성, 부여라는 단어를 사용한 것이다. 그러면 이제 실제로 적용하는 방법을 알아보자

Spring, S3, IAM 적용하기

준비물

EC2, S3, Spring 프로젝트 이미 EC2에 배포된 Spring프로젝트이면 좋겠다. 배포하는 것 까지는 이 포스트에서 다루지 않겠다. S3도 미리 만들어 놓기를 바란다. 그리고 버킷이름을 어딘가에 복사해두자.

S3에 IAM 역할 부여하기

해당 단계는 4가지 단계로 나누어서 설명하겠다.

  1. IAM 정책 생성: S3에 접근할 수 있다 라는 정책을 먼저 생성해야된다.
  2. IAM 역할 생성: EC2가 위에서 생성한 정책을 할 수 있다는 역할을 생성해야된다.
  3. EC2 역할 부여: EC2에서 위에서 생성한 역할을 할 수 있다고 부여 해주어야 한다.
  4. Spring에서 S3 인증하기: EC2환경의 Spring 프로젝트에서 S3에 인증을 해본다.

1. IAM 정책 생성

image2 IAM → 정책 → 정책생성을 선택한다 그리고 서비스 선택 → S3 선택 그러면 S3에 관한 여러가지 엑세스 수준이 나오는데, 필자는 사진 넣고 빼는 것만 하면 되기에, PutObject, GetObject만 선택한다. 필요한 것을 더 추가해서 넣어도 된다. image3 그리고 리소스를 선택해야 하는데, s3의 어느 버킷에 접근 권한을 줄 것 인가를 선택하는 것이다. 모든 버킷에 접근해도되면 모두를 선택하고, 특정 버킷에 허용하고 싶으면 특정을 선택해서 S3에 생성해둔 버킷 이름을 넣는다. 만약 버킷안에서도 특정 오브젝트그룹에만 접근하게 하고 싶으면 obejct name도 작성한다. 예를들어, dodo-bucket-test버킷의 public이라는 패키지의 모든 오브젝트에 접근하게 해주고 싶으면, Resource object name에 **“public/*”**을 하면된다. 필자는 모든 object name을 허용했다. image4 다음은, 적절한 정책이름을 주고 정책생성을 클릭하면 된다.

2. IAM 역할 생성

image5 IAM→역할→역할생성을 한다. 신뢰할 수 있는 엔터티 유형은 AWS 서비스로한다. 그리고 서비스는 EC2를 선택한다. 다음을 눌린다. image6 아까 생성한 정책이름을 검색해서 선택하고 다음을 눌린다 image7 적절한 역할 이름을 부여하고 역할 생성을 눌리면 된다. 다음을 눌리기전에 몇가지 항목이 더 있는데, 요약이니까 보고 넘어가면 된다.

3. EC2 역할 부여

image8 적용시킬 EC2에 들어가서 작업→보안→IAM 역할 수정을 누른다 image9 그리고 생성한 IAM 역할을 부여하고 IAM 역할 업데이트를 누른다.

4. Spring에서 S3 인증하기

이제 해당 EC2에서는 키 없어도 S3에 Put과 Get이 가능하게 되었다. 하지만 제일 중요한 스프링에서는 어떻게 접근하는 지는 알려주지 않았다. 이번 단계는 IAM 부여 전, 부여 후 나누어서 보여주겠다. 필자의 s3 sdk 버전은 아래와 같다

implementation "software.amazon.awssdk:s3:2.26.21"

IAM 적용 전 코드

public class S3StorageManage {

    // application.yml에서 값을 불러옴
    @Value("${aws.access-key}")
    private String accessKey;

    @Value("${aws.secret-key}")
    private String secretKey;

    private final S3Client s3Client;

    public S3StorageManager() {
        this.s3Client = S3Client.builder()
                .credentialsProvider(
                        StaticCredentialsProvider.create(
                                AwsBasicCredentials.create(accessKey, secretKey)
                        ))
                .region(Region.AP_NORTHEAST_2)
                .build();
    }
}

세부적인 구현 방법은 다를 수 있으니 초기화 하는 부분만 참고하길 바란다.

코드에서 보이 듯이 @Value 어노테이션을 활용해서 yml에 있는 accessKey와 secretKey값을 참조하고 있다.

그리고 해당 key를 통해 Aws인증을 거치고 있다.

이러한 코드의 문제는 서두에서 이야기 했으니 넘어가겠다.

IAM 적용 후 코드

public class S3StorageManage {

    private final S3Client s3Client;

    public S3StorageManager() {
        this.s3Client = S3Client.builder()
                .credentialsProvider(
                        InstanceProfileCredentialsProvider.create())
                .region(Region.AP_NORTHEAST_2)
                .build();
    }
}

필드에 아무런 정보도 없게 됐다. InstanceProfileCredentialsProvider.create() 를 통해 인증을 거치고 있는 것으로 보인다. 이렇게 개발자는 코드에 민감정보인 Key를 올리지 않아 보안적으로 노출될 위험은 없어졌다.

그러면 어떻게 인증을 하고 있는가?

이 부분은 동작 원리 내용이라 궁금하면 읽고 아니면 넘어가길 바란다.

IAM은 어떻게 EC2의 어플리케이션에서 인증을 하고 있는 것인가?

해당 create()내부를 보면 이러한 코드를 찾을 수 있다.

private String[] getSecurityCredentials(String imdsHostname, String metadataToken) {
        ResourcesEndpointProvider securityCredentialsEndpoint =
            new StaticResourcesEndpointProvider(URI.create(imdsHostname + SECURITY_CREDENTIALS_RESOURCE),
                                                getTokenHeaders(metadataToken));
...생략

여기서 주목할 부분은 URI.create(imdsHostname + SECURITY_CREDENTIALS_RESOURCE 은 이 부분이다.

이 URI는 변수를 찾아서 바꿔 보면 http://169.254.169.254/latest/meta-data/iam/security-credentials/ 가 나오게 된다.

169.254.169.254는 EC2 인스턴스가 자신과 관련된 메타데이터 및 IAM 역할의 자격 증명을 조회하는 데 사용하는 특별한 내부 IP 주소이다.

/latest/meta-data/iam/security-credentials 를 통해 해당 ec2에 부여된 iam을 역할을 찾게 된다.

더 자세하게 말하면 http://169.254.169.254/latest/meta-data/iam/security-credentials/{역할이름} 을 통해 가져온다.

더 자세한 내용은 **인스턴스 메타데이터를 사용하여 EC2 인스턴스를 관리** 를 참고하길 바란다.

이렇게 해당 EC2에 적절한 S3 접근권한이 있다면 S3Client 객체를 생성해서 사용할 수 있게 된다.

이후 동작은 기존에 하던 코드대로 하면된다. 이 글에서는 Key정보를 코드에 노출되지 않도록 하는 것에 집중하여 이후 이야기는 하지 않겠다.

마무리

IAM이 무엇인지, EC2에 IAM역할을 부여하기, Spring에서 IAM인증을 거치는 방법까지 알아보았다.

이렇게 하면, 이제 로컬 환경에서는 Spring IAM인증 과정에서 실패하게 될 것이다.

이러한 문제는 필자는 Local환경과 ec2환경 각각 다른 빈을 선택하도록 해결했다. 다른 해결 방법도 여러가지 있으니, 여러가지 알아보기를 바란다.

참조

IAM 역할을 사용하여 Amazon EC2 인스턴스에서 실행되는 애플리케이션에 권한 부여

인스턴스 메타데이터를 사용하여 EC2 인스턴스를 관리

Amazon EC2에 대한 IAM 역할

Clone this wiki locally