IAM Role을 이용하여 ec2에서 S3 api 호출하기(환경변수,access key X)

지누·2024년 7월 29일
0
post-thumbnail

왜?

원격으로 aws에 접속하거나, ec2에서 aws의 서비스(s3, rds, ...)를 이용하려고 하면 인증 정보가 필요하다. 지금까진 id&password 또는 인증 및 인가를 할 수 있는 각종 secret key를 이용하여 로그인 했다.

그리고 해당 비밀 정보들은 유출되지 않도록 관리하는 것이 중요하다.
환경변수여러가지 시크릿 툴을 이용하여 관리를 해 왔고 일반적인 방식이다. 다만 이 경우에도 서버의 환경변수 디렉터리나, 시크릿 정보, .env, application-aws.yml 등 하드코딩을 이용하여 관리를 해야한다.

서버가 여러대인 경우, 하나의 정보가 변경되면 일일이 수정을 해야한다. 무엇보다 한 번의 실수로 인해 계정 정보가 털릴 수 있다는 점이 무섭다.

그런데, aws의 IAM role을 적절하게 사용한다면 aws서비스를 이용하는 계정 정보를 관리하지 않고, 인증 및 인가를 할 수 있게 해 준다.

루트 사용자와 IAM

aws 콘솔에 로그인 하려고 하면, 아래처럼 생긴 화면이 나온다.
지금까지는 잘 모르고 썼지만, 회사에 들어가거나 타인의 계정을 사용한다면, 두 개의 차이를 반드시 이해를 할 필요가 있다.

루트 사용자


루트 사용자는, aws account에 대한 모든 권한을 가지고 있는 슈퍼유저이다.
생성할 때 사용했던 이메일과 비밀번호를 이용하여 로그인한다.

IAM이란?

AWS Identity and Access Management(IAM)은 AWS 리소스에 대한 액세스를 안전하게 제어할 수 있는 웹 서비스입니다.

간단히 말해, 모든 권한을 가지고 있는 루트 사용자의 사용을 제한하고, 필요한 권한만 부여한 사용자를 만드는 것이다.

이것을 이해하기 위해, 인증(Authentication)과 인가(Authorization)의 개념을 알아야 한다. 다만 인증&인가에 대해 설명하진 않을것이니, 관련 내용을 알고 있다고 생각하겠다.

IAM에서는 4가지의 개념을 이해해야 한다.

  • User, 사용자(인증)
    aws콘솔 또는 자원에 접근하여 사용하려는 사람이다. 흔히 생각하는 사용자이다.
    사용자를 인증하는 두 가지 방법은 account를 이용한 로그인, accessKey & secretAccessKey를 이용한 권한 부여 방법이 존재한다.
  • Group, 그룹(인증)
    user의 모임이며, group에 부여된 정책이 있다면 속하는 유저들에게 정책을 상속한다.
  • Role, 역할(인증)
    user에게 정책을 덮어씌운다.
    여러개의 role을 만들어서, user에게 부여한 역할을 자유롭게 스위칭 가능하다.
  • Policy, 정책(인가)
    특정 user, group, role에게 접근 가능한 자원, 불가능한 자원 등을 명시한 JSON문서이다.

루트 사용자 대신 IAM을 사용해야 하는 이유

모든 권한이 부여된 루트 사용자를 사용하지말고, 각각의 권한에 맞는 사용자정책을 생성하여 역할을 부여함으로써 관리가 쉽도록 해야한다.


적다보니 위의 내용은 IAM이 뭔지 설명을 하는 내용이 되어버렸다. 하지만 꼭 알아두면 좋은 개념이라고 생각한다.

그럼, IAM의 role을 이용하여, accessKeysecret access key를 관리하지 않고 ec2 내부에서 aws서비스인 s3를 사용하도록 해 보자.

0. IAM Role을 사용하지 않고 S3에 접근하려면?

원래라면, 코드 내부에 aws 계정 정보를 넣거나, ec2의 환경변수로 관리하여 aws cli에 대한 접근 권한을 얻는 방식을 사용 할 것이다.

하지만, 코드레벨에 계정 정보가 들어가는 것은 매우 위험하다. 환경변수로 관리를 한다고 하더라도, 누군가 ec2 내부에 접근하였을 때 관리중이던 환경변수가 모두 노출될 수도 있다.

따라서 계정 정보는 코드에 존재하지 않게 하고, 하드코딩으로 값을 관리하지 않는 것이 좋다.

ec2에서 s3를 비롯한 aws service를 이용하고 싶을 때, 해당 ec2role을 부여하면 IAM Role Metadata를 이용한 접근 권한이 자동으로 생성된다. 따라서 우리는 이 방식을 이용 할 것이다.

내가 설명하는 것 보다, 아래의 영상을 보는게 이해가 잘 될 것 같다. aws초보도 이해할 수 있게 설명하니, 꼭 한번 봤으면 좋겠다.
https://youtu.be/vOI_oAP_j04?si=lrqmi2dMA7OvEU9A

1. IAM Role 생성하기

ec2에서 접근할 aws service의 policy를 설정한다.
나는 s3를 사용할 것이므로 s3에 대한 모든 권한을 주는 역할을 생성했다.

2. ec2 인스턴스에 역할 부여하기

위 사진처럼 ec2를 생성할 때 역할을 부여할 수 있다.

인스턴스 - 작업 - 보안 - IAM 역할 수정 을 통해 존재하는 인스턴스에도 역할을 부여할 수 있다.

이렇게 하면, 별도의 액세스 키를 사용하지 않고도 ec2내부에서 위에서 지정한 aws 서비스를 사용 가능하다.

3. S3에 이미지 업로드하는 api 작성

S3Config

@Configuration
@RequiredArgsConstructor
public class S3Config {

    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public AmazonS3 amazonS3() {
        return AmazonS3ClientBuilder
                .standard()
                // 환경변수의 key 체크 후(로컬), 존재하지 않으면 IAM Role 메타데이터를 확인함(서버)
                .withCredentials(new DefaultAWSCredentialsProviderChain())
                .withRegion(region)
                .build();
    }
}

S3클라이언트를 빈으로 등록하는 과정이다.
new DefaultAWSCredentialsProviderChain()에서 aws에 대한 자격 증명을 찾게 되는데, 아래와 같은 순서로 이루어진다.

  1. 환경 변수 (AWS_ACCESS_KEY_ID 및 AWS_SECRET_ACCESS_KEY)
  2. 자격 증명 파일 (~/.aws/credentials)
  3. AWS CLI 구성 파일 (~/.aws/config)
  4. EC2 인스턴스 메타데이터 (IAM Role)

로컬에서 개발할 때는 .aws/configure에 액세스키와 시크릿액세스키를 넣어서 개발한다.(1번)
그리고 서버에 배포가 되면, ec2가 역할을 부여받았으니 4번에 의해 자격 증명을 얻는다.

S3Service

@RequiredArgsConstructor
public class S3Service {
    private final AmazonS3 amazonS3;

    @Value("${cloud.aws.s3.bucket}")
    private String bucket;

    /**
     * S3에 파일을 업로드 하는 서비스 로직
     * @param file
     * @return 파일 이름
     */
    public String uploadFile(MultipartFile file) {
        String fileName = createFileName(file.getOriginalFilename());
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(file.getSize());
        objectMetadata.setContentType(file.getContentType());
        String filePath;
        // 파일 업로드
        try (InputStream inputStream = file.getInputStream()) {
            amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata));
            // 올린 오브젝트에 대한 s3 urls
             filePath = amazonS3.getUrl(bucket, fileName).toString();
        } catch (IOException e) {
            throw new IllegalArgumentException("파일이 없습니다.");
            //TODO : 커스텀 에러 관리하기
        }
        log.info(filePath," is successfully created in S3");
        return filePath;
    }

    // caecae+UUID 로 파일 이름 생성하기
    private String createFileName(String fileName) {
        return "caecae-" + UUID.randomUUID().toString().concat(getFileExtension(fileName));
    }

    // 파일 확장자 추출하기
    private String getFileExtension(String fileName) {
        log.info("fileName : {}", fileName.substring(fileName.lastIndexOf(".")));
        return fileName.substring(fileName.lastIndexOf("."));
    }
}

파일을 업로드 할 때, 파일의 액세스 권한인 ACL을 설정 가능한데, 오류가 나서 일단 지워버렸다. s3버킷의 ACL권한과 관련이 있는 것 같은데 조금 더 찾아보도록 하겠다. ㅠ^ㅠ

withCannedAcl(CannedAccessControlList) 메서드는 S3 객체에 대한 권한을 설정하는 데 사용된다. CannedAccessControlList는 미리 정의된 ACL(Access Control List) 세트를 나타내며, 객체를 업로드할 때 S3 버킷에서의 액세스 권한을 쉽게 설정할 수 있다.

  • Private: 기본값으로, 객체에 대한 모든 권한이 객체 소유자에게만 부여.
  • PublicRead: 객체가 모든 사람에게 읽기 가능한 상태.
  • PublicReadWrite: 객체가 모든 사람에게 읽기 및 쓰기 가능한 상태.
  • AuthenticatedRead: AWS에 인증된 사용자에게 객체를 읽을 수 있는 권한이 부여.

S3Controller

@RestController
@RequiredArgsConstructor
public class S3Controller {
    private final S3Service s3Service;

    @PostMapping("/api/s3")
    public String upload(@RequestParam("file") MultipartFile file) {
        String filePath = s3Service.uploadFile(file);
        return filePath +"created!";

    }

    
}

테스트를 하기 위한 간단한 컨트롤러를 작성하고, postman을 이용하여 테스트를 했고, IAM Role을 이용하여 정상적으로 S3에 파일이 업로드 되는 것을 확인할 수 있다!

profile
열심히 살자😱

0개의 댓글