본문 바로가기
IaC/Terraform

Terraform + Vault AWS AssumeRole 연동

by wlsdn3004 2023. 4. 24.
728x90
반응형

 

 

Terraform에서 AWS Provider를 사용할 때 AWS 자격 증명 정보를 하드 코딩하는 것은 보안에 매우 취약하다.

Vault에서는 해당 문제를 해결하기 위해 AWS Secrets Engine 기능을 제공한다.

 

AWS Secrets Engine은 동적으로 AWS 자격 증명을 생성하고 관리하는 데 사용된다. 이를 통해, Terraform 코드에 자격 증명 정보를 저장하지 않고도 AWS 리소스를 관리할 수 있다.

 

Vault의 AppRole은 Vault에서 생성된 role_id와 secret_id를 통해 동적으로 자격 증명을 생성하여 인증하는 방식이다. Vault의 AWS STS Assume Role은 Vault에서 AWS STS API를 사용하여 AWS iam role을 통해 일회성 자격 증명을 생성한다. 이러한 방식으로 생성된 자격 증명은 일정 기간이 지나면 자동으로 만료되므로 보안성이 높아진다.

 

동작 절차는 아래와 같다.

 

이번 글에서는 AppRole 인증 방식과 AWS Secrets Engine의 AWS STS AssumeRole을 사용하여 테라폼에서 자격증명을 얻어 리소스를 생성하는 실습을 다룰 것이다.

 

실습은 Vault 설정이 필요하므로 Vault가 구성되어 있지 않다면, [HashiCorp Vault 설치] 글을 참고하여 설치하고 진행하면 된다.

 

 

실습 환경

  • Terraform 버전 : v1.2.1
  • Vault provider 버전 : v2.24.1
  • Vault APP 버전: 1.12.1
  • Vault Helm Chart 버전 : 0.23.0

 

실습

1. Vault aws secrets engine(sts assumed_role) 구성


Vault에서 사용할 role을 만든다.

## trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::{account_id}:user/{user_name}"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
$ aws iam create-role \
    --role-name my-role \
    --assume-role-policy-document file://trust-policy.json \
    --description "my role for AssumeRole"

 

만든 iam role에 알맞은 정책을 넣어준다.

 

다른 Account에 대한 role은 아래와 같이 정책을 추가하면 된다.

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:role/RoleNameToAssume"
  }
}

 

root token으로 Vault에 로그인한다.

$ kubectl exec -it -n vault sts/vault -- /bin/sh

/ $ vault login hvs.XXXXXXXXXX
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                hvs.XXXXXXXXXXXX
token_accessor       8mPHZDIUmjluDM2sI3koW1bj
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

 

aws secrets을 활성화한다.

$ vault secrets enable aws
Success! Enabled the aws secrets engine at: aws/

Vault 대시보드

 

AWS 자격 증명에 필요한 aws_access_key_id, aws_secret_access_key를 알맞게 넣어준다.

아래 credentials 권한을 이용하여 aws role에 접근한다.
$ vault write aws/config/root \ 
     access_key={aws_access_key_id} \  ## 알맞게 aws credentials 입력
     secret_key={aws_secret_access_key} \ ## 알맞게 aws credentials 입력
     region=ap-northeast-2
Success! Data written to: aws/config/root

 

위에서 만든 aws iam role의 arn을 입력하고 assumed_role type의 role을 만든다.

$ vault write aws/roles/my-role \
  role_arns={aws_role_arn} \
  credential_type=assumed_role
Success! Data written to: aws/roles/my-role

Vault 대시보드

 

2. Vault approle 구성


approle Auth Methods를 활성화한다.

$ vault auth enable approle
Success! Enabled approle auth method at: approle/

 

approle에서 사용할 policy를 생성한다.

$ vault policy write aws_policy -<<EOF
path "aws/sts/my-role" {
  capabilities = ["read","update"]
}
path "aws/creds/my-role" {
  capabilities = ["read","update"]
}
path "auth/token/create" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
EOF

 

위에서 만든 aws_policy를 지정하여 approle을 생성한다.

$ vault write auth/approle/role/aws_role token_policies="aws_policy" \
    token_ttl=1h token_max_ttl=4h
Success! Data written to: auth/approle/role/aws_role

