개요
이번 게시글은 OpenSearch에서 Snapshot Repository를 설정하여 로그를 백업하는 방법을 작성하려 했었다. 그러나 해당 작업을 구현하던 중 Terraform으로 생성한 IAM Role에서 이상이 있는 것을 확인하게 되었고, 이를 인지하기까지 많은 시간이 소요되었다. 이번 게시글에서는 Terraform Assume Role 문제를 해결하기 위해 겪은 과정을 공유하고자 한다.
Assume Role
Assume Role은 AWS STS를 이용해 특정 역할(Role)을 임시로 부여 받아 엑세스 권한을 획득하는 방식을 뜻한다. 각각의 개념을 먼저 알아보고, Assume Role이 어떤 역할을 하는지 살펴보도록 하자.
AWS STS
AWS STS(Security Token Service)는 특정 AWS 리소스를 사용할 수 있는 '임시 보안 자격 증명'을 생성하는 서비스이다. 서비스의 이름과 같이 생성된 '임시 자격 증명'은 단기적으로만 사용할 수 있으며, 자격 증명이 만료 되면 그 어떠한 종류의 권한 요청도 실패하도록 구성되어 있다.
IAM User 또는 AWS 리소스는 AWS STS를 통해 '임시 자격 증명'을 요청할 수 있으며, 해당 자격 증명에 특정 역할(Role)에 설정된 권한을 부여받아 사용할 수 있게 된다. 이를 통해 '단기적으로 사용할 수 있는 권한을 소유한 임시 자격 증명'을 생성할 수 있게 되며, 이 과정 자체를 Assume Role이라고 부르게 된다.
Assume Role
Assume Role은 특정 역할(Role)을 일시적으로 부여받아 AWS 리소스에 접근할 수 있도록 도와준다. IAM User에 영구적인 Role을 부여하는 것과 달리, Assume Role은 일시적인 권한을 부여하여 지정된 시간 이후에는 해당 권한이 제거되므로 보안을 강화할 수 있다.
예를 들어, EC2 인스턴스가 S3 Bucket에 접근하기 위해 Assume Role을 사용하면, EC2는 일시적으로만 S3 Bucket을 사용할 수 있게 된다. 이는 IAM User뿐만 아니라, 다양한 AWS 서비스에서도 사용 가능하다.
Assume Role 설정하기
Assume Role을 사용하려면 먼저 IAM Role을 생성하고, 신뢰 관계(Trust Relationship)를 정의해야 한다. 신뢰 관계는 특정 주체(예: EC2 인스턴스, OpenSearch 등)가 해당 Role을 사용할 수 있는 권한을 명시한다. 이렇게 설정된 주체는 해당 역할(Role)을 'Assume'하여 필요한 권한을 획득하고 원하는 리소스에 접근할 수 있다.
이슈 해결하기
이번 글에서 이야기할 내용은 다소 복잡하게 들릴 수도 있다. IAM Policy, Role, User와 익숙하지 않은 OpenSearch 도메인 및 Access Policy, Security Role, Snapshot Repository 생성 스크립트를 실행하는 등 여러 과정이 포함되어 있다. 하지만 간결한 의식의 흐름을 전달하기 위해 일부 상황을 각색하여 작성할 예정이다.
이슈 발생 상황
S3 Bucket을 Snapshot Repository로 사용하려면 OpenSearch에 특정 권한을 부여해야 하는데, Terraform으로 생성한 IAM Role에서 Assume Role이 정상적으로 동작하지 않았다. 이로 인해 S3 접근 권한을 획득하지 못했는데, 이 문제를 해결하기 위해 어떤 의식의 흐름이 있었는지 하나씩 살펴보도록 하자.
1. Terraform으로 생성된 Assume Role이 S3 Bucket 권한 획득에 실패했다.
{
"error": {
"root_cause": [
{
"type": "repository_verification_exception",
"reason": "[js-outbound-logs-repo2] path [js-outbound-logs] is not accessible on cluster-manager node"
}
],
"type": "repository_verification_exception",
"reason": "[js-outbound-logs-repo2] path [js-outbound-logs] is not accessible on cluster-manager node",
"caused_by": {
"type": "i_o_exception",
"reason": "Unable to upload object [js-outbound-logs/tests-<SIGN_PRIVATE_KEY>/master.dat] using a single upload",
"caused_by": {
"type": "sts_exception",
"reason": "User: arn:aws:sts::832035452523:assumed-role/cp-sts-grant-role/swift-ap-northeast-2-prod-<AWS_ACCOUNT_ID> is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::<AWS_ACCOUNT_ID>:role/os-repo-prg-OPENSEARCHRepositoryRole (Service: Sts, Status Code: 403, Request ID: f28a9073-f14e-4282-8628-c87b4842929b)"
}
}
},
"status": 500
}
해당 전문은 IAM User가 OpenSearch의 Snapshot Repository 생성 API 를 호출한 응답값이다. reason
에 출력된 내용을 요약하면, S3 버킷 하위에 위치한 js-outbound-logs
디렉토리와 js-outbound-logs-repo2
라는 OpenSearch Snapshot Repository를 연동하지 못한 것이다.
전문 하위에 출력된 응답값은 S3 Bucket에 접근하지 못한 이유에 대해 상세하게 설명 되어있는데, 832035452523 AWS Account ID에서 STS를 통해 os-repo-prg-OPENSEARCHRepositoryRole
을 발급하려 했으나 인증 실패로 인해 STS를 발급하지 못해 실패한 것으로 확인된다.
여기서, 나를 더욱 미궁에 빠뜨린 의문은 832035452523에 해당하는 계정은 Terraform으로 배포된 AWS Account ID와 전혀 상관 없는 값이며 그 어느 곳에서도 해당 계정에 대한 ID를 찾지 못했다.
2. 신뢰 관계(Trust Relationship), 정책(Policy)는 모두 정상이었다.
STS가 Assume Role을 발급받기 위한 역할(Role)은 os-repo-prg-OpenSearchRepositoryRole
인데, 해당 역할에 정의된 잘못된 설정값이 있는지 검토해보았다.
가장 먼저, 해당 역할은 OpenSearch(es.amazonaws.com
) 서비스에서 STS 발급을 요청하도록 구성되어 있다. 그로 인해, 신뢰 관계 또한 동일한 서비스가 Assume Role을 발급받을 수 있도록 sts:AssumeRole
작업(Action)을 허용하고 있으며, S3에 접근할 수 있도록 os-repo-prg-OpenSearchRepositoryUserPolicy
정책(Policy)을 설정하고 있다.
상세한 역할(Role)과 정책(Policy)를 확인하고 싶다면 Github Repository 를 참고하기 바란다.
3. Assume Role 발급을 요청하는 IAM User를 변경해 보았다.
위에서 확인한 것처럼 Assume Role의 대상인 역할(Role)은 모두 정상적인 것으로 확인되었고, 문제가 발생할 여지가 없다는 것을 확실하게 검증하게 되었다. 그렇다면, "Assume Role을 요청하는 주체에서 문제가 발생하는 것이 아닐까?" 라는 생각이 들어 Assume Role을 요청하는 주체를 변경해보았다.
Assume Role 발급을 요청하는 주체는 IAM User의 Access Key를 OpenSearch에 등록하여, OpenSearch Snapshot Repository API 를 사용할 수 있도록 Security Role을 부여한 후 OpenSearch API 호출하는 방식이었다. 여기서, IAM User를 검증하기 위해 두 가지의 방법을 사용하였는데, 첫 번째는 "IAM User를 신규 생성"하는 방식이었고, 두 번째는 "IAM User의 Access Key를 변경"하는 작업을 진행하였다.
3-1. Access Key 재발급받기
가장 먼저 OpenSearch API는 호출할 때 IAM User의 Access Key를 사용하는데, 잘못된 Access Key를 사용하고 있을 가능성을 염두에 두고 Access Key를 재발급 받아보았다.
현재 사용 중인 os-repo-prg-OpenSearchRepositoryUser
IAM User는 "RK70"에 해당하는 Access Key를 사용하고 있었으나, 새롭게 "7R4DB" Access Key를 발급받았다. 하지만, 변경된 Access Key를 사용하여 OpenSearch API를 호출하더라도 동일한 에러가 발생하였다.
3-2. IAM User를 신규 생성하기
두 번째로 IAM User에 잘못된 설정이 되어 있는 것이 아닌지 확인하기 위해 Administrator Role을 부여하여 User를 생성하고, 해당 정보를 OpenSearch Security Role에 부여한 후 OpenSearch API를 호출해 보았다.
하지만, 역시나 Opensearch API에서는 무심하게 동일한 에러 응답값을 전달받게 되었다.
4. IAM Role을 AWS Console로 생성해 보았다.
모든 리소스는 Terraform을 통해 생성하였는데, 이번에는 AWS Console을 통해 opensearch-test-role IAM Role을 생성해보았다. AWS Console에서 생성한 Role은 Terraform으로 생성한 Role과 동일한 신뢰 관계(Trust Relationship)와 정책(Policy)을 가지고 있었으며, Assume Role을 요청하는 주체도 동일하게 설정하였다.
하지만, 이번에는 OpenSearch API를 요청하였을 때, 정상적으로 S3 Bucket에 접근하여 Snapshot Repository를 생성할 수 있었다.
5. Terraform과 Console로 생성된 Role 비교해보았다.
Terraform으로 생성하였을 때 일부 속성값이 누락된 것이 아닐까 하는 고민이 있었지만, 가장 확실한 방법을 두 Role을 상세하게 비교해보는 것이 가장 효율적이라고 판단하였다. Terraform으로 생성된 os-repo-prg-OPENSEARCHRepositoryRole
과 AWS Console로 생성된 opensearch-test-role
을 각각 Terraform으로 import하여 tfstate 파일에서 비교해보았다.
하지만, 모든 tfstate의 설정값을 비교해 보았음에도 불구하고 차이점을 찾을 수 없었다. 두 Role의 설정값은 토씨하나 틀린 것이 없었으며, 다른 것 이라고는 이름 말고는 그 어느 것도 찾을 수 없었다.
해결 방안
해당 이슈는 임시적으로 IAM Role을 Terraform으로 생성하지 않고, AWS Console을 통해 IAM Role을 생성하는 방법으로 대응하였다.
결론
이번 이슈를 디버깅하면서 “왜 안되지?” “다른게 없잖아!”와 같이 분노에 가득찬 혼잣말을 하면서 시간을 보냈었는데, 도저히 풀 수 없는 문제를 해결하고 있는 느낌을 엄청 오랜만에 다시 느끼게 된 것 같다.
이번 게시글에서는 어째서 발생하게 된 이슈인지 근본적인 해결 방법을 도출하지 못했다. 해결 방안을 찾기 전 까지는 임시적으로 Terraform으로 IAM Policy와 User는 모두 생성하지만, IAM Role은 Terraform output으로 출력된 값을 바탕으로 AWS Console에서 수동 발급 하도록 대응하고 있다. 사실 근본적인 원인을 모두 검증한 후 글을 작성하고 싶었지만, 이번 게시글에서는 해결 방안을 찾지 못한 채 글을 작성하게 되었다.
다음 게시글에서는 근본적인 원인을 확인하고 명확한 해결 방안을 찾아내어 공유하고자 한다. 다음 게시글에서는 terraform-provider-aws
와 aws-sdk-go-v2
의 구현체를 확인하고, IAM Role 생성 시 동작 흐름에 대해 분석하여 AWS Console와 차이점을 분석해보도록 하겠다.
'분석과 탐구' 카테고리의 다른 글
글또 8기 회고와 9기의 목표 (0) | 2023.12.10 |
---|---|
제로부터 시작하는 Prisma와 Nest.js (0) | 2023.07.16 |
아키텍처 패턴 그리고 헥사고날 아키텍처 (0) | 2023.07.02 |
제로부터 시작하는 DDD를 위한 이벤트스토밍 (0) | 2023.06.18 |
Amazon API Gateway의 WebSocket API란 무엇인가? (0) | 2023.05.21 |