본문 바로가기
카테고리 없음

[AEWS] 10주차 - Vault sidercar 연동 패턴과 AppRole 방식

by james_janghun 2025. 4. 9.

Vault sidecar 연동 패턴

vault를 설치할 때 vault와 Injector 파드가 같이 설치된 것을 확인했습니다.

 

Injector 파드는 기본적으로 sidecar agent 들을 관리하기 위한 매니저입니다.

실제로 injector 파드는 vault-k8s의 코드를 통해 설치되는데 annotation을 주입하면 됩니다.

vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "example-role"

 

 

https://docmoa.github.io/04-HashiCorp/06-Vault/04-UseCase/vault-k8s-manually-using-the-sidecar.html

 

 

AppRole 방식의 인증 구현

AppRole 인증 방식은 기계나 애플리케이션이 Vault에 정의된 역할을 통해 인증할 수 있게 해주는 방법입니다. 자세한 내용은 공식문서에서 잘 설명하고 있습니다.

 

이 방식의 주요 특징은:

  • 대규모 애플리케이션을 처리하기 위한 다양한 워크플로우와 구성을 지원하는 유연한 설계
  • 사람보다는 자동화된 워크플로우(기계 및 서비스)에 초점을 맞춤
  • AppRole과 함께 batch 토큰 사용을 권장

AppRole은 특정 정책 집합과 로그인 제약 조건을 나타내며, 이를 충족해야 해당 정책이 포함된 토큰을 받을 수 있습니다. 적용 범위는 필요에 따라 좁거나 넓게 설정할 수 있으며, 특정 기계, 해당 기계의 특정 사용자, 또는 여러 기계에 분산된 서비스를 위해 생성될 수 있습니다. 성공적인 로그인에 필요한 자격 증명은 해당 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

 

App Role 생성

앞서 만든 sampleapp-policy를 sampleapp-role에 적용시키고, ttl 설정을 통해서 토큰 만료 시점을 잡습니다.

# 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

 

 

 

kubernetes를 사용할 경우

해당 app Role정보를 secret으로 사용해 해당 정보를 저장해두고 사용할 수 있습니다.

# 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 -

 

 

Agent를 Sidecar로 연동하기

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

 

1. Vault agent 설정 파일 작성 및 생성하기

하시코프 사의 제품으로 HCL을 사용하기 때문에 vault-agent-config.hcl 로 작성하면 됩니다.

해당 파일을 configmap으로 배포합니다.

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

 

해당 내용을 조금 자세히 살펴보면 다음과 같습니다.

 

vault address 지정

vault의 위치를 지정하는 것입니다. 실습 상 쿠버네티스 내에 존재하므로 서비스 디스커버리가 가능한 도메인으로 호출하였습니다.

만약 쿠버네티스 내에 존재하는게 아니라 외부에 존재한다면 해당 호출 주소를 기입해야합니다.

vault {
  address = "http://vault.vault.svc:8200"
}

 

auto_auth

AppRole의 인증방식을 활성화할 경우 해당 정보를 사용하도록 인증내용을 기입한다.

해당 토큰 파일의 경우 속도나 보안성을 위해 sink를 로컬에 두고 사용하도록 한다.

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"
}

 

시크릿 정보의 템플릿 지정

실제 시크릿의 정보 파일의 형식을 지정합니다. 위치(destination)랑 값을 랜더링 할 수 있도록 합니다.

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
}

 

샘플 애플리케이션 배포

이제 실제 vault에서 시크릿 값을 읽어서 반영할 수 있는지 확인하기 위해서 샘플 nginx 애플리케이션을 배포해보겠습니다. 해당 애플리케이션에서는 nginx에 html-volume이라는 시크릿을 읽은 index.html이라는 파일을 생성해서 마운트 시키는 예시입니다.

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

 

해당 pod에 sidercar가 제대로 붙은것을 확인할 수 있습니다.

 

한번 실제 vault와 소통이 잘되는 지 확인해보겠습니다. nginx 파드로 들어가서 다음과 같이 입력합니다.

cat /usr/share/nginx/html/index.html

보면 정상적으로 username과 password를 읽어서 해당 파일을 생성한 것을 확인할 수 있습니다. 이는 html-volume에서 가져온 값이므로 nginx와 injector에서 모두 가지고 있는 정보입니다. 따라서 agent-sidercar 파드에도 들어가서 확인해봅시다.

cat /etc/secrets/index.html

 

동일 내용이 들어간 것을 확인할 수 있습니다.

 

자 그렇다면 만약 secret 정보를 변경하면 반영이 잘될지 확인해보겠습니다. 기존 예시에서 Create new version이라는 버튼을 통해서 기존 시크릿을 업데이트 하겠습니다.

 

다음과 같이 new-라는 정보를 붙여서 새로운 버전을 생성해보겠습니다.

 

바꾸고 확인해보니 다음과 같이 바로 변경된 것을 확인할 수 있습니다.

 

 

어떻게 이게 가능할까요?

inject이 되는 곳은 MutatingWebhookConfiguration리소스의 vault-agent-injector-cfg라는 해당 웹훅을 통해서 inject을 시켜서 새로운 값을 갱신할 수 있게 됩니다.

 

해당 웹훅은 sidecar를 통해 전달하는 것으로 보이는데, 이 외의 별도의 로그는 찾아보기는 힘들었습니다.

 

 

(참고) agent vs proxy

vault의 경우 sidecar agent 방식 말고 proxy 방식도 있는데, proxy의 역할을 중심으로 주기위해서 별도로 이런 기능을 가지게 되었습니다. 기본적으로 agent에서 지원하는 기능의 일부를 빼고 proxy방식으로 만든것이기 때문에 proxy를 꼭 써야하는 상황이 아니면 agent가 더 좋으니 장단점과 기능을 잘 비교해보고 판단해보시면 좋을 것 같습니다.

Capability Vault Agent Vault Proxy
Auto-auth O O
Caching O O
Templating O X
API Proxy 중단예정 O
Run as a Windows Service O X
Process Supervisor O X

 

다만 proxy는 proxy의 성능을 가져갈 수 있으므로 조금 더 빠르고, caching을 지원하기 때문에 vault와의 연결이 끊겨도 proxy에서 일정시간동안 값을 가지고 갈 수 있어 유리한 부분도 있습니다.

 

이상으로 Sidercar 연동실습을 마칩니다.