Tài liệu này mô tả cách đặt Cloudflare phía trước CampusCore khi bạn đã có:
- một domain đang quản lý trong Cloudflare,
- một Kubernetes cluster thật,
- một ingress controller trong cluster,
- secret manager hoặc private secret workflow của riêng bạn,
cert-managernếu muốn cấp TLS tự động,External Secretsnếu muốn sync secret runtime từ secret manager.
Repo public không commit hostname thật, token thật, TLS private key, hay secret manager path thật. Các giá trị đó đi vào private overlay copy từ k8s/templates/private-operator/*.
Đường production-like mặc định:
flowchart LR
U["Browser"] --> CF["Cloudflare DNS / WAF / CDN"]
CF --> LB["Ingress controller public address"]
LB --> IN["Kubernetes Ingress campuscore"]
IN --> NG["Service campuscore-nginx"]
NG --> S["CampusCore services"]
Cloudflare chỉ đứng ở lớp DNS/WAF/CDN trước ingress. Cloudflare không thay runtime cluster, không thay campuscore-nginx, và không thay service boundary hiện tại.
Chế độ TLS nên dùng là Full (strict): browser đến Cloudflare được mã hóa, Cloudflare đến origin cũng được mã hóa, và origin phải có certificate hợp lệ cho hostname thật. Không dùng Flexible cho CampusCore vì browser auth/cookie/session dễ gặp redirect loop hoặc mixed security model.
Cloudflare Tunnel là lựa chọn tốt nếu cluster không có public LoadBalancer hoặc bạn cố ý không mở inbound public traffic. Khi đó bạn chạy cloudflared như một deployment riêng trong cluster và route hostname Cloudflare vào service nội bộ.
Với CampusCore, Tunnel chưa phải đường mặc định của repo public vì handoff hiện tại đang chuẩn hóa theo Ingress cloud-agnostic. Nếu chọn Tunnel, hãy làm trong private overlay riêng và vẫn giữ campuscore-nginx là service edge nội bộ.
Khi bạn chưa có IP public hoặc cloud Kubernetes thật, có thể expose Docker Desktop Kubernetes local qua Cloudflare Tunnel. Repo đã có helper:
node scripts/run-cloudflare-tunnel-local.mjsScript này sẽ:
- gọi
node scripts/run-k8s-local-edge.mjsđể đảm bảo app đang mở tạihttp://127.0.0.1:8080; - nếu máy có
cloudflarednative thì dùng native connector; - nếu chưa có
cloudflared, dùng Docker imagecloudflare/cloudflared:latest; - nếu chưa set
CLOUDFLARE_TUNNEL_TOKEN, tạo quick tunnel tạm thời dạnghttps://*.trycloudflare.com; - nếu đã set
CLOUDFLARE_TUNNEL_TOKEN, chạy named tunnel để bạn gắn hostname thật trong Cloudflare.
Stop Docker connector nếu cần:
node scripts/stop-cloudflare-tunnel-local.mjsDùng để kiểm demo ngay:
node scripts/run-cloudflare-tunnel-local.mjsChờ terminal in ra URL dạng:
https://something.trycloudflare.com
Rồi mở:
https://something.trycloudflare.com/healthhttps://something.trycloudflare.com/loginhttps://something.trycloudflare.com/api/docs
Quick tunnel chỉ nên dùng demo/test. URL tạm sẽ đổi khi tunnel dừng.
Để dùng hostname như campuscore.tienson.io.vn, làm trong Cloudflare dashboard:
- Vào Zero Trust.
- Vào Networks -> Tunnels.
- Chọn Create a tunnel.
- Chọn connector Cloudflared.
- Đặt tên, ví dụ
campuscore-local. - Chọn environment Docker.
- Copy token value. Không paste token vào chat hoặc commit vào repo.
- Trong PowerShell tại repo:
$env:CLOUDFLARE_TUNNEL_TOKEN = "paste-token-vua-copy-o-day"
node scripts/run-cloudflare-tunnel-local.mjs- Ở bước Public Hostname trong Cloudflare:
| Field | Giá trị khuyến nghị |
|---|---|
| Subdomain | campuscore |
| Domain | tienson.io.vn |
| Type | HTTP |
| URL | host.docker.internal:8080 |
Vì script mặc định chạy connector bằng Docker, container phải đi qua host.docker.internal để gọi local edge trên máy Windows. Nếu bạn cài cloudflared native và chạy với CLOUDFLARE_TUNNEL_CONNECTOR=native, Service URL trong Cloudflare đổi thành http://127.0.0.1:8080.
Sau đó verify:
curl -i https://campuscore.tienson.io.vn/health
curl -i https://campuscore.tienson.io.vn/login
curl -i https://campuscore.tienson.io.vn/api/docs
curl -i https://campuscore.tienson.io.vn/api/v1/internal/auth-context/users/test
curl -i https://campuscore.tienson.io.vn/api/v1/health/readinessExpected:
/healthtrả200/logintrả200/api/docstrả200khi local overlay đang bật Swagger/api/v1/internal/*trả403/api/v1/health/readinesstrả403
Cloudflare accepting the domain is only DNS/proxy ownership. The app is reachable only when an origin is running behind the hostname route.
Common cases:
- Tunnel is healthy but the hostname does not load: check the tunnel detail
page under Published application routes. For the Docker connector, the
CampusCore route should point to
http://host.docker.internal:8080. Do not use the CIDR route or private hostname route for the public web app. A Cloudflare530response usually means the hostname is routed through Cloudflare but the tunnel connector or origin service is not currently reachable. - Cloudflare shows a catch-all
http_status:404rule: this is expected. It catches unmatched tunnel routes and does not block the published CampusCore hostname. - Root domain or
wwwdoes not resolve: only the hostname that has a published application route will work. If the route iscampuscore.tienson.io.vn, thentienson.io.vnandwww.tienson.io.vnstill need their own DNS/tunnel routes or a production ingress. - The site loads an older UI: the tunnel is forwarding correctly, but the
local origin is stale. Verify image tags with
kubectl -n campuscore get deploy -o wideor rerun the local deploy helper so the namespace uses the intended release image, for examplev1.4.1. - The local helper fails with “Kubernetes cluster is not reachable”: Docker
Desktop Kubernetes is down or
kubectlis pointed at another context. Start Docker Desktop, enable Kubernetes, and confirm the context before rerunningnode scripts/run-k8s-local-edge.mjs. - The page is slower through Cloudflare than local edge: a Tunnel from a home machine is constrained by the local computer, local network upload, and the connector process. This is acceptable for demo/staging; production traffic should use a stable origin such as cloud Kubernetes or a VPS.
- Cloudflare Zero Trust asks for a payment method: Zero Trust may request billing setup for account management even on a free plan. Do not paste payment details into repo docs or scripts. The tunnel token remains secret material and must stay in private environment configuration only.
Quick operator checks:
node scripts/run-container-inventory.mjs
node scripts/run-observability-smoke.mjs
curl -i https://campuscore.tienson.io.vn/health
curl -i https://campuscore.tienson.io.vn/viCopy một template ra private repo hoặc private folder ngoài repo public:
cp -R k8s/templates/private-operator/staging ../campuscore-private-k8s/staging
cp -R k8s/templates/private-operator/prod ../campuscore-private-k8s/prodSau đó điền các file sau:
| Giá trị thật | File cần sửa |
|---|---|
Hostname, ví dụ campuscore.example.com |
patch-ingress.yaml, patch-certificate.yaml, patch-configmap.yaml |
| TLS secret name | patch-ingress.yaml, patch-certificate.yaml |
| Ingress class nếu cluster yêu cầu | patch-ingress.yaml |
| Ingress annotations của controller/DNS | patch-ingress.yaml |
ClusterSecretStore thật |
patch-external-secret.yaml |
| Remote secret key/path thật | patch-external-secret.yaml |
ClusterIssuer thật |
patch-certificate.yaml |
| Replicas/resources/rolling update theo capacity | patch-runtime-overrides.yaml |
- Chuẩn bị cluster:
kubectl get nodes
kubectl get ingressclass
kubectl get svc -A- Cài hoặc xác nhận ingress controller đã có public address:
kubectl get svc -A | Select-String -Pattern "LoadBalancer|ingress"Trên Linux/macOS thay Select-String bằng grep nếu cần. Bạn cần một IP hoặc hostname public để Cloudflare trỏ tới.
- Copy private operator template phù hợp rồi sửa:
kubectl kustomize ../campuscore-private-k8s/staging
kubectl kustomize ../campuscore-private-k8s/staging/bootstrapVới prod:
kubectl kustomize ../campuscore-private-k8s/prod
kubectl kustomize ../campuscore-private-k8s/prod/bootstrap- Điền hostname thật trong private overlay:
patch-configmap.yaml:FRONTEND_URL=https://<your-hostname>patch-ingress.yaml:spec.rules[].host,spec.tls[].hosts[]patch-certificate.yaml:spec.dnsNames[]
- Tạo DNS record trong Cloudflare:
- dùng
A/AAAAnếu ingress controller có IP public, - dùng
CNAMEnếu ingress controller có DNS hostname, - bật proxy Cloudflare cho web traffic sau khi origin/TLS đã sẵn sàng,
- giữ mail/MX/non-HTTP records ở DNS-only nếu có.
- Cấu hình Cloudflare SSL/TLS:
- chọn Full (strict),
- origin phải phục vụ HTTPS trên
443, - certificate origin phải còn hạn và khớp hostname.
- Nếu dùng cert-manager DNS-01 với Cloudflare, tạo Cloudflare API Token có quyền tối thiểu:
Zone - DNS - EditZone - Zone - Read
Token này không đi vào repo. Lưu nó vào secret manager hoặc Kubernetes Secret của cert-manager namespace theo chính sách của cluster.
Ví dụ ClusterIssuer private:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-cloudflare-prod
spec:
acme:
email: admin@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-cloudflare-prod-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-tokenTrong private template, patch-certificate.yaml sẽ trỏ tới ClusterIssuer này.
- Điền secret runtime qua
ExternalSecret:
patch-external-secret.yaml phải bind đúng:
ClusterSecretStore- remote secret key/path thật
- các property bắt buộc:
POSTGRES_PASSWORD,JWT_SECRET,JWT_REFRESH_SECRET,HEALTH_READINESS_KEY,INTERNAL_SERVICE_TOKEN,RABBITMQ_PASSWORD,MINIO_USER,MINIO_PASSWORD
SMTP có thể để trống chỉ khi môi trường đó không gửi email thật.
- Apply runtime trước:
kubectl apply -k ../campuscore-private-k8s/staging
kubectl -n campuscore-staging get externalsecret,certificate,ingress
kubectl -n campuscore-staging rollout status deploy/campuscore-nginx
kubectl -n campuscore-staging get pods- Khi secret/TLS/runtime đã sẵn sàng, chạy bootstrap:
kubectl apply -k ../campuscore-private-k8s/staging/bootstrap
kubectl -n campuscore-staging get jobs- Verify public edge:
curl -i https://<your-hostname>/health
curl -i https://<your-hostname>/login
curl -i https://<your-hostname>/api/docs
curl -i https://<your-hostname>/api/v1/internal/auth-context/users/test
curl -i https://<your-hostname>/api/v1/health/readinessExpected:
/healthtrả200/logintrả200/api/docschỉ mở nếu môi trường cho phép Swagger/api/v1/internal/*trả403/api/v1/health/readinesstrả403
- Sau khi edge ổn, bật thêm Cloudflare WAF/rate limiting theo nhu cầu. Không cache các route auth/session/API mutating. Nếu muốn cache frontend static assets, chỉ làm theo rule rõ ràng cho static paths.
- Domain đã add vào Cloudflare và nameserver đã trỏ đúng.
- Cluster có ingress controller và public address.
- Copy
k8s/templates/private-operator/prodra private overlay. - Điền hostname thật, ingress class/annotations thật, TLS secret thật.
- Tạo Cloudflare DNS record tới ingress address.
- Cấu hình Cloudflare SSL/TLS Full (strict).
- Tạo Cloudflare API Token cho cert-manager nếu dùng DNS-01.
- Tạo/kiểm
ClusterSecretStorevà remote secret path. - Render private overlay và bootstrap.
- Apply runtime, chờ ExternalSecret/Certificate/Ingress/rollout.
- Apply bootstrap jobs.
- Verify health/login/docs/internal deny/readiness deny.
Thường là origin certificate không hợp lệ với Full (strict). Kiểm tra:
Certificatetrong namespace đãReady=Truechưa,- TLS secret name trong
Ingresscó khớpCertificate.spec.secretNamekhông, - DNS hostname có khớp
Certificate.spec.dnsNames[]không, - ingress controller có terminate TLS bằng đúng secret không.
Kiểm tra FRONTEND_URL, COOKIE_SECURE, TLS mode, và domain cookie. Với staging/prod qua HTTPS, COOKIE_SECURE phải là true.
Kiểm tra Cloudflare token có đủ Zone - DNS - Edit và Zone - Zone - Read, token đang nằm đúng secret namespace, và ClusterIssuer đang trỏ đúng apiTokenSecretRef.
Local path vẫn là:
node scripts/run-k8s-local-deploy.mjs
node scripts/run-k8s-local-edge.mjsLocal dùng ClusterIP + port-forward ở 127.0.0.1:8080. Cloudflare production dùng DNS/proxy/TLS trước Kubernetes Ingress. Hai đường này dùng chung k8s/base nhưng khác overlay.
- Cloudflare DNS records: https://developers.cloudflare.com/dns/manage-dns-records/
- Cloudflare Full (strict): https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/full-strict/
- cert-manager Cloudflare DNS-01: https://cert-manager.io/docs/configuration/acme/dns01/cloudflare/
- Cloudflare Tunnel on Kubernetes: https://developers.cloudflare.com/tunnel/deployment-guides/kubernetes/