Cấu hình Jenkins/GitLab CI để Build và Push Image có ký số tự động
Bước đầu tiên là tích hợp Harbor Client (harbor-cli) vào pipeline để thực hiện build image và ký số ngay sau khi build thành công.
Chúng ta cần cài đặt Harbor Client trên build agent, cấu hình biến môi trường chứa thông tin đăng nhập, và thêm bước ký (sign) trước khi đẩy (push) lên registry.
Việc này đảm bảo mọi artifact được đẩy lên registry đều đã có chữ ký điện tử, ngăn chặn việc giả mạo image sau khi build.
Trước tiên, cài đặt Harbor Client vào container build agent (Docker-based Jenkins/GitLab Runner).
curl -sSL https://raw.githubusercontent.com/goharbor/harbor/main/src/harbor/src/core/registry.go | bash -s -- --install
Kết quả mong đợi: Command `harbor` hoặc `harbor-cli` có sẵn trong PATH của agent.
Thiết lập biến môi trường trong pipeline (Jenkins Global Config hoặc GitLab CI Variables) để lưu thông tin kết nối.
export HARBOR_HOST="harbor.yourdomain.com"
export HARBOR_CREDENTIALS="admin:Password123"
export HARBOR_PROJECT="secure-app"
export NOTARY_TLS_INSECURE="false"
Kết quả mong đợi: Các biến môi trường được xuất, sẵn sàng cho các bước tiếp theo.
Viết script build và ký số tự động. Script này sẽ build image, login vào Harbor, ký image bằng Notary và push lên.
#!/bin/bash
set -e
# Build image
IMAGE_TAG="${HARBOR_PROJECT}/my-app:${BUILD_NUMBER}"
docker build -t ${IMAGE_TAG} .
# Login vào Harbor
echo "${HARBOR_CREDENTIALS}" | docker login ${HARBOR_HOST} -u admin --password-stdin
# Cấu hình Harbor CLI để ký
harbor config set --host ${HARBOR_HOST}
harbor config set --user admin
harbor config set --password ${HARBOR_CREDENTIALS#*:}
# Ký image (Sign)
harbor sign ${IMAGE_TAG}
# Push image lên Harbor (chỉ push nếu ký thành công)
docker push ${IMAGE_TAG}
Kết quả mong đợi: Image được đẩy lên Harbor với trạng thái "Signed" trong giao diện quản trị Harbor.
Verify kết quả: Truy cập giao diện Harbor -> Projects -> secure-app -> Artifacts. Tìm artifact vừa đẩy, check cột "Signed" có hiện dấu tích xanh hoặc trạng thái "Signed".
Tích hợp bước quét lỗ hổng vào pipeline trước khi Deploy
Trước khi artifact được phân phối đến môi trường Production, chúng ta phải chặn ngay lập tức nếu phát hiện lỗ hổng nghiêm trọng.
Sử dụng Trivy (hoặc Clair đã cấu hình ở Phần 5) để quét image trong pipeline. Pipeline sẽ bị lỗi (fail) nếu điểm severity cao hơn ngưỡng cho phép.
Cấu hình bước quét trong GitLab CI (tương tự áp dụng cho Jenkins Pipeline stage).
stages:
- build
- security-scan
- deploy
security_scan:
stage: security-scan
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH ${IMAGE_TAG}
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
allow_failure: false
Kết quả mong đợi: Nếu image có lỗ hổng CRITICAL hoặc HIGH, bước này trả về exit code 1 và pipeline dừng lại, không chuyển sang stage deploy.
Tùy chọn: Tích hợp với Harbor Policy Engine (đã cấu hình ở Phần 6) để chặn push nếu quét lỗi.
# Cấu hình trong Harbor (qua API hoặc UI) để chặn push nếu có Critical
# Đây là ví dụ cấu hình trong file config.yml của Harbor (phần pre-push hook)
hooks:
pre_push:
- name: "trivy-scan"
command: "/usr/local/bin/trivy image --exit-code 1 --severity CRITICAL"
args: ["${IMAGE_NAME}"]
Kết quả mong đợi: Nếu image không đạt chuẩn, lệnh push sẽ bị từ chối ngay tại registry, không cần đợi pipeline chạy xong.
Verify kết quả: Tạo một image test có lỗ hổng cao (ví dụ: `alpine:3.8` cũ), chạy pipeline. Quan sát log thấy bước `security-scan` báo lỗi và pipeline dừng. Ngược lại, dùng image sạch, pipeline chạy qua bước này.
Cấu hình bước xác thực chữ ký Notary trong quá trình Deployment
Khi artifact đã sẵn sàng để deploy, bước cuối cùng trước khi chạy container là xác minh chữ ký.
Chúng ta sử dụng `notary` client hoặc `cosign` (nếu dùng Sigstore) để verify signature và timestamp trước khi gửi lệnh `docker run` hoặc `kubectl apply`.
Viết script verify trong stage deploy của pipeline.
#!/bin/bash
set -e
IMAGE_TAG="${HARBOR_PROJECT}/my-app:${BUILD_NUMBER}"
HARBOR_HOST="harbor.yourdomain.com"
# Cấu hình Notary
export NOTARY_HOST=${HARBOR_HOST}
export NOTARY_TLS_INSECURE="false" # Đặt true nếu dùng self-signed cert trong dev
# Xác thực chữ ký
echo "Verifying signature for ${IMAGE_TAG}..."
notary verify ${IMAGE_TAG}
# Nếu verify thành công, in log
echo "Signature verified successfully. Proceeding to deployment."
Kết quả mong đợi: Command trả về "Signature verified successfully" nếu chữ ký hợp lệ. Nếu image bị giả mạo hoặc chưa ký, command trả về lỗi và pipeline dừng.
Tích hợp vào Helm Chart hoặc Kustomize trước khi apply.
helm upgrade --install my-app ./chart \
--set image.tag=${BUILD_NUMBER} \
--set image.repository=${HARBOR_HOST}/${HARBOR_PROJECT}/my-app \
--wait
Kết quả mong đợi: Deployment chỉ xảy ra sau khi bước verify thành công.
Verify kết quả: Thử sửa đổi một byte trong image đã ký (hoặc push một image khác có cùng tag nhưng không ký), chạy bước verify. Lệnh phải báo lỗi "Verification failed".
Triển khai GitOps (ArgoCD) để đồng bộ Artifact an toàn
Sử dụng ArgoCD để tự động hóa việc đồng bộ trạng thái cluster với Git repository, nhưng chỉ cho phép deploy các image đã được ký và quét sạch.
ArgoCD sẽ đọc manifest từ Git, nhưng chúng ta cần cấu hình Policy Enforcement hoặc Admission Controller để chặn việc deploy image không đạt chuẩn.
Triển khai ArgoCD trên Kubernetes cluster (nếu chưa có).
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Kết quả mong đợi: Namespace argocd được tạo và các pod của ArgoCD chạy trạng thái Running.
Cấu hình ArgoCD Application để sync từ Git repository chứa Helm/Kustomize.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: secure-app
namespace: argocd
spec:
project: default
source:
repoURL: https://git.yourdomain.com/secure-app-manifests.git
targetRevision: HEAD
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
Kết quả mong đợi: ArgoCD hiển thị trạng thái "OutOfSync" và tự động sync khi có thay đổi trong Git.
Tích hợp Policy Enforcement (OPA Gatekeeper hoặc Kyverno) để chặn deployment nếu image không có chữ ký Notary.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-notary-signature
spec:
rules:
- name: verify-signature
match:
any:
- kind: Pod
validate:
message: "Image must be signed by Notary. Use a signed image from Harbor."
deny:
conditions:
- key: "{{image.isSigned}}"
operator: NotEquals
value: true
Kết quả mong đợi: Khi ArgoCD cố gắng deploy một Pod dùng image chưa ký, Kubernetes Admission Controller sẽ chặn request với lỗi "Image must be signed...".
Verify kết quả: Deploy một Pod dùng image đã ký -> Thành công. Deploy Pod dùng image chưa ký -> Thất bại (Error: forbidden).
Xây dựng Audit Log để theo dõi vòng đời Artifact
Cuối cùng, cần tập trung hóa log để theo dõi toàn bộ hành trình: Build -> Sign -> Scan -> Deploy -> Run.
Sử dụng ELK Stack (Elasticsearch, Logstash, Kibana) hoặc Loki để thu thập log từ Jenkins/GitLab, Harbor, và Kubernetes Audit Log.
Cấu hình Jenkins/GitLab CI để xuất log chi tiết về file hoặc stdout với format JSON.
cat
Kết quả mong đợi: File log JSON được tạo sau mỗi lần build thành công.
Bật Kubernetes Audit Log để ghi lại các sự kiện deployment vào cluster.
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["pods", "deployments"]
- group: apps
resources: ["deployments"]
Thêm config này vào kube-apiserver command line arguments: `--audit-policy-file=/etc/kubernetes/audit/policy.yaml --audit-log-path=/var/log/kubernetes/audit.log`.
Kết quả mong đợi: File `/var/log/kubernetes/audit.log` chứa chi tiết ai, khi nào, deploy image nào vào cluster.
Tích hợp log của Harbor (nginx access log + harbor log) vào tập trung hóa.
# Cấu hình trong Docker Compose của Harbor hoặc K8s Deployment
# Thêm volume mount log directory
volumes:
- /var/log/harbor:/var/log/harbor
# Sau đó dùng Filebeat hoặc Fluentd để ship log này vào Elasticsearch/Loki
Kết quả mong đợi: Trong Kibana/Loki, có thể query theo `image_name` để xem timeline: Build time -> Sign time -> Scan time -> Deploy time.
Verify kết quả: Chạy một pipeline hoàn chỉnh. Truy cập dashboard log (Kibana/Loki), tìm query theo tên image. Kiểm tra xem có đầy đủ 4 sự kiện (Build, Sign, Scan, Deploy) không.
Điều hướng series:
Mục lục: Series: Series: Xây dựng nền tảng Secure Multi-Cloud Container Registry với Harbor, Notary và Policy Engine để kiểm soát phân phối artifact an toàn
« Phần 7: Tối ưu hóa hiệu năng và bảo mật cho registry trong môi trường Multi-Cloud
Phần 9: Xử lý sự cố, giám sát và các mẹo nâng cao cho Secure Multi-Cloud Registry »