Programming/IaC

[IaC] Managing Secrets with Terraform

Hayley Shim 2023. 10. 28. 17:51

안녕하세요. CloudNet@ Terraform Study를 진행하며 해당 내용을 이해하고 공유하기 위해 작성한 글입니다. 도서 ‘Terraform: Up & Running(By Yevgeniy Brikman)’ 의 내용 및 스터디 시간 동안 언급된 주요 내용 위주로 간단히 정리했습니다.

배포 과정에서 민감 정보 (DB암호, API 키, TLS인증서, SSH키, GPG 키 등)를 안전하게 관리가 필요합니다.

민감 정보 관리 방식

2가지 암호 저장 방식이 있습니다.

  1. 파일 기반 암호 저장 : 민감 정보를 암호화 후 저장 → 암호화 관련 키 관리가 중요 ⇒ 해결책 : AWS KMS, GCP KMS 혹은 PGP Key
  2. 중앙 집중식 암호 저장 : 데이터베이스(MySQL, Psql, DynamoDB 등)에 비밀번호를 암호화하여 저장, 암호화 키는 서비스 자체 혹은 클라우드 KMS를 사용

민감 정보 관리 방법(with Terraform)

IAM 자격 증명 정보를 코드의 Provider 에 직접 입력하는 방법은 안전하지 않습니다.

  1. 사용자(Human users) : 환경 변수 사용, 여전히 컴퓨터 파일에 평문으로 저장 시 문제 될 수 있어 1Password / LastPass (유료)저장 후 사용 or aws-vault(오픈소스)
# mac 설치
brew install --cask aws-vault

# 윈도우 설치
choco install aws-vault  # 윈도우 Chocolatey
scoop install aws-vault  # 윈도우 Scoop

# 버전 확인
aws-vault --version
v6.6.0

# 설정
#aws-vault add <PROFILE_NAME>
aws-vault add t101
Enter Access Key Id: (YOUR_ACCESS_KEY_ID)
Enter Secret Key: (YOUR_SECRET_ACCESS_KEY)
  • aws-vault 설정 후 ~/.aws/credentials 파일은 삭제를 권장합니다.
  • 좀 더 보안 강화 : MFA 사용, IAM 사용자 기반 → IAM 역할 기반

2. 시스템 사용자(Machine users) : 시스템에 따라 인증 방식이 조금 다름, 예) CI서버

  • [Providers]CircleCI as a CI server, with stored secrets : CI/CD 플랫폼인 CircleCI 를 통해 테라폼 코드를 실행한다고 가정
  • [Providers]GitHub Actions as a CI server, with OIDC : Github Actions 은 직접 자격 증명과 OIDC(Open ID Connect) 지원
  • [Providers]EC2 Instance running Jenkins as a CI server, with IAM roles : EC2에 Jenkins 설치 후 CI서버로 테라폼 코드를 실행한다고 가정 시 IAM roles 활용
  • [Resources and data sources] Encrypted files : 암호를 파일에 저장 후 버전 관리 → 암호화 키를 클라우드 공급자 KMS를 통해 안전하게 저장 혹은 PGP 키 사용

실습(EC2 Instance running Jenkins as a CI server, with IAM roles)

# SSH 키페어 이름 지정
SSHKP=<각자 자신의 SSH 키페어 이름>
SSHKP=kp-hayley
# init & plan & apply
$ terraform init && terraform plan && terraform apply -auto-approve
  • 각자 자신의 SSH 키페어로 EC2 접속 후 IAM role 동작 확인합니다.

#ssh -i <자신의 SSH 키파일 지정> ec2-user@$(terraform output -raw instance_ip)
ssh -i ~/.ssh/kp-hayley.pem ec2-user@$(terraform output -raw instance_ip)


curl -s http://169.254.169.254/latest/meta-data/
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials ; echo
IAMROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAMROLE


# EC2 관련 명령 실행 확인
export AWS_DEFAULT_REGION=ap-northeast-2
aws ec2 describe-vpcs --output table
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text
# 실습 자원 삭제
$ terraform destroy -auto-approve

실습(Encrypted files)

  • AWS KMS : AWS 키 관리 서비스 KMS는 공유 하드웨어 보안 모듈HSM 을 사용하면서 암호화키를 생성하고 관리할 수 있게 도와줍니다.
  • CloudHSM : AWS 내에서 암호화키를 관리할 수 있지만 보안 강화를 위해 전용 HSM을 사용할 수 있는 서비스입니다.
# 키 생성(기본값)
# aws kms create-key --description "my text encrypt descript demo"
CREATE_KEY_JSON=$(aws kms create-key --description "my text encrypt descript demo")
echo $CREATE_KEY_JSON | jq

