Cấu hình Role-Based Access Control (RBAC) cho ArgoCD và Tekton
Chúng ta cần thiết lập RBAC để đảm bảo chỉ những người có quyền hạn cụ thể mới được phép thực thi pipeline hoặc deploy ứng dụng lên cluster.
Trong ArgoCD, RBAC được cấu hình qua một file YAML trong ConfigMap của namespace ArgoCD. Chúng ta sẽ tạo một Role cho "developer" chỉ được phép xem và trigger deploy cho môi trường Dev, và một Role cho "admin" có toàn quyền.
Tại sao: Ngăn chặn rủi ro con người thao tác sai hoặc tấn công nội bộ khi có quá nhiều quyền.
Kết quả mong đợi: Khi login với tài khoản developer, người dùng chỉ thấy namespace dev và không thể thao tác trên namespace prod.
cat > /tmp/argocd-rbac-config.yaml <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
labels:
app.kubernetes.io/name: argocd-rbac-cm
app.kubernetes.io/part-of: argocd
data:
policy.default: role:readonly
policy.csv: |
g, developer, role:dev-user
g, admin, role:admin
p, role:dev-user, applications, *, dev/*, allow
p, role:dev-user, applications, get, prod/*, deny
p, role:admin, applications, *, *, allow
EOF
kubectl apply -f /tmp/argocd-rbac-config.yaml
Kết quả: ConfigMap được cập nhật, ArgoCD server sẽ reload chính sách ngay lập tức.
Đối với Tekton, RBAC được quản lý qua Kubernetes Role và RoleBinding. Chúng ta sẽ tạo Role chỉ cho phép thực thi Task và Pipeline trong namespace cụ thể.
Tại sao: Tekton chạy dưới dạng Pod, nếu không giới hạn quyền, một Pod có thể đọc Secret của namespace khác hoặc tạo Pod mới.
Kết quả mong đợi: User chỉ có thể trigger Pipeline trong namespace được phân quyền, việc tạo Pod ngoài namespace đó sẽ bị từ chối.
cat > /tmp/tekton-rbac.yaml <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: tekton-pipeline-runner
namespace: dev
rules:
- apiGroups: ["tekton.dev"]
resources: ["pipelineruns", "tasks", "taskruns"]
verbs: ["get", "list", "watch", "create", "update"]
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tekton-pipeline-runner-binding
namespace: dev
subjects:
- kind: ServiceAccount
name: tekton-pipeline-runner
namespace: dev
- kind: Group
name: developer-group
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: tekton-pipeline-runner
apiGroup: rbac.authorization.k8s.io
EOF
kubectl apply -f /tmp/tekton-rbac.yaml
Kết quả: Role và RoleBinding được tạo thành công trong namespace dev.
Verify kết quả
Đăng nhập vào ArgoCD với tài khoản developer, cố gắng truy cập Application thuộc namespace prod. Hệ thống phải báo lỗi "Access Denied".
Chạy lệnh kubectl để kiểm tra RoleBinding:
kubectl get rolebinding tekton-pipeline-runner-binding -n dev -o yaml
Kiểm tra xem subject có đúng nhóm developer-group không.
Sử dụng OPA Gatekeeper để kiểm tra tuân thủ Policy
Chúng ta sẽ triển khai OPA Gatekeeper để ngăn chặn việc deploy các tài nguyên không tuân thủ chính sách bảo mật trước khi chúng được áp dụng vào cluster.
Tại sao: RBAC chỉ kiểm soát ai được làm gì, còn Gatekeeper kiểm soát cái gì được phép tồn tại (ví dụ: không cho phép Pod chạy với privilege=true).
Kết quả mong đợi: Khi cố gắng apply một Pod không an toàn, request sẽ bị API Server từ chối với thông báo từ Gatekeeper.
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
helm install gatekeeper gatekeeper/gatekeeper-crds
helm install gatekeeper gatekeeper/gatekeeper
Kết quả: Namespace gatekeeper-system được tạo và các Pod controller của Gatekeeper chạy ổn định.
Bây giờ, chúng ta tạo một Constraint để đảm bảo không có Pod nào được phép chạy với quyền root (runAsNonRoot: true).
Tại sao: Chạy container với quyền root là rủi ro bảo mật lớn nhất trong containerization.
Kết quả mong đợi: Mọi Pod không khai báo securityContext.runAsNonRoot sẽ bị chặn.
cat > /tmp/gatekeeper-no-root.yaml <<EOF
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPRunAsNonRoot
metadata:
name: deny-root-pods
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- gatekeeper-system
- kube-system
- argocd
parameters:
runAsNonRoot: true
EOF
kubectl apply -f /tmp/gatekeeper-no-root.yaml
Kết quả: Constraint được tạo thành công, Gatekeeper bắt đầu giám sát.
Tiếp theo, áp dụng chính sách ngăn chặn việc deploy Image từ registry không tin cậy (chỉ cho phép image từ quay.io hoặc registry nội bộ).
Tại sao: Ngăn chặn việc sử dụng image bị nhiễm malware từ các registry công cộng không rõ nguồn gốc.
Kết quả mong đợi: Deploy image từ docker.io (ngoại trừ quay.io) sẽ bị từ chối.
cat > /tmp/gatekeeper-image-policy.yaml <<EOF
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabel
metadata:
name: require-image-label
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
excludedNamespaces:
- gatekeeper-system
- kube-system
parameters:
labels:
- security-approved
EOF
# Lưu ý: Đây là ví dụ đơn giản, trong thực tế thường dùng Custom Constraint với Rego
# Để đơn giản hóa cho tutorial này, ta dùng K8sPSPRunAsNonRoot ở trên là đủ
# Dưới đây là ví dụ Custom Constraint để chặn image không an toàn
cat > /tmp/gatekeeper-image-allowed.yaml <<EOF
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sImageAllowlist
metadata:
name: allow-specific-registries
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
allowedRegistries:
- quay.io
- registry.internal.company.com
EOF
kubectl apply -f /tmp/gatekeeper-image-allowed.yaml
Kết quả: Constraint mới được áp dụng, Gatekeeper bắt đầu enforce chính sách image.
Verify kết quả
Thử deploy một Pod chạy với user root:
cat > /tmp/test-pod.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: insecure-pod
spec:
containers:
- name: nginx
image: nginx:1.14
command: ["sleep", "1000"]
EOF
kubectl apply -f /tmp/test-pod.yaml
Quan sát output, bạn sẽ thấy lỗi: "error validating data: forbidden: Constraint K8sPSPRunAsNonRoot deny-root-pods denied pod".
Xóa test pod và thử deploy một Pod an toàn (có runAsNonRoot: true) để xác nhận nó được phép.
Đảm bảo chỉ pipeline đã thông qua kiểm thử mới được deploy lên Production
Chúng ta sẽ cấu hình Tekton Pipeline để có một bước "Gatekeeper" kiểm tra trạng thái của Artifact từ bước test trước khi chuyển sang bước deploy.
Tại sao: Đảm bảo quy trình CI/CD tự động chặn các bản build không đạt yêu cầu chất lượng (Quality Gate) trước khi chạm vào môi trường Production.
Kết quả mong đợi: Nếu bước test fail hoặc artifact không có dấu hiệu "approved", bước deploy sẽ không chạy.
cat > /tmp/tekton-pipeline-gate.yaml <<EOF
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: secure-deploy-pipeline
spec:
params:
- name: image-tag
default: "latest"
tasks:
- name: build
taskRef:
name: build-task
params:
- name: IMAGE
value: $(params.image-tag)
- name: test
taskRef:
name: test-task
runAfter:
- build
params:
- name: IMAGE
value: $(params.image-tag)
workspaces:
- name: test-results
workspace: test-results
- name: security-scan
taskRef:
name: trivy-scan-task
runAfter:
- test
params:
- name: IMAGE
value: $(params.image-tag)
workspaces:
- name: scan-results
workspace: scan-results
- name: deploy
taskRef:
name: deploy-task
runAfter:
- security-scan
params:
- name: IMAGE
value: $(params.image-tag)
- name: ENVIRONMENT
value: "production"
when:
- input: $(tasks.security-scan.results.VULNERABILITY_LEVEL)
operator: in
values: ["LOW", "PASS"]
- input: $(tasks.test.results.TEST_STATUS)
operator: in
values: ["SUCCESS"]
workspaces:
- name: test-results
- name: scan-results
EOF
kubectl apply -f /tmp/tekton-pipeline-gate.yaml
Kết quả: Pipeline được tạo với các điều kiện "when". Bước deploy chỉ chạy nếu test pass và scan vulnerability ở mức thấp.
Để tích hợp chặt chẽ hơn với ArgoCD, chúng ta sẽ sử dụng "Sync Policy" trong ArgoCD Application để yêu cầu một annotation xác nhận từ Tekton trước khi sync.
Tại sao: Ngăn chặn ArgoCD tự động sync (auto-sync) nếu pipeline chưa chạy xong hoặc chưa đạt chuẩn.
Kết quả mong đợi: ArgoCD ở trạng thái "OutOfSync" cho đến khi Tekton đánh dấu version mới là "ready".
cat > /tmp/argocd-app-policy.yaml <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/company/my-app
targetRevision: HEAD
path: manifests/prod
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- Validate=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# Annotation để đánh dấu pipeline đã approve
# Tekton sẽ cập nhật annotation này sau khi test pass
annotations:
argocd.argoproj.io/sync-wave: "1"
ci-pipeline-approved: "false"
EOF
kubectl apply -f /tmp/argocd-app-policy.yaml
Kết quả: Application được tạo, ArgoCD sẽ chờ trạng thái sync. Trong thực tế, bạn cần script webhook từ Tekton để cập nhật annotation "ci-pipeline-approved: true" khi pipeline thành công.
Verify kết quả
Tạo một PipelineRun sử dụng pipeline ở trên với một image biết là có lỗi test.
cat > /tmp/pipelinerun-fail.yaml <<EOF
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: secure-deploy-run-fail
spec:
pipelineRef:
name: secure-deploy-pipeline
params:
- name: image-tag
value: "v1.0-broken"
EOF
kubectl apply -f /tmp/pipelinerun-fail.yaml
Quan sát trạng thái PipelineRun. Bước "deploy" sẽ không được tạo (skipped) do điều kiện "when" không được thỏa mãn.
Kiểm tra ArgoCD UI, trạng thái của Application vẫn là "OutOfSync" hoặc "Pruning" nhưng không thực hiện deploy nếu annotation chưa được cập nhật bởi Tekton.
Cấu hình Audit Log cho Vault và các hoạt động CI/CD
Chúng ta sẽ cấu hình Vault để ghi lại mọi hoạt động truy cập secret (read, write, renew) vào một file log hoặc gửi về Elasticsearch/Syslog.
Tại sao: Khi xảy ra sự cố bảo mật, audit log là bằng chứng duy nhất để truy vết ai đã lấy secret nào và vào thời điểm nào.
Kết quả mong đợi: File log của Vault chứa các dòng record chi tiết về truy cập secret.
cat > /tmp/vault-audit-policy.hcl <<EOF
# Enable audit device for file
vault audit enable file file_path=/var/log/vault/audit.log
# Cấu hình log format
vault write sys/audit/file file_path=/var/log/vault/audit.log
# Kiểm tra xem audit đã được enable chưa
vault audit list
EOF
# Thực thi lệnh enable audit
vault audit enable file file_path=/var/log/vault/audit.log
Kết quả: Vault báo "Success! Audit device enabled".
Đối với Kubernetes, chúng ta cần bật audit log của API Server để ghi lại các hoạt động của Tekton và ArgoCD.
Tại sao: Cần biết ai đã tạo Pod, ai đã chỉnh sửa ConfigMap, ai đã kích hoạt PipelineRun.
Kết quả mong đợi: File audit log của K8s chứa các event với level "RequestResponse" hoặc "Metadata".
cat > /tmp/k8s-audit-policy.yaml <<EOF
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["secrets", "configmaps"]
- group: "tekton.dev"
resources: ["pipelineruns", "taskruns"]
- group: "argoproj.io"
resources: ["applications"]
- level: Metadata
resources:
- group: ""
resources: ["pods"]
- group: "tekton.dev"
resources: ["pipelines", "tasks"]
- group: "argoproj.io"
resources: ["applicationsets"]
nonResourceURLs:
- /healthz*
- /ready*
EOF
kubectl apply -f /tmp/k8s-audit-policy.yaml
Kết quả: Policy được apply. Cần restart kube-apiserver để áp dụng (thường qua kubelet config hoặc master config file).
Cấu hình Vault Policy để chỉ cho phép ServiceAccount của Tekton đọc secret trong path cụ thể, và ghi lại hoạt động này.
Tại sao: Giới hạn quyền truy cập secret của CI pipeline, chỉ cho phép lấy secret cần thiết cho build/deploy.
Kết quả mong đợi: Nếu Tekton cố đọc secret ngoài path được phép, Vault trả về lỗi "permission denied" và log sự kiện đó.
cat > /tmp/vault-ci-policy.hcl <<EOF
path "secret/data/ci/pipeline-secrets/*" {
capabilities = ["read"]
}
path "secret/data/ci/deploy-secrets/*" {
capabilities = ["read"]
}
# Chặn các path khác
path "secret/*" {
capabilities = ["deny"]
}
EOF
vault policy write ci-pipeline-policy /tmp/vault-ci-policy.hcl
Kết quả: Policy "ci-pipeline-policy" được tạo thành công.
Gán policy này vào ServiceAccount của Tekton thông qua Vault Kubernetes Auth Method.
Tại sao: Đảm bảo Pod của Tekton khi tự động lấy token từ Vault sẽ chỉ có quyền đọc các secret được phép.
Kết quả mong đợi: Khi Pod Tekton yêu cầu token, nó nhận được token với scope giới hạn.
cat > /tmp/vault-role-ci.yaml <<EOF
apiVersion: vaultproject.io/v1
kind: Role
metadata:
name: tekton-ci-role
namespace: tekton-pipelines
spec:
boundServiceAccountNames:
- tekton-pipeline-runner
policies:
- ci-pipeline-policy
ttl: 1h
maxTTL: 24h
EOF
kubectl apply -f /tmp/vault-role-ci.yaml
Kết quả: Role Vault được tạo, liên kết với ServiceAccount của Tekton.
Verify kết quả
Thực hiện một lần deploy nhỏ để tạo activity.
tail -f /var/log/vault/audit.log
Trong terminal khác, chạy một lệnh `kubectl get secret` hoặc kích hoạt một PipelineRun.
Quan sát log file, bạn sẽ thấy dòng log ghi lại: `path="secret/data/ci/pipeline-secrets"`, `method="GET"`, `entity_name` (tên user/service account).
Thử truy cập một secret không nằm trong policy (ví dụ `secret/data/admin`), Vault sẽ log dòng `err="permission denied"`.
Kiểm tra audit log của Kubernetes (thường nằm ở `/var/log/kubernetes/audit.log` trên master node):
grep "tekton.dev" /var/log/kubernetes/audit.log
Bạn sẽ thấy các request tạo PipelineRun hoặc TaskRun được ghi lại chi tiết.
Điều hướng series:
Mục lục: Series: Series: Xây dựng hệ thống CI/CD an toàn và tự động hóa cho Kubernetes với ArgoCD, Tekton và HashiCorp Vault
« Phần 5: Tích hợp Vault vào pipeline CI và quy trình deploy
Phần 7: Tự động hóa quy trình rollback và disaster recovery »