TheKoguryo's 기술 블로그

 Version 2024.04.01

1.6.1 Secret Store로 OCI Vault 사용하기

OKE에서는 Secret를 etcd에서 관리, 저장하며, 암호화 키를 통해 암호화해서 저장하고 있습니다. 더불어 Secret이 YAML상에 포함되어 사용되는 경우가 많은데, 이것이 보안상의 문제가 우려되는 경우, 외부 Secret 저장소를 사용하여 관리하고자 하는 요구사항이 있습니다. OKE에서는 OCI Secrets Store CSI Driver Provider를 통해 OCI Vault에서 Secret을 저장하고 사용하는 것을 지원하고 있습니다.

아래 과정은 OCI Vault을 Secret Store로 사용하기 위해 OCI Secrets Store CSI Driver Provider를 OKE 클러스터에 설치, 구성해서 사용하는 과정입니다. OCI Secrets Store CSI Driver Provider에 대한 추가적인 항목에 대한 설명은 공식 GitHub 저장소를 참조하세요.

Vault에 Secret 생성하기

OCI Vault 만들기

  1. OCI 콘솔에 로그인합니다.

  2. 좌측 상단 햄버거 메뉴에서 Identity & Security > Vault로 이동합니다.

  3. Create Vault를 클릭합니다.

    image-20230503132037374

  4. 위치할 Compartment를 선택하고, 이름을 입력하여 생성합니다.

    image-20230503132358134

  5. 생성된 Vault를 클릭합니다.

마스터 키 만들기

  1. 먼저 마스터키 생성을 위해 Resources > Master Encryption Keys로 이동합니다.

  2. Create Key를 클릭합니다.

  3. 마스터 키 생성 정보를 입력합니다.

    • Create in Compartment: 위치할 Compartment 지정
    • Protection Mode: HSM(Hardware Security Module) 또는 Software 중에 선택, Software는 무료
    • Name: 원하는 이름 입력
    • Key Shape Algorithm: Secret 생성시 사용할 것이므로 대칭키인 AES를 선택합니다.

    image-20230503133352999

  4. Create Key를 클릭하여 키를 생성합니다.

암호를 저장할 Secret 만들기

  1. Secret 생성을 위해 Resources > Secrets로 이동합니다.

  2. Create Secret을 클릭합니다.

  3. Secret 생성 정보를 입력합니다.

    • Name: 이름을 입력합니다. 이후 OKE 클러스터에 설치될 Secrets Store Provider에서 이 이름으로 조회하게 됩니다.
    • Encryption Key: 앞서 생성한 마스터키 선택
    • Secret Contents: 저장할 패스워드를 입력

    image-20230503134525995

  4. Create Secret을 클릭하여 생성합니다.

  5. 같은 방법으로 secret2도 생성합니다.

Secrets Store CSI Driver Provider for OCI Vault 설치
  1. Helm 또는 YAML로 대상 OKE 클러스터에 설치 가능합니다. 여기서는 Helm으로 설치합니다.

    helm repo add oci-provider https://oracle-samples.github.io/oci-secrets-store-csi-driver-provider/charts
    helm install oci-provider oci-provider/oci-secrets-store-csi-driver-provider --namespace kube-system
    
    • 실행예시
    $ helm repo add oci-provider https://oracle-samples.github.io/oci-secrets-store-csi-driver-provider/charts
    "oci-provider" has been added to your repositories
    $ helm install oci-provider oci-provider/oci-secrets-store-csi-driver-provider --namespace kube-system
    NAME: oci-provider
    LAST DEPLOYED: Wed May  3 01:26:51 2023
    NAMESPACE: kube-system
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    NOTES:
    The Secrets Store CSI Driver and the provider are getting deployed to your cluster.
    
    Verify DaemonSets readiness:
      kubectl get daemonset \
          --namespace kube-system \
          --selector='app.kubernetes.io/name in (oci-secrets-store-csi-driver-provider, secrets-store-csi-driver)'
    
    $   kubectl get daemonset \
    >       --namespace kube-system \
    >       --selector='app.kubernetes.io/name in (oci-secrets-store-csi-driver-provider, secrets-store-csi-driver)'
    NAME                                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
    oci-provider-secrets-store-csi-driver   3         3         3       3            3           kubernetes.io/os=linux   2m3s
    oci-secrets-store-csi-driver-provider   3         3         3       3            3           <none>                   2m3s      
    
#1. Instance Principal을 사용하는 경우

OKE 클러스터의 Worker Node인 Compute 인스턴스에 사용할 OCI Vault에 접근 권한을 부여하는 방법입니다.

Policy 설정

  1. OCI 콘솔로 이동합니다.

  2. Dynamic Group을 생성합니다.

    • Name: 이름을 입력합니다, 예, oke-worker-nodes-dynamic-group
    • Matching Rules: Worker Node가 속한 Compartment의 ID로 변경하여 생성합니다.
    Any {instance.compartment.id = 'ocid1.compartment.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'}
    
  3. Policy를 생성합니다.

    • Name: 이름을 입력합니다, 예, oke-worker-nodes-to-vault-policy
    • Policy: 만든 Dynamic Group 이름, OCI Secret이 있는 compartment-name, 사용할 OCI Vault OCID로 변경하여 생성합니다.
    allow dynamic-group <dynamic-group-name> to use secret-family in compartment <compartment-name> where target.vault.id = 'ocid1.vault.oc1..aaaaaaaaaaaaaaa'
    

SecretProviderClass 설정

  1. SecretProviderClass 생성을 위한 파일을 생성합니다.

    • 파일이름 예, secret-provider-class.yaml
    • 대상 OCI Vault에 있는 secret1과 secret2을 가져오고, secret2는 컨테이너에 app1-db-password 파일이름으로 마운트해 사용하는 예시입니다.
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: test-oci-provider-class
    spec:
      provider: oci 
      parameters: # provider-specific parameters
        secrets: |
          - name: secret1              # Name of the secret in vault
          - name: secret2
            fileName: app1-db-password # Secret will be mounted with this name instead of secret name      
        authType: instance             # possible values are: user, instance
        vaultId: ocid1.vault.oc1..aaaaaaaaaaaaaaa
    
  2. 작성한 파일을 배포합니다.

    kubectl apply -f secret-provider-class.yaml
    

테스트 앱 배포

  1. 테스트할 앱을 다운로드 받습니다.

    wget https://raw.githubusercontent.com/oracle-samples/oci-secrets-store-csi-driver-provider/main/deploy/example/app.deployment.yaml
    
  2. 해당 YAML 파일을 보면, 작성한 SecretProviderClass을 아래와 같이 volume으로 추가하고 있습니다.

          volumes:
            - name: some-creds
              csi:
                driver: 'secrets-store.csi.k8s.io'
                readOnly: true
                volumeAttributes:
                  secretProviderClass: 'test-oci-provider-class'  # here we reference particular SecretProviderClass
    
  3. 그리고 컨테이너 Pod의 /mnt/secrets-store 경로에 마운트하고 있습니다.

              volumeMounts:
                - name: 'some-creds'
                  mountPath: '/mnt/secrets-store'  # here are mounted secrets
                  readOnly: true
    
  4. 다운로드 받은 테스트 앱을 배포합니다.

    kubectl apply -f app.deployment.yaml
    
  5. 마운트가 성공하면, Pod가 RUNNING 상태가 됩니다. 실패시 권한 및 OCI Vault에 Secret이 있는지 등을 확인합니다.

    $ kubectl get pod
    NAME                     READY   STATUS    RESTARTS   AGE
    nginx-5fd7f7876d-vtpgp   1/1     Running   0          23m
    
  6. 다음 명령으로 컨테이너에 Secret이 잘 마운트 되었는지 확인합니다.

    kubectl exec -it deployment.apps/nginx  -- sh;
    ls /mnt/secrets-store/;
    cat /mnt/secrets-store/secret1; echo
    cat /mnt/secrets-store/app1-db-password; echo
    exit;
    
    • 실행예시
      • 대상 위치에서 파일로 확인할 수 있으며, 두번째 secret은 이름이 변경되어 마운트된 것을 알 수 있습니다.
    $ kubectl exec -it deployment.apps/nginx  -- sh;
    / # ls /mnt/secrets-store/;
    app1-db-password  secret1
    / # cat /mnt/secrets-store/secret1; echo
    test-password-1
    / # cat /mnt/secrets-store/app1-db-password; echo
    test-password-2
    / # exit;
    $
    
#2. User Principal을 사용하는 경우

User 인증을 통해서 OCI Vault에 접근 권한을 부여하는 방법입니다. OKE 클러스터 전체단위가 아닌 부분단위로 개별 유저로 사용할 수 있겠습니다.

Policy 설정

  1. OCI 콘솔로 이동합니다.

  2. Policy를 생성합니다.

    • Name: 이름을 입력합니다, 예, oke-user-to-vault-policy
    • Policy: User OCID, OCI Secret이 있는 compartment-name, 사용할 OCI Vault OCID로 변경하여 생성합니다.
    allow any-user to use secret-family in compartment <compartment-name> where all { request.user.id = 'ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', target.vault.id = 'ocid1.vault.oc1..aaaaaaaaaaaaaaa' }
    

OKE 클러스터에 User에 대한 Secret 만들기

  1. User 인증에 사용할 User에 대한 API Key가 없는 경우에 OCI 콘솔에서 생성합니다.

    • Resources > API Keys로 이동합니다.
    • Add API Key를 클릭하여 API Key로 사용할 키쌍을 생성합니다.
    • 미리보기로 뜨는 Configuration File Preview을 사용해 인증파일 작성에 사용합니다.
  2. API Key 기반 User 인증을 위한 파일을 생성합니다,

    • 파일이름 예, user-auth-config-example.yaml
    auth:
      region: ap-chuncheon-1
      tenancy: ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      user: ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      fingerprint: 12:bf:17:7b:5f:e0:7d:13:75:11:d6:39:0d:e2:84:74
    
  3. 설정 파일과 API Key - Private Key를 사용하여 Secret을 만듭니다.

    • private-key: API Key 경로 입력
    • namespace: OKE 클러스터상의 컨테이너가 있는 namespace
    kubectl create secret generic oci-config \
             --from-file=config=user-auth-config-example.yaml \
             --from-file=private-key=./oci_api_key.pem \
             --namespace <workload-namespace>
    

SecretProviderClass 설정

  1. SecretProviderClass 생성을 위한 파일을 생성합니다.

    • 파일이름 예, secret-provider-class.yaml
    • 대상 OCI Vault에 있는 secret1과 secret2을 가져오고, secret2는 컨테이너에 app1-db-password 파일이름으로 마운트해 사용하는 예시입니다.
    • authType은 user 로 변경합니다.
    • authSecretName: oci-config 을 새로 추가하여 방금 생성한 Secret으로 지정합니다.
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: test-oci-provider-class
    spec:
      provider: oci 
      parameters: # provider-specific parameters
        secrets: |
          - name: secret1              # Name of the secret in vault
          - name: secret2
            fileName: app1-db-password # Secret will be mounted with this name instead of secret name      
        authType: user                 # possible values are: user, instance
        authSecretName: oci-config     # required only for user authType
        vaultId: ocid1.vault.oc1..aaaaaaaaaaaaaaa
    
  2. 작성한 파일을 배포합니다.

    kubectl apply -f secret-provider-class.yaml
    

테스트 앱 배포

테스트 절차는 #1. Instance Principal과 동일합니다.

  1. 테스트할 앱을 다운로드 받습니다.

    wget https://raw.githubusercontent.com/oracle-samples/oci-secrets-store-csi-driver-provider/main/deploy/example/app.deployment.yaml
    
  2. 다운로드 받은 테스트 앱을 배포합니다. 또는 이미 배포된 경우 Pod를 삭제하고 다시 시작합니다.

    kubectl apply -f app.deployment.yaml
    kubectl delete pod -l app=nginx
    
  3. 다음 명령으로 컨테이너에 Secret이 잘 마운트 되었는지 확인합니다.

    kubectl exec -it deployment.apps/nginx  -- sh;
    ls /mnt/secrets-store/;
    cat /mnt/secrets-store/secret1; echo
    cat /mnt/secrets-store/app1-db-password; echo
    exit;
    
    • 실행예시
      • 대상 위치에서 파일로 확인할 수 있으며, 두번째 secret은 이름이 변경되어 마운트된 것을 알 수 있습니다.
    $ kubectl exec -it deployment.apps/nginx  -- sh;
    / # ls /mnt/secrets-store/;
    app1-db-password  secret1
    / # cat /mnt/secrets-store/secret1; echo
    test-password-1
    / # cat /mnt/secrets-store/app1-db-password; echo
    test-password-2
    / # exit;
    $
    
참고


이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.

Last updated on 3 May 2023