Sổ tay vận hành — GIW POC Identity Platform

Trạng thái: POC · Ngày: 21/09/2026


1. Khởi động, dừng, làm lại từ đầu

cd poc/identity

docker compose up -d --build     # khởi động (build lần đầu ~3-5 phút)
docker compose ps                # tình trạng sức khoẻ mọi service
docker compose logs -f keycloak  # theo dõi một service
docker compose down              # dừng, giữ lại cơ sở dữ liệu
docker compose down -v           # dừng và xoá sạch — lần sau realm import lại

Build lại một service sau khi đổi code:

docker compose up -d --build entitlement-service

Đổi authenticator Java hoặc theme login thì phải build lại Keycloak:

docker compose build keycloak && docker compose up -d keycloak

2. Kiểm chứng

node tests/e2e.mjs                                    # 39 test
cd api && npx @redocly/cli lint giw-poc               # OpenAPI
cd api/collection && npx @usebruno/cli run "01 HR Verification" --env local \
  --env-var hrApiKey=giw-poc-hr-key-change-me

3. Truy vết một hành trình

Mọi request đều mang X-Correlation-ID. Để đi theo nó từ đầu tới cuối:

CORR=corr-<uuid>
for c in giw-wifi-portal giw-keycloak giw-mock-hr-api giw-entitlement giw-session; do
  echo "── $c"; docker logs $c 2>&1 | grep "$CORR"
done

Portal in correlation id lên màn hình Access Granted, nên hành khách có thể đọc nó cho nhân viên hỗ trợ.

4. Tiêm lỗi

# HR hết giờ
curl -X POST localhost:3001/admin/fault -H 'Content-Type: application/json' \
  -d '{"mode":"timeout","delayMs":4000}'

# HR trả 503
curl -X POST localhost:3001/admin/fault -H 'Content-Type: application/json' \
  -d '{"mode":"unavailable"}'

# về bình thường
curl -X POST localhost:3001/admin/fault -H 'Content-Type: application/json' \
  -d '{"mode":"none"}'

curl -s localhost:3001/health   # cho biết chế độ lỗi đang bật

Dừng hẳn một service để thử sự cố thật:

docker compose stop mock-hr-api    # luồng nhân viên phải fail closed
docker compose stop entitlement-service  # cả hai luồng phải fail closed
docker compose start mock-hr-api entitlement-service

5. Admin của Keycloak

http://localhost:8080 — admin / admin (lấy từ .env).

Việc cần làm Ở đâu
Xem luồng nhân viên Authentication → Flows → employee-browser
Đổi endpoint HR hoặc timeout Authentication → Flows → employee-browser → execution giw-employee-verify → biểu tượng bánh răng
Xác nhận flow binding Clients → wifi-portal-employee → Advanced → Authentication flow overrides → Browser Flow
Xem một nhân viên đã được tạo Users → tìm emp- → Attributes
Theo dõi event trực tiếp Realm settings → Sessions, và Events → User events
Kiểm tra các claim mapper Client scopes → giw-identity → Mappers

6. Xử lý sự cố

