원격으로 aws에 접속하거나, ec2에서 aws의 서비스(s3, rds, ...)를 이용하려고 하면 인증 정보가 필요하다. 지금까진 id
&password
또는 인증 및 인가를 할 수 있는 각종 secret key
를 이용하여 로그인 했다.
그리고 해당 비밀 정보들은 유출되지 않도록 관리하는 것이 중요하다.
환경변수나 여러가지 시크릿 툴을 이용하여 관리를 해 왔고 일반적인 방식이다. 다만 이 경우에도 서버의 환경변수 디렉터리나, 시크릿 정보, .env
, application-aws.yml
등 하드코딩을 이용하여 관리를 해야한다.
서버가 여러대인 경우, 하나의 정보가 변경되면 일일이 수정을 해야한다. 무엇보다 한 번의 실수로 인해 계정 정보가 털릴 수 있다는 점이 무섭다.
그런데, aws의 IAM role을 적절하게 사용한다면 aws서비스를 이용하는 계정 정보를 관리하지 않고, 인증 및 인가를 할 수 있게 해 준다.
aws 콘솔에 로그인 하려고 하면, 아래처럼 생긴 화면이 나온다.
지금까지는 잘 모르고 썼지만, 회사에 들어가거나 타인의 계정을 사용한다면, 두 개의 차이를 반드시 이해를 할 필요가 있다.
루트 사용자는, aws account에 대한 모든 권한을 가지고 있는 슈퍼유저이다.
생성할 때 사용했던 이메일과 비밀번호를 이용하여 로그인한다.
AWS Identity and Access Management(IAM)은 AWS 리소스에 대한 액세스를 안전하게 제어할 수 있는 웹 서비스입니다.
간단히 말해, 모든 권한을 가지고 있는 루트 사용자의 사용을 제한하고, 필요한 권한만 부여한 사용자를 만드는 것이다.
이것을 이해하기 위해, 인증(Authentication)과 인가(Authorization)의 개념을 알아야 한다. 다만 인증&인가에 대해 설명하진 않을것이니, 관련 내용을 알고 있다고 생각하겠다.
IAM에서는 4가지의 개념을 이해해야 한다.
account
를 이용한 로그인, accessKey
& secretAccessKey
를 이용한 권한 부여 방법이 존재한다.user
의 모임이며, group
에 부여된 정책이 있다면 속하는 유저들에게 정책을 상속한다.user
에게 정책을 덮어씌운다.role
을 만들어서, user
에게 부여한 역할을 자유롭게 스위칭 가능하다.user
, group
, role
에게 접근 가능한 자원, 불가능한 자원 등을 명시한 JSON문서이다.모든 권한이 부여된 루트 사용자를 사용하지말고, 각각의 권한에 맞는 사용자
와 정책
을 생성하여 역할
을 부여함으로써 관리가 쉽도록 해야한다.
적다보니 위의 내용은 IAM이 뭔지 설명을 하는 내용이 되어버렸다. 하지만 꼭 알아두면 좋은 개념이라고 생각한다.
그럼, IAM의 role을 이용하여, accessKey
와 secret access key
를 관리하지 않고 ec2 내부에서 aws서비스인 s3를 사용하도록 해 보자.
원래라면, 코드 내부에 aws 계정 정보를 넣거나, ec2
의 환경변수로 관리하여 aws cli
에 대한 접근 권한을 얻는 방식을 사용 할 것이다.
하지만, 코드레벨에 계정 정보가 들어가는 것은 매우 위험하다. 환경변수로 관리를 한다고 하더라도, 누군가 ec2
내부에 접근하였을 때 관리중이던 환경변수가 모두 노출될 수도 있다.
따라서 계정 정보는 코드에 존재하지 않게 하고, 하드코딩으로 값을 관리하지 않는 것이 좋다.
ec2
에서 s3
를 비롯한 aws service를 이용하고 싶을 때, 해당 ec2
에 role을 부여하면 IAM Role Metadata를 이용한 접근 권한이 자동으로 생성된다. 따라서 우리는 이 방식을 이용 할 것이다.
내가 설명하는 것 보다, 아래의 영상을 보는게 이해가 잘 될 것 같다. aws초보도 이해할 수 있게 설명하니, 꼭 한번 봤으면 좋겠다.
https://youtu.be/vOI_oAP_j04?si=lrqmi2dMA7OvEU9A
ec2
에서 접근할 aws service의 policy
를 설정한다.
나는 s3
를 사용할 것이므로 s3
에 대한 모든 권한을 주는 역할을 생성했다.
위 사진처럼 ec2
를 생성할 때 역할을 부여할 수 있다.
인스턴스 - 작업 - 보안 - IAM 역할 수정 을 통해 존재하는 인스턴스에도 역할을 부여할 수 있다.
이렇게 하면, 별도의 액세스 키를 사용하지 않고도 ec2
내부에서 위에서 지정한 aws 서비스를 사용 가능하다.
@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에 대한 자격 증명을 찾게 되는데, 아래와 같은 순서로 이루어진다.
- 환경 변수 (AWS_ACCESS_KEY_ID 및 AWS_SECRET_ACCESS_KEY)
- 자격 증명 파일 (~/.aws/credentials)
- AWS CLI 구성 파일 (~/.aws/config)
- EC2 인스턴스 메타데이터 (IAM Role)
로컬에서 개발할 때는 .aws/configure
에 액세스키와 시크릿액세스키를 넣어서 개발한다.(1번)
그리고 서버에 배포가 되면, ec2가 역할을 부여받았으니 4번에 의해 자격 증명을 얻는다.
@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에 인증된 사용자에게 객체를 읽을 수 있는 권한이 부여.
@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에 파일이 업로드 되는 것을 확인할 수 있다!