GitOps 환경을 구성하기 위해 kubernetes 설정 파일을 git 저장소에 올려서 ArgoCD와 연동을 했었습니다.
하지만 configmap이나 secret 을 git 저장소에 올리기에는 보안상 문제가 있다고 생각했습니다.
물론 private한 저장소를 사용하는 방법을 사용하면 외부에 공유가 되지는 않지만
그래도 소스코드에 DB 패스워드 같은 정보가 들어있는게 부담이 되었습니다.
ArgoCD에서도 비밀관리에 대한 다양한 방법을 여기에 제시해 놓았습니다.
이 중 IBM에서 만든 HashiCorp Vault를 이용한 argocd-vault-plugin 방식이 마음에 들어 적용해 보았습니다.
Vault는 HashiCorp에 의해서 개발된 크로스플랫폼 패스워드 및 인증 관리 시스템이다. 공개되면 안되는 비밀번호, API 키, 토큰 등을 UI, CLI, HTTP API를 사용하여 저장하고 관리한다.
vault 설치를 위한 다양한 방법이 있는데 전 Helm을 이용해 kubernetes에 설치하였습니다.
$ git clone https://github.com/hashicorp/vault-guides.git
$ cd vault-guides/operations/provision-vault/kubernetes/minikube/getting-started
$ helm repo add hashicorp https://helm.releases.hashicorp.com
$ helm repo update
$ helm install consul hashicorp/consul --values helm-consul-values.yml
서버 및 클라이언트 Pod이 Running일 때까지 기다립니다.
$ helm install vault hashicorp/vault --values helm-vault-values.yml
봉인해제 키를 가져옵니다.
$ kubectl exec vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json > cluster-keys.json
$ VAULT_UNSEAL_KEY=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[]")
vault-0
, vault-1
, vault-2
Pod에서 봉인해제 합니다.
$ kubectl exec vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY
$ kubectl exec vault-1 -- vault operator unseal $VAULT_UNSEAL_KEY
$ kubectl exec vault-2 -- vault operator unseal $VAULT_UNSEAL_KEY
root token을 가져옵니다.
$ cat cluster-keys.json | jq -r ".root_token"
vault-0
에 접속
$ kubectl exec --stdin=true --tty=true vault-0 -- /bin/sh
/ $
Login 합니다.
/ $ vault login
kv-v2 비밀을 secret
경로에 활성화합니다.
/ $ vault secrets enable -path=secret kv-v2
secret
에 비밀을 생성합니다.
/ $ vault kv put secret/example-app/secret \
db-user="example-app" \
db-password="password" \
db-root-password="root-password"
kubernetes의 서비스 계정 토큰으로 vault 비밀에 접근하기 위해서 kubernetes 인증을 활성화 합니다.
/ $ vault auth enable kubernetes
/ $ vault write auth/kubernetes/config \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
example-app
비밀에 대한 read 권한을 생성합니다.
/ $ vault policy write example-app - <<EOF
path "secret/data/example-app/*" {
capabilities = ["read"]
}
EOF
ArgoCD의 default
서비스 계정과 vault의 example-app
정책을 연결하는 역할을 만듭니다.
ArgoCD의 서비스 계정은 kubectl get sa -n argocd
명령으로 확인할 수 있습니다.
vault write auth/kubernetes/role/argocd \
bound_service_account_names=default \
bound_service_account_namespaces=argocd \
policies=example-app \
ttl=24h
policies는 위에서 생성한 example-app
policy를 추가합니다.
argocd-vault-plugin는 kubernetes 설정파일에서 <placeholder>
형식을 vault 값으로 변환해주는 역할을 합니다. <>
안의 값은 vault의 실제 key입니다.
argocd-repo-server
에 argocd-vault-plugin을 설치하고 vault 인증정보를 설정하면
ArgoCD에서 동기화할 때 vault 값을 kubernetes 설정 정보에 반영해서 적용합니다.
자세한 사용법은 여기를 참고하세요.
$ kubectl get svc vault
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
vault ClusterIP 10.110.116.153 <none> 8200/TCP,8201/TCP 5d2h
vault 주소는 http://CLUSTER-IP:8200
입니다.
http://10.110.116.153:8200
argocd-repo-server에서 vault에 접근하기 위한 인증정보를 생성합니다.
VAULT_ADDR
는 위에서 확인한 vault 주소입니다
AVP_K8S_ROLE
는 Kubernetes 인증 구성에서 만든 역할 이름 입니다. argocd
argocd-vault-plugin-credentials.yaml
kind: Secret
apiVersion: v1
metadata:
name: argocd-vault-plugin-credentials
namespace: argocd
type: Opaque
stringData:
VAULT_ADDR: http://10.110.116.153:8200
AVP_TYPE: vault
AVP_AUTH_TYPE: k8s
AVP_K8S_ROLE: argocd
secret를 argocd 네임스페이스에 적용합니다.
$ kubectl apply -n argocd -f argocd-vault-plugin-credentials.yaml
$ kubectl edit deployment argocd-repo-server -n argocd -o yaml
아래 내용 추가 후 저장
# automountServiceAccountToken 추가
automountServiceAccountToken: true
# custom-tools volume 추가
volumes:
- name: custom-tools
emptyDir: {}
# initContainer 추가
initContainers:
- name: download-tools
image: alpine:3.8
command: [sh, -c]
args:
- wget -O argocd-vault-plugin
https://github.com/IBM/argocd-vault-plugin/releases/download/v1.0.0/argocd-vault-plugin_1.0.0_linux_amd64
chmod +x argocd-vault-plugin && mv argocd-vault-plugin /custom-tools/
volumeMounts:
- mountPath: /custom-tools
name: custom-tools
# custom-tools mount 추가, env 추가
containers:
- name: argocd-repo-server
volumeMounts:
- name: custom-tools
mountPath: /usr/local/bin/argocd-vault-plugin
subPath: argocd-vault-plugin
envFrom:
- secretRef:
name: argocd-vault-plugin-credentials
$ kubectl edit configmap argocd-cm -n argocd -o yaml
아래 내용 추가 후 저장
data:
configManagementPlugins: |
- name: argocd-vault-plugin
generate:
command: ["argocd-vault-plugin"]
args: ["generate", "./"]
- name: argocd-vault-plugin-helm
generate:
command: ["sh", "-c"]
args: ["helm template . > all.yaml && argocd-vault-plugin generate all.yaml"]
- name: argocd-vault-plugin-kustomize
generate:
command: ["sh", "-c"]
args: ["kustomize build . > all.yaml && argocd-vault-plugin generate all.yaml"]
ArgoCD에서 앱 등록화면에서 Directory
를 클릭해서 Plugin
을 선택합니다.
kustomize로 구성되어있어서 argocd-vault-plugin-kustomize
를 선택하고 저장합니다.
overlays/dev/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: example-app-secret
labels:
app: example-app
annotations:
avp.kubernetes.io/path: "secret/data/example-app/secret"
type: Opaque
data:
DB_USERNAME: <db-user | base64encode>
DB_PASSWORD: <db-password | base64encode>
DB_ROOT_PASSWORD: <db-root-password | base64encode>
avp.kubernetes.io/path는 vault 비밀이 저장된 path를 입력한다. Vault 비밀 설정
secret
와 example-app
사이에 data
를 넣어야함.
Kubernetes Secret의 data는 base64로 인코딩된 값이어야하므로 | base64encode
키워드를 붙혀준다.
overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: ghcr.io/wlgns5376/example-app
newTag: e70be61b
patchesStrategicMerge:
- secret.yaml
resources:
- ../../base
이제 수정한 파일을 git에 push
하고 ArgoCD에서 동기화가 잘되는지 확인하면 됩니다.
위에 코드들은 여기에 올려져 있습니다.
아주 Simple하게나마 GitOps라는걸 살짝 맛만 보았는데도 다양한 도구를 사용했네요.
GitHub + Kubernetes + Kustomize + ArgoCD + Vault
kubernetes에 배포하는데 git 저장소에 있는 파일을 수정하면 자동으로 배포가 된다는것이 매력적이었고 대부분이 git에서 돌아가니 개발자도 접근하기도 쉬웠던 것 같습니다.
기회가 된다면 실제 서비스에 적용해봐야겠습니다.
References: