Lâu rồi không viết bài mới tại mình … lười quá. Nay viết bài này như là tài liệu note lại vì ông anh đồng nghiệp lâu năm cứ nhằn “sao mày ko share document cho taooo !!!” =)))
Bài viết sẽ chia sẽ cách để bạn enject secrets đến pods sử dụng vault agent. Bài toán đặt ra là trong mô hình K8S của bạn cần một giải pháp lưu trữ secrets đáp ứng nhu cầu vừa bảo mật vừa hỗ trợ dev team một cách nhanh chóng khi họ cần update key-value một cách liên tục. Giải pháp mình sẽ sử dụng vault để tạo các căp kv (key-value) và sử dụng vault agent để nhúng vào pods mình cần sau đó thủ thuật chút để có thể biến các kv này như các biến trong env.
1. Cài đặt Consul và Vault high-available mode
Consul là service mesh giải pháp lưu trữ kv như backend storage, còn Vault là một công cụ dùng để quản lý Secret, nó được phát triển bởi công ty Hashicorp. Vault phải có một trình quản lý storage backend để cấu hình và mã hoá khi vault chạy dưới mode HA. Bài này mình sẽ cài cả consul và vault bằng helm.
Tạo file `helm-consul-values.yml`:
global: datacenter: vault-kubernetes-tutorial client: enabled: true server: replicas: 1 bootstrapExpect: 1 disruptionBudget: maxUnavailable: 0
Nạp repo hashicorp trên helm:
helm repo add hashicorp https://helm.releases.hashicorp.com helm repo update helm install consul hashicorp/consul --values helm-consul-values.yml
Tiếp tục tạo fiel helm-vault-values.yml:
server: affinity: "" ha: enabled: true
install Vault:
helm install vault hashicorp/vault --values helm-vault-values.yml
Vault pod và Vault agent injector được deploy vào default namespace.
Các pods vault-{0,1,2} và vault-agent-injector đã được deploy. Ban đầu nó sẽ không ở trạng thái running như trong hình đâu bỏi vì readnessProbe.
kubectl exec vault-0 -- vault status Key Value --- ----- Seal Type shamir Initialized false Sealed true Total Shares 0 Threshold 0 Unseal Progress 0/0 Unseal Nonce n/a Version n/a HA Enabled false command terminated with exit code 2
đó là do Vault đang bị sealed. mình cần unseal, trước hết khởi tạo vault:
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[]") 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
2. Vault agent injector là gì và hoạt động ra sao?
Vault agent injector là một controller được add vào như một sidecar và init container đến pods trong runtime.
Job của init container này sẽ xác thực bằng cách dùng service account của k8s và lấy secrets từ vault server đặt chúng trong shared location (In memory volume) để application có thể access đến được.
Vậy nó hoạt động thế nào?
Ví dụ, nếu một pod được triển khai với anotation như sau: “vault.hashicorp.com/agent-inject: ‘true'”, sau đây là những gì sẽ xảy ra.
- MutatingWebhookConfiguration gửi một webhook với tất cả thông tin về pod đến injector controller deployment.
- Sau đó, controller sửa đổi phần Pod spec trong runtime để refer các sidecar và agent của container khởi tạo vào đặc tả pod thực tế.
- Controller sau đó trả về object đã được sửa đổi để validate object.
- Sau khi validation, pod spec đã được sửa đổi sẽ được triển khai với một sidecar và container khởi tạo.
Vì vậy, khi pod được khởi đầu, nó sẽ bao gồm container application, một sidecar và một init container.
Container khởi tạo có trách nhiệm lấy các secrets. Ngoài ra, một container sidecar là cần thiết nếu application của bạn sử dụng các dynamic secrets. dynamic secrets là các secrets được tạo ra theo yêu cầu với thời gian hết hạn. Container sidecar đảm bảo rằng các bí mật mới nhất có mặt bên trong pod sau mỗi lần renew secrets.
3. Tạo Vault secrets và Policy
Đầu tiên exec vào Vault-0
kubectl exec -it vault-0 -- /bin/sh
Enable vault kv engine (key-value store).
vault secrets enable -version=2 -path="kickir" kv
Tạo thử một secrets nào:
vault kv put kickir/staging/webcreds APPLICATION_ENV="staging"
Tạo policy cho kv này, lưu ý là version 2 thì bạn phải thêm /data vào nữa nhé, không lỗi sml ko biết đường debug đâu:
vault policy write kickir-staging - <<EOH path "kickir/data/staging/webcreds" { capabilities = ["read"] } EOH
Enable Kubernetes authentication.
vault auth enable kubernetes
vault write auth/kubernetes/config \ token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ kubernetes_host="https://YOUR_ADDR_CLUSTER_K8S:6443" \ kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Tạo Vault role tên kickirstaging và mount đến service account vault-auth và namespace default:
vault write auth/kubernetes/role/kickirstaging \ bound_service_account_names=vault-auth \ bound_service_account_namespaces=default \ policies=kickir-staging \ ttl=72h
Bây giờ exit khỏi pod, quay trờ lại k8s cluster và tạo service account tương ứng:
kubectl create serviceaccount vault-auth
4. Injecting Secrets With Vault Agents
Mặc định Vault agent sẽ ghi secrets đến path /vault/secrets/, đây là pod volume shared in memory.
OK, giờ cùng mình tạo một file deployment tên deployment-env.yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx labels: app: nginx spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: annotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/role: 'kickirstaging' vault.hashicorp.com/agent-pre-populate-only: 'true' vault.hashicorp.com/agent-inject-secret-database-config: 'kickir/staging/webcreds' vault.hashicorp.com/agent-inject-template-database-config: | {{- with secret "kickir/staging/webcreds" -}}{{- range $k, $v := .Data.data }} export {{ $k }}={{ $v }} {{- end }}{{- end -}} labels: app: nginx spec: serviceAccountName: vault-auth containers: - name: nginx image: nginx ports: - containerPort: 80 command: ["/bin/bash"] args: - "-c" - > source /vault/secrets/database-config && env >> ~/.bashrc && source ~/.bashrc && nginx -g 'daemon off;' --- apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: selector: app: nginx type: ClusterIP sessionAffinity: None sessionAffinityConfig: clientIP: timeoutSeconds: 10800 ports: - name: nginx protocol: TCP port: 80
vault.hashicorp.com/agent-inject: ‘true’ – Enable sidecar và init container.
vault.hashicorp.com/role: ‘kickirstaging‘ : Gắn vào role kickirstaging đã tạo trước đó trên vault.
vault.vault.hashicorp.com/agent-inject-secret-database-config: ‘kickir/staging/webcreds’: gắn path vào path /vault/secrets/database-config
vault.hashicorp.com/agent-inject-template-database-config: với anotation này mình dùng loop để đọc ghi tất cả kv trên vault vào file, sau đó dùng Command trên manifest để nạp vào như biến trên env.
Apply deployment:
kubectl apply -f deployment-env.yaml
Mình có thêm một vài kv nữa, hãy cùng xem lại trong Vault-0:
Và đây là giá trị được ghi vào nginx deployment của mình:
Giờ thử dùng command echo print ra thử xem nó đã đc export như env hay không nha:
Vậy là success.
5. Phân quyền access trong Vault-UI
Bạn có thể dùng expose port forward hoặc gắn domain vào ingress để access vault-ui
Mình cần UI vì mình sẽ share quyền access cho dev team để họ tự managed kv của họ, như vậy quá trình phát triển phần mềm sẽ diễn ra liên tục không phụ thuộc bên DevOps hay bên vận hành nữa.
Đặng nhập bằng method token, root_token bạn đã lưu lại khi nãy
Trong menu bạn chọn access >> Authentication Methods >> Enabled new method:
Chọn username & password
Khi tạo xong, bạn vào method userpass >> create new user:
Tại đây bạn nhập username, password và Genarate token’s policies bạn điền tên dev vào (policy này mình sẽ tạo sau)
Save lại.
Quay lại main menu, chọn Policies >> Create ACL policies:
Bạn sẽ tạo dev policy tại đây:
path "kickir/*" { capabilities = [ "list" ] } path "kickir/data/*" { capabilities = [ "list" ] } path "kickir/data/staging/webcreds" { capabilities = [ "create", "read", "update", "delete", "list" ] }
Như vậy user này sẽ full quyền trên kv path kickir/data/staging/webcreds
Xong, logout ra và login bằng method user password thử xem:
kết quả ta đã access được kv engine này rồi:
Mình tạo thử các kv khác thử:
Save lại, restart lại deployment:
Done, như vậy mình đã hướng dẫn xong tuts này, cám ơn các bạn đã theo dõi. 🙂