# 키 ID확인
KEY_ID=$(echo $CREATE_KEY_JSON | jq -r ".KeyMetadata.KeyId")
echo $KEY_ID

# 키 alias 생성
export ALIAS_SUFFIX=hayley
aws kms create-alias --alias-name alias/$ALIAS_SUFFIX --target-key-id $KEY_ID

# 생성한 별칭 확인
aws kms list-aliases

# CMK로 평문을 암호화해보기
echo "Hello 123123" > secrect.txt
aws kms encrypt --key-id alias/$ALIAS_SUFFIX --cli-binary-format raw-in-base64-out --plaintext file://secrect.txt --output text --query CiphertextBlob | base64 --decode > secrect.txt.encrypted

# 암호문 확인
cat secrect.txt.encrypted

# 복호화해보기
aws kms decrypt --ciphertext-blob fileb://secrect.txt.encrypted --output text --query Plaintext | base64 --decode
Hello 123123

AWS CMK(KMS Key) 생성을 위해 Key policy 생성 : 현재 사용자가 CMK 권한을 가질 수 있게 코드 설정합니다.

# 테라폼에서 aws_caller_identity 데이터소스로 사용자 정보 확인
provider "aws" {
  region = "us-east-2"
}

# Look up the details of the current user
data "aws_caller_identity" "self" {}
  • 아래 처럼 현재 사용자에 KMS 권한 부여 정책 생성 할 수 있습니다.
data "aws_iam_policy_document" "cmk_admin_policy" {
  statement {
    effect    = "Allow"
    resources = ["*"]
    actions   = ["kms:*"]
    principals {
      type        = "AWS"
      identifiers = [data.aws_caller_identity.self.arn]
    }
  }
}
  • 먼저, DB계정 정보가 포함된 db-creds.yml 파일을 생성합니다.
cat <<EOT > db-creds.yml
username: admin
password: password
EOT
  • AWS KMS CMK로 파일 암호화 하기 : 암호화파일 생성 후 db-creds.yml 삭제합니다.
# CMK로 평문을 암호화해보기
#aws kms encrypt --key-id alias/$ALIAS_SUFFIX  --cli-binary-format raw-in-base64-out --plaintext file://db-creds.yml --output text --query CiphertextBlob | base64 --decode | tee db-creds.yml.encrypted2
aws kms encrypt --key-id alias/$ALIAS_SUFFIX  --cli-binary-format raw-in-base64-out --plaintext file://db-creds.yml --output text --query CiphertextBlob | tee db-creds.yml.encrypted2

# 암호문 확인
cat db-creds.yml.encrypted2


# 복호화해보기
aws kms decrypt --ciphertext-blob fileb://db-creds.yml.encrypted2 --output text --query Plaintext | base64 --decode
  • 테라폼 코드에서 암호화(파일) 활용 할 수 있게 설정합니다.
# 먼저 aws_kms_secrets data source 설정

data "aws_kms_secrets" "creds" {
  secret {
    name    = "db"
    payload = file("${path.module}/db-creds.yml.encrypted")
  }
}

# 아래 처럼 복호화된 값을 로컬 변수로 지정

locals {
  db_creds = yamldecode(data.aws_kms_secrets.creds.plaintext["db"])
}

# DB 계정과 암호를 aws_kms_secrets 의 복호화된 로컬 변수를 전달
resource "aws_db_instance" "example" {
  identifier_prefix   = "terraform-up-and-running"
  engine              = "mysql"
  allocated_storage   = 10
  instance_class      = "db.t2.micro"
  skip_final_snapshot = true
  db_name             = var.db_name

  # Pass the secrets to the resource
  username = local.db_creds.username
  password = local.db_creds.password
}

$ terraform init & terraform plan & terraform apply -auto-approve

# 리소스 생성 및 AWS RDS 정보 확인(username 등)
$ aws rds describe-db-instances --query "*[].[DBInstanceIdentifier,MasterUsername,Endpoint.Address,Endpoint.Port]" --output text


# 실습 완료 후 삭제
$ terraform destroy -auto-approve
# 생성한 키 ID 변수 지정
KEY_ID=$(echo $CREATE_KEY_JSON | jq -r ".KeyMetadata.KeyId")

# 키 비활성화
aws kms disable-key --key-id $KEY_ID

# 키 삭제 예약 : 대기 기간(7일) 
aws kms schedule-key-deletion --key-id $KEY_ID --pending-window-in-days 7

 

blog migration project

written in 2022.11.20

https://medium.com/techblog-hayleyshim/iac-managing-secrets-with-terraform-b1f25ab6fc74