Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terraform, K8s Pod 등 ENV/CONFIGURE 주입 구현 #4

Closed
2 of 3 tasks
Tracked by #1
junha-ahn opened this issue Aug 1, 2023 · 2 comments · Fixed by #45
Closed
2 of 3 tasks
Tracked by #1

Terraform, K8s Pod 등 ENV/CONFIGURE 주입 구현 #4

junha-ahn opened this issue Aug 1, 2023 · 2 comments · Fixed by #45
Assignees
Labels
Major Major topic

Comments

@junha-ahn
Copy link
Member

junha-ahn commented Aug 1, 2023

Description

EKS에 Backend Pod에는 현재 아래와 같은 정보가 필요합니다.

  • 그 중 JWT_SECRET 정보등은 특히 암호화되어 저장할 필요가 있습니다 (평문저장 X, AWS KMS 등 사용)
  • 또한 해당 정보를 요청/응답 가능한 권한 및 네트워크 (같은 VPC) 설정 등이 필요합니다.
spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_SCHEMA}?createDatabaseIfNotExist=true
spring.datasource.username=${MYSQL_USERNAME}
spring.datasource.password=${MYSQL_PASSWORD}
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQLDialect

spring.security.user.name=user
spring.security.user.password=1234

ticketing.jwt.secret=${JWT_SECRET}
ticketing.jwt.expiration-hours=${JWT_EXPIRATION_HOURS}
ticketing.jwt.issuer=${JWT_ISSUER}

Hashicorp-Vault, AWS-Secrets manager 등의 서비스가 있습니다.

각 서비스를 비교해서 Terraform-AWS-EKS 환경에 알맞는 서비스를 선택해주세요

  • 다만, 이미 AWS S3, EKS 등에 KMS, Secrets manager를 사용하고 있기에 특별한 차이점이 없다면 AWS 서비스 사용을 최우선 고려해주세요.

레퍼런스

To do

  • POD에 ENV 주입 가능화

Test Checklist

  • Backend Pod에서 위 환경변수 Access 가능
  • JWT_SECRET은 KMS 등을 통한 암호화 후 저장
@junha-ahn junha-ahn mentioned this issue Aug 1, 2023
8 tasks
@junha-ahn junha-ahn moved this to Todo in Kanban Infra Aug 1, 2023
@junha-ahn junha-ahn removed this from Kanban Infra Aug 6, 2023
@junha-ahn junha-ahn added the Major Major topic label Aug 28, 2023
@junha-ahn junha-ahn moved this to Todo in Kanban Infra Aug 28, 2023
@junha-ahn junha-ahn moved this from Todo to In Progress in Kanban Infra Aug 28, 2023
@junha-ahn
Copy link
Member Author

junha-ahn commented Sep 11, 2023

사전 지식 (개념)

전체 내용의 출처 : https://kim-dragon.tistory.com/279

IRSA란?

  • IRSA는 IAM Role for Service Account의 약자
  • kubernetes의 serviceaccount를 사용하여 pod의 권한을 IAM Role로 제어할 수 있도록 하는 기능

serviceaccount는 AWS의 자원이 아닌데 어떻게 IAM Role을 할당할 수 있는 걸까요?

  • 이걸 해주는게 바로 OIDC라고 부르는 OpenID Connect

serviceaccount란?

  • 실제로 권한을 정의하고, 설정하는 부분은 Role, ClusterRole, RoleBinding, ClusterRoleBinding의 역할
  • ServiceAccount 는 이러한 권한을 적용할 수 있는 주체 중 한가지로서, Pod에게는 신분증과 같은 인증서역할을 하게 됩니다.

OIDC란?

  • OpenID Connect는 Google 등의 IdP(ID 공급자)에 로그인할 수 있도록 지원하는 표준 인증 프로토콜
  • 권한허가 프로토콜인 OAuth 2.0 기술을 이용하여 만들어진 인증 레이어로 JSON 포맷을 이용하여 RESTful API 형식을 사용하여 인증을 하게 됩니다.
  • OIDC를 사용하면 손쉽게 외부 서비스를 통해 사용자 인증을 구현할 수 있게 됩니다.
  • 즉, Kunernetes의 리소스와 AWS리소스 처럼 서로 다른 리소스간의 인증을 OIDC를 사용하여 손쉽게(?)할 수 있게 됩니다.

순서

  1. pod위에서 동작하는 application이 AWS SDK를 사용하여 S3의 리스트를 가져오려고 합니다. 이때 JWT와 IAM Role의 ARN정보를 AWS STS에게 전달 합니다.
  2. STS는 AWS IAM에게 임시 자격증명을 줄 수 있는지 확인을 요청합니다.
  3. IAM은 IAM OIDC Provider와 통신하고, pod에 할당된 serviceaccount에 IAM Role 정보가 잘 annotate되어 있는 지 확인한 후에 IAM에게 확인 되었다는 응답을 주게 됩니다.
  4. IAM은 STS에게 권한을 줘도 된다고 응답을 주게 되고
  5. AWS STS는 pod의 AWS SDK에게 임시 자격증명을 전달합니다.
  6. 최종적으로 pod의 AWS SDK는 aws s3의 리스트를 가져올 수 있게 됩니다.

IRSA 사용 해보기

아래와 같은 작업이 필요합니다.

  • AWS 에서 EKS cluster OICD provider 생성
  • AWS 에서 IAM Role 생성
  • K8s에서 serviceaccount 생성
  • k8s에서 pod에 serviceaccount 할당

@junha-ahn junha-ahn linked a pull request Sep 11, 2023 that will close this issue
@junha-ahn
Copy link
Member Author

junha-ahn commented Sep 11, 2023

How to do 가이드를 따라 해보기 (By Terraform)

총 작업 시간 4시간

다만 처음부터 해당 가이드를 따라했다면 시간이 매우 절약됬을 것임...

terraform 을 통해 CSI-Driver 설치

resource "helm_release" "secrets-store-csi-driver" {
  chart            = "secrets-store-csi-driver"
  name             = "csi-secrets-store"
  namespace        = "kube-system"
  repository       = "https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts"

  set {
    name  = "syncSecret.enabled"
    value = "true"
  }
}
resource "helm_release" "secrets-store-csi-driver-provider-aws" {
  chart            = "secrets-store-csi-driver-provider-aws"
  name             = "secrets-provider-aws"
  namespace        = "kube-system"
  repository       = "https://aws.github.io/secrets-store-csi-driver-provider-aws"
}

왜 Terraform 을 사용하는가?

그냥 helm install을 하면 되는거 아닌가?

  • 불편하지 않다면 그냥 써도 문제 없다.
  • 하지만 만약 helm install <...> 을 통해 직접 설치했다면 팔로업에 많은 리소스가 낭비된다. (특히 Document 등이 미비되어있다면 더욱 큰 문제가 발생한다)

어떤 리소스가 낭비되는가?

  • (해당 작업 중 개인적 경험) 기존 작업 진행이 선언형으로 이루어져있지 않았고, 문서정리가 안되어 있었기 때문에 팔로업에 시간 추가.
  • 당장 무엇이 설치되어 있는지 확인할 수 없었기에 '오류 메세지'에 집중하며 하나하나 장님이 코끼리 만지듯이 문제에 접근했다.

아래와 같이 직접 명령어를 통해 확인해야 한다

  • 선언형 코드와 비교하면 추가 리소스가 많이 생긴다
  • 심지어 작업자는 처음에 설치되어 있는지 조차 인지하지 못한다
 $ helm list -n=kube-system
NAME                    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                                           APP VERSION
csi-secrets-store       kube-system     1               2023-09-08 13:41:47.163856656 +0000 UTC deployed        secrets-store-csi-driver-1.3.4                  1.3.4      
secrets-provider-aws    kube-system     1               2023-09-08 13:42:45.440840482 +0000 UTC deployed        secrets-store-csi-driver-provider-aws-0.3.4     

Terraform으로 Secret Manager Group을 생성한다.

module "secrets_manager" {
  source = "terraform-aws-modules/secrets-manager/aws"

  name_prefix             = "development/ticketing-backend"


  ignore_secret_changes = true
  secret_string = jsonencode({
    MYSQL_PASSWORD   = ""
    PORT: ""
    MYSQL_HOST: ""
    MYSQL_PORT: ""
    MYSQL_USERNAME: ""
    MYSQL_SCHEMA: ""
    JWT_SECRET: ""
    JWT_EXPIRATION_HOURS: ""
    JWT_ISSUER: ""
  })
}

해당 Secret Manager Group을 읽을 수 있는 Iam Policy 생성

module "secrets_manager_access_policy" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-policy"

  name          = "secrets-manager-access-policy"
  create_policy = true

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "secretsmanager:GetSecretValue", 
          "secretsmanager:DescribeSecret"
        ]
        Effect   = "Allow"
        Resource = [
          module.secrets_manager.secret_arn
        ]
      }
    ]
  })
}

해당 IAM Policy를 통해 Service Account 생성

어떻게 '연결' 또는 '생성'을 할 수 있을까?

  • Terraform 을 통해 Service-account를 생성 하고 IAM Policy 권한을 연결
  • 그리고 Helm 차트에서 해당 Service-Account의 이름(plain text)을 사용한다.
resource "kubernetes_service_account" "backend_account" {
  metadata {
    name = "ticketing-backend-account"
    namespace = "default"
    annotations = {
      "eks.amazonaws.com/role-arn" = module.iam_eks_role.iam_role_arn # 중요
    }
  }
}


module "iam_eks_role" {
  source    = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
  role_name = "backend-role"

  role_policy_arns = {
    policy = module.secrets_manager_access_policy.arn
  }

  oidc_providers = {
    backend = {
      provider_arn               =  module.eks.oidc_provider_arn
      namespace_service_accounts = ["default:ticketing-backend-account"]
    }
  }
}
$ kubectl describe serviceaccount  ticketing-backend-account
Name:                ticketing-backend-account
Namespace:           default
Labels:              <none>
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::213060417361:role/backend-role
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

Error: secret "secret-provider" not found

Events:
  Type     Reason     Age               From               Message
  ----     ------     ----              ----               -------
  Warning  Failed     4s (x5 over 42s)  kubelet            Error: secret "secret-provider" not found
  Normal   Pulled     4s                kubelet            Successfully pulled image "213060417361.dkr.ecr.ap-northeast-2.amazonaws.com/ticketing-backend-ecr:latest" in 110.731209ms (110.739094ms including waiting)
$ kubectl get secret
NAME                          TYPE                 DATA   AGE
sh.helm.release.v1.test1.v1   helm.sh/release.v1   1      4h42m

$ kubectl get SecretProviderClass
NAME              AGE
secret-provider   7m52s

원인 추정

The creation of SecretProviderClass object does not automatically create the secret within kubernetes. The secret object gets created only when there is an actual pod running within the cluster which is utilising the defined SecretProviderClass. - from

Did you set secrets-store-csi-driver.syncSecret.enabled=true when installing the driver and provider with helm? If not, then the secret creation could be failing because... - from

해결

CSI Driver 설치시 설정값 추가

...
  set {
    name  = "syncSecret.enabled"
    value = "true"
  }

정확한 문법 사용

-            - name: env_secret
-              valueFrom:
-                secretKeyRef:
-                  name: ticketing-secret
-                  key: MYSQL_PASSWORD
+            - name: MYSQL_USERNAME
+              valueFrom:
+                secretKeyRef:
+                 name: ticketing-secret
+                  key: MYSQL_USERNAME

완료

image

@junha-ahn junha-ahn self-assigned this Sep 11, 2023
@github-project-automation github-project-automation bot moved this from In Progress to Done in Kanban Infra Sep 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Major Major topic
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants