10주차 - 시크릿 관리
개요
이번에는 볼트를 활용해서 시크릿을 관리하는 실습을 진행한다.
젠킨스, 아르고 cd를 활용해 cicd환경을 구축하고 여기에 관리해야할 모든 시크릿을 볼트로 관리한다.
클러스터, cicd 환경 구축
먼저 클러스터는 KIND를 이용한다.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: myk8s
networking:
apiServerAddress: "127.0.0.1"
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: 30005
- containerPort: 30006
hostPort: 30006
- role: worker
- role: worker
3개의 노드를 가진 클러스터로 구성했다.
kind create cluster --config kind.yaml
이후에 젠킨스는 따로 구축했다.
services:
jenkins:
container_name: jenkins
image: jenkins/jenkins
restart: unless-stopped
networks:
- cicd-network
ports:
- "8080:8080"
- "50000:50000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- jenkins_home:/var/jenkins_home
volumes:
jenkins_home:
networks:
cicd-network:
driver: bridge
젠킨스는 도커 컴포즈로 배포하여 클러스터와 별개의 환경으로 관리한다.
docker compose up -d
위 설정에 따라 사이트는 8080 포트로 열린다.
dex:
enabled: false
server:
service:
type: NodePort
nodePortHttps: 30002
extraArgs:
- --insecure
아르고 cd도 최대한 간략하게 세팅했다.
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 --create-namespace
아르고 cd는 30002번 포트로 접근될 것이다.
그럼 이제 젠킨스와 아르고 cd 초기 세팅을 해보자.
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
비번은 이렇게 찾아서 넣어주면 된다.
초기 플러그인은 그냥 추천된 것들 해줘도 된다.
(젠킨스 쓰기 싫었는데. 오랜만에 보니까 ui가 더 이뻐진 것 같기도)
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
admin 계정에 비번을 치고 들어오면 기본 준비는 끝.
볼트 세팅
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
disable_mlock = true
cluster_name = "vault-local"
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_disable = 1
}
storage "raft" { # Raft 구성 권장
path = "/vault/data"
node_id = "vault-dev-node-1"
}
service:
enabled: true
type: NodePort
port: 8200
targetPort: 8200
nodePort: 30000 # Kind에서 열어둔 포트 중 하나 사용
injector:
enabled: true
ui:
enabled: true
serviceType: "NodePort"
볼트를 헬름으로 설치한다.
helm repo add hashicorp https://helm.releases.hashicorp.com
helm upgrade vault hashicorp/vault -n vault -f vault-values.yaml --install --create-namespace
처음 세팅을 진행하면 볼트가 레디 상태가 되지 않는 것을 볼 수 있다.
로그를 보면 봉인 설정에 대한 이슈라는 것을 알 수 있다.
kubectl exec -ti vault-0 -- vault status
볼트 명령어로 보면 봉인 상태가 됐고, 이에 따라 초기화가 진행되지 않았다는 것을 확인할 수 있다.
봉인 해제
VAULT_POD="vault-0"
alias VAULT_CMD="kubectl exec -ti $VAULT_POD -- vault"
먼저 간단한 세팅을 몇 개 해주었다.
VAULT_CMD operator init -key-shares=3 -key-threshold=2
임계치를 2로 설정하면 최소 2개의 키가 들어와야만 루트 키를 얻어낼 수 있다.
굳이 3개의 키를 만들어달라고 할 필요는 없지만 어떤 식으로 출력되는지 보고 싶어 이렇게 해봤다.
보다시피 3개의 키가 나온 것이 확인된다.
출력 상에서 바로 루트 키를 보여주고 있는데, 이건 쉐어링 키를 조합해서 나오는 마스터 키를 의미하는 게 아니다!
쉐어링 키를 이용해 복호화되는 키는 vault 내부에 들어있어 사용자는 이를 확인할 수 없다.
현재 출력된 루트 키는 해금이 된 이후에 vault에 처음 접근할 때 사용하는 키로, 볼트 내부에는 저장되지 않기 때문에 처음 출력된 이후로 다시 확인할 수 없다.
그러니 이 키도 잘 저장해두자.
VAULT_CMD operator unseal
바로 키를 전부 넣어 봉인을 푸는 것도 가능하나, 하나씩 넣어보았다.
키를 하나 넣었을 때, 해금 프로세스가 하나 증가하는 것을 볼 수 있다.
해금이 완료되자 비로소 레디 상태가 됐다.
위에서 본 루트 키를 이용해 로그인한다.
web ui로도 보겠지만, cli로도 접근하여 설정을 조금 더 편리하게 해보자.
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
공식 문서만 봤을 때, vault 전용 cli를 다운 받는 방식이 분리돼있지는 않은 것 같다.
애초에 vault가 쿠버네티스 전용 툴도 아니니 당연히 cli가 클라-서버 구조로 설계되지 않은 것이 이상한 일은 아니라고 생각한다.
그래도 500메가나 잡아먹으면서 별 세팅 한꺼번에 되는 건 좀 꼴받긴 하네..
알아서 systemd 등록까지.. 불편하긴 하네..
export VAULT_ADDR='http://localhost:30000'
아무튼 cli로 현재 로컬에 볼트를 설정할 것은 아니기에, vault의 주소를 환경변수로 등록하여 사용한다.[1]
이제 클러스터에 위치한 볼트를 로컬 cli로 조작할 수 있게 됐다.
이렇게 나오면 성공
vault login
을 통해 cli에서도 조작을 할 수 있도록 루트키를 입력해준다.
KV 시크릿 엔진 활성화 후 정적 시크릿 생성
볼트의 시크릿 엔진은 현재 2버전이 있는데, 2버전의 경우 시크릿의 버전 관리가 가능하다.
vault secrets enable -path=secret kv-v2
vault kv put secret/sampleapp/config \
username="demo" \
password="p@ssw0rd"
vault kv get secret/sampleapp/config
성공적으로 데이터가 들어간 것이 확인된다.
이때 시크릿 경로를 보면 put을 할 때 secret/sampleapp/config
로 넣었는데도 중간에 data라는 경로가 추가된 것을 볼 수 있다.
볼트의 kv 시크릿 엔진 v2는 시크릿의 버전 관리를 해주기에 들어온 시크릿을 데이터와 메타데이터를 구분해서 관리한다.
이에 따라 실제 데이터는 data라는 중간 경로를 포함해서 경로가 노출되는 것이다.
페이지로 가서 봐도 추가된 것을 잘 확인할 수 있다.
해당 시크릿을 꺼내기 위해 어떻게 api 접근을 하면 되는지, cli로는 어떻게 인자를 넣어야 하는지도 잘 나와서 사용성은 좋은 것 같다.
볼트 에이전트 - 사이드카
그럼 이제 이 볼트에서 시크릿을 클러스터 내 워크로드가 꺼내오도록 하는 실습을 진행한다.
흔히 그렇듯, 볼트에서도 사람이 아닌 계정이나 어플리케이션에 대해 인증이나 권한을 설정하는 방식이 AppRole로 따로 마련돼있다.
approle 인증을 활용한 사이드카 수동 설정(실습 미진행)
vault auth enable approle
vault auth list
먼저 approle 인증 방식을 보자.(이 방식으로 하지는 않을 것이다.)
vault policy write sampleapp-policy - <<EOF
path "secret/data/sampleapp/*" {
capabilities = ["read"]
}
EOF
vault write auth/approle/role/sampleapp-role \
token_policies="sampleapp-policy" \
secret_id_ttl="1h" \
token_ttl="1h" \
token_max_ttl="4h"
다음으로 kv 시크릿에 읽기 권한을 허용하는 정책을 만들고, 이 정책을 approle에 붙여준다.
approle 인증 경로에 어떤 정책을 매핑하기 위해 write를 하는 행위 자체가 하나의 role을 만드는 행위이다.
사용될 토큰의 기본 수명과, 시크릿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)
mkdir -p approle-creds
echo "$ROLE_ID" > approle-creds/role_id.txt
echo "$SECRET_ID" > approle-creds/secret_id.txt
롤 id와 시크릿 id는 이렇게 받아온다.
굳이 파일로 저장하고 싶지 않았으나, 아래 설정 때문에 파일로 저장하는 수밖에 없다고 판단했다.
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에 설정을 넣을 때 컨피그맵 방식으로 hcl 파일을 넣어서 세팅을 해줄 수 있다.
보통 예제가 있으면 나는 무조건 일단 파일화를 시켜서 적용을 하는데, 위 히어독 방식이 내 머리를 살짝 띵하게 만드는 노하우가 담겨있어 그대로 옮겼다.
간혹 명령어 중에서는 표준 입력을 인자로 받았으면 하는 것들이 있는데 막상 지원하는 방식은 파일인 경우가 더러 있다.
kubectl처럼 -
를 표준 입력으로 인식해주면 참 좋겠지만, 그렇지 않은 케이스도 왕왕 있다.
이럴 때 /dev/stdin
을 이용해서 표준 입력을 파일로 지정해서 받아오는 방법이 가능하다.
아무튼 위 hcl 파일은 볼트 에이전트를 설정한다.
먼저 vault 블록으로 볼트 서버를 명시하는데, 클러스터 내부에서 사용될 것이므로 클러스터 dns를 사용했다.
이때 auto_auth를 통해 자동 로그인 설정이 되는데, 이때 위에서 설정한 approle이 쓰인다.
(무조건 파일 경로로 전달하도록 설정돼있다.[2])
이후 가져올 시크릿 값을 어떤 식으로 가져올지 템플릿을 지정하는 방식이다.
자세한 설정 방식은 이 문서에 있다.[3]
컨피그맵으로 설정하는 방식은 이곳을 참고하자[4]
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
이 다음에는 별 거 없이 직접 agent 컨테이너를 설정해서 넣어주면 된다.
이 실습 자체를 진행하지 않았지만, approle 자체는 다른 실습에서도 활용할 것이기에 위 과정을 해두는 것을 추천한다.
사이드카 인젝터 활용
그런데 쿠버네티스 환경에서는 쿠버네티스의 인증 인가 방식을 활용하면서 에이전트를 사이드카로 자동 주입을 할 수 있도록 세팅을 지원하기 때문에 이쪽을 위주로 실습을 진행해본다.
apiVersion: v1
kind: Secret
metadata:
name: vault
annotations:
kubernetes.io/service-account.name: vault
type: kubernetes.io/service-account-token
일단 볼트에서 클러스터 api에 접근하기 위한 서비스 어카운트 토큰을 만들어둔다.
볼트의 서비스 어카운트는 system:auth-delegator
를 받는데, 이건 토큰 리뷰와 확인을 요청할 수 있는 권한을 가지고 있다.
이를 기반으로 볼트는 시크릿을 접근하려는 요청의 jwt 토큰의 신원을 파악할 수 있게 된다.
vault auth enable kubernetes
vault write auth/kubernetes/config \
token_reviewer_jwt="$(kubectl get secret vault -o go-template='{{ .data.token }}' | base64 --decode)" \
kubernetes_host="https://kubernetes.default.svc.cluster.local:443" \
kubernetes_ca_cert="$(kubectl config view --raw -o jsonpath='{.clusters[?(@.name=="'"$(kubectl config current-context)"'")].cluster.certificate-authority-data}' | base64 -d)" \
disable_local_ca_jwt=true
먼저 쿠버네티스 인증 방식을 활성화한다.
클러스터 루트 인증서와 호스트, 그리고 토큰을 요청하게 될 서비스어카운트의 토큰을 넣어서 설정하는 것을 확인할 수 있다.
이 문서를 보면 분명 볼트가 클러스터에서 구동될 때 알아서 주입된 서비스 어카운트 토큰과 클러스터 인증서를 사용한다고 나와있다.[5]
(이후 진행을 하면서 나온 사진인데, 마치 에이전트가 권한이 없는 것마냥 로그를 내뿜어서 헷갈리게 한다.)
그러나 여태까지 해봤을 때, 초기화컨테이너가 지속적으로 권한을 부여받지 못했다.
어쩔 수 없이 이 문서를 참고해 아예 서비스 어카운트 시크릿도 만들고 이를 초반에 주입하는 식으로 해결했다.[6]
vault read auth/kubernetes/config
값이 제대로 세팅됐는지 확인한다.
vault policy write sampleapp-policy - <<EOF
path "secret/data/sampleapp/*" {
capabilities = ["read"]
}
EOF
vault write auth/kubernetes/role/sampleapp-role \
bound_service_account_names="vault-ui-sa" \
bound_service_account_namespaces="vault" \
policies="sampleapp-policy" \
ttl="24h"
위 참고 단락을 따라하지 않았다면 정책을 만들어줘야 한다.
그리고 해당 정책을 받게 될 쿠버네티스 롤(이 롤은 쿠버 네이티브 리소스 롤이 아니라 하나의 주체라 보면 되겠다)을 지정한다.
vault write auth/kubernetes/login role="sampleapp-role" jwt="$(kubectl create token vault-ui-sa)"
이렇게 해서 로그인 테스트를 해봤을 때 token이 나오면 성공이다.
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-token: "true"
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: 30001
selector:
app: vault-injected-ui
오토 인젝팅 방식을 사용할 때는 인젝팅될 워크로드에 어노테이션을 다는 방식으로 세팅한다.
보면 모든 세팅을 일일히 어노테이션으로 넣고 있는데, configmap을 만들고 이것을 어노테이션으로 넣어 설정을 전달하는 방법도 존재하긴 한다.
실행하면 이렇게 알아서 컨테이너가 주입된다.
초기화 컨테이너와 일반 컨테이너가 주입되는 것을 확인할 수 있는데, 초기화 컨테이너가 실질 로그인을 담당한다.
이렇게 데이터가 출력되면 성공이다!
웹 ui에서 시크릿을 조금 수정했다.
의도한 실습의 내용 대로라면 이 수정이 그대로 실습 웹 서버에도 반영이 돼야 하겠지만..
해당 컨테이너의 코드 로직 상 한번이라도 데이터를 읽어오면 더 이상 다시 불러오도록 되어 있지 않기 때문에 이게 반영되진 않는다..
k logs vault-injected-ui-77fb865789-z9bxd -c vault-agent
다만 사이드카의 로그를 보면 동적으로 시크릿을 다시 렌더링하여 파일에 반영했다는 것을 알 수 있다.
실습용 컨테이너에 직접 데이터를 뜯어봐도 제대로 데이터는 반영된것을 확인할 수 있다.
시간이 많았으면 앱 로직 자체를 수정해서 다시 할 것 같은데.. 문서만 보고 하다가 너무 정신력이 빨려서 이 실습은 여기까지만..
젠킨스 연동
이제 본격적으로 cicd 툴과 연동하는 실습을 진행한다.
docker network connect kind jenkins
컴포즈 파일에서 미리 external 설정을 하면 좋은데, 젠킨스를 띄우면서 이걸 간과했다.
아쉬운대로 젠킨스도 카인드 네트워크를 활용할 수 있게 해둔다.
젠킨스에 볼트 플러그인을 먼저 설치한다.
vault read -field=role_id auth/approle/role/sampleapp-role/role-id
vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id
젠킨스에서 써야할 것은 role과 시크릿 id이다.
시크릿 id의 경우 위에서 한 시간 정도의 수명을 가지도록 세팅했기에 일찌감치 여기에서 다시 받는 것이 좋다.
이제 플러그인 세팅을 본격적으로 해보자.
일단 볼트 플러그인 설정을 하는 창으로 가서 볼트의 url을 입력해준다.
add 버튼을 누르면 신원 제공자 세팅을 할 수 있는데, 여기에서 approle을 골라서 위의 값들을 넣어준다.
pipeline {
agent any
environment {
VAULT_ADDR = 'http://172.18.0.4:30000'
}
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-role'
]
]) {
sh '''
echo "Username from Vault: $USERNAME"
echo "Password from Vault: $PASSWORD"
'''
script {
echo "Username (env): ${env.USERNAME}"
echo "Password (env): ${env.PASSWORD}"
}
}
}
}
}
}
볼트 주소에는 클러스터에서 노드포트로 서비스를 열고 있기 때문에 아무 노드의 주소를 적어주면 된다.
위에서 신원 세팅을 했던 이름을 그대로 설정 블록에 넣어주면 된다.
참고로 kv 버전 1의 경우 path를 설정할 때 중간에 data를 넣어주어야 한다고 한다.
위에서 말한 거랑 반대인 격이라 헷갈리지 말자..
성공적으로 빌드가 완료됐다.
민감한 정보라는 것을 워닝으로 띄우고 있는 것이 보인다.
보다시피 볼트에서 꺼내온 데이터에 대해 콘솔 출력으로 볼 수 없도록 세팅된 것을 볼 수 있다.
아르고 cd 연동
젠킨스에서는 approle 방식을 썼으니, 아르고에서는 다시 k8s 인증 방식을 사용해보자.
아르고 레포 서버에 특정 플러그인을 설치할 거고, 이를 위해 해당 파드가 사용하는 신원이 필요하다.
vault write auth/kubernetes/role/argo-role \
bound_service_account_names="argocd-repo-server" \
bound_service_account_namespaces="argocd" \
policies="sampleapp-policy" \
ttl="24h"
위와 같은 방식으로 세팅을 해주면, 볼트 측에서 할 설정은 끝이다.
이제 ArgoCD Vault Plugin(AVP)를 설치해야 하는데, 이게 조금 번거롭다.
물론 아르고 cd에 커스텀 플러그인을 설치하는 것이 복잡한 거고, 유달리 이 플러그인만 귀찮은 건 아니다.[7]
아무튼, 최신에 권장되고 있는 사이드카 방식을 사용해본다.[8]
apiVersion: v1
kind: ConfigMap
metadata:
name: cmp-plugin
data:
avp-helm.yaml: |
---
apiVersion: argoproj.io/v1alpha1
kind: ConfigManagementPlugin
metadata:
name: argocd-vault-plugin-helm
spec:
allowConcurrency: true
# 헬름 차트 디렉토리를 찾는 필드
discover:
find:
command:
- sh
- "-c"
- "find . -name 'Chart.yaml' && find . -name 'values.yaml'"
generate:
# HELM_ARGS를 쓰면 ARGOCD_ENV_HELM_ARGS로 전달된다.
# 근데 이건 레포 서버에 임의의 코드를 실행하는 게 가능해서 정말 신뢰되는 운영자만 설정할 수 있어야 한다.
# 그러면 이게 왜 더 좋은 방식이란 거임?
# 아무튼 받은 values 파일에 들어갈 시크릿 값을 적절하게 바꾸어 생성해준다.
command:
- sh
- "-c"
- |
helm template $ARGOCD_APP_NAME -n $ARGOCD_APP_NAMESPACE ${ARGOCD_ENV_HELM_ARGS} . |
argocd-vault-plugin generate - -s argocd-vault-plugin-credentials
lockRepo: false
플러그인 사이드카 양식을 먼저 컨피그맵을 생성한다.
이 파일은 이후 사이드카 컨테이너가 고대로 사용하게 될 설정 파일이다.
실습 예제가 헬름이기 때문에 헬름 설정을 하는 것이고, 쿠스토마이즈나 일반 양식 파일이면 조금씩 다르게 설정해야 한다.[9]
여기 설정에 기입된 이름이 아르고 cd에서 실제로 플러그인 이름으로서 인식될 것이다.
가장 아래 필드에 보면 -s argocd-vault-plugin-credentials
가 있는데, 여기에 아르고 cd가 실질적으로 볼트 서버와 통신할 수 있도록 설정이 기입된 시크릿을 넣어주면 된다.
(근데 왜 내가 이걸 문서를 짜깁기해가며 추론하고 있어야 하나)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: argo:argocd-repo-server
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: argo:argocd-repo-server
subjects:
- kind: ServiceAccount
name: argocd-repo-server
namespace: argocd
roleRef:
kind: ClusterRole
name: argo:argocd-repo-server
apiGroup: rbac.authorization.k8s.io
---
kind: Secret
apiVersion: v1
metadata:
name: argocd-vault-plugin-credentials
namespace: argocd
type: Opaque
stringData:
VAULT_ADDR: "http://vault.vault.svc:8200"
AVP_TYPE: "vault"
AVP_AUTH_TYPE: "k8s"
AVP_K8S_ROLE: "argo-role"
시크릿을 만들고, 아울러 이 시크릿을 아르고 레포 서버가 불러올 수 있도록 권한 세팅을 해준다.
시크릿에 auth type을 k8s로 지정했기에 쿠버 인증 방식을 사용하는 것이고, approle 방식으로 하는 것도 가능하다.
automountServiceAccountToken: true
# 볼륨 쪽
volumes:
- configMap:
name: cmp-plugin
name: cmp-plugin
- name: custom-tools
emptyDir: {}
# 초기화 컨테이너쪽
initContainers:
- name: download-tools
image: registry.access.redhat.com/ubi8
env:
- name: AVP_VERSION
value: 1.16.1
command: [sh, -c]
args:
- >-
curl -L https://github.com/argoproj-labs/argocd-vault-plugin/releases/download/v$(AVP_VERSION)/argocd-vault-plugin_$(AVP_VERSION)_linux_amd64 -o argocd-vault-plugin &&
chmod +x argocd-vault-plugin &&
mv argocd-vault-plugin /custom-tools/
volumeMounts:
- mountPath: /custom-tools
name: custom-tools
## 컨테이너 쪽
containers:
- name: avp-helm
command: [/var/run/argocd/argocd-cmp-server]
image: quay.io/argoproj/argocd:v2.7.9
securityContext:
runAsNonRoot: true
runAsUser: 999
volumeMounts:
- mountPath: /var/run/argocd
name: var-files
- mountPath: /home/argocd/cmp-server/plugins
name: plugins
- mountPath: /tmp
name: tmp
# Register plugins into sidecar
- mountPath: /home/argocd/cmp-server/config/plugin.yaml
subPath: avp-helm.yaml
name: cmp-plugin
# Important: Mount tools into $PATH
- name: custom-tools
subPath: argocd-vault-plugin
mountPath: /usr/local/bin/argocd-vault-plugin
이제 거의 다 왔다.
아르고 repo 서버 디플로이먼트에 위의 양식을 추가한다.
초기화 컨테이너를 통해 먼저 플러그인 명령 툴을 받고, 이걸 사이드카 컨테이너로 띄워서 해당 명령어를 실행할 수 있는 상태로 만들어둔다.
플러그인이 필요한 시점에 띄워져있는 사이드카로 띄워진 argocd-cmp-server는 위에서 설정한 컨피그맵의 설정 파일에 명시된 명령어를 발동시킬 것이다.
초기화 컨테이너로 받은 명령툴은 컨맵 설정 파일에서 명시돼있으므로 이때 사용되게 된다.
쿠스토마이즈를 활용하면 이 설정들을 병합하는 방식으로 쉽게 설정할 수 있는데, 나는 아직 익숙치 않아 edit으로 수정했다.
기다리다보면 알아서 뚝딱 된다.
설치가 제대로 됐다면, 이렇게 ui에서 새로운 어플리케이션을 배포하려할 때 위와 같이 플러그인 관련 상태가 표시된다.
(최하단에 Directory 칸을 누르면 플러그인도 볼 수 있게 돼있다.)
argocd app create you-app-name --config-management-plugin argocd-vault-plugin
아르고 cli로 세팅할 때는 이런 식으로 명시해주면 된다.
ui로 본 김에 그대로 ui로 세팅하기로 마음 먹었다.
사이드카 방식의 플러그인 설치에서는 플러그인을 yaml 파일로 명시하지 말라는 문서가 있길래, 일단 당장 조금 더 확실해보이는 방법을 선택했다.
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
syncPolicy:
automated:
prune: true
selfHeal: true
EOF
실습에서 제공해준 yaml 파일은 이렇게 생겼으니, 이거보고 알아서 세팅하자.
플러그인에 인자로 전달하는 new-values.yaml
은 무어냐, 실습 레포를 보면 답을 알 수 있다.[10]
꺽쇠로 닫힌 부분에 대해 위 플러그인이 작동해 볼트 서버에 요청을 날려 시크릿을 받아와 해당 부분을 채워주는 것이다!
세팅이 완료됐다.
디플로이먼트 환경변수에 이렇게 값이 들어가면 성공이다..
결론
시간에 쫓겨서 이론 및 지식 정리는 아직 다하지 못 했다.
현재로서는 스터디에서 이뤄진 실습 외에 다른 인증 방식, 다른 설치 방식으로 실습을 진행했다는 것에 의의를 두고 글을 마치려고 한다.
고통 받았던 부분
초기에 더 세팅을 해야할 게 있었던 건지는 모르겠는데, 단순하게 이렇게 하는 것만으로는 해결이 되지 않았다.
그리고 AVP 문서는.. 좀 반성 좀 해야할 것 같다..
문서를 무슨 퍼즐 풀듯이 이거 봤다 저거봤다 하면서 짜맞추기해서 설정했다...
스터디
cicd 환경 구성
vault 구성
vault sidecar
vault secret operator - 세 방법
볼트란
하시코프에서 상용, 오픈소스 시크릿 암호화 관리 시스템
볼트는 금고란 뜻.
왜 필요해?
자격 증명이 무분별하게 분산. 이를 secret sprawl이라한다.
관리가 매우 어렵다.
그래서 중앙 집중화해 불필요한 노출 줄이고, 책임 관리 명확하기.
클라를 검증한다.
여기서 클라는 사람, 기계, 앱이 된다.
누가 - 클라
무엇에 - 시크릿 엔진이라 함
얼마동안 - ttl
라이프사이클 - 자동화!
시크릿 종류
- 비밀번호
- cloud cred
- db cred
- ssh key
- token, apikey
- certs
볼트는 토큰 기반.
(토큰 회수는 어떻게할까?)
원리는 인증, 검증, 부여
기능
- 정적 비밀 저장 - 영구 저장소에 암호화해 저장.
- 동적 비밀 - 일정 시간 지나면 폐기
- 데이터 암호화 - 데이터 저장없이도 암복호화 가능. 근데 엔터랑 오픈소스 기능이 조금 다르다고 함
- 임대 및 갱신 -
- 폐기
통합 사례. - 얼굴 위주로 보라.
시크릿 관리 요약
기본 구조.
호텔 체크인.
ㅐ
처음에 그냥 하면 레디네스가 실패.
보안 장벽 초기화를 해야 함
봉인 해제 ㅅㄹ정 필요.
shamir 시크릿 공유
아디 샤미르, rsa 만든 사람
unseal에는 기본으로 키가 여러개 피룡하다
unseal키로 봉인 뜯고 루트키로 접근
vault cli, vauld addr 환경변수 필ㄹ요
우리는 kv2를 쓴다.
데이터 버전관리가 가능하다.
단계적으로 데이터를 업데이트하는 것이 가능.
시크릿 엔진별로 관리된다.
엔진 안에 data라는 추가 경로가 생긴다.
이게 v2라서.
사이드카.
앱이 시크릿을 볼륨으로 마운팅하도록 구성.
이게 agent injector를 통해서.
설치되는 것은 vault k8s. 소스코드가 잇다
파드에 어노테이션 달면 됨
사이드카가 볼트 서버에 통신
이놈이 agent
앱롤 인증 사용
볼트 내에서 앱 친화적인 인증방식
role id, secret id가 이름과 비번
auth로 인증 방식 활성화
policy로 정책 생성
정책을 롤에 매핑
프록시라고 agent보다 조금 더 편하게 사용할 수 있는 기능
ㅐ
아르고 cd에서는 컨피그맵 방식도 가능.
근데 사이드카가 좀더 최신 방식이고 안전하다.
볼트 플러그인을 사용한다.
이 방식은 오퍼레이터 패턴은 아니다.
avp helm, avp kustomize, avp로 가능
k9s에서 s누르면 시크릿 내용물 볼 수 잇음
다른 글 보기
이름 | noteType | index | created |
---|
관련 문서
이름 | noteType | created |
---|
참고
https://developer.hashicorp.com/vault/docs/commands#configure-environment-variables ↩︎
https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth/methods/approle ↩︎
https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth ↩︎
https://developer.hashicorp.com/vault/docs/platform/k8s/injector/examples#configmap-example ↩︎
https://developer.hashicorp.com/vault/docs/auth/kubernetes#use-local-service-account-token-as-the-reviewer-jwt ↩︎
https://support.hashicorp.com/hc/en-us/articles/4404389946387-Kubernetes-auth-method-Permission-Denied-error ↩︎
https://argo-cd.readthedocs.io/en/stable/operator-manual/config-management-plugins/#installing-a-config-management-plugin ↩︎
https://argocd-vault-plugin.readthedocs.io/en/stable/installation/#initcontainer-and-configuration-via-sidecar ↩︎
https://github.com/argoproj-labs/argocd-vault-plugin/blob/main/manifests/cmp-sidecar/cmp-plugin.yaml ↩︎
https://github.com/hyungwook0221/spring-boot-debug-app/blob/main/infra/helm/new-values.yaml ↩︎