[AWS] VPC 엔드포인트를 사용하여S3 – EC2간 프라이빗 통신 구성하기

안녕하세요, 누리클라우드입니다. 클라우드 사용이 증가하면서 AWS S3를 사진, 영상, 파일 등을 보관하거나 업로드 저장소로 잘 활용하고 있는 분들이 많아졌습니다. 그런데, S3버킷의 접근제어 방법이 다양하고 복잡하다 보니 개발 단계에서 쉬운 기능 구현을 위해 퍼블릭으로 오픈해 두고 운영단계에서도 그대로 오픈해 두는 경우가 있어 보안상 취약한 구성을 보게 됩니다.

이번 글에서는 EC2에서 S3 버킷에 접근할 때 퍼블릭 영역이 아닌 프라이빗 영역에서만 접근할 수 있도록 하여 보다 안전한 통신 구성을 하는 방법을 알아보겠습니다.

들어가기 전에

본 기능 구성에 필요한 VPC Endpoint는 유료이며 약 $10/월 정도 고정 비용과 $3.5/1TB의 트래픽 비용이 발생합니다.
본 기능 구성에 필요한  S3 게이트웨이용 VPC Endpoint는 무료이며 동일 리전에서는 S3-EC2 간 전송비용도 무료입니다. 참고

기본 개념

기본 구성도는 아래와 같습니다. 프라이빗 서브넷에 있는 EC2가, 좌측에서는 퍼블릭 영역을 통해 S3와 통신하는 구성이며, 우측은 VPC Endpoint를 추가하여 프라이빗 영역으로 S3와 통신하는 구성입니다.

VPC Endpoint구성 전

VPC Endpoint구성 후

구성하기

1. EC2 인스턴스 생성

먼저 S3와 통신을 테스트해 볼 수 있는 EC2를 생성합니다. 외부 네트워크와 단절되어 있는 프라이빗 서브넷을 만들고 여기에 작은 타입의 EC2를 생성하여 배치합니다. 프라이빗 영역의 인스턴스에 접속하기 위해서는 '베스천 호스트', 'Systems Manager' 등 여러가지 방법이 있지만 여기서는 'EC2 직렬콘솔'을 사용하여 접속하도록 하겠습니다. 'EC2 직렬콘솔'은 마치 물리서버에 시리얼콘솔을 연결하여 원격에서 편리하게 서버를 네트워크에 연결하지 않고도 관리할 수 있는 기능과 같은 것으로 매우 편리하고 비용이 발생하지 않습니다.
다만, 이 기능은 Nitro 계열 타입 이상에서만 지원하므로 사용을 위해 t3나 c5 정도 이상의 타입으로 인스턴스를 생성합니다.
또 한가지 미리 알아 두면 좋은 것은, 직렬콘솔 연결은 결국 서버 계정의 패스워드를 입력해야 하고 이것은 결국 키페어 인증으로만 기본 구성된 EC2 인스턴스에 미리 접속하여 패스워드를 업데이트하여 활성화해야만 하는 것입니다. 이럴 바에는 차라리 처음부터 '베스천 호스트'를 구성하거나 'Systems Manager'를 사용할 수 있는 환경을 구성하는 편이 나을 텐데요,
아래와 같이, 처음 인스턴스를 생성할 때 '사용자 데이터'에 한 줄로 패스워드를 변경할 수 있는 명령어를 입력해 두면 인스턴스 생성 과정에서 자동으로 root 계정의 패스워드를 원하는 문자열로 입력해줍니다.

#!/bin/bash
echo 'my!p4sswd' | passwd –stdin root

인스턴스에 접속하는 방법은, AWS 콘솔의 EC2 인스턴스 페이지에서 해당 인스턴스를 선택하고 '작업'>'모니터링 및 문제해결'>'EC2 직렬콘솔'을 클릭하면 아래와 같은 접속창이 뜨고 위에서 인스턴스 생성 과정에서 입력한 서버계정 아이디와 패스워드로 로그인 할 수 있습니다.

2. S3 버킷 정책 설정(선택)

VPC와 통신하기를 원하는 S3 버킷에서 버킷정책을 아래와 같이 업데이트합니다. 버킷 정책에 Deny정책이 없다면 이 정책이 설정되어 있지 않아도 됩니다.