## 생성 확인
$ vault read auth/approle/role/aws_role
Key                        Value
---                        -----
bind_secret_id             true
local_secret_ids           false
secret_id_bound_cidrs      <nil>
secret_id_num_uses         0
secret_id_ttl              0s
token_bound_cidrs          []
token_explicit_max_ttl     0s
token_max_ttl              4h
token_no_default_policy    false
token_num_uses             0
token_period               0s
token_policies             [aws_policy]
token_ttl                  1h
token_type                 default

 

approle의 role_id, secret_id를 가져온다. 해당 값은 Terraform tf파일에 넣어줘야 하니 기억해 두자.

$ vault read auth/approle/role/aws_role/role-id
Key        Value
---        -----
role_id    {approle role_id}

$ vault write -force auth/approle/role/aws_role/secret-id
Key                   Value
---                   -----
secret_id             {approle secret id}
secret_id_accessor    10e457d1-f96b-c306-faf7-ca26db9481cb
secret_id_num_uses    0
secret_id_ttl         0s

 

approle로 로그인하여 assumed_role을 통한 token 정보를 잘 가져오는지 확인한다.

$ vault write auth/approle/login role_id="{approle role_id}" secret_id="{approle secret_id}"
Key                     Value
---                     -----
token                   hvs.xxxxxxxxxxx3cxxxxU1MR1k   ## 해당 값으로 로그인
token_accessor          Ym18YCNx86HqDyGxye50f4U1
token_duration          1h
token_renewable         true
token_policies          ["aws_policy" "default"]
identity_policies       []
policies                ["aws_policy" "default"]
token_meta_role_name    aws_role

 

위 token값으로 로그인

## approle 로그인
$ vault login hvs.xxxxxxxxxxx3cxxxxU1MR1k
Key                     Value
---                     -----
token                   hvs.xxxxxxxxxxx3cxxxxU1MR1k
token_accessor          Ym18YCNx86HqDyGxye50f4U1
token_duration          59m53s
token_renewable         true
token_policies          ["aws_policy" "default"]
identity_policies       []
policies                ["aws_policy" "default"]
token_meta_role_name    aws_role
## sts 조회
$ vault read aws/sts/my-role
Key                Value
---                -----
lease_id           aws/sts/my-role/nmoyR2muYPi4bNQr9SifNoLa
lease_duration     1h
lease_renewable    false
access_key         AXXXXXXXXXXXX
arn                arn:aws:sts::xxxxxxxxxx:assumed-role/my-role/vault-approle-my-role-1682401311-3UFyrfLQeNDhtGflrOB4
secret_key         PxxxxmRxxxxxxxx
security_token     Ixxxxxxxxxxxxxxxxxxx

 

3. Terraform 파일 설정


아래는 테라폼 코드이다.

## vault.tf

terraform {
#...
    vault = {
      source  = "hashicorp/vault"
      version = "~> 2.0"
    }
  }
}


## vault provider
provider "vault" {
  address = "{vault ipaddr}"

  auth_login {
    path = "auth/approle/login"
    parameters = {
      role_id   = "{approle role_id}"  ## 위 approle role_id 정보를 넣어준다 
      secret_id = "{approle secret_id}" ## 위 approle secret_id 정보를 넣어준다 
    }
  }
}

## vault access credentials
data "vault_aws_access_credentials" "iam" {
  region  = "ap-northeast-2"
  backend = "aws"
  role    = "my-role"
  type    = "sts"
}

## aws provider
provider "aws" {
  access_key = data.vault_aws_access_credentials.iam.access_key
  secret_key = data.vault_aws_access_credentials.iam.secret_key
  token      = data.vault_aws_access_credentials.iam.security_token
}

## output
output "token" {
  value = data.vault_aws_access_credentials.iam
#  sensitive = true  ## vault provider version 3.0 이상부터 필수로 적용해야 한다.

}

 

terraform apply 하면 data.vault_aws_access_credentials 블록에서 정보를 읽어오는 것을 확인할 수 있다.

$ terraform apply
data.vault_aws_access_credentials.cred: Reading...
data.vault_aws_access_credentials.cred: Read complete after 0s [id=aws/sts/my-role/QdAK2e70AQg1o9Ha1DK4d382]

 

output 내용

Outputs:

token = {
  "access_key" = "ASIXXXXXXXXX"
  "backend" = "aws"
  "id" = "aws/sts/my-role/QdAK2e70AQg1o9Ha1DK4d382"
  "lease_duration" = 7200
  "lease_id" = "aws/sts/my-role/QdAK2e70AQg1o9Ha1DK4d382"
  "lease_renewable" = false
  "lease_start_time" = "2023-04-25T05:18:52Z"
  "region" = "ap-northeast-2"
  "role" = "my-role"
  "role_arn" = tostring(null)
  "secret_key" = "PXXXXXXXXXXXXXXFV"
  "security_token" = "IQoJb3JpZ2luXXXXXXXXXXXXXXjV12HWz42yEINSQxjbQVWXXXXXXXXXXXXXX5eZZWvPkD8RNaxZmLBtYc8ofmEo1jKnayGQ9uak6irSAgj+//////////8BEAMaDDAwMDk4MjE5MTIxOCIMyyWAwWTwUY49GCylKqYCOQwWZIgyWszlR9Rrt4IhQUnECRJ22nN+p12ranr/BGvW+mJif1M0MnoAyPkrU5uYKpdbspz7EF4cojxq3S0+VQVxiqsZVazAcGAI5HwUBzS9hnAQNzxf0noDort18pNf+pSbHChbiKoTn8/JJG21eu/80pNrjiXgb+56u6V3F8sXquscdgN1fHPMwzbtXkljenStTs+SVFkB0cEzUWaJRyEHTf6k7Oom9y2nmJyeVP4BJuYWUvQhd2sMcB46hBaH2rn+fVoQMMjxrlssNQEzNVeTkleHvgrc3e4utqDigvij63boQvIx6kNw+ueIFwZLtJbP3simW3yUekGMLVZMcdeYK+E/M2FSKDF2p2iodAnflaeR6E+E/RxuHzzMsV6718yquumUMLzFnaIGOp4BjZwiJxfr9RgqanErhShOTqpVE3ReyawucVADMkvsE8sld1WtN36GjeevmeKVi/cjDHv2GTXIyEt01qh8OXgU0tVMjVrktyq7gAjkdLm9epV391gpGCXwq+BQ9slecUiKaWxarl0EFbksO+URdRQJ2C6CnmXPcvcFrkZF9sKIzMlhEIFDVsBb7cc6ve3J0sNQHxHvGfzCNbbq7u1/NZ0="
  "ttl" = "7200"
  "type" = "sts"
}

 

 

approle을 통한 로그인 이력 및 token 정보를 확인한다.

 

[Vault 대시보드]

[AWS Console Cloud Trail]

 

4. 이슈 해결


[참고]
테라폼에서 helm, kubectl, kubernetes provider를 사용할 경우 "error: Unauthorized"와 같은 에러가 나올 수 있다.
이는 vault assumed_role을 통해 토큰을 받아 terraform을 사용하려는데, 만료된 경우이거나, 권한이 없어 EKS에 접근할 수 없는 경우 발생할 수 있다.

 

체크 1.

aws-auth configMap에 aws iam role이 잘 들어가 있는지 확인한다.

$ kubectl get cm -n kube-system aws-auth -oyaml
  mapRoles: |
    - rolearn: arn:aws:iam::{account_id}:role/my-role  ## aws role arn
      username: testuser
      groups:
        - system:masters

 

체크 2.

assumed_role을 용하여 토큰을 다시 받아오도록 Terraform retry 시도.

토큰이 만료되지 않게 aws iam role의 최대 세션 지속시간과 token ttl 시간을 적절하게 설정한다.

[참고]
aws iam role의 MaxSessionDuration 시간 이하로 설정해야 한다

## vault.tf
data "vault_aws_access_credentials" "cred" {
  region  = "ap-northeast-2"
  backend = "aws"
  role    = "my-role"
  type    = "sts"
  ttl     = "7200"   ## ttl 시간을 늘린다.
}

 

 

마치며


이번 포스팅에서는 Terraform + Vault approle + Vault aws secrets engine 연동 실습을 통해 AWS 자격 증명을 동적으로 생성하고, Terraform에서 안전하게 사용하는 방법을 알아보았다. 이를 통해 AWS 자격 증명을 효율적으로 안전하게 관리할 수 있다.

 

현재 Vault 최신 버전인 1.13.1에서 위와 같이 구성하면 Vault aws secrets engine의 iam_user 방식은 정상 동작하지만 Assumed_role 방식을 사용하면, Terraform에서 vault_aws_access_credentials 값들을 전부 null로 가져오는 이슈가 있다. 해당 문제는 추 후 해결하면 내용을 추가할 예정이다.

 

반응형

댓글