WellSpring

AEWS 10주차 - K8s 시크릿 관리 Update 본문

AEWS

AEWS 10주차 - K8s 시크릿 관리 Update

daniel00324 2025. 4. 10. 18:00

목차

     

    ※ 본 게재 글은 gasida님의 'AEWS' 스터디 중 유형욱님 특별 강의내용과 실습예제 및 AWS 공식 사이트와 관련 Blog 를 참고하여 작성하였습니다.


    0. 실습환경 구성

    ☞ Kind (k8s cluster) 생성 및 docker 기반 Jenkins 서비스 연동을 통해 Vault를 통한 Secret 관리 자동화 기능을 검토해 보고자 한다.

     

    A. kind 로 k8s 배포 : Windows (WSL2) 사용자 → 필수

    더보기

    ▶ 기본 정보 확인

    # 클러스터 배포 전 확인
    docker ps
    mkdir cicd-labs
    cd ~/cicd-labs
    
    # WSL2 Ubuntu eth0 IP를 지정
    ifconfig eth0
    
    MyIP=<각자 자신의 WSL2 Ubuntu eth0 IP>
    MyIP=172.19.21.65
    
    # cicd-labs 디렉터리에서 아래 파일 작성
    cat > kind-3node.yaml <<EOF
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    networking:
      apiServerAddress: "$MyIP"
    nodes:
    - role: control-plane
      extraPortMappings:
      - containerPort: 30000
        hostPort: 30000
      - containerPort: 30001
        hostPort: 30001
      - containerPort: 30002
        hostPort: 30002
      - containerPort: 30003
        hostPort: 30003
      - containerPort: 30004
        hostPort: 30004
      - containerPort: 30005
        hostPort: 30006
    - role: worker
    - role: worker
    EOF
    kind create cluster --config kind-3node.yaml --name myk8s --image kindest/node:v1.32.2
    
    # 확인
    kind get nodes --name myk8s
    kubens default
    
    # kind 는 별도 도커 네트워크 생성 후 사용 : 기본값 172.18.0.0/16
    docker network ls
    docker inspect kind | jq
    
    # k8s api 주소 확인 : 어떻게 로컬에서 접속이 되는 걸까?
    kubectl cluster-info
    
    # 노드 정보 확인 : CRI 는 containerd 사용
    kubectl get node -o wide
    
    # 파드 정보 확인 : CNI 는 kindnet 사용
    kubectl get pod -A -o wide
    
    # 네임스페이스 확인 >> 도커 컨테이너에서 배운 네임스페이스와 다릅니다!
    kubectl get namespaces
    
    # 컨트롤플레인/워커 노드(컨테이너) 확인 : 도커 컨테이너 이름은 myk8s-control-plane , myk8s-worker/worker-2 임을 확인
    docker ps
    docker images
    
    # 디버그용 내용 출력에 ~/.kube/config 권한 인증 로드
    kubectl get pod -v6
    
    # kube config 파일 확인 : "server: https://172.19.21.65:35413" 부분에 접속 주소 잘 확인해두자!
    cat ~/.kube/config
    ls -l ~/.kube/config

     

    ▶ (참고) Cluster 자원 삭제

    # 클러스터 삭제
    kind delete cluster --name myk8s
    docker ps
    cat ~/.kube/config

     

    [ 실행 결과 - 한 눈에 보기 ]

    B. Jenkins 환경 구성 

    ☞ 주의 : windows (WSL2 + Docker) 경우 아래 1번 과정에 kind(k8s) 설치를 먼저 진행하여 docker network(kind) 생성 후 아래 Jenkins 생성 할 것

    더보기

    Step1. Jenkins 컨테이너 기동

    # 작업 디렉토리 생성 후 이동
    mkdir cicd-labs
    cd cicd-labs
    
    # cicd-labs 작업 디렉토리 IDE(VSCODE 등)로 열어두기
    
    # kind 설치를 먼저 진행하여 docker network(kind) 생성 후 아래 Jenkins,gogs 생성 할 것
    # docker network 확인 : kind 를 사용
    docker network ls
    ...
    7e8925d46acb   kind      bridge    local
    ...
    
    # 
    cat <<EOT > docker-compose.yaml
    services:
    
      jenkins:
        container_name: jenkins
        image: jenkins/jenkins
        restart: unless-stopped
        networks:
          - kind
        ports:
          - "8080:8080"
          - "50000:50000"
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
          - jenkins_home:/var/jenkins_home
    
    volumes:
      jenkins_home:
    
    networks:
      kind:
        external: true
    EOT
    
    
    # 배포
    docker compose up -d
    docker compose ps
    docker inspect kind
    
    
    # 기본 정보 확인
    for i in jenkins ; do echo ">> container : $i <<"; docker compose exec $i sh -c "whoami && pwd"; echo; done
    
    # 도커를 이용하여 각 컨테이너로 접속
    docker compose exec jenkins bash
    exit

     

    Step1. Jenkins 컨테이너 초기 설정

    # Jenkins 초기 암호 확인
    docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
    09a21116f3ce4f27a0ede79372febfb1
    
    # Jenkins 웹 접속 주소 확인 : 계정 / 암호 입력 >> admin / qwe123
    웹 브라우저에서 http://127.0.0.1:8080 접속 # Windows
    
    # (참고) 로그 확인 : 플러그인 설치 과정 확인
    docker compose logs jenkins -f

     

    Step3. Jenkins URL 설정 : 각자 http://<각자 자신의 WSL2 Ubuntu eth0 IP>:8080/ 를 입력

    • Windows WSL2(Ubuntu) 에서 eth0 IP 확인 ifconfig eth0

     

    [ 실행 결과 - 한 눈에 보기 ]

    * Jenkins 초기화 > Unlock > 설치 진행 화면

    C. Argo CD 설치 - helm_chart

    더보기

    Step1. Argo CD 설치

    # 네임스페이스 생성 및 파라미터 파일 작성
    cd cicd-labs
    
    kubectl create ns argocd
    cat <<EOF > argocd-values.yaml
    dex:
      enabled: false
    
    server:
      service:
        type: NodePort
        nodePortHttps: 30002
      extraArgs:
        - --insecure  # HTTPS 대신 HTTP 사용
    EOF
    
    # 설치
    helm repo add argo https://argoproj.github.io/argo-helm
    helm install argocd argo/argo-cd --version 7.8.13 -f argocd-values.yaml --namespace argocd
    
    # 확인
    kubectl get pod,svc,ep,secret,cm -n argocd
    kubectl get crd | grep argo
    kubectl get appproject -n argocd -o yaml
    
    # configmap
    kubectl get cm -n argocd argocd-cm -o yaml
    kubectl get cm -n argocd argocd-rbac-cm -o yaml
    ...
    data:
      policy.csv: ""
      policy.default: ""
      policy.matchMode: glob
      scopes: '[groups]'
    
    
    # 최초 접속 암호 확인
    kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
    XxJMMJUv8MHZa-kk
    
    # Argo CD 웹 접속 주소 확인 : 초기 암호 입력 (admin 계정)
    open "http://127.0.0.1:30002" # macOS
    # Windows OS경우 직접 웹 브라우저에서 http://127.0.0.1:30002 접속

     

    Step2. Argo CD 웹 접속 확인

    • User info → UPDATE PASSWORD 로 admin 계정 암호 변경 (qwe12345)

      

    Step3. 기본 정보 확인 (Settings) : Clusters, Projects, Accounts

     

    [ 실행 결과 - 한 눈에 보기 ]

    1) ArgoCD 설치 후 확인

    2) k9s 에서 설치된 argocd-repo-server  파드 및 컨테이너 정보 확인 

    3) 젠킨스에서 "HashiCopr Vault" 플러그 인 설치

    ( 참고 ) - K9S 설치 ( Ubuntu )

    더보기

    Step1. 파일 다운로드 및 압축 풀기 ( 명령어 권한 부여 )

    $ wget https://github.com/derailed/k9s/releases/download/v0.26.7/k9s_Linux_x86_64.tar.gz // 다운로드
    $ tar -zxvf ./k9s_Linux_x86_64.tar.gz // 압축 해제
    
    $ mkdir -p ~/.local/bin
    $ mv ./k9s ~/.local/bin && chmod +x ~/.local/bin/k9s // 바이너리 이동 및 실행권한 추가
    $ rm ./k9s_Linux_x86_64.tar.gz LICENSE README.md  // 불필요한 코드 제거

     

     Step2. Path 설정  ( ~/.bashrc)

    export PATH=$PATH:$HOME/.local/bin

     

    Step3. 변경사항 온라인 적용  ( or 터미널 재시작 )

    $ source ~/.bashrc // 변경의 적용

    (참고) - docker compose 관리

    더보기

    ▶ 실습 완료 후 해당 컨테이너 중지 상태로 둘 경우 → 재부팅 및 이후에 다시 실습을 위해 컨테이너 시작 시

    # 실습 완료 후 해당 컨테이너 중지 상태로 둘 경우
    docker compose stop
    docker compose ps
    docker compose ps -a
    
    # mac 재부팅 및 이후에 다시 실습을 위해 컨테이너 시작 시
    docker compose start
    docker compose ps

     

    ▶ 특정 컨테이너만 삭제 후 다시 초기화 상태로 기동 시

    # gogs : 볼륨까지 삭제
    docker compose down gogs -v
    docker compose up gogs -d
    
    # jenkins : 볼륨까지 삭제
    docker compose down jenkins -v
    docker compose up jenkins -d

     

    ▶ 모든 실습 후 삭제 시

    docker compose down --volumes --remove-orphans

     


     1. Vault 개요

    더보기

    ☞ HashiCorp Vault는 신원 기반(identity-based)의 시크릿 및 암호화 관리 시스템입니다. 이 시스템은 인증(authentication)인가(authorization) 방법을 통해 암호화 서비스를 제공하여 비밀에 대한 안전하고 감사 가능하며 제한된 접근을 보장합니다.

     

    시크릿(Secret)이란 접근을 철저히 통제하고자 하는 모든 것을 의미하며, 예를 들어 토큰, API 키, 비밀번호, 암호화 키 또는 인증서 등이 이에 해당합니다. Vault는 모든 시크릿에 대해 통합된 인터페이스를 제공하면서, 엄격한 접근 제어와 상세한 감사 로그 기록 기능을 제공합니다.

    외부 서비스용 API 키, 서비스 지향 아키텍처 간 통신을 위한 자격 증명 등은 플랫폼에 따라 누가 어떤 비밀에 접근했는지를 파악하기 어려울 수 있습니다. 여기에 키 롤링(교체), 안전한 저장, 상세한 감사 로그까지 추가하려면 별도의 커스텀 솔루션 없이는 거의 불가능합니다. Vault는 바로 이 지점에서 해결책을 제공합니다.

    Vault는 클라이언트(사용자, 기계, 애플리케이션 등)를 검증하고 인가한 후에만 비밀이나 저장된 민감한 데이터에 접근할 수 있도록 합니다.

     

    출처 : hashicorp 자료

      

    [ Vault 요약 - 한 눈에 보기 ] - 출처 : 가시다님 강의자료 중

     

    특징
    HashiCorp Vault
    주요 기능
    다양한 환경과 플랫폼에서 secret을 저장, 액세스 및 배포하기 위한 포괄적인 secret 관리 솔루션
    암호화
    높은 유연성과 보안을 위해 여러 암호화 백엔드(예: AWS KMS, Azure Key Vault, GCP KMS)를 지원하는 고급 암호화 메커니즘을 제공합니다.
    접근 제어
    역할, 경로 및 작업을 기반으로 세분화된 권한을 허용하는 세부적인 액세스 제어 정책(ACL)을 구현합니다.
    동적 secret
    데이터베이스, 클라우드 자격 증명, SSH 키와 같은 리소스에 대한 일시적이고 주문형 액세스를 제공하여 동적 secret 생성을 지원합니다.
    감사 로깅
    모든 작업과 액세스 요청을 추적하여 책임과 추적성을 보장하기 위한 포괄적인 감사 로그를 제공합니다.
    완성
    Kubernetes, Terraform, Jenkins 및 클라우드 공급자와 같은 광범위한 도구 및 서비스와의 광범위한 통합을 통해 광범위한 애플리케이션 범위가 가능합니다.
    설치 복잡성
    서버 배포 및 구성을 포함한 더 많은 설정 및 인프라가 필요하며 학습 곡선이 더 가파를 수 있습니다.
    secret rotation
    자동화된 secret rotation을 지원하여 secret이 정기적으로 업데이트되고 노출 위험이 최소화되도록 합니다.
    API 및 CLI
    다양한 작업을 위한 vault와의 광범위한 프로그래밍 액세스 및 상호 작용을 허용하는 풍부한 API 및 CLI를 제공합니다.
    사용 사례
    동적 secret 생성, 서비스로서의 암호화, 다양한 애플리케이션 및 환경에서의 안전한 데이터 저장을 포함한 광범위한 사용 사례에 적합합니다.
    전개
    최적의 성능과 보안을 위해 전용 인프라와 리소스가 필요한 독립 실행형 서비스로 배포됨
    확장성
    분산 환경에서 대량의 secret과 여러 클라이언트를 처리할 수 있는 높은 확장성을 위해 설계되었습니다.
    버전 관리
    secret 버전 관리를 지원하여 사용자가 시간 경과에 따라 다양한 버전의 secret을 추적하고 관리할 수 있도록 합니다.
    오픈소스
    추가 기능과 지원을 제공하는 엔터프라이즈 버전과 함께 오픈 소스 도구로 사용 가능

    ☞ 대표적인 Secret 종류

    • 비밀번호
    • Cloud Credentials : AWS, GCP, Azure, NCP
    • Database Credentials : MySQL,
    • SSH Key
    • Token, API Key : GitHub, Telegram, Slack, OpenAI, Claude
    • 인증서(PKI, TLS 등)

     

     Vault의 동작방식? - Docs

    더보기

    Vault는 주로 토큰(Token)을 기반으로 작동하며, 이 토큰은 클라이언트의 정책(Policy)과 연결되어 있습니다. 정책경로(path) 기반으로 설정되며, 정책 규칙은 클라이언트가 해당 경로에서 수행할 수 있는 작업과 접근 가능성을 제한합니다.

    Vault에서는 토큰을 수동으로 생성해 클라이언트에 할당할 수도 있고, 클라이언트가 로그인하여 토큰을 직접 획득할 수도 있습니다.

     

    [ Vault 의 핵심 워크플로우 - 4 단계 ]

    1. 인증 (Authenticate): Vault에서 인증은 클라이언트가 Vault에 자신이 누구인지 증명할 수 있는 정보를 제공하는 과정입니다. 클라이언트가 인증 메서드를 통해 인증되면, 토큰이 생성되고 정책과 연결됩니다.
    2. 검증 (Validation): Vault는 Github, LDAP, AppRole 등과 같은 신뢰할 수 있는 외부 소스를 통해 클라이언트를 검증합니다.
    3. 인가 (Authorize): 클라이언트는 Vault의 보안 정책과 비교됩니다. 이 정책은 Vault 토큰을 사용하여 클라이언트가 접근할 수 있는 API 엔드포인트를 정의하는 규칙의 집합입니다. 정책은 Vault 내 특정 경로나 작업에 대한 접근을 허용하거나 거부하는 선언적 방식으로 권한을 제어합니다.
    4. 접근 (Access): Vault는 클라이언트의 신원에 연관된 정책을 기반으로 토큰을 발급하여 비밀, 키, 암호화 기능 등에 대한 접근을 허용합니다. 클라이언트는 이후 작업에서 해당 Vault 토큰을 사용할 수 있습니다.

      

    ▶ Vault 의 주요기능

    더보기

    1. 안전한 비밀 저장 (Secure Secret Storage): → Static 시크릿

    Vault는 임의의 key/value 형식의 시크릿을 저장할 수 있으며, 이 시크릿은 영구 저장소에 기록되기 전에 암호화됩니다. 따라서 저장소에 직접 접근하더라도 비밀을 열람할 수 없습니다. Vault는 Disk, Consul 등 다양한 저장소를 지원합니다.

    2. 동적 비밀 (Dynamic Secrets):

    Vault는 AWS나 SQL 데이터베이스와 같은 일부 시스템에 대해 요청 시 비밀을 동적으로 생성할 수 있습니다. 예를 들어, 애플리케이션이 S3 버킷에 접근해야 할 때 Vault에 자격 증명을 요청하면, Vault는 해당 권한을 가진 AWS 키쌍을 생성해줍니다. 이 동적 시크릿은 일정 시간이 지나면 자동으로 폐기됩니다.

    3. 데이터 암호화 (Data Encryption):

    Vault는 데이터를 저장하지 않고 암호화 및 복호화를 수행할 수 있습니다. 이를 통해 보안 팀은 암호화 매개변수를 정의하고, 개발자는 암호화된 데이터를 SQL 데이터베이스 등 외부 저장소에 안전하게 저장할 수 있습니다.

    4. 임대 및 갱신 (Leasing and Renewal):

    Vault에 저장된 모든 시크릿은 임대 기간(lease)이 설정되어 있으며, 이 기간이 끝나면 해당 비밀은 자동으로 폐기됩니다. 클라이언트는 내장된 갱신 API를 통해 임대를 연장할 수 있습니다.

    5. 폐기 (Revocation):

    Vault는 비밀 폐기를 기본적으로 지원합니다. 단일 비밀뿐만 아니라 특정 사용자에 의해 읽힌 모든 비밀, 또는 특정 유형의 모든 비밀 등 비밀의 계층 구조 전체를 폐기할 수 있습니다. 이 기능은 키 롤링이나 침입 발생 시 시스템을 신속하게 차단하는 데 유용합니다.


    2. Vault 기본 구조의 이해

    2-1. hasicorp Vault , Security LifeCycle Management, Secret change automation

    더보기

    ☞ 개별 ID 인증/인가를 통해 필요한 자격 증명을 동적을 발급

    참조 : 하시코프 설명자료

     

     

    ☞ Work-Flow 이해

    Vault 의 동작 Flow 도식 ( By GPT )

     

    Step1. 사용자(App)가 권한을 얻기 위한 요청을 진행

     

     

    Step2. 권한 관리주체(솔루션-Vault)가 요청 대상의 적합성 검토 후, Token (or Access key)를 사용자에게 발급함 (Authentication

     

     

    Step3. 권한 관리주체(솔루션-Vault)가 대상 서비스(CSP - 리소스)에 대한 권한 정책 부여 (Authorization)

     

     

    Step4. 사용자(App)가 부여받은 Token (or Access key) 로 대상 서비스에 접근하여 작업을 진행함

     


    ☞ Vault 의 다양한 인증방식

    1. Vault는 다양한 IdPs(Identity Providers)와 통합하여 사용자의 인증 요청을 검증
    2. 사전 정의된 정책(Policy)과 연결된 Token 제공
      a. 자격 증명에 대한 검증(Validation) 후 Vault에 액세스 할 수 있는 Vault Token 반환
      b. Vault Token은 인증된 사용자(클라이언트)에게 할당된 액세스 대상 및 기능(Engine)에 Vault 정책과 연결
    • 사용자 인증/인가를 위해 외부 IDP를 연동하고, 엔티티를 기준으로 인증 방법/정책/엔진 접근을 관리

    ☞ 사람 또는 애플리케이션(시스템)에 최적화된 인증방식

    • Vault의 인증 및 권한 부여
      • Vault는 사용자 인증이 아닌 권한 부여를 제공
      • 실제 인증 프로세스는 관리하지 않음
    • 다양한 인증 방법 권장
      • 사람: LDAP, OIDC, SAML 등의 외부 IDP 활용
      • 애플리케이션/시스템:
        • AWS IAM, EC2 등
        • Token/AppRole/Kuberntes/TLS 등
    • 보안 및 신뢰성 확보
      • 다양한 유형에 적합한 인증 방법을 사용

    ☞ Vault 내부 아키텍처 - Docs

    •  High Level  ( 외부 인증 요청 API call > Policy 검토 > Path Routing > 필요한 Back system 및 인증 방법에 매핑 )
    출처 : 하시코프 설명자료

     


    3. Kubernetes에 Vault 설치 - Docs1 Docs2

    ▶ 학습목표

    더보기
    • 실습용 K8s 환경(KinD)에 Vault를 Helm으로 설치
    • 설치된 Vault 서버에 접속하고 UI/CLI로 기본 기능을 확인
    • Vault의 동작 방식과 구조의 이해

    ▶ 실습 환경 구성

    Step1. Helm을 사용한 Vault 배포 - Vault helm

    더보기
    • 네임스페이스 생성 및 Helm Repo 추가
    # Create a Kubernetes namespace.
    kubectl create namespace vault
    
    # View all resources in a namespace.
    kubectl get all --namespace vault
    
    # Setup Helm repo
    helm repo add hashicorp https://helm.releases.hashicorp.com
    
    # Check that you have access to the chart.
    helm search repo hashicorp/vault
    # NAME                                    CHART VERSION   APP VERSION     DESCRIPTION                          
    # hashicorp/vault                         0.30.0          1.19.0          Official HashiCorp Vault Chart       
    # hashicorp/vault-secrets-gateway         0.0.2           0.1.0           A Helm chart for Kubernetes          
    # hashicorp/vault-secrets-operator        0.10.0          0.10.0          Official Vault Secrets Operator Chart
    cat <<EOF > override-values.yaml
    global:
      enabled: true
      tlsDisable: true  # Disable TLS for demo purposes
    
    server:
      image:
        repository: "hashicorp/vault"
        tag: "1.19.0"
      standalone:
        enabled: true
        replicas: 1
        config: |
          ui = true
    
          listener "tcp" {
            address = "[::]:8200"
            cluster_address = "[::]:8201"
            tls_disable = 1
          }
    
          storage "file" {
            path = "/vault/data"
          }
    
      service:
        enabled: true
        type: NodePort
        port: 8200
        targetPort: 8200
        nodePort: 30000   # 🔥 Kind에서 열어둔 포트 중 하나 사용
    
    injector:
      enabled: true
    EOF
    # Helm Install 실행
    helm upgrade vault hashicorp/vault -n vault -f override-values.yaml --install
    
    # 네임스페이스 변경 : vault
    kubens vault
    Context "kind-myk8s" modified.
    Active namespace is "vault".
    
    # 배포확인
    k get pods,svc,pvc
    
    NAME                                        READY   STATUS              RESTARTS   AGE
    pod/vault-0                                 0/1     ContainerCreating   0          11s
    pod/vault-agent-injector-56459c7545-9n94t   0/1     ContainerCreating   0          11s
    
    NAME                               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                         AGE
    service/vault                      NodePort    10.96.36.121   <none>        8200:30000/TCP,8201:31091/TCP   11s
    service/vault-agent-injector-svc   ClusterIP   10.96.240.81   <none>        443/TCP                         11s
    service/vault-internal             ClusterIP   None           <none>        8200/TCP,8201/TCP               11s
    
    NAME                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
    persistentvolumeclaim/data-vault-0   Bound    pvc-6f5739c5-14e9-4a62-bed2-b98fd327bcb1   10Gi       RWO            standard       <unset>                 11s

    [ 실행 결과 - 한 눈에 보기 ]

    1) k8s 에 'vault' 네임스페이스 생성 

    2) helm chart 통한 vault repo 등록

    3) vault 설치 내용 확인

     4) 편의 실습을 위해 namespace 를 'vault' 변경 후, 리소스 확인

     

     

    Step2. Vault 초기화 및 잠금해제

    더보기
    • 상태 확인
    # Vault Status 명령으로 Sealed 상태확인
    kubectl exec -ti vault-0 -- vault status
    cat <<EOF > init-unseal.sh
    #!/bin/bash
    
    # Vault Pod 이름
    VAULT_POD="vault-0"
    
    # Vault 명령 실행
    VAULT_CMD="kubectl exec -ti \$VAULT_POD -- vault"
    
    # 출력 저장 파일
    VAULT_KEYS_FILE="./vault-keys.txt"
    UNSEAL_KEY_FILE="./vault-unseal-key.txt"
    ROOT_TOKEN_FILE="./vault-root-token.txt"
    
    # Vault 초기화 (Unseal Key 1개만 생성되도록 설정)
    \$VAULT_CMD operator init -key-shares=1 -key-threshold=1 | sed \$'s/\\x1b\\[[0-9;]*m//g' | tr -d '\r' > "\$VAULT_KEYS_FILE"
    
    # Unseal Key / Root Token 추출
    grep 'Unseal Key 1:' "\$VAULT_KEYS_FILE" | awk -F': ' '{print \$2}' > "\$UNSEAL_KEY_FILE"
    grep 'Initial Root Token:' "\$VAULT_KEYS_FILE" | awk -F': ' '{print \$2}' > "\$ROOT_TOKEN_FILE"
    
    # Unseal 수행
    UNSEAL_KEY=\$(cat "\$UNSEAL_KEY_FILE")
    \$VAULT_CMD operator unseal "\$UNSEAL_KEY"
    
    # 결과 출력
    echo "[🔓] Vault Unsealed!"
    echo "[🔐] Root Token: \$(cat \$ROOT_TOKEN_FILE)"
    EOF
    
    # 실행 권한 부여
    chmod +x init-unseal.sh
    
    # 실행
    ./init-unseal.sh

     

    • vault status 명령을 사용하여 Unseal 되었는지 확인한다 → Sealed=false
    kubectl exec -ti vault-0 -- vault status
    
    Key             Value
    ---             -----
    Seal Type       shamir
    Initialized     true
    Sealed          false
    Total Shares    1
    Threshold       1
    Version         1.19.0
    Build Date      2025-03-04T12:36:40Z
    Storage Type    file
    Cluster Name    vault-cluster-26e23c75
    Cluster ID      326975b8-4907-2d62-9da1-f3165d7cc0c6
    HA Enabled      false

    (참고) UI에 접속해서 Unseal Key 입력도 가능

    ☞ 이제 Unseal이 마무리 되었으므로 Root Token 값을 사용하여 UI에 접속할 수 있습니다.

     

    • Root Token 입력 후 Vault UI 화면 - vault-root-token.txt 파일에서 획득

    [ 실행 결과 - 한 눈에 보기 ]

     

    Step3. CLI 설정(MacOS) - Docs / 수동설치

    더보기
    brew tap hashicorp/tap
    brew install hashicorp/tap/vault
    vault --version  # 설치 확인
    
    # NodePort로 공개한 30000 Port로 설정
    export VAULT_ADDR='http://localhost:30000'
    
    # vault 상태확인
    vault status
    
    # Root Token으로 로그인
    vault login
    
    Token (will be hidden): 
    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.egeVgnhMKgnFqof6YLfiPtLa
    token_accessor       wWpBsO3TqQEybFF0le2YmNGX
    token_duration       ∞
    token_renewable      false
    token_policies       ["root"]
    identity_policies    []
    policies             ["root"]

     

    Step4. CLI 설정(Windows) - Docs

    더보기

    1) GPG key를 추가합니다

    $ curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -

    2) 리포지토리를 추가합니다

    $ sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"

    3) apt(Advanced Packaging Tool)을 업데이트합니다

    $ sudo apt-get update

     

    4) vault를 설치합니다

    $ sudo apt-get install vault
     
     

     

    5) vault 개발서버를 시작합니다. (필요 시)

    $ vault server -dev

    [ 결과 ]

    ==> Vault server configuration:
    
                 Api Address: http://127.0.0.1:8200
                         Cgo: disabled
             Cluster Address: https://127.0.0.1:8201
                  Go Version: go1.14.7
                  Listener 1: tcp (addr: "127.0.0.1:8200", cluster address: "127.0.0.1:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
                   Log Level: info
                       Mlock: supported: true, enabled: false
               Recovery Mode: false
                     Storage: inmem
                     Version: Vault v1.5.3
                 Version Sha: 9fcd81405feb320390b9d71e15a691c3bc1daeef

     

    WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
    and starts unsealed with a single unseal key. The root token is already
    authenticated to the CLI, so you can immediately begin using Vault.
    
    You may need to set the following environment variable:
    
        $ export VAULT_ADDR='http://127.0.0.1:8200'
    
    The unseal key and root token are displayed below in case you want to
    seal/unseal the Vault or re-authenticate.
    
    Unseal Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    Root Token: s.xxxxxxxxxxxxxxxxxxxxxx
    
    Development mode should NOT be used in production installations!

     

    6) VAULT_ADDR 환경변수를 등록합니다.

    $ vi ~/.bashrc

     

    추가할 내용

    export VAULT_ADDR='http://127.0.0.1:8200'  ## 자신의 Local PC IP:Port로 대체할 것 !!
    ## export VAULT_ADDR='http://172.31.79.21:30000'

     

     

    환경변수를 적용하고 확인해봅니다.

    $ source ~/.bashrc $ echo $VAULT_ADDR // [결과] http://127.0.0.1:8200

    [ 실행 결과 - 한 눈에 보기 ]

     

    ▶ 실습

    KV 시크릿 엔진 활성화 및 샘플 구성 → Static Secret

    더보기

    → Vault KV version 2 엔진을 활성화하고 샘플 데이터를 저장합니다.

    → Version1 : KV 버전관리 불가 / Version2 : KV 버전관리 가능

     

    Step 1. KV 엔진 활성화 및 샘플 데이터 추가

    # KV v2 형태로 엔진 활성화
    vault secrets enable -path=secret kv-v2
    
    # 샘플 시크릿 저장
    vault kv put secret/sampleapp/config \
      username="demo" \
      password="p@ssw0rd"
      
    # 입력된 데이터 확인
    vault kv get secret/sampleapp/config
    
    ======== Secret Path ========
    secret/data/sampleapp/config
    
    ======= Metadata =======
    Key                Value
    ---                -----
    created_time       2025-03-30T05:18:47.797852422Z
    custom_metadata    <nil>
    deletion_time      n/a
    destroyed          false
    version            1
    
    ====== Data ======
    Key         Value
    ---         -----
    password    p@ssw0rd
    username    demo

     

    Step2. Vault UI : [Secrets Engine] 탭에 접속 후 [sampleapp - config] 접속하여 실제 저장된 Key / Value 확인

    • username : demo
    • password : p@ssw0rd

     

    Step3. (참고) 경로 확인하는 명령 가이드


    [ 실행 결과 - 한 눈에 보기 ]

     

    ▶ 추가 학습자료


    4. Vault Sidecar 연동 (Vault Agent) - 링크1 링크2

    ▶ 학습목표

    더보기

    공식문서 요약(ChatGPT)

    더보기

    Vault Agent Injector는 Kubernetes Pod 내부에 Vault Agent를 자동으로 주입해주는 기능입니다. 이를 통해 어플리케이션이 Vault로부터 자동으로 비밀 정보를 받아올 수 있게 됩니다. 하지만 이를 사용하기 전에 몇 가지 사전 준비가 필요합니다.

    1. Vault가 설치되어 있고, Kubernetes와 통합되어 있어야 합니다

    • Vault가 실행 중이어야 하고, Kubernetes 클러스터에 접근 가능해야 합니다.
    • Vault는 Kubernetes 인증 방식을 설정하고 있어야 하며, 이를 통해 서비스 어카운트를 기반으로 토큰을 발급받을 수 있습니다.

    2. Vault Agent Injector가 클러스터에 배포되어 있어야 합니다

    • Injector는 Kubernetes에 배포되는 별도의 구성 요소입니다.
    • 일반적으로 Helm Chart를 통해 배포하며, 이 컴포넌트가 있어야 Pod에 Vault Agent가 자동으로 주입됩니다.

    3. Kubernetes 인증 방식이 활성화되어야 합니다

    • Vault에서 Kubernetes Auth Method를 활성화하고 구성해야 합니다.
    • 이 설정을 통해 특정 서비스 어카운트에 Vault 접근 권한을 부여할 수 있습니다.

    4. 정책과 역할이 정의되어 있어야 합니다

    • Vault에 접근할 수 있도록 적절한 PolicyRole이 설정되어야 합니다.
    • 예를 들어, 특정 서비스 어카운트가 특정 경로의 시크릿에만 접근할 수 있도록 제한할 수 있습니다.

    5. 애플리케이션 Pod에 주입할 주석(annotation)을 추가해야 합니다

    • Vault Agent Injector는 특정 주석이 있는 Pod에 대해서만 Vault Agent를 주입합니다.
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "example-role"

      

     

    Vault Kubernetes Sidecar 아키텍처 및 워크플로우

    더보기
    이미지 출처: HashiCorp 공식 블로그

     

    Vault - Kubernetes 연동시 동작흐름(출처: Medium )

     

    ▶ 실습

    Step1. Vault  AppRole 방식 인증 구성 - AppRole 인증이란?

    더보기
    • 인증 구성 및 정책 적용
    # 1. AppRole 인증 방식 활성화
    vault auth enable approle || echo "AppRole already enabled"
    vault auth list
    
    # 2. 정책 생성
    vault policy write sampleapp-policy - <<EOF
    path "secret/data/sampleapp/*" {
      capabilities = ["read"]
    }
    EOF
    
    # 3. AppRole Role 생성
    vault write auth/approle/role/sampleapp-role \
      token_policies="sampleapp-policy" \
      secret_id_ttl="1h" \
      token_ttl="1h" \
      token_max_ttl="4h"
    
    # 4. Role ID 및 Secret ID 추출 및 저장
    ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role/role-id)
    SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)
    
    echo "ROLE_ID: $ROLE_ID"
    echo "SECRET_ID: $SECRET_ID"
    
    
    # 5. 파일로 저장
    mkdir -p approle-creds
    echo "$ROLE_ID" > approle-creds/role_id.txt
    echo "$SECRET_ID" > approle-creds/secret_id.txt
    
    
    # 6. (옵션) Kubernetes Secret으로 저장
    kubectl create secret generic vault-approle -n vault \
      --from-literal=role_id="${ROLE_ID}" \
      --from-literal=secret_id="${SECRET_ID}" \
      --save-config \
      --dry-run=client -o yaml | kubectl apply -f -

    [ 실행 결과 - 한 눈에 보기 ]

    Step2. Vault Agent Sidecar 연동

    Vault Agent는 vault-agent-config.hcl 설정을 통해 연결할 Vault의 정보와, Template 구성, 렌더링 주기, 참조할 Vault KV 위치정보 등을 정의한다.  

     

    1. Vault Agent 설정 파일 작성 및 생성 (vault-agent-config.hcl) - HCL ( HashiCorp Configuration Language )

    더보기
    cat <<EOF | kubectl create configmap vault-agent-config -n vault --from-file=agent-config.hcl=/dev/stdin --dry-run=client -o yaml | kubectl apply -f -
    vault {
      address = "http://vault.vault.svc:8200"
    }
    
    auto_auth {
      method "approle" {
        config = {
          role_id_file_path = "/etc/vault/approle/role_id"
          secret_id_file_path = "/etc/vault/approle/secret_id"
          remove_secret_id_file_after_reading = false
        }
      }
    
      sink "file" {
        config = {
          path = "/etc/vault-agent-token/token"
        }
      }
    }
    
    template_config {
      static_secret_render_interval = "20s"
    }
    
    template {
      destination = "/etc/secrets/index.html"
      contents = <<EOH
      <html>
      <body>
        <p>username: {{ with secret "secret/data/sampleapp/config" }}{{ .Data.data.username }}{{ end }}</p>
        <p>password: {{ with secret "secret/data/sampleapp/config" }}{{ .Data.data.password }}{{ end }}</p>
      </body>
      </html>
    EOH
    }
    EOF

    2. 샘플 애플리케이션 + Sidecar 배포(수동방식)

    더보기
    • Nginx + Vault Agent 생성
    kubectl apply -n vault -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-vault-demo
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: nginx-vault-demo
      template:
        metadata:
          labels:
            app: nginx-vault-demo
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
            volumeMounts:
            - name: html-volume
              mountPath: /usr/share/nginx/html
          - name: vault-agent-sidecar
            image: hashicorp/vault:latest
            args:
              - "agent"
              - "-config=/etc/vault/agent-config.hcl"
            volumeMounts:
            - name: vault-agent-config
              mountPath: /etc/vault
            - name: vault-approle
              mountPath: /etc/vault/approle
            - name: vault-token
              mountPath: /etc/vault-agent-token
            - name: html-volume
              mountPath: /etc/secrets
          volumes:
          - name: vault-agent-config
            configMap:
              name: vault-agent-config
          - name: vault-approle
            secret:
              secretName: vault-approle
          - name: vault-token
            emptyDir: {}
          - name: html-volume
            emptyDir: {}
    EOF

     

     

    3. SVC 생성

    더보기
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      type: NodePort
      selector:
        app: nginx-vault-demo
      ports:
        - protocol: TCP
          port: 80
          targetPort: 80
          nodePort: 30001 # Kind에서 설정한 Port
    EOF

    4. 생성된 컨테이너 확인

    더보기
    # 파드 내에 사이드카 컨테이너 추가되어 2/2 확인
    kubectl get pod -l app=nginx-vault-demo
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-vault-demo-7776649597-tcnxd   2/2     Running   0          5m32s
    
    kubectl describe pod -l app=nginx-vault-demo
    ...
    Containers:
      nginx:
        Container ID:   containerd://c160c2268ce6d7602b718336b9036ae89646b5fdb7ebf310bed3dcf497b8675e
        Image:          nginx:latest
      ...
      vault-agent-sidecar:
        Container ID:  containerd://6d7e37e8925e681d163b06ae7d18f5d3a236164a60ebf422abd4229a2e337e4e
        Image:         hashicorp/vault:latest
        Image ID:      docker.io/hashicorp/vault@sha256:ee674e47dcf85849aadf255b5341f76c0e1a474bc5fa9be9cdfff2a2edf9a628
        Port:          <none>
        Host Port:     <none>
        Args:
          agent
          -config=/etc/vault/agent-config.hcl
    ...
    
    
    # 볼륨 마운트 확인
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- ls -l /etc/vault-agent-token
    total 4
    -rw-r-----    1 vault    vault           95 Apr 10 02:09 token
    
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault-agent-token/token ; echo
    hvs.CAESIEKGV7vnUk7UxQ4c-QQq4pSEDGdWuFNKjOESJ7WfyKHuGh4KHGh2cy5paXJCTHFMWlBKU0pFMXlQelpqaFJDYWU
    
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- ls -al /etc/vault
    ...
    drwxr-xr-x    2 root     root          4096 Apr 10 02:09 ..2025_04_10_02_09_17.2775716426
    lrwxrwxrwx    1 root     root            32 Apr 10 02:09 ..data -> ..2025_04_10_02_09_17.2775716426
    lrwxrwxrwx    1 root     root            23 Apr 10 02:09 agent-config.hcl -> ..data/agent-config.hcl
    drwxrwxrwt    3 root     root           120 Apr 10 02:09 approle
    
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault/agent-config.hcl
    ...
    
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- ls -al /etc/vault/approle
    ...
    lrwxrwxrwx    1 root     root            14 Apr 10 02:09 role_id -> ..data/role_id
    lrwxrwxrwx    1 root     root            16 Apr 10 02:09 secret_id -> ..data/secret_id
    
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault/approle/role_id ; echo
    5bcefce3-e2ff-9ae3-39c1-19d380a1635e
    
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault/approle/secret_id ; echo
    d50a1232-926a-81da-b053-132ee65a0ef9
    
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- ls -l /etc/secrets       
    total 4
    -rw-r--r--    1 vault    vault           94 Apr 10 02:09 index.html
    
    kubectl exec -it deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/secrets/index.html
      <html>
      <body>
        <p>username: demo</p>
        <p>password: p@ssw0rd</p>
      </body>
      </html>
    
    kubectl exec -it deploy/nginx-vault-demo -c nginx -- cat /usr/share/nginx/html/index.html
      <html>
      <body>
        <p>username: demo</p>
        <p>password: p@ssw0rd</p>
      </body>
      </html>
    
    
    # 로그 확인
    kubectl stern -l app=nginx-vault-demo -c vault-agent-sidecar
    
    
    # mutating admission
    kubectl get mutatingwebhookconfigurations.admissionregistration.k8s.io   
    NAME                       WEBHOOKS   AGE
    vault-agent-injector-cfg   1          3h10m

    5. 실제 배포된 화면 확인

    6. KV 값 변경 후 확인

     

    (참고) Annotation을 활용한 Vault Sidecar Injection - Docs

    더보기
    • Vault의 Kubernetes 인증 활성화 및 구성
    # Kubernetes Auth Method 활성화
    vault auth enable kubernetes
    
    # Kubernetes Auth Config 설정
    vault write auth/kubernetes/config \
      token_reviewer_jwt="$(kubectl get secret $(kubectl get serviceaccount vault -n vault -o jsonpath='{.secrets[0].name}') -n vault -o jsonpath="{.data.token}" | base64 --decode)" \
      kubernetes_host="$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[0].cluster.server}')" \
      kubernetes_ca_cert="$(kubectl get secret $(kubectl get serviceaccount vault -n vault -o jsonpath='{.secrets[0].name}') -n vault -o jsonpath='{.data.ca\.crt}' | base64 --decode)"
      
    # 필요한 Policy 작성 (앞선 과정에서 만들었으므로 생략가능)
    vault policy write sampleapp-policy - <<EOF
    path "secret/data/sampleapp/*" {
      capabilities = ["read"]
    }
    EOF
    
    # Role 생성 (Injector가 로그인할 수 있도록)
    vault write auth/kubernetes/role/sampleapp-role \
        bound_service_account_names="vault-ui-sa" \
        bound_service_account_namespaces="vault" \
        policies="sampleapp-policy" \
        ttl="24h"

     

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: vault-ui-sa
      namespace: vault
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: vault-injected-ui
      namespace: vault
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: vault-injected-ui
      template:
        metadata:
          labels:
            app: vault-injected-ui
          annotations:
            vault.hashicorp.com/agent-inject: "true"
            vault.hashicorp.com/role: "sampleapp-role"
            vault.hashicorp.com/agent-inject-secret-config.json: "secret/data/sampleapp/config"
            vault.hashicorp.com/agent-inject-template-config.json: |
              {{- with secret "secret/data/sampleapp/config" -}}
              {
                "username": "{{ .Data.data.username }}",
                "password": "{{ .Data.data.password }}"
              }
              {{- end }}
            vault.hashicorp.com/agent-inject-output-path: "/vault/secrets"
        spec:
          serviceAccountName: vault-ui-sa
          containers:
          - name: app
            image: python:3.10
            ports:
            - containerPort: 5000
            command: ["sh", "-c"]
            args:
              - |
                pip install flask && cat <<PYEOF > /app.py
                import json, time
                from flask import Flask, render_template_string
                app = Flask(__name__)
                while True:
                    try:
                        with open("/vault/secrets/config.json") as f:
                            secret = json.load(f)
                        break
                    except:
                        time.sleep(1)
                @app.route("/")
                def index():
                    return render_template_string("<h2>🔐 Vault Injected UI</h2><p>👤 사용자: {{username}}</p><p>🔑 비밀번호: {{password}}</p>", **secret)
                app.run(host="0.0.0.0", port=5000)
                PYEOF
                python /app.py
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: vault-injected-ui
      namespace: vault
    spec:
      type: NodePort
      ports:
        - port: 5000
          targetPort: 5000
          nodePort: 30002
      selector:
        app: vault-injected-ui
    EOF

    ▶  추가 학습자료

    더보기

    1. Retrieve secrets for Kubernetes workloads with Vault Agent - 실습

     

    2. What is Vault Proxy?

    Vault Proxy는 애플리케이션이 Vault와 통합하는 초기 진입 장벽을 낮추고, 더 확장 가능하고 단순한 방식을 제공하기 위해 만들어졌습니다. Vault Proxy는 Vault의 API 프록시 역할을 하며, 클라이언트가 자동 인증된 토큰을 사용하도록 허용하거나 강제할 수 있습니다.

    Vault Proxy는 클라이언트 데몬으로 다음과 같은 기능을 제공합니다:

    • Auto-Auth: Vault에 자동으로 인증하고, 로컬에서 가져온 동적 비밀의 토큰 갱신 프로세스를 관리합니다.
    • API Proxy: Vault의 API에 대한 프록시 역할을 하며, Auto-Auth 토큰을 선택적으로 또는 강제로 사용하게 할 수 있습니다.
    • 캐싱(Caching): 새로 생성된 토큰이나 이 토큰으로부터 생성된 리스 비밀(leased secrets)에 대한 응답을 클라이언트 측에서 캐싱할 수 있도록 합니다. 또한, 에이전트는 캐시된 토큰과 리스의 갱신도 관리합니다.

    3. Vault Agent and Vault Proxy quick start

     


    5. Jenkins + Vault (AppRole) - CI

    ▶ 학습 목표

    더보기
    • Vault KV Store에 저장한 username, password을 Jenkins을 활용해서 획득하는 방안
    • CI 파이프라인에서 정적(Static) 시크릿을 외부에 저장하고 관리할 경우 사용할 수 있습니다.

     

    🔐 CI/CD 보안 고려사항

    더보기

    Vault - Jenkins Plugin with AppRole 인증방식 워크플로우 - Docs

    더보기

    공식문서 요약(ChatGPT)

    젠킨스는 Vault에 시크릿으로 분류된 데이터를 필요로 하는 작업(job)을 실행해야 합니다. 젠킨스는 마스터 노드와 워커 노드를 가지고 있으며, 워커 노드는 짧은 시간 동안 실행되는 컨테이너 러너에서 작업을 실행합니다.

    • 프로세스는 다음과 같습니다:
      1. 젠킨스 워커가 Vault에 인증
      2. Vault는 토큰을 반환
      3. 워커는 이 토큰을 사용해 작업에 해당하는 역할의 Wrapped SecretID를 요청
      4. Vault는 Wrapped SecretID를 반환
      5. 워커는 작업 러너를 생성하고, Wrapped SecretID를 변수로 전달
      6. 러너 컨테이너는 Wrapped SecretID의 unwrap을 요청
      7. Vault는 SecretID를 반환
      8. 러너는 RoleID와 SecretID를 사용해 Vault에 인증
      9. Vault는 필요한 시크릿 정보를 읽을 수 있는 정책이 포함된 토큰을 반환
      10. 러너는 이 토큰을 사용해 Vault에서 시크릿을 가져옴

    ▶ 실습

    Step1. Jenkins에서 Vault Plugin 설치

    더보기
    1. Jenkins UI 접속
    2. 상단 메뉴에서 Manage JenkinsPlugins
    3. Available 탭에서 Vault 검색
    4. HashiCorp Vault Plugin 설치 후 Jenkins 재시작

      

    Step2. Vault AppRole 정보 확인 ⇒ Secret ID는 1시간 만료이므로, 그냥 다시 생성해서, 해당 값을 젠킨스에 설정하고 빌드 실습 하자.

    더보기
    • Vault에서 발급된 ROLE_ID, SECRET_ID는 이전에 생성한 role_id.txt secret_id.txt 값을 참고하여 사용할 수 있습니다.
    # Role ID 확인 및 Secret ID 신규 발급
    ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role/role-id)
    SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)
    
    echo "ROLE_ID: $ROLE_ID"
    echo "SECRET_ID: $SECRET_ID"

     

    (참고) Role ID, Secret ID 획득방안

    # Role ID
    vault read auth/approle/role/<role-name>/role-id
    
    # Secret ID
    vault write -f auth/approle/role/<role-name>/secret-id
    
    # 예시
    vault read auth/approle/role/sampleapp-role/role-id
    Key        Value
    ---        -----
    role_id    678c0c6e-57df-bb23-1427-f6318843a514

    Step3. Jenkins에서 Vault 설정 및 Credentials 추가

    더보기

    1. Jenkins UI(admi/qwe123) → Manage JenkinsConfigure System

     

    2. 스크롤 하단의 Vault Plugin Configuration 섹션으로 이동

    3. Vault Credential 다음 값 입력:

    • 종류: Vault AppRole Credential
    • Role ID & Secret ID 입력 → 생성해놓은 변수 또는 파일참고
    • ID는 기억하기 쉬운 이름으로 지정 (vault-approle-creds 등)

     

    Step4. Jenkins Pipeline Job 생성

    더보기
    1. Jenkins UI → New Item → Pipeline 선택
    2. jenkins-vault-kv 입력 후 생성
    3. Jenkinsfile 작성

     

    ▶ Jenkinsfile 예시

    pipeline {
      agent any
    
      environment {
        VAULT_ADDR = 'http://192.168.0.2:30000' // 실제 Vault 주소로 변경!!!
      }
    
      stages {
        stage('Read Vault Secret') {
          steps {
            withVault([
              vaultSecrets: [
                [
                  path: 'secret/sampleapp/config',
                  engineVersion: 2,
                  secretValues: [
                    [envVar: 'USERNAME', vaultKey: 'username'],
                    [envVar: 'PASSWORD', vaultKey: 'password']
                  ]
                ]
              ],
              configuration: [
                vaultUrl: "${VAULT_ADDR}",
                vaultCredentialId: 'vault-approle-creds'
              ]
            ]) {
              sh '''
                echo "Username from Vault: $USERNAME"
                echo "Password from Vault: $PASSWORD"
              '''
              script {
                echo "Username (env): ${env.USERNAME}"
                echo "Password (env): ${env.PASSWORD}"
              }
            }
          }
        }
      }
    }
    • Jenkins 실행결과 → 보안상 취약하므로 마스킹처리됨. 우회방안은 있음(파일로 저장)

      

    유의사항

    • KV Version1은 경로에 data을 넣고 Version2는 경로에 data을 넣지 않습니다! - 참고링크
      • Version 1 : secret/data/sampleapp/config
      • Version 2 : secret/sampleapp/config
    • sh 블록 vs script 블록

    [ 실행 결과 - 한 눈에 보기 ]

    * Token  TTL 만료로  Secret ID 재발급 필요 시, 기존 과정 재수행 필요

     


    6. ArgoCD + Vault Plugin (Kubernetes Auth/AppRole) - CD

    사전준비 : Argo CD 설치 및 기본 설정 - helm_chart앞에서 진행했다면 생략!

     

    Vault + ArgoCD Plugin 패턴 - 링크1 링크2


    ▶ ArgoCD Vault Plugin 소개

    더보기
    • Argo CD에는 다양한 시크릿 관리 도구(HashiCorp Vault, IBM Cloud Secrets Manager, AWS Secrets Manager 등)플러그인을 통해 Kubernetes 리소스에 주입할 수 있도록 지원합니다.
    • 플러그인을 통해 Operator 또는 CRD(Custom Resource Definition)에 의존하지 않고 GitOps와 Argo CD로 시크릿 관리 문제를 해결할 수 있습니다.
    • 특히 Secret 뿐만 아니라, deployment, configMap 또는 기타 Kubernetes 리소스에도 사용할 수 있습니다. 

    ▶ 설치/구성관련 내용 요약(ChatGPT) - Docs

    더보기

    Argo CD Vault Plugin의 공식 문서에서는 Argo CD에 플러그인을 설치하는 방법으로 네 가지를 제시하고 있습니다:

    1. argocd-cm ConfigMap을 통한 설치:
      • argocd-repo-server에 InitContainer를 추가하여 플러그인을 다운로드하고 설정합니다.
      • 또는 플러그인이 사전 설치된 커스텀 이미지를 생성하여 사용합니다.
    2. 사이드카 컨테이너를 통한 설치:
      • 사이드카 컨테이너를 추가하여 플러그인과 필요한 도구들을 포함시킵니다.
      • 또는 플러그인이 사전 설치된 커스텀 사이드카 이미지를 생성하여 사용합니다.

    Step 1. ArgoCD Vault Plugin을 위한 Credentials 활성화 - AppRole 인증

    더보기
    • argocd 네임스페이스 활성화
    kubens argocd
    • 이전 실습에서 획득한 Role_ID, Secret_ID활용
    kubectl apply -f - <<EOF
    kind: Secret
    apiVersion: v1
    metadata:
      name: argocd-vault-plugin-credentials
      namespace: argocd
    type: Opaque
    stringData:
      VAULT_ADDR: "http://vault.vault:8200"
      AVP_TYPE: "vault"
      AVP_AUTH_TYPE: "approle"
      AVP_ROLE_ID: 7e0c2ccf-b973-14ad-8efe-942b0bb1a2c5 #Role_ID
      AVP_SECRET_ID: a93f212d-886c-5611-7233-4f91eb85accd #Secret_ID
    EOF

     

     

    Step 2. ArgoCD Vault Plugin 설치 - Blog

    더보기

    참고사항

    ArgoCD Vault Plugin 설치 방법은 2가지가 있으며 현재는 Installation via a sidecar container 방식을 사용하는 것을 권장합니다.

    → 이번 스터디에서는 편의상 Helm으로 배포한 ArgoCD에 Kustomize을 활용해 기존 YAML에 대한 Patch을 적용합니다.

    https://github.com/hyungwook0221/argocd-vault-plugin/tree/main/manifests/cmp-sidecar

     

    • ArgoCD Helm 설치시 Plugin 활성화 방안
    git clone https://github.com/hyungwook0221/argocd-vault-plugin.git
    cd argocd-vault-plugin/manifests/cmp-sidecar
    
    # 예전 문법으로 적용된 부분을 edit fix 명령으로 현행화
    # kustomize edit fix
    
    # argocd 네임스페이스 설정
    kubens argocd
    
    # 생성될 메니페스트 파일에 대한 확인
    kubectl kustomize .
    
    # -k 옵션으로 kusomize 실행
    kubectl apply -n argocd -k .
    k exec -it -n vault vault-0 -- sh
    
    # vault pod shell에 접속 후 root token으로 로그인
    vault login
    Token (will be hidden): <토큰입력>
        
    # 확인명령
    vault read auth/kubernetes/role/argocd

    Step 3. 샘플 Application 배포하여 Vault와 동기화

    더보기

    ☞ (참고) luafanti/spring-boot-debug-app:main 이미지는 amd64 CPU만 가능 (arm CPU는 기동 실패) - Link

     

    Step3-1. Application.yaml 작성

    더보기
    • GitHub에 저장된 Helm Repo을 배포하며, Helm 메니페스트 내에 변수로 치환된 값(username/password)을 CD 단계에서 Vault 통해서 읽고 렌더링하여 배포
    kubectl apply -n argocd -f - <<EOF
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: demo
      namespace: argocd
      finalizers:
      - resources-finalizer.argocd.argoproj.io
    spec:
      destination:
        namespace: argocd
        server: https://kubernetes.default.svc
      project: default
      source:
        path: infra/helm
        repoURL: https://github.com/hyungwook0221/spring-boot-debug-app
        targetRevision: main
        plugin:
          name: argocd-vault-plugin-helm
          env:
            - name: HELM_ARGS
              value: -f new-values.yaml
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
    EOF

    Step3-2. Application 배포시 참조하는 new-values.yaml 확인 - GitHub

    더보기
    serviceAccount:
      create: true
    
    image:
      repository: luafanti/spring-boot-debug-app
      tag: main
      pullPolicy: IfNotPresent
    
    replicaCount: 1
    
    resources:
      memoryRequest: 256Mi
      memoryLimit: 512Mi
      cpuRequest: 500m
      cpuLimit: 1
    
    probes:
      liveness:
        initialDelaySeconds: 15
        path: /actuator/health/liveness
        failureThreshold: 3
        successThreshold: 1
        timeoutSeconds: 3
        periodSeconds: 5
      readiness:
        initialDelaySeconds: 15
        path: /actuator/health/readiness
        failureThreshold: 3
        successThreshold: 1
        timeoutSeconds: 3
        periodSeconds: 5
    
    ports:
      http:
        name: http
        value: 8080
      management:
        name: management
        value: 8081
    
    envs:
      - name: VAULT_SECRET_USER
        value: <path:secret/data/sampleapp/config#username>
      - name: VAULT_SECRET_PASSWORD
        value: <path:secret/data/sampleapp/config#password>
    
    log:
      level:
        spring: "info"
        service: "info"

    [ 실행 결과 - 한 눈에 보기 ]

    https://github.com/hyungwook0221/spring-boot-debug-app/blob/main/infra/helm/new-values.yaml

     

    spring-boot-debug-app/infra/helm/new-values.yaml at main · hyungwook0221/spring-boot-debug-app

    Spring Boot application for debugging purpose. Contribute to hyungwook0221/spring-boot-debug-app development by creating an account on GitHub.

    github.com

    Step 3-3. 실제 배포시 적용된 화면

    더보기
    • ArgoCD Vault Plugin 적용된 화면 : [DETAILS] - [PARAMETERS]
    • Application 배포화면
    • Deployment에 적용된 env 값 확인 :
    envs:
      - name: VAULT_SECRET_USER
        value: <path:secret/data/sampleapp/config#username>
      - name: VAULT_SECRET_PASSWORD
        value: <path:secret/data/sampleapp/config#password>

     

    ☞ 실습 완료 후, ArgoCD App 삭제

    kubectl delete applications demo

     

     

    (참고) Unknown 에러 발생시 redis 파드 재배포 후 필요 

    더보기
    • ArgoCD repo-server 재기동 → Redis 재기동 → Application 재생성

     


    ▶ 도전과제

    1) ArgoCD Helm 설치시 Vault Plugin 한번에 설치하기

    더보기

    👉 이번 스티디에서 학습한 방식으로 ArgoCD을 구성할 경우에는 운영시 버전관리에 어려움이 생길 수 있으므로 ArgoCD을 Helm, Kustomize, Operator 방식 한 가지를 채택하여 Plugin 설치하는 것을 적용해보자.

    2) AVP Helm대신 AVP Kustomize, AVP 방식으로 배포해보기 - Docs

    3) 다른 타입의 리소스 배포해보기 - Docs

    더보기

    예시) K8s Secret

    kind: Secret
    apiVersion: v1
    metadata:
      name: example-secret
      annotations:
        avp.kubernetes.io/path: "path/to/secret"
    type: Opaque
    data:
      password: <password-vault-key>

     

    ▶ 추가 학습자료


    7. Vault Secrets Operator (VSO)

    학습 목표

    더보기
    • Vault Secrets Operator(이하, VSO)에 대한 주요 개념과 구성요소를 알아본다.
    • VSO을 통해서 Vault을 통해 획득한 시크릿을 Kubernetes Secret을 자동으로 동기화하고 관리하는 방법을 알아본다.

     

    ▶ VSO란? - Docs

    더보기

    Vault Secrets Operator(VSO)는 Kubernetes Secrets에서 Vault secrets 및 HCP Vault Secrets Apps를 네이티브하게 사용할 수 있도록 Pods에 제공해줍니다.


    1) 개요

    Vault Secrets Operator는 지원하는 Custom Resource Definitions(CRD) 집합의 변경 사항을 감시하여 작동합니다. 각 CRD는 시크릿의 지원되는 소스 중 하나에서 Kubernetes Secret으로 동기화할 수 있도록 필요한 사양을 제공합니다.

    오퍼레이터는 소스 시크릿 데이터를 대상 Kubernetes Secret에 직접 작성하며, 소스에 변경 사항이 발생할 경우 해당 내용을 대상에도 수명 주기 동안 지속적으로 반영합니다. 이렇게 함으로써 애플리케이션은 대상 시크릿에만 접근하면 그 안의 시크릿 데이터를 사용할 수 있습니다.


    2) 기능

    Vault Secrets Operator가 지원하는 주요 기능은 다음과 같습니다:

    • 여러 시크릿 소스로부터의 동기화 지원
    • 자동 시크릿 일탈 감지 및 수정
    • Deployment, ReplicaSet, StatefulSet Kubernetes 리소스 유형에 대한 자동 시크릿 교체
    • 오퍼레이터 모니터링을 위한 Prometheus 전용 계측 지원
    • Helm 또는 Kustomize를 통한 설치 지원
    • 시크릿 데이터 변환 지원

    3) 지원되는 Kubernetes 배포판

    Vault Secrets Operator는 다음과 같은 호스팅된 Kubernetes 환경에서 성공적으로 테스트되었습니다:

    • Amazon Elastic Kubernetes Service (EKS)
    • Google Kubernetes Engine (GKE)
    • Microsoft Azure Kubernetes Service (AKS)
    • Red Hat OpenShift (공식 인증됨)

    4)  유사한 오픈소스 프로젝트는? - ESO(External Secrets Operator)

     

    ▶ VSO 구성도 - 개발자(Developer) / 운영자(Operator)의 역할

    ▶ Vault 매핑구조

    더보기

     

    ☞ vault policy 에 의해 key/value 값을 지정하고, 이에 대한 vault Role을 지정하여 application 배포 관련 Yaml 내 주요 정보들을 동적으로 rendoring 함으로써 보안성을 강화 할 수 있다. 또한, PKI 인증서와 같은 값들에 대해서도 vault Engine 활성화하여 내부 에 저장된 Secret 값들을 k8s 의 secret(static/dynamic) 연계하여 seamless 하게 사용할 수 있다.

     

    ▶ VSO 주요 CRD 정보 및 예시

    더보기

    1) VSO CRD 관계도

    ☞ Namespace에 국한된 분산된 secret 값들을 "vault Secret Operator"를 통하여 중앙 집중적으로 편리하게 관리할 수 있다.

    2) VaultConnect

    ---
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultConnection
    metadata:
      namespace: vso-example
      name: vault-connection
    spec:
      # required configuration
      # address to the Vault server.
      address: http://vault.vault.svc.cluster.local:8200

    3) VaultAuth

    ---
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultAuth
    metadata:
      namespace: vso-example
      name: vault-auth
    spec:
      # required configuration
      # VaultConnectionRef of the corresponding VaultConnection CustomResource.
      # If no value is specified the Operator will default to the `default` VaultConnection,
      # configured in its own Kubernetes namespace.
      vaultConnectionRef: vault-connection
      # Method to use when authenticating to Vault.
      method: kubernetes
      # Mount to use when authenticating to auth method.
      mount: kubernetes
      # Kubernetes specific auth configuration, requires that the Method be set to kubernetes.
      kubernetes:
        # role to use when authenticating to Vault
        role: example
        # ServiceAccount to use when authenticating to Vault
        # it is recommended to always provide a unique serviceAccount per Pod/application
        serviceAccount: default

    4) VaultAuthGlobal

    ---
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultAuthGlobal
    metadata:
      namespace: vso-example
      name: vault-auth-global
    spec:
      defaultAuthMethod: kubernetes
      kubernetes:
        audiences:
        - vault
        mount: kubernetes
        namespace: example-ns
        role: auth-role
        serviceAccount: default
        tokenExpirationSeconds: 600
    ---
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultAuth
    metadata:
      namespace: vso-example
      name: vault-auth
    spec:
      vaultAuthGlobalRef:
        name: vault-auth-global
      kubernetes:
        role: local-role

    5) VaultStaticSecret

    # KV Version 1
    ---
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultStaticSecret
    metadata:
      namespace: vso-example
      name: vault-static-secret-v1
    spec:
      vaultAuthRef: vault-auth
      mount: kvv1
      type: kv-v1
      path: eng/apikey/google
      refreshAfter: 60s
      destination:
        create: true
        name: static-secret1
    ---
    # KV Version 2
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultStaticSecret
    metadata:
      namespace: vso-example
      name: vault-static-secret-v2
    spec:
      vaultAuthRef: vault-auth
      mount: kvv2
      type: kv-v2
      path: eng/apikey/google
      version: 2
      refreshAfter: 60s
      destination:
        create: true
        name: static-secret2

    6) VaultDynamicSecret

    # DB Secret 예시
    ---
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultDynamicSecret
    metadata:
      namespace: vso-example
      name: vault-dynamic-secret-db
    spec:
      vaultAuthRef: vault-auth
      mount: db
      path: creds/my-postgresql-role
      destination:
        create: true
        name: dynamic-db
    ---
    # AWS Secret 예시
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultDynamicSecret
    metadata:
      namespace: vso-example
      name: vault-dynamic-secret-aws-iam
    spec:
      vaultAuthRef: vault-auth
      mount: aws
      path: creds/my-iam-role
      destination:
        create: true
        name: dynamic-aws-iam

    7) VaultPKISecret

    ---
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultPKISecret
    metadata:
      namespace: vso-example
      name: vault-pki
    spec:
      vaultAuthRef: vault-auth
      mount: pki
      role: default
      commonName: example.com
      format: pem
      expiryOffset: 1s
      ttl: 60s
      namespace: tenant-1
      destination:
        create: true
        name: pki1

     

    🔐 위협 모델 및 보안 고려사항 - Docs

    7-1. Static Secrets (KV) - Docs

    ▶ 개요

    더보기
    • 운영자가 단일 Vault 정적 시크릿을 단일 Kubernetes 시크릿으로 동기화하는 데 필요한 구성입니다.
    • 지원되는 시크릿 엔진: kv-v2, kv-v1

    ▶ 워크플로우 및 아키텍쳐

    ▶ 실습

    7-2. Dynamic Secrets

    ▶ 개요

    더보기
    • Vault의 Dynamic Secret Engine을 활용하여 동적으로 변경되는 Secrets을 K8s Secrets에 동기화
    • 지원되는 시크릿 엔진: DB Credentials, Cloud Credentials(AWS, Azure, GCP 등)

     

    ▶ 시나리오

    더보기
    • Spring(Web Application) → DB 접근하기 위해서는 DB Credentials을 K8s Secrets으로 참조해야함
    • 대상 DB에 접근하기 위한 DB Credentials을 Vault의 Dynamic Secrets 기능을 활용하여 주기적으로 변경하고 VSO을 통해 K8s Secets에 갱신(업데이트)
    • Spring(Web Application)에서는 갱신된 DB Credentials 정보를 K8s Secret을 통해서 읽어오기 위해 재기동(rolloutRestartTargets 설정)
    (1) 10주차 - K8s 시크릿 관리 Update (진행 _ 유형욱).html
    0.61MB

     

     

    ▶ 실습

    Step1. VSO 배포를 위한 Chart Values 파일 작성

    더보기
    # vault-operator-values.yaml
    defaultVaultConnection:
      enabled: true
      address: "http://vault.vault.svc.cluster.local:8200"
      skipTLSVerify: false
    controller:
      manager:
        clientCache:
          persistenceModel: direct-encrypted
          storageEncryption:
            enabled: true
            mount: k8s-auth-mount
            keyName: vso-client-cache
            transitMount: demo-transit
            kubernetes:
              role: auth-role-operator
              serviceAccount: vault-secrets-operator-controller-manager
              tokenAudiences: ["vault"]

    Step2. VSO 배포

    더보기
    helm install vault-secrets-operator hashicorp/vault-secrets-operator \
      -n vault-secrets-operator-system \
      --create-namespace \
      --values vault-operator-values.yaml
      
    #
    kubectl get-all -n vault-secrets-operator-system
    
    #
    kubectl get pod -n vault-secrets-operator-system      
    NAME                                                         READY   STATUS    RESTARTS   AGE
    vault-secrets-operator-controller-manager-7f67cd89fd-d2t2k   2/2     Running   0          53s
    
    kubectl describe pod -n vault-secrets-operator-system                                       
    ...
    Service Account:  vault-secrets-operator-controller-manager
    ...
    Containers:
      kube-rbac-proxy:
        Container ID:  containerd://db3eae7b836fb4f1b4236c494c8fa96ada94769a6c602e1a150c75293a6a4162
        Image:         quay.io/brancz/kube-rbac-proxy:v0.18.1
      ...
      manager:
        Container ID:  containerd://1ab1545fb4bd86ac52d6c7609a3e962cd2d1a81daa9bbd9c82f79d9a0d8b6466
        Image:         hashicorp/vault-secrets-operator:0.10.0
    ...
    
    #
    kubectl rbac-tool lookup vault-secrets-operator-controller-manager
      SUBJECT                                   | SUBJECT TYPE   | SCOPE       | NAMESPACE                     | ROLE                                        | BINDING                                             
    --------------------------------------------+----------------+-------------+-------------------------------+---------------------------------------------+-----------------------------------------------------
      vault-secrets-operator-controller-manager | ServiceAccount | ClusterRole |                               | vault-secrets-operator-proxy-role           | vault-secrets-operator-proxy-rolebinding            
      vault-secrets-operator-controller-manager | ServiceAccount | ClusterRole |                               | vault-secrets-operator-manager-role         | vault-secrets-operator-manager-rolebinding          
      vault-secrets-operator-controller-manager | ServiceAccount | Role        | vault-secrets-operator-system | vault-secrets-operator-leader-election-role | vault-secrets-operator-leader-election-rolebinding  
    
    #
    kubectl rolesum vault-secrets-operator-controller-manager -n vault-secrets-operator-system
    ServiceAccount: vault-secrets-operator-system/vault-secrets-operator-controller-manager
    Secrets:
    
    Policies:
    • [RB] vault-secrets-operator-system/vault-secrets-operator-leader-election-rolebinding ⟶  [R] vault-secrets-operator-system/vault-secrets-operator-leader-election-role
      Resource                    Name  Exclude  Verbs  G L W C U P D DC  
      configmaps                  [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      events                      [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✔ ✖ ✖   
      leases.coordination.k8s.io  [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
    
    
    • [CRB] */vault-secrets-operator-manager-rolebinding ⟶  [CR] */vault-secrets-operator-manager-role
      Resource                                                Name  Exclude  Verbs  G L W C U P D DC  
      configmaps                                              [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      daemonsets.apps                                         [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✔ ✖ ✖   
      deployments.apps                                        [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✔ ✖ ✖   
      events                                                  [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✔ ✖ ✖   
      hcpauths.secrets.hashicorp.com                          [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      hcpauths.secrets.hashicorp.com/finalizers               [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      hcpauths.secrets.hashicorp.com/status                   [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
      hcpvaultsecretsapps.secrets.hashicorp.com               [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      hcpvaultsecretsapps.secrets.hashicorp.com/finalizers    [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      hcpvaultsecretsapps.secrets.hashicorp.com/status        [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
      rollouts.argoproj.io                                    [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✔ ✖ ✖   
      secrets                                                 [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔   
      secrettransformations.secrets.hashicorp.com             [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      secrettransformations.secrets.hashicorp.com/finalizers  [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      secrettransformations.secrets.hashicorp.com/status      [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
      serviceaccounts                                         [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      serviceaccounts/token                                   [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✖ ✖ ✖ ✖   
      statefulsets.apps                                       [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✔ ✖ ✖   
      vaultauthglobals.secrets.hashicorp.com                  [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      vaultauthglobals.secrets.hashicorp.com/finalizers       [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      vaultauthglobals.secrets.hashicorp.com/status           [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
      vaultauths.secrets.hashicorp.com                        [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      vaultauths.secrets.hashicorp.com/finalizers             [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      vaultauths.secrets.hashicorp.com/status                 [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
      vaultconnections.secrets.hashicorp.com                  [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      vaultconnections.secrets.hashicorp.com/finalizers       [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      vaultconnections.secrets.hashicorp.com/status           [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
      vaultdynamicsecrets.secrets.hashicorp.com               [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      vaultdynamicsecrets.secrets.hashicorp.com/finalizers    [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      vaultdynamicsecrets.secrets.hashicorp.com/status        [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
      vaultpkisecrets.secrets.hashicorp.com                   [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      vaultpkisecrets.secrets.hashicorp.com/finalizers        [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      vaultpkisecrets.secrets.hashicorp.com/status            [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
      vaultstaticsecrets.secrets.hashicorp.com                [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✖   
      vaultstaticsecrets.secrets.hashicorp.com/finalizers     [*]     [-]     [-]   ✖ ✖ ✖ ✖ ✔ ✖ ✖ ✖   
      vaultstaticsecrets.secrets.hashicorp.com/status         [*]     [-]     [-]   ✔ ✖ ✖ ✖ ✔ ✔ ✖ ✖   
    
    
    • [CRB] */vault-secrets-operator-proxy-rolebinding ⟶  [CR] */vault-secrets-operator-proxy-role
      Resource                                   Name  Exclude  Verbs  G L W C U P D DC  
      subjectaccessreviews.authorization.k8s.io  [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
      tokenreviews.authentication.k8s.io         [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
    
    #
    kubectl get vaultauth -n vault-secrets-operator-system vault-secrets-operator-default-transit-auth -o jsonpath='{.spec}' | jq
    {
      "kubernetes": {
        "audiences": [
          "vault"
        ],
        "role": "auth-role-operator",
        "serviceAccount": "vault-secrets-operator-controller-manager",
        "tokenExpirationSeconds": 600
      },
      "method": "kubernetes",
      "mount": "k8s-auth-mount",
      "storageEncryption": {
        "keyName": "vso-client-cache",
        "mount": "demo-transit"
      },
      "vaultConnectionRef": "default"
    }
    
    kubectl get vaultconnection -n vault-secrets-operator-system default -o jsonpath='{.spec}' | jq
    {
      "address": "http://vault.vault.svc.cluster.local:8200",
      "skipTLSVerify": false
    }

     

    Step3. PostgreSQL 설치 (Bitnami Helm Chart)

    더보기
    kubectl create ns postgres
    
    helm repo add bitnami https://charts.bitnami.com/bitnami
    
    helm upgrade --install postgres bitnami/postgresql \
      --namespace postgres \
      --set auth.audit.logConnections=true \
      --set auth.postgresPassword=secret-pass
      
    # psql 로그인 확인
    kubectl exec -it -n postgres postgres-postgresql-0 -- sh -c 'PGPASSWORD=secret-pass psql -U postgres -h localhost'
    kubectl exec -it -n postgres postgres-postgresql-0 -- sh -c "PGPASSWORD=secret-pass psql -U postgres -h localhost -c '\l'"

     

    Step4. Kubernetes Auth Method 설정

    더보기
    #
    kubectl exec -it vault-0 -n vault -- cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    -----BEGIN CERTIFICATE-----
    MIIDBTCCAe2gAwIBAgIIfA5oV86RmOswDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
    AxMKa3ViZXJuZXRlczAeFw0yNTA0MTAwMDUzMTdaFw0zNTA0MDgwMDU4MTdaMBUx
    EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
    AoIBAQDSUCHyBXVhBb91uZKdqwFAussfSxqPadYhrj659fp/XuJQrRrOMLZAh5eP
    6MCkeYbHeMY2stwBzuwzSzyaxkJdKiR5yKhtIHT7vpYn4XT0bNdyWWKAHkjyqjsk
    DYkAbwQ1xksL3DwArRfUSdZPCVsDLhu+G3wbGKiW64q0Rb1pEk/gK2AhxnSSqoeR
    YNzh10FA/CxS2wiLFfsm8NAn4KHG134AKY0Vxb/EkXnL9b2jRK+BfSh/sSpuEzN3
    16Xy1fC5UX/xCdTvwx+57xOoFhymaF0aVISSavzrkaYd8kaPwAKtoAlxvDmDrVhv
    YKRfCTerHUCOL3IPQqChIZQIUvoVAgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP
    BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSDK0FYEL+FNdgIpMhCnj9g2CkAIjAV
    BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQC5/EdWTRD6
    SoghjPe/bKKfQzFxPmEZVTb0JI0CWVUZorGzFcHCMCXMOf/XuECkK5tqn7QR/Nw8
    yy3XvmT6DY+J2GshrusJXfMEOJ9RQIwDBJKHoZqbcyI/w294Ecp1Mpivb8WeI2FW
    wMsaDoswQ3H/0zrEBwhhpUAWcmjm6w4/7Tsid9t1DsQrp9xH37wU/uP07HyEIKPS
    TCQ2tUueHSVihNhCg7RejjbiGy8EUZV6aGIrtFgss9Mzk+5m+1KXm8o+Lj/YNYFx
    JFeII2h3ZptO4I9vHrohXKVw0esHNzdHQPeGEXCWHTd+K38jIJPfggA8Yu1htbUa
    MKEIbcT53UHH
    -----END CERTIFICATE-----
    
    # 해당 ca.crt 는 k8s 의 루트인증서 정보
    kubectl exec -it vault-0 -n vault -- cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt > vso-ca.crt
    openssl x509 -in vso-ca.crt -noout -text
    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number: 8939197036714105067 (0x7c0e6857ce9198eb)
            Signature Algorithm: sha256WithRSAEncryption
            Issuer: CN=kubernetes
    ...
    
    # vault 서비스 어카운트의 token 확인
    kubectl exec -it vault-0 -n vault -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
    eyJhbGciOiJSUzI1NiIsImtpZCI6InJBT1dvdDVZME1wMjZ4NmJuZkVvNk9nc1BGY1FIVDgyaUllUy1VN05oejgifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzc1ODAzNjMyLCJpYXQiOjE3NDQyNjc2MzIsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiZTJkOTdiYTItNTMzMi00ZDFlLWJjYWMtYzg3NDdmNGQ2MWIwIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJ2YXVsdCIsIm5vZGUiOnsibmFtZSI6Im15azhzLWNvbnRyb2wtcGxhbmUiLCJ1aWQiOiI5ZGZkYTg5YS1hYzZkLTRmNTYtYjlhOC03MTg2YTI3MWVjZjMifSwicG9kIjp7Im5hbWUiOiJ2YXVsdC0wIiwidWlkIjoiNGFlMTVhZTctOTI0NS00OTZkLWE0ZDItYWI3ZTk4NDA1Yjg1In0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJ2YXVsdCIsInVpZCI6ImNjYjgwNjJkLTQ4OTctNDI1MC1hYzg3LTYxMTAxYWQ0YjI4NyJ9LCJ3YXJuYWZ0ZXIiOjE3NDQyNzEyMzl9LCJuYmYiOjE3NDQyNjc2MzIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDp2YXVsdDp2YXVsdCJ9.dekUKWseyWNgYO4uUi1MHI16BvHOAmeO61gxO-LIEK7m5ahG72wLwUnl5G22eRlA8T9X2DnvB8tIPyGDawzkCJWve7PTq-_76QOXZO8a3-KUY9kJ3gI-GymN9jwV9eUxXoFGqp73WxZCK-derP6aj5WZirSCHb1YQnioKmxQ4frhQVYi-GDLLz6A2OH-MnZIM68LvFPxK06Id2McMlqOFnV3JCDSzxvyEtnANQgdNWXIit-o6FFzDvKhlfwN6Yrjs9AO7JEyFeQy7M-C2_mQq_Xqht-cO28b8tA4kEJLIdWSIfu1LAWP06YZYeEbQM-8qZ9HoXR1L-QFtonMLkJ1pg
    
    # https://jwt.io/ 에서 token 디코드 확인 시 PAYLOAD 부분
    {
      "aud": [
        "https://kubernetes.default.svc.cluster.local"
      ],
      "exp": 1775803632,
      "iat": 1744267632,
      "iss": "https://kubernetes.default.svc.cluster.local",
      "jti": "e2d97ba2-5332-4d1e-bcac-c8747f4d61b0",
      "kubernetes.io": {
        "namespace": "vault",
        "node": {
          "name": "myk8s-control-plane",
          "uid": "9dfda89a-ac6d-4f56-b9a8-7186a271ecf3"
        },
        "pod": {
          "name": "vault-0",
          "uid": "4ae15ae7-9245-496d-a4d2-ab7e98405b85"
        },
        "serviceaccount": {
          "name": "vault",
          "uid": "ccb8062d-4897-4250-ac87-61101ad4b287"
        },
        "warnafter": 1744271239
      },
      "nbf": 1744267632,
      "sub": "system:serviceaccount:vault:vault"
    }
    
    # vault SA 토큰 만료 기간 : 대략 1시간(3600초)+7초
    k get pod -n vault vault-0 -o yaml
    ...
     - name: kube-api-access-9rqgv
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              expirationSeconds: 3607
              path: token
          - configMap:
              items:
              - key: ca.crt
                path: ca.crt
              name: kube-root-ca.crt
    
    
    
    kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh
    ----------------------------------------------------------------
    # 최초 설치시 획득한 vault root token 사용. 
    # 예시) hvs.hnlFKWjE10FOyrwLMeK9PrCC
    vault login
    
    # Kubernetes 인증 메서드 활성화
    vault auth enable -path k8s-auth-mount kubernetes
    
    #
    vault auth list
    Path               Type          Accessor                    Description                Version
    ----               ----          --------                    -----------                -------
    approle/           approle       auth_approle_03723e90       n/a                        n/a
    k8s-auth-mount/    kubernetes    auth_kubernetes_a2ee4150    n/a                        n/a
    token/             token         auth_token_de4f4e43         token based credentials    n/a
    
    
    # Kubernetes 클러스터 정보 구성
    vault write auth/k8s-auth-mount/config \
      kubernetes_host="https://kubernetes.default.svc:443" \
      kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
      token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
    
    # 설정 확인  
    vault read auth/k8s-auth-mount/config
    Key                                  Value
    ---                                  -----
    disable_iss_validation               true
    disable_local_ca_jwt                 false
    issuer                               n/a
    kubernetes_ca_cert                   -----BEGIN CERTIFICATE-----
    MIIDBTCCAe2gAwIBAgIIfA5oV86RmOswDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
    AxMKa3ViZXJuZXRlczAeFw0yNTA0MTAwMDUzMTdaFw0zNTA0MDgwMDU4MTdaMBUx
    EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
    AoIBAQDSUCHyBXVhBb91uZKdqwFAussfSxqPadYhrj659fp/XuJQrRrOMLZAh5eP
    6MCkeYbHeMY2stwBzuwzSzyaxkJdKiR5yKhtIHT7vpYn4XT0bNdyWWKAHkjyqjsk
    DYkAbwQ1xksL3DwArRfUSdZPCVsDLhu+G3wbGKiW64q0Rb1pEk/gK2AhxnSSqoeR
    YNzh10FA/CxS2wiLFfsm8NAn4KHG134AKY0Vxb/EkXnL9b2jRK+BfSh/sSpuEzN3
    16Xy1fC5UX/xCdTvwx+57xOoFhymaF0aVISSavzrkaYd8kaPwAKtoAlxvDmDrVhv
    YKRfCTerHUCOL3IPQqChIZQIUvoVAgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP
    BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSDK0FYEL+FNdgIpMhCnj9g2CkAIjAV
    BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQC5/EdWTRD6
    SoghjPe/bKKfQzFxPmEZVTb0JI0CWVUZorGzFcHCMCXMOf/XuECkK5tqn7QR/Nw8
    yy3XvmT6DY+J2GshrusJXfMEOJ9RQIwDBJKHoZqbcyI/w294Ecp1Mpivb8WeI2FW
    wMsaDoswQ3H/0zrEBwhhpUAWcmjm6w4/7Tsid9t1DsQrp9xH37wU/uP07HyEIKPS
    TCQ2tUueHSVihNhCg7RejjbiGy8EUZV6aGIrtFgss9Mzk+5m+1KXm8o+Lj/YNYFx
    JFeII2h3ZptO4I9vHrohXKVw0esHNzdHQPeGEXCWHTd+K38jIJPfggA8Yu1htbUa
    MKEIbcT53UHH
    -----END CERTIFICATE-----
    kubernetes_host                      https://kubernetes.default.svc:443
    pem_keys                             []
    token_reviewer_jwt_set               true
    use_annotations_as_alias_metadata    false
    
    ----------------------------------------------------------------

     

    ▶ vault write 명령 분석

    • Vault가 K8s API 서버에 인증 요청을 보낼 수 있도록 설정
      • kubernetes_host: API 서버 주소
      • kubernetes_ca_cert: API 서버의 CA 인증서 (Pod 내 ServiceAccount에서 자동 주입됨)
      • token_reviewer_jwt: TokenReviewer 권한이 있는 토큰으로 Vault가 요청자의 토큰을 검증할 수 있도록 설정

    [ 실행 결과 - 한 눈에 보기 ]

     

     

    Step5. Vault Kubernetes Auth Role 설정

    더보기

    실행 중인 애플리케이션이 Vault로부터 DB 자격증명(동적 사용자)을 받아올 수 있도록 권한을 연결을 위한 설정

    vault write auth/k8s-auth-mount/role/auth-role \
       bound_service_account_names=demo-dynamic-app \
       bound_service_account_namespaces=demo-ns \
       token_ttl=0 \
       token_period=120 \
       token_policies=demo-auth-policy-db \
       audience=vault

    [ 상세 설명 ]

    # auth-role 생성
    vault write auth/k8s-auth-mount/role/auth-role \
       bound_service_account_names=demo-dynamic-app \ # demo-dynamic-app 서비스 어카운트를 사용하는 Pod에서만 인증 허용, Pod 내부에서 Vault에 로그인하려면 이 서비스 어카운트를 써야 함
       bound_service_account_namespaces=demo-ns \ # 이 Role은 오직 demo-ns 네임스페이스에서 오는 요청만 허용
       token_ttl=0 \ # 발급되는 Vault 토큰의 기본 TTL 없음(0). 일반적으로 token_ttl=0이면 token_period가 사용됨
       token_period=120 \ # Lease 기간이 120초인 Renewable Token 발급. TTL이 없는 대신, 2분마다 갱신 가능한 토큰을 생성. 보통 이 설정은 "periodic token"이라 불리며, long-lived session에 적합.
       token_policies=demo-auth-policy-db \ # 인증에 성공하면 발급되는 토큰에 demo-auth-policy-db 정책이 적용
       audience=vault # JWT 토큰의 aud (Audience) claim 검증에 사용.

    Step6. Vault Database Secret Engine 설정

    더보기
    # demo-db라는 경로로 Database Secret Engine을 활성화
    vault secrets enable -path=demo-db database
    
    # PostgreSQL 연결 정보 등록
    # 해당 과정은 postgres가 정상적으로 동작 시 적용 가능
    # allowed_roles: 이후 설정할 Role 이름 지정
    vault write demo-db/config/demo-db \
       plugin_name=postgresql-database-plugin \
       allowed_roles="dev-postgres" \
       connection_url="postgresql://{{username}}:{{password}}@postgres-postgresql.postgres.svc.cluster.local:5432/postgres?sslmode=disable" \
       username="postgres" \
       password="secret-pass"
      
    # DB 사용자 동적 생성 Role 등록
    # 해당 Role 사용 시 Vault가 동적으로 사용자 계정과 비밀번호를 생성 가능
    # TTL은 생성된 자격증명의 유효 시간 (30초~10분)
    ## creation_statements=... : Vault가 계정을 자동 생성할 때 실행할 SQL 문. {{name}}, {{password}}, {{expiration}}은 Vault가 자동으로 채워줌.
    ## revocation_statements=... : Vault가 동적 사용자 계정을 폐기(revoke)할 때 실행할 SQL 문. 권한을 모두 회수.
    ## default_ttl="1m" / max_ttl="1m" : 기본 1분만 유효합니다. 갱신을 안 하면 1분 후 자동 만료 (계정도 자동 삭제). 최대 TTL도 1분이므로 연장도 최대 1분까지만.
    vault write demo-db/roles/dev-postgres \
       db_name=demo-db \
       creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
          GRANT ALL PRIVILEGES ON DATABASE postgres TO \"{{name}}\";" \
       revocation_statements="REVOKE ALL ON DATABASE postgres FROM  \"{{name}}\";" \
       backend=demo-db \
       name=dev-postgres \
       default_ttl="1m" \
       max_ttl="1m"
    
    # 정책 설정: DB 자격증명 읽기 권한
    # demo-db/creds/dev-postgres 경로에 대한 read 권한 부여
    # 추후 Kubernetes 서비스 어카운트(demo-dynamic-app)에 이 정책을 연결해서 자격증명 요청 가능
    vault policy write demo-auth-policy-db - <<EOF
    path "demo-db/creds/dev-postgres" {
       capabilities = ["read"]
    }
    EOF

     

    Step7. Transit Secret Engine 설정 + VSO 연동 Role 구성 - Docs , Client-cache

    더보기
    kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh
    ----------------------------------------------------------------
    # Transit Secret Engine 활성화
    # transit 엔진을 demo-transit 경로로 활성화.
    # 데이터를 저장하지 않고 암복호화 기능만 제공하는 Vault의 기능
    # 클라이언트 캐시는 리더십 변경 시에도 Vault 토큰 및 동적 비밀 임대를 계속 추적하고 갱신할 수 있으므로 원활한 업그레이드를 지원합니다
    ## Vault 서버에 클라이언트 캐시를 저장하고 암호화할 수 있습니다.
    ## Vault 동적 비밀을 사용하는 경우 클라이언트 캐시를 영구적으로 저장하고 암호화하는 것이 좋습니다.
    ## 이렇게 하면 재시작 및 업그레이드를 통해 동적 비밀 임대가 유지됩니다.
    vault secrets enable -path=demo-transit transit
    vault secrets list -detailed
    
    # vso-client-cache라는 키를 생성
    # 이 키는 VSO가 암복호화 시 사용할 암호화 키 역할
    vault write -force demo-transit/keys/vso-client-cache
    
    # vso-client-cache 키에 대해 암호화(encrypt), 복호화(decrypt)를 허용하는 정책 생성
    vault policy write demo-auth-policy-operator - <<EOF
    path "demo-transit/encrypt/vso-client-cache" {
       capabilities = ["create", "update"]
    }
    path "demo-transit/decrypt/vso-client-cache" {
       capabilities = ["create", "update"]
    }
    EOF
    
    # Vault Secrets Operator가 사용하는 ServiceAccount에 위 정책을 바인딩
    # vso가 Vault에 로그인할 때 사용할 수 있는 JWT 기반 Role 설정
    # 해당 Role을 통해 Operator는 Transit 엔진을 이용한 암복호화 API 호출 가능
    vault write auth/k8s-auth-mount/role/auth-role-operator \
       bound_service_account_names=vault-secrets-operator-controller-manager \
       bound_service_account_namespaces=vault-secrets-operator-system \
       token_ttl=0 \
       token_period=120 \
       token_policies=demo-auth-policy-operator \
       audience=vault
       
    vault read auth/k8s-auth-mount/role/auth-role-operator

     

    Step8. 샘플 애플리케이션 YAML 작성 및 배포

    더보기

    1) demo-ns 네임스페이스 생성

    kubectl create ns demo-ns
    mkdir vso-dynamic
    cd vso-dynamic

     

    2) vault-auth-dynamic.yaml :

    • 앱이 Vault에 인증하기 위한 ServiceAccount 및 VaultAuth 리소스
    • Vault에서 발급한 동적 PostgreSQL 크레덴셜을 얻기 위해 반드시 필요
    ---
    # vault-auth-dynamic.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      namespace: demo-ns
      name: demo-dynamic-app
    ---
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultAuth
    metadata:
      name: dynamic-auth
      namespace: demo-ns
    spec:
      method: kubernetes
      mount: k8s-auth-mount
      kubernetes:
        role: auth-role
        serviceAccount: demo-dynamic-app
        audiences:
          - vault

     

    3) app-secret.yaml :

    • Spring App에서 PostgreSQL에 접속할 때 사용할 해당 시크릿에 username/password을 동적으로 생성
    ---
    # app-secret.yaml
    apiVersion: v1
    kind: Secret
    metadata:
      name: vso-db-demo
      namespace: demo-ns

     

    4) vault-dynamic-secret.yaml

    • Vault에서 동적으로 PostgreSQL 접속정보 생성하고 K8s Secret에 저장
    • 생성된 Secret(vso-db-demo)은 앱에서 환경 변수(env)로 사용
    • 애플리케이션에서 Dynamic Reloading을 지원하지 않을 경우 rolloutRestartTargets 을 사용하여 애플리케이션을 재배포하여 새로 업데이트된 시크릿을 사용하도록 할 수 있음
    • refreshAfter 설정을 통해 VSO가 소스 시크릿 데이터를 동기화 하는 주기 설정가능 - Docs → Event Notification 기능을 통해 변경이 있을 경우 즉시 반영할 수도 있음 - Docs
    ---
    # vault-dynamic-secret.yaml
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultDynamicSecret
    metadata:
      name: vso-db-demo
      namespace: demo-ns
    spec:
      refreshAfter: 25s
      mount: demo-db
      path: creds/dev-postgres
      destination:
        name: vso-db-demo
        create: true
        overwrite: true
      vaultAuthRef: dynamic-auth
      rolloutRestartTargets:
      - kind: Deployment
        name: vaultdemo

     

    5) app-spring-deploy.yaml :

    • DB 접속 테스트를 위한 Spring App →
    ---
    # app-spring-deploy.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: vaultdemo
      namespace: demo-ns
      labels:
        app: vaultdemo
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: vaultdemo
      template:
        metadata:
          labels:
            app: vaultdemo
        spec:
          volumes:
            - name: secrets
              secret:
                secretName: "vso-db-demo"
          containers:
            - name: vaultdemo
              image: hyungwookhub/vso-spring-demo:v5
              imagePullPolicy: IfNotPresent
              env:
                - name: DB_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: "vso-db-demo"
                      key: password
                - name: DB_USERNAME
                  valueFrom:
                    secretKeyRef:
                      name: "vso-db-demo"
                      key: username
                - name: DB_HOST
                  value: "postgres-postgresql.postgres.svc.cluster.local"
                - name: DB_PORT
                  value: "5432"
                - name: DB_NAME
                  value: "postgres"
              ports:
                - containerPort: 8088
              volumeMounts:
                - name: secrets
                  mountPath: /etc/secrets
                  readOnly: true
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: vaultdemo
      namespace: demo-ns
    spec:
      ports:
        - name: vaultdemo
          port: 8088         
          targetPort: 8088 
          nodePort: 30003
      selector:
        app: vaultdemo
      type: NodePort
    • 애플리케이션 배포 : kubectl apply -f .

    Step9. 동작확인(UI) : postgres-postgresql.postgres.svc , db는 postgres

    더보기
    • localhost:30003 접속 후 K8s Secrets에 생성된 username, password 값으로 연결 테스트

     

     

    Step9-1. 동작확인(CLI) 

    더보기
    # 스크릿이 변경되고, 적용을 위해서 파드가 42~45초 사이로 재생성됨
    while true; do kubectl get pod -n demo-ns ; echo; kubectl view-secret -n demo-ns vso-db-demo --all; date; sleep 30 ; echo ; done 
    NAME                         READY   STATUS    RESTARTS   AGE
    vaultdemo-7779bcc8c6-tn74g   1/1     Running   0          27s
    
    _raw='{"password":"igZwq-mC04usPQ7QOVDM","username":"v-k8s-auth-dev-post-5mYscZjhGgMhqcWIRpjo-1744273869"}'
    password='igZwq-mC04usPQ7QOVDM'
    username='v-k8s-auth-dev-post-5mYscZjhGgMhqcWIRpjo-1744273869'
    Thu Apr 10 17:31:36 KST 2025
    
    NAME                         READY   STATUS    RESTARTS   AGE
    vaultdemo-59784754f8-d85bm   1/1     Running   0          12s
    
    _raw='{"password":"V2-a1wJQk7MhDACwJYfb","username":"v-k8s-auth-dev-post-HYgtwKnrMqBML4831xiO-1744273914"}'
    password='V2-a1wJQk7MhDACwJYfb'
    username='v-k8s-auth-dev-post-HYgtwKnrMqBML4831xiO-1744273914'
    Thu Apr 10 17:32:06 KST 2025
    
    NAME                         READY   STATUS              RESTARTS   AGE
    vaultdemo-59784754f8-d85bm   1/1     Running             0          42s
    vaultdemo-79576986c7-cb5h9   0/1     ContainerCreating   0          0s
    
    _raw='{"password":"-YZ2ebxyEskpl1ZP1Mcm","username":"v-k8s-auth-dev-post-LGpcqVAfuYdWNNWBkbKA-1744273956"}'
    password='-YZ2ebxyEskpl1ZP1Mcm'
    username='v-k8s-auth-dev-post-LGpcqVAfuYdWNNWBkbKA-1744273956'
    Thu Apr 10 17:32:36 KST 2025
    
    
    # 실제 postgresql 에 사용자 정보 확인 : 계속 추가되고 있음..
    kubectl exec -it -n postgres postgres-postgresql-0 -- sh -c "PGPASSWORD=secret-pass psql -U postgres -h localhost -c '\du'"
                                                      List of roles
                          Role name                      |                         Attributes                         
    -----------------------------------------------------+------------------------------------------------------------
     postgres                                            | Superuser, Create role, Create DB, Replication, Bypass RLS
     v-k8s-auth-dev-post-23yTKlIlbnInwyQAaO55-1744274290 | Password valid until 2025-04-10 08:39:15+00
     v-k8s-auth-dev-post-3Vtbg8lmlyxRRzHfM7Zt-1744272836 | Password valid until 2025-04-10 08:15:01+00
     v-k8s-auth-dev-post-5I5y27xxG61G1m1MxPWp-1744273652 | Password valid until 2025-04-10 08:28:37+00
     v-k8s-auth-dev-post-5mYscZjhGgMhqcWIRpjo-1744273869 | Password valid until 2025-04-10 08:32:14+00
     v-k8s-auth-dev-post-6x0KoTXiUn0XcattYd5A-1744273218 | Password valid until 2025-04-10 08:21:23+00
     v-k8s-auth-dev-post-7EzuV4eRnE3sRLaGsM2h-1744273475 | Password valid until 2025-04-10 08:25:40+00
     v-k8s-auth-dev-post-8SRaQap9V1Rcd4ZQ9k73-1744273607 | Password valid until 2025-04-10 08:27:52+00
     v-k8s-auth-dev-post-9AHIb55XgqeDFqwphmby-1744274501 | Password valid until 2025-04-10 08:42:46+00
    ...
    
    
    # 로그 확인
    kubectl stern -n demo-ns -l app=vaultdemo
    ...
    
    kubectl stern -n vault vault-0
    ...
    vault-0 vault 2025-04-10T08:32:09.484Z [INFO]  expiration: revoked lease: lease_id=demo-db/creds/dev-postgres/Ph1sg8efnqssOU5FVIuAqhMW
    vault-0 vault 2025-04-10T08:32:54.229Z [INFO]  expiration: revoked lease: lease_id=demo-db/creds/dev-postgres/XKkHZ65ZYLeILvo8GqWfE7F3
    vault-0 vault 2025-04-10T08:33:36.804Z [INFO]  expiration: revoked lease: lease_id=demo-db/creds/dev-postgres/rk1KUFsV7SjZPNFsCqCHGZVx
    ...
    
    
    # vault-secrets-operator 가 clientCachePersistenceModel 설정 적용 정보 확인 : 참고로 해당 파드가 재시작되지는 않음.
    kubectl stern -n vault-secrets-operator-system -l app.kubernetes.io/name=vault-secrets-operator
    ...
    vault-secrets-operator-controller-manager-7f67cd89fd-d2t2k manager {"level":"info","ts":"2025-04-10T06:50:14Z","logger":"initCachingClientFactory","msg":"Initializing the CachingClientFactory"}
    vault-secrets-operator-controller-manager-7f67cd89fd-d2t2k manager {"level":"info","ts":"2025-04-10T06:50:14Z","logger":"setup","msg":"Starting manager","gitVersion":"0.10.0","gitCommit":"aebf0c1c59485059a9ea6c58340fd406afe4cbef","gitTreeState":"clean","buildDate":"2025-03-04T22:22:24+0000","goVersion":"go1.23.6","platform":"linux/arm64","clientCachePersistenceModel":"direct-encrypted","clientCacheSize":10000,"backoffMultiplier":1.5,"backoffMaxInterval":60,"backoffMaxElapsedTime":0,"backoffInitialInterval":5,"backoffRandomizationFactor":0.5,"globalTransformationOptions":"","globalVaultAuthOptions":"allow-default-globals"}
    ...
    vault-secrets-operator-controller-manager-7f67cd89fd-d2t2k manager {"level":"info","ts":"2025-04-10T08:11:00Z","logger":"lifetimeWatcher","msg":"Starting","id":"aee67272-40e9-41ba-a380-b9f948acea7e","entityID":"77fb779f-c79e-567e-1c72-e83c0a0552a7","clientID":"024b5ed8d389816c9a4a99f2968ffc4ba66282d7f39d34b465aba1ebe3a8bb67","cacheKey":"kubernetes-5530fb1481fb1695773196"}
    ...
    
    # 암호화된 캐시 저장소 확인 : vso-cc-<auth method>
    kubectl get secret -n vault-secrets-operator-system -l app.kubernetes.io/component=client-cache-storage 
    NAME                                       TYPE     DATA   AGE
    vso-cc-kubernetes-5530fb1481fb1695773196   Opaque   2      5m21s
    
    kubectl get secret -n vault-secrets-operator-system -l app.kubernetes.io/component=client-cache-storage -o yaml
    ...
      data:
        messageMAC: AZ2WpecD1Ecc4fu6TQY8qtqrIGuZEBfBIIecuOrLMgY=
        secret: eyJjaXBoZXJ0ZXh0IjoidmF1bHQ6djE6c1U3RkVjZ2NldW1SSDR4K3hjRGF1eTNTRzNyejBnMUgxZlY4TmozRUJ5cCtuZnN6WWxMTmwrSDdtVy96ZnAvcVNUVDMxeU5ORlNTTjNWOFh6Zkx6TlJnMGt3QXZaeTBwbUZXclFlUGVBVmdLU3RSSllDSVA0YjRkTnk1cWgxcFA4Tnh0cDVvV1QvRnRpNWptZWpBN2FxWkxXRjJ3L1Y1K2FLZ0tTN1hZcG9pRnJpUTc3QWRvaEltTjlweUdpVHZVNFF4OTVSOW94Y3N6cW1JaTB6MUVCZ2l4U1AxWmNqUDZkSUN0QTREK3hTMWsxNmtneFV2elpUZHN5R01QS01manBzVGRCbmQ1NnFJYlQ1OTZ4bEh3WmJvSnU4dUhaY1pKaGVtWnc2eUJXekV4VHdYTXIxV3ZqbVdZWXNwL0dkTzRXTktpZE5nQjB3QnBqM0o3VFd5UEgreWVJTXFkREdmZWFCWkxhRkJQN1VtNnNpNEI1TUZSRFQrdElEZVhER3dLcjdCbWRSMVVpT0M5WU9hN1MxRlpacGFGTis2QXJRUUJDMGFicHZNSlN1blQ0R1o4L1lGUUpPcnFYaTlwNWJrYUhWUFU5S1dXQ2VtTFRqRDJHdnN5ZkZkMmkyZlhJNHR5Ly8vczZIYndGbDZZT0EyRWw1c1QxbjNkbkF6NXFXOUtuVUdYYVVTY0NhWTRDOG5rU3dLeVlRejY2WHU2QkJvYXkvT3k0QjRBUy83bS9seXpHRCtQZUFNQU84cjE1YmlUZTgvdWtmZWIxTHl1cXVFbG5saGdHMmxYTjFSd1laSElwVnpuZXlkZWlYd2RsdW5leFVXeFk3QUg2QjNEVmVTWWlWczE5dEIxRGt2Mm1KbkJLRThtL09QcVliSENYaS9zTXFEMGpoYjFQUWJ4QzdiQkY3ODRIQnByUDFDNmo0Szd5VVpKdGtqVUpsVTBxclRpMXBNQS9nbnovVXV2RHZSb3RUTkE5RTk1U0I1c1pVbmZsZHlsZGNCNjZ3RG5oaXJDNkluZnlmcFN2ZUFMNTErNUc3bDZzOXlVbUQ4aDNpQW5nWTlEemt1bGlmakxkYyt4VE9sY2ZjUWp2RVJ1UVA0b2psTkxvY3BuOVZ4b3E4ZUZDZllsaEVENlNJTzZCdHc4SWZWWndZa0JNWHVieVFtMGl1b3VXaVFZcXdnNEhSL2l1SVZyMVB2S3ZZcUJ3LzhHQ1g4QjhyVzM0SWFHeHlIRGN4UG9kUHJuKy95QnFYSVphMm1IZDBkempOQjB4UytVYU5WZS9GamdLM1hnbE1aSkdjWldyT0NxbDQrYmpCa1NJTkVXUHV5Y3BoL1hxRFVmVStMTDJFTEsxRUo5OUprTUd3ZEdzRDBOR0FjZTIrclpPQTVNNUk4aDE4R040aEt1MUdZPSIsImtleV92ZXJzaW9uIjoxfQ==
      immutable: true
    ...

    7-3. PKI Secrets(수정중)

    ▶ 개요

    더보기
    • 운영자가 단일 Vault PKI 시크릿을 단일 Kubernetes 시크릿으로 동기화하는 데 필요한 구성입니다.
    • Ingress, mTLS, Webapp 등 PKI 인증서를 사용하는 애플리케이션에서 동적으로 변경된 PKI 인증서를 사용할 수 있도록 지원합니다.

     

    ▶ 실습

    Step1. Vault PKI 시크릿엔진 사용을 위한 설정

    더보기

    1. PKI 엔진 활성화(마운트)

    kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh
    vault secrets enable -path=pki pki
    • Vault에 PKI Secrets Engine을 pki라는 사용자 정의 경로로 마운트합니다.

     

    2. Root CA(Common Name) 인증서 생성(발급)

    vault write pki/root/generate/internal \
      common_name="hashicorp.local" \
      ttl=87600h
    • Vault 자체가 루트 인증기관(CA)이 됨
    • Root CA 인증서와 개인키를 Vault 내부에 생성·보관 - 10년 TTL
    • PEM 형식으로 루트 인증서 반환됨 (출력 포함)
    • 공개 인증서/CRL URL도 함께 설정해야 함 (config/urls 경로 설정 생략됨 – 권장 추가)

    3. Issuing Cert & CRL Distribution URL 설정

    vault write pki/config/urls \
      issuing_certificates="http://vault.vault.svc:8200/v1/pki/ca" \
      crl_distribution_points="http://vault.vault.svc:8200/v1/pki/crl"
    • Vault가 발급하는 인증서에 포함될 CA/CRL 메타데이터

    4. 인증서 발급용 Role 생성

    vault write pki/roles/cert-role \
      allowed_domains="hashicorp-app.svc.cluster.local" \
      allow_subdomains=true \
      allow_bare_domains=true \
      allow_any_name=true \
      enforce_hostnames=false \
      max_ttl="24h"

     

    5. 인증서 발급용 Policy 정의

    vault policy write pki-policy - <<EOF
    path "pki/issue/cert-role" {
      capabilities = ["update", "read", "list"]
    }
    EOF

     

    6. Role 생성과 함께 policy 바인딩

    vault write auth/approle/role/cert-role \
      token_policies="pki-policy" \
      token_ttl="30m" \
      token_max_ttl="1h"

     

    7. Role ID / Secret ID 발급 및 K8s Secret에 저장  

    # exit로 Vault 파드에서 빠져나오기
    exit
    
    cd ..
    mkdir dynamic-secret
    cd dynamic-secret
    
    # Local에서 실행
    vault read -field=role_id auth/approle/role/cert-role/role-id > role_id.txt
    vault write -f -field=secret_id auth/approle/role/cert-role/secret-id > secret_id.txt
    
    # 확인
    cat role_id.txt
    cat secret_id.txt

    Step2. VSO의 CR(Custom Resource) 배포

    더보기

    1. webapp 네임스페이스 생성

    kubectl create ns webapp

     

    2. AppRole Secret 생성

    kubectl create secret generic vso-cert-role \
      -n webapp \
      --from-file=role_id=role_id.txt \
      --from-file=id=secret_id.txt

     

    3. VaultAuth CR 생성

    kubectl create -f - <<EOF
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultAuth
    metadata:
      name: pki-auth
      namespace: webapp
    spec:
      method: appRole
      mount: approle
      appRole:
        roleId: <Role ID>
        secretRef: vso-cert-role
    EOF

     

    4. VaultPKISecret CR 생성

    # 1. vault agent 역할. 사용자를 대행하여 vault에 주기적 요청을 통해 인증서를 갱신
    # 2. 인증서를 사용하는 devployment에 대해 롤 아웃 수행하여 바뀐 인증서 정보 반영
    
    kubectl create -f - <<EOF
    apiVersion: secrets.hashicorp.com/v1beta1
    kind: VaultPKISecret
    metadata:
      name: hashicorp-tls
      namespace: webapp
    spec:
      vaultAuthRef: pki-auth
      mount: pki
      role: cert-role
      commonName: hashicorp-app.svc.cluster.local
      ttl: 1m
      destination:
        create: true
        name: vso-pki-cert
      rolloutRestartTargets:
        - kind: Deployment
          name: nginx-vault-ssl
    EOF

     

    5. 인증서 갱신 동작 로직의 이해

    ☞ VSO는 VaultPKISecret 리소스를 watch 하면서 아래 조건을 기준으로 자동 갱신 시점을 판단합니다:

    • 인증서의 NotAfter (만료일) 확인
    • 현재 시간과 비교해서 남은 시간이 ⅓ 미만일 때부터 재발급 트리거
    • 단, 실제 갱신은 Controller의 reconcile 주기에 따라 수 초 단위로 오차가 있을 수 있음

     

     

     

    Step3. 생성된 인증서 확인

    더보기
    kubectl get secret vso-pki-cert -n webapp -o json | jq -r .data._raw | base64 -d
    
    # view-secret 플러그인
    kubectl view-secret -n webapp vso-pki-cert --all
    
    # 만료기간 확인 Tip (MacOS 기준)
    kubectl view-secret -n webapp vso-pki-cert expiration
    1743932886
    
    # date 명령으로 확인
    date -r 1743948834
    Sun Apr  6 18:45:08 KST 2025

     

    Step4. 검증을 위한 서비스(Nginx) 배포

    더보기

    기능 검증 수행용 nginx 배포

    • 구성파일 상세
      • ConfigMap : nginx의 config 저장
      • Deployment : TLS가 enable된 Nginx deployment
        • initContainer : 인증서 갱신정보(만료일)을 화면에 표기할 웰컴페이지 수정(index.html)
        • secret mount : CR이 생성한 인증서가 담긴 secret 서비스내에 마운트
      • Service : nginx 서비스의 추상화된 진입점
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: nginx-ssl-config
      namespace: webapp
    data:
      nginx.conf: |
        events {}
        http {
          server {
            listen 443 ssl;
            server_name localhost;
    
            ssl_certificate     /etc/nginx/tls/certificate;
            ssl_certificate_key /etc/nginx/tls/private_key;
    
            location / {
              root /usr/share/nginx/html;
              index index.html;
            }
          }
        }
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-vault-ssl
      namespace: webapp
    spec:
      replicas: 1
      minReadySeconds: 10
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxUnavailable: 1
          maxSurge: 1
      selector:
        matchLabels:
          app: nginx-vault
      template:
        metadata:
          labels:
            app: nginx-vault
        spec:
          initContainers:
            - name: generate-expiration-html
              image: alpine:latest
              command: ["/bin/sh", "-c"]
              args:
                - |
                  apk add --no-cache openssl > /dev/null
                  expiry=\$(openssl x509 -enddate -noout -in /certs/certificate | cut -d= -f2)
                  echo "<html><head><meta http-equiv='refresh' content='10'></head><body><h1>Certificate Expiration: \$expiry</h1></body></html>" > /output/index.html
              volumeMounts:
                - name: tls
                  mountPath: /certs
                  readOnly: true
                - name: html
                  mountPath: /output
          containers:
            - name: nginx
              image: nginx:latest
              ports:
                - containerPort: 443
              volumeMounts:
                - name: tls
                  mountPath: /etc/nginx/tls
                  readOnly: true
                - name: html
                  mountPath: /usr/share/nginx/html
                - name: nginx-conf
                  mountPath: /etc/nginx/nginx.conf
                  subPath: nginx.conf
          volumes:
            - name: tls
              secret:
                secretName: vso-pki-cert
            - name: html
              emptyDir: {}
            - name: nginx-conf
              configMap:
                name: nginx-ssl-config
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-vault-service
      namespace: webapp
    spec:
      selector:
        app: nginx-vault
      ports:
        - protocol: TCP
          port: 443
          targetPort: 443
          nodePort: 30004
      type: NodePort
    EOF

     

     

    Step5. 서비스 배포확인

     

    Step6. Ingress 연동을 통한 TCP Passthrough추후 업데이트 예정


    ▶ 추가 학습 자료

    더보기
    apiVersion: cert-manager.io/v1
    kind: Issuer
    metadata:
      name: vault-issuer
      namespace: sandbox
    spec:
      vault:
        path: pki_int/sign/example-dot-com
        server: https://vault.local
        caBundle: <base64 encoded CA Bundle PEM file>
        auth:
          ...

    [ 마무리 ]

    Hashcorp Vault 라는 솔루션을 통해서 k8s 에서 사용하는 secret 을 중앙집중식으로 동적으로 관리할 수 있는 방법에 대해 체험해 볼 수 있는 좋은 기회였던 것 같다. 중간 중간 환경설정이나 진행 상 오류가 있어 애를 먹었던 것 같다. 보안 인증의 관리 효율화 측면에서 Vault 같은 자동화 관리 툴의 도입을 검토해 보면 좋을 것 같다.


    [ 참고 링크 모음 ]

    더보기

    [ 사전 지식 ]

    ☞ 암호(대칭키/비대칭키), 전자서명, PKI(X.509) 등 이해 필요**(출처: 가시다님 6주차)**

    • 정보 보안 3요소 : 기밀성(암호화, 액세스 제어..), 무결성(무단 변조 방지), 가용성 - Link
    • 액세스 제어(AAA) : 인증(Who?), 인가(What?), 감사 - Link

    [ 하시코프 Vault 관련 영상 ]

    ☞ Introduction to HashiCorp Vault with Armon Dadgar : Link

    Vault 통합 및 활용 사례 : Youtube

     

    [ 환경 설치 관련 ]

    Ubuntu 에서 hashicorp vault-CLI 설치하기 : Blog