{
"Version": "2012-10-17",
"Id": "Policy1415115909152",
"Statement": [
{
"Sid": "Access-to-specific-VPCE-only",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::upload-test-bucket/*",
"Condition": {
"StringEquals": {
"aws:sourceVpce": "vpce-1a2b3c4d"
}
}
}
]
}

위의 버킷 이름과 VPC 엔드포인트 이름은 예시이므로 실제 적용할때는 수정해야 합니다.


S3의 메인 정책인 '퍼블릭 엑세스 차단 정책'은 외부 엑세스에 대해 차단되어 있어도 앞으로 구성할 엔드포인트를 통한 VPC와의 통신에는 문제가 없습니다.

3. VPC 엔드포인트 생성

이제 VPC 엔드포인트를 생성하게 되는데, VPC>엔드포인트 메뉴에서 생성할 수 있으며, 아래와 같이 'AWS 서비스'를 선택 후, s3를 검색하여 나오는 두개의 서비스 중 유형이 Gateway인 서비스를 선택하고, 원하는 VPC와 서브넷을 선택하고 마지막으로 정책을 선택하는 곳에서는 '모든 엑세스'를 선택하여 생성을 완료합니다.

접속 테스트

테스트용 인스턴스가 S3 버킷에 접근할 수 있도록 IAM 역할 중 'S3FullAccessRole'을 자격 부여합니다.
콘솔 메뉴: EC2 > 인스턴스 > [선택 체크] > 작업 > 보안 > IAM 역할 수정

테스트용 인스턴스에 'EC2 직렬연결'을 통해 접속합니다.
콘솔 메뉴: EC2 > 인스턴스 > [선택 체크] > 작업 > 모니터링 및 문제 해결 > EC2 직렬 콘솔

이제 대상 S3 버킷에 파일을 업로드하고 조회해 봅니다.

[root@ip-10-10-100-23 ~]# touch testfile
[root@ip-10-10-100-23 ~]# aws s3 cp testfile s3://upload-test-bucket
upload: ./testfile to s3://upload-test-bucket/testfile

[root@ip-10-10-100-23 ~]# aws s3 ls s3://upload-test-bucket
2021-08-18 08:13:32 0 testfile

대상 버킷에 'testfile'이 성공적으로 업로드 되고 목록 조회가 됩니다.

그런데, 과연 VPC 엔드포인트를 통해서 통신하고 있는지는 어떻게 확인해 볼 수 있을까요? 프라이빗 서브넷에 있는 인스턴스라 해도 NATGateway를 구성하였다면 외부 인터넷 통신이 가능하므로 정말로 VPC 엔드포인트를 경유하는지 확인이 필요합니다. 테스트 환경에서는 간단히 NATGateway를 제거해 보고 여전히 정상적으로 S3에 대한 작업이 가능한지 보면 됩니다.
하지만, 운영중인 환경에서는 네트워크 구성을 변경하는 것이 쉽지 않습니다. 그래서 아래와 같이 S3 버킷 정책에서 "Effect"만 Allow -> Deny로 수정해 보면,

{
"Version": "2012-10-17",
"Id": "Policy1415115909152",
"Statement": [
{
"Sid": "Access-to-specific-VPCE-only",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::upload-test-bucket/*",
"Condition": {
"StringEquals": {
"aws:sourceVpce": "vpce-1a2b3c4d"
}
}
}
]
}

아래와 같이 파일을 업로드 다운로드하는 작업이 거부되는 것을 통해 엔드포인트의 정상 동작 여부를 확인할 수 있습니다.

[root@ip-10-10-100-23 ~]# aws s3 cp s3://upload-test-bucket/testfile .
fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden

[root@ip-10-10-100-23 ~]# aws s3 cp testfile s3://upload-test-bucket/testfile
upload failed: ./testfile to s3://upload-test-bucket/testfile An error occurred

맺음말

이상으로 VPC 엔드포인트를 사용하여 S3와 EC2 사이에 안전한 통신을 하는 방법을 살펴봤는데요, 일반적으로 VPC 엔드포인트는 유료이지만, S3를 위한 게이트웨이 유형 VPC 엔드포인트는 무료이니 구성해서 쓰지 않을 이유가 없겠습니다.   VPC 엔드포인트가 비록 유료이기는 하지만 조금이라도 안전한 시스템 구성을 생각한다면 그 기능에 비해 저렴한 것이고 충분히 그 값을 지불할 만하다고 생각됩니다.