Triệu chứng Nguyên nhân Cách xử lý
dependency failed to start: container giw-keycloak is unhealthy Keycloak còn đang khởi động, hoặc health probe sai docker logs giw-keycloak. Health nằm ở cổng 9000, không phải 8080.
Token không có claim profile/email Một mảng clientScopes trong bản import realm đầy đủ thay thế các scope dựng sẵn của Keycloak Đừng đặt clientScopes vào realm-export.json. bootstrap tạo giw-identity qua Admin API chính vì lý do này.
Luồng nhân viên hiện form mật khẩu thông thường Ghi đè flow binding không có hiệu lực docker logs giw-bootstrap — tìm dòng flow.binding.set. Chạy lại bằng docker compose up -d --force-recreate bootstrap.
500 ở trang nhân viên Lỗi FreeMarker trong employee-verify.ftl `docker logs giw-keycloak
Entitlement trả UNAUTHORIZED dù token còn mới Lệch iss iss trong token là hostname hướng trình duyệt (KC_HOSTNAME), trong khi JWKS được lấy qua mạng container. OIDC_ISSUER và OIDC_JWKS_URI là hai biến riêng, có chủ đích.
ENTITLEMENT_DENIED · COMPANY_NOT_ELIGIBLE Đúng như thiết kế Công ty không nằm trong EMPLOYEE_COMPANIES.
Nhân viên xác thực được nhưng chỉ nhận BASIC Scope giw-identity chưa gắn vào client Client scopes → wifi-portal-employee → tab Client scopes.
RATE_LIMITED dù đăng nhập đúng 5 lần đoán sai trên hồ sơ nhân viên đó trong vòng 60 s Chờ, hoặc curl -X POST localhost:3001/admin/reset-limits (chỉ dành cho bộ test). Lần đăng nhập thành công không bao giờ bị tính.
DMN_UNAVAILABLE Bộ máy quyết định đang sập — fail closed theo thiết kế docker compose ps dmn-service, docker logs giw-dmn. POLICY_FALLBACK=local bật bảng dự phòng dựng sẵn.
Sửa luật không có tác dụng Bộ máy vẫn giữ model cũ curl -X POST localhost:3005/api/v1/decisions/reload, rồi GET /api/v1/decisions để xác nhận nó nạp file nào.
DMN_EVALUATION_ERROR File .dmn nạp được nhưng không đánh giá được Lỗi mô hình hoá, không phải một lần từ chối. docker logs giw-dmn cho thấy thông báo của engine.
IDENTITY_LINK_CONFLICT Hai user cùng một employee_ref Phải sửa dữ liệu thủ công. Luồng từ chối đoán.
Mất phiên portal sau khi restart Kho trong bộ nhớ Đúng như dự kiến. Đăng nhập lại.
Cổng đã bị chiếm 3000-3003, 5432, 8080, 9000 lsof -ti tcp:8080
Build Maven chậm hoặc lỗi Lần build đầu phải giải cây phụ thuộc SPI của Keycloak Cần truy cập mạng tới repo1.maven.org. Sau đó layer được cache.

7. Cái gì hỏng khi cái gì sập

Service sập Luồng khách hàng Luồng nhân viên
dmn-service fail closed — không có luật thì không có truy cập fail closed
mock-hr-api không ảnh hưởng fail closed ở bước xác minh
keycloak hỏng hỏng
entitlement-service xác thực xong rồi 502 — không có truy cập như trên
session-service cấp entitlement xong rồi 502 — không có truy cập như trên
postgres Keycloak hỏng Keycloak hỏng

Không đường nào cấp quyền truy cập khi một phụ thuộc đang sập. Đó là tính chất đáng kiểm lại sau mọi thay đổi.

8. Sức khoẻ hệ thống

for p in 3000 3001 3002 3003; do echo -n "$p: "; curl -s localhost:$p/health; echo; done
curl -s localhost:9000/health/ready    # Keycloak

9. Trước khi thứ này rời khỏi máy tính cá nhân

Chỉ thị đang có hiệu lực: nó không rời khỏi máy tính cá nhân. Không cloud, không môi trường test, không server dùng chung, không production, không hosting bên ngoài. Danh sách dưới đây là những gì một lần triển khai trong tương lai sẽ phải đáp ứng — không phải danh sách việc phải làm ngay bây giờ. Xem ENVIRONMENTS.md §3.

  • Thay toàn bộ secret trong .env
  • DEBUG_EXPOSE_TOKEN=0
  • Gỡ POST /admin/fault và POST /admin/reset-limits khỏi mock-hr-api và mock-loyalty-api
  • Gỡ các trang giới thiệu service (GET / trên cổng 3001–3003, 3005–3006) — chúng liệt kê toàn bộ bề mặt endpoint
  • Xác thực cho POST /api/v1/decisions/reload, và đặt việc sửa luật sau một quy trình duyệt
  • TLS: sslRequired: external, KC_HOSTNAME dạng https, cookie sid có Secure
  • Thay cả hai chặng X-API-Key bằng mTLS hoặc service token
  • Thay kho entitlement và session đang nằm trong bộ nhớ
  • Thêm giới hạn tần suất cho Entitlement và Session
  • Thu hẹp audience được chấp nhận — ngừng chấp nhận account
  • Thay Direct Grant bằng browser OIDC / federation — Mode B trong AUTH-STRATEGY.md. Direct Grant chỉ dành cho POC.
  • Chốt quyết định D7 (Bên kiểm soát / Bên xử lý) trước khi trỏ vào dữ liệu HR thật
  • Thu thập bằng chứng đồng ý cho đường CCCD, hoặc bỏ CCCD theo ADR-003

GIW POC Identity Platform · bản demo local · không phải production · sinh từ repository