L3 - Monitoring Platform - Agent - Enroll, Credentials và Config (Danh tính, cấu hình, CLI)
Thông tin tài liệu đầy đủ
| Trường | Giá trị |
|---|---|
| Tên trang | L3 - Monitoring Platform - Agent - Enroll, Credentials và Config |
| Trạng thái | BẢN NHÁP (tài liệu chưa sẵn sàng trình thẩm định) |
| Phiên bản | v0.1, 2026-09-30: bản nháp đầu tiên, dựng từ mã tại commit a23f929 (internal/enroll, internal/creds, internal/hostinfo, internal/config, internal/cli, phần nạp lại trong internal/agent/agent.go) và hợp đồng Agent - Collector ở docs/10-wire-contract.md. Phần internal/cli/cli.go đang bị sửa chưa commit bởi AGT-6 (lệnh service, uninstall), xem L2 D-13 |
| Tên dự án | Monitoring Platform (Access Hub Monitoring) |
| Bên thẩm định / Phê duyệt | chưa chỉ định. Chưa ai sign-off |
| Tài liệu tầng trên (Parent) | L2 - Monitoring Platform - Agent - SAD, thành phần CMP-6 Enroll và Credentials, CMP-7 Config Loader (mục 2.3) và phần nạp lại cấu hình, CLI của CMP-1 Runtime Core (L2 D-14). FR-01, FR-02, FR-03, FR-07, FR-14, FR-15, FR-16, FR-17, FR-20, FR-21, L2-NFR-06, 12, 13. Truy vết tiếp lên L1: L1 HLD mục 2 (Mục tiêu 5), mục 3 (T7) |
| Tài liệu anh em | L3 Collectors, L3 WAL và Sender, L3 Service và Packaging |
| Mục lục | 0 Governance, 1 Phạm vi, 2 Yêu cầu, 3 Kiến trúc, 4 Domain model, 5 Hợp đồng API, 6 Dữ liệu vật lý, 7 Thuật toán, 8 Xử lý lỗi, 9 Suy thoái, 10 Đồng thời, 11 Bảo mật, 12 Cấu hình, 13 Telemetry, 14 Kiểm thử, 15 Trình tự xây dựng, Phụ lục A, B, C |
Quy ước nhãn trạng thái (giống L2): ĐÃ HIỆN THỰC (đã xác minh trong mã), THIẾT KẾ, CHƯA XÂY (chỉ có trong tài liệu thiết kế), MỘT PHẦN, ĐỀ XUẤT. Khi mã và tài liệu khác nhau, mã thắng và sai lệch được nêu trong tài liệu này và gom về L2 mục 16.2. Mọi con số chưa có trong mã hoặc giao thức được gắn nhãn "đề xuất".
0. Front Matter & Approvals
Governance metadata
| Trường | Giá trị |
|---|---|
| Component | CMP-6 Enroll và Credentials (internal/enroll, internal/creds, internal/hostinfo), CMP-7 Config Loader (internal/config), cộng lớp CLI (internal/cli: enroll, check-config, collect-once, status, diag, khung lệnh, mã thoát) và đường nạp lại SIGHUP (internal/cli/run.go, Agent.Reload) |
| Truy vết L2 | L2-SAD-agent.md mục 2.3, 3 (FR-01 đến FR-03, FR-07, FR-14 đến FR-17, FR-20, FR-21), 4 (L2-NFR-06, 12, 13), 7, 16 (R-01, R-02, R-06, D-01, D-04, D-05, D-08, D-11, D-13, D-14) |
| Phân loại rủi ro | Đề xuất, chưa xác nhận: Tier 2, cao hơn mức kế thừa của L2 mục 14. Lý do: đây là nơi duy nhất agent giữ bí mật dài hạn (agent_token, License) và nơi quyết định agent nói chuyện với Collector nào (R-02, R-06) |
| Data classification | Bí mật cho License (dùng một lần, sống ngắn) và agent_token (dài hạn, văn bản rõ trong credentials.json). Nội bộ cho agent.yaml, hostname, IP, thông tin hệ điều hành, machine_id đã băm. machine_id thô không bao giờ rời máy |
| Blast radius | Một máy chủ. Lỗi nặng nhất: rò agent_token cho phép đẩy số liệu giả đúng cho máy đó (R-02). Enroll lỗi làm mất một License (token dùng một lần, phải xin lại). Cấu hình sai làm agent không khởi động (mã 3), không ảnh hưởng máy khác |
Sign-off gate
| Vai trò | Tên | Trách nhiệm duyệt | Trạng thái | Ngày |
|---|---|---|---|---|
| Tech lead thành phần | chưa chỉ định | Đúng đắn của luồng enroll, lưu thông tin đăng nhập, nạp cấu hình | Chưa duyệt | chưa có |
| SA hệ thống Agent | chưa chỉ định | Nhất quán với L2 và giao thức | Chưa duyệt | chưa có |
| Bảo mật | chưa chỉ định | Xử lý token, quyền tệp, thứ tự ưu tiên cấu hình, hiển thị bí mật | Chưa duyệt | chưa có |
| SA Access Hub | chưa chỉ định | Nhất quán quy tắc ràng buộc License với Server (422 binding_failed) | Chưa duyệt | chưa có |
1. Component Scope & Non-Goals
Vai trò: cụm này là danh tính và cấu hình cục bộ của agent. Nó gồm ba việc: (1) đổi License dùng một lần lấy agent_id và agent_token rồi lưu bền (mẫu trao đổi thông tin xác thực một lần có lưu nguyên tử), (2) nạp cấu hình YAML nghiêm ngặt theo thứ tự cờ, biến môi trường, tệp, mặc định (mẫu cấu hình phân lớp có kiểm tra toàn bộ lỗi một lượt), (3) cung cấp khung CLI và mã thoát ổn định cho người vận hành. Mọi thứ chạy cục bộ, chỉ enroll gọi mạng (một lần POST /agent/v1/enroll).
Sơ đồ ngữ cảnh
flowchart LR
classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff
classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff
classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff
classDef sensitive fill:#5a2d2d,stroke:#d96f6f,color:#fff
classDef infra fill:#444,stroke:#aaa,color:#fff
ADM(["Quản trị viên máy chủ"]):::infra
COLL(["Collector: /agent/v1/enroll"]):::infra
FS[("agent.yaml, credentials.json, license.token")]:::datastore
subgraph BC["Enroll, Credentials, Config"]
CLI["CLI và mã thoát"]:::owned
ENR["Enroll"]:::owned
CRD["Credentials Store"]:::sensitive
CFG["Config Loader"]:::owned
end
RT["Runtime: Run và Reload"]:::bc
ADM -->|"lệnh và tín hiệu"| CLI
CLI -->|"nạp cấu hình"| CFG
CLI -->|"enroll"| ENR
ENR -->|"POST enroll"| COLL
ENR -->|"lưu nguyên tử"| CRD
CRD -->|"tệp 0600"| FS
CFG -->|"đọc YAML"| FS
RT -->|"Load và Reload"| CFG
RT -->|"Load thông tin đăng nhập"| CRDMô tả quan hệ
| Chiều | Bên | Nội dung |
|---|---|---|
| Vào | Quản trị viên | Lệnh CLI, cờ (--token, --token-file, --token-stdin, --force, --config, --collector, --state-dir, --log-level), biến môi trường (AH_LICENSE, AH_LICENSE_FILE, AH_CONFIG, AH_COLLECTOR_URL, AH_STATE_DIR, AH_LOG_LEVEL, AH_INSECURE_SKIP_VERIFY), tệp agent.yaml, tệp license.token, tín hiệu SIGHUP và SIGUSR1 |
| Vào | Hệ điều hành | /etc/machine-id (hoặc /var/lib/dbus/machine-id), hostname, địa chỉ giao diện, /etc/os-release, /proc/sys/kernel/osrelease |
| Ra | Collector | Một POST /agent/v1/enroll (không có header Authorization). Phản hồi: agent_id, agent_token, collector_url, config, server_time_ms |
| Ra | Thư mục state_dir | credentials.json (0600, thư mục 0700) |
| Ra | Runtime | *config.Config (đã kiểm tra), *creds.Credentials, ErrNotEnrolled, mã thoát |
| Ra | Người vận hành | Thông điệp lỗi có số dòng, gợi ý xử lý theo mã lỗi enroll, mã thoát 0 đến 6 |
Trong phạm vi (bao phủ)
| Trong phạm vi | Ghi chú |
|---|---|
| Phân giải License theo nguồn và thứ tự ưu tiên, kiểm tra hợp lệ, xóa an toàn tệp token | internal/enroll/enroll.go. ĐÃ HIỆN THỰC |
Luồng enroll.Do: gom thông tin máy, gọi POST /enroll, lưu thông tin đăng nhập, chặn enroll lặp | internal/enroll/enroll.go. ĐÃ HIỆN THỰC |
Kho creds.Store: lưu nguyên tử 0600, nạp có kiểm tra quyền, che token, MachineID băm có muối | internal/creds/creds.go. ĐÃ HIỆN THỰC (Linux) |
| Gom thông tin máy: hostname, IP dùng được (tối đa 16), thông tin hệ điều hành | internal/hostinfo/hostinfo.go. ĐÃ HIỆN THỰC |
| Nạp cấu hình: mặc định, YAML nghiêm ngặt, biến môi trường, cờ, kiểm tra toàn bộ, báo số dòng | internal/config/load.go, types.go, validate.go, defaults.go. ĐÃ HIỆN THỰC |
Che thông tin nhạy cảm trong URL hiển thị (RedactURL) | internal/config/redact.go. ĐÃ HIỆN THỰC |
Khung CLI: bảng lệnh, cờ chung, mã thoát, trợ giúp, parse | internal/cli/cli.go. ĐÃ HIỆN THỰC |
Lệnh enroll, check-config, collect-once, version | internal/cli/enroll.go, checkconfig.go, collectonce.go, version.go. ĐÃ HIỆN THỰC |
| Nạp lại SIGHUP cho cấu hình cục bộ, giữ cấu hình cũ nếu lỗi | internal/cli/run.go, Agent.Reload. ĐÃ HIỆN THỰC. Chỉ cập nhật tham số ở L3 WAL và Sender FR-W16 |
Kiểm tra danh tính máy lúc chạy (identity_mismatch) | internal/agent/agent.go. ĐÃ HIỆN THỰC, mô tả chi tiết ở L3 WAL và Sender FR-W14. Tài liệu này mô tả phía machine_id và enroll --force |
status, diag | THIẾT KẾ, CHƯA XÂY. Hai lệnh là stub trả thông báo và mã 1 (L2 D-04, AGT-10) |
Cấu hình từ xa (GET /config theo ETag, allow_remote_config) | THIẾT KẾ, CHƯA XÂY (L2 D-01, AGT-10). Khóa allow_remote_config được đọc nhưng không dùng |
Xoay token POST /credentials/renew | ĐÃ HIỆN THỰC phía agent: internal/renew, creds.TokenHolder, transport.Client.Renew. Chờ Access Hub endpoint renew để chạy thật (L2 D-08) |
| Lưu token bằng DPAPI, kiểm tra ACL trên Windows | THIẾT KẾ, CHƯA XÂY (L2 D-05, AGT-7) |
Ngoài phạm vi
| Không thuộc BC | Thuộc về |
|---|---|
| Đóng lô, WAL, gửi, backoff, xử lý 401 và 403 khi chạy | L3 WAL và Sender |
Đọc /proc, danh sách trắng mẫu, cắt series | L3 Collectors |
Tạo tài khoản accesshub-agent, thư mục /etc/accesshub-agent, unit systemd, script gói gọi enroll, install.sh | L3 Service và Packaging |
| Phát hành, ràng buộc, thu hồi License, quy tắc khớp Server theo IP và hostname | Access Hub và Collector (quyết định ở Access Hub, giao thức mục 2.2) |
Vòng chu kỳ, dừng êm, log (logx), thống kê | Runtime Core (L2 D-14). Chỉ phần nạp lại và kiểm tra danh tính được nêu ở đây |
2. Detailed Requirements & Acceptance Criteria
Functional Requirements
| # | Trách nhiệm | Giải thích | Hiện thực ở |
|---|---|---|---|
| FR-E01 | Nguồn License | Ba cờ loại trừ nhau: --token, --token-file, --token-stdin. Nếu không có cờ, thử AH_LICENSE_FILE, rồi AH_LICENSE, rồi tệp mặc định license.token (nếu tồn tại). Nhiều cờ cùng lúc là lỗi. --token in cảnh báo vì lộ trong danh sách tiến trình | enroll.go (ResolveToken). ĐÃ HIỆN THỰC |
| FR-E02 | Kiểm tra token | Cắt khoảng trắng hai đầu, từ chối rỗng, dài quá MaxTokenLen (256), hoặc có khoảng trắng bên trong. Đọc stdin và tệp bị chặn bằng MaxTokenLen*4 byte | enroll.go (validated, fromFile). ĐÃ HIỆN THỰC |
| FR-E03 | Tệp token an toàn | Tệp token có quyền rộng hơn 0600 thì cảnh báo "run chmod 600". Tệp chỉ định tường minh không có thì lỗi, tệp mặc định không có thì coi như không có token. Sau enroll thành công, mọi tệp token đã dùng (từ --token-file, AH_LICENSE_FILE hoặc tệp mặc định) bị ghi đè số không, Sync, rồi xóa. Enroll bị từ chối hoặc lỗi thì tệp được giữ | enroll.go (fromFile, Wipe), cli/enroll.go. ĐÃ HIỆN THỰC |
| FR-E04 | Gom thông tin máy | Hostname, tối đa 16 IP unicast toàn cục (bỏ loopback, link-local, multicast, không xác định, loại trùng), hệ điều hành (family, name, version, kernel, arch) từ os-release và kernel, có dự phòng bằng runtime.GOOS và GOARCH | hostinfo.go (Gather, UsableIPs, ReadOS). ĐÃ HIỆN THỰC |
| FR-E05 | machine_id băm | sha256("accesshub-agent/machine-id/v1:" + machine-id) dạng hex. Không tìm được machine-id thì lỗi no machine id found. machine_id thô không rời máy | creds.go (MachineID). ĐÃ HIỆN THỰC |
| FR-E06 | Đổi token lấy danh tính | POST /agent/v1/enroll không có Authorization, mang license, hostname, ip_addresses, machine_id, agent_version, os. Nhận agent_id, agent_token. Lưu agent_id, agent_token, collector_url (của phản hồi), machine_id, enrolled_at | enroll.go (Do). ĐÃ HIỆN THỰC. Trường config của phản hồi bị bỏ qua (L2 D-01, D-11) |
| FR-E07 | Chặn enroll lặp | Đã có credentials.json và không có --force thì từ chối trước khi gọi mạng, nêu agent_id hiện có. Có --force thì enroll lại và ghi đè. Agent cũ không bị thu hồi phía Collector bởi lệnh này | enroll.go (Do, ErrAlreadyEnrolled). ĐÃ HIỆN THỰC |
| FR-E08 | Lưu thất bại vẫn báo đúng | Nếu Collector đã trả token nhưng ghi đĩa lỗi, trả *SaveError nói rõ token đã bị tiêu hao và cần token mới. Tệp token không bị xóa | enroll.go (SaveError), cli/enroll.go. ĐÃ HIỆN THỰC |
| FR-E09 | Ánh xạ lỗi enroll ra mã thoát | Thiếu hoặc sai token, cờ xung đột: mã 2. Collector từ chối (400, 401, 403, 409, 422, 426): mã 5 kèm gợi ý theo code (token_used, already_enrolled, binding_failed, 426). Collector lỗi khác hoặc lỗi mạng: mã 6. Đã enroll, lỗi ghi: mã 1. Cấu hình sai hoặc không dựng được client: mã 3 | cli/enroll.go (reportEnrollError). ĐÃ HIỆN THỰC |
| FR-E10 | Kho thông tin đăng nhập | Ghi credentials.json bằng tệp tạm .credentials-*.tmp cùng thư mục: Chmod 0600 trước khi ghi, Write, Sync, Close, Rename, rồi Sync thư mục. Thư mục MkdirAll 0700. Lỗi thì dọn tệp tạm và giữ tệp cũ | creds.go (Save). ĐÃ HIỆN THỰC |
| FR-E11 | Nạp có kiểm tra | Không có tệp: ErrNotEnrolled. Trên không phải Windows, quyền có bit 0o077: ErrInsecurePermissions. JSON hỏng hoặc thiếu agent_id hay agent_token: lỗi. Windows không kiểm tra ACL | creds.go (Load). ĐÃ HIỆN THỰC (Linux) |
| FR-E12 | Che token | Credentials.String() và LogValue() che token. Result của enroll không chứa token | creds.go, enroll.go. ĐÃ HIỆN THỰC |
| FR-E13 | Nạp cấu hình phân lớp | Thứ tự: Default(), giải mã YAML KnownFields(true), applyEnv (chỉ 4 biến), applyOverrides (3 cờ), Validate, rồi cắt / cuối của collector_url. Cờ thắng env thắng tệp thắng mặc định | load.go. ĐÃ HIỆN THỰC |
| FR-E14 | Đường dẫn cấu hình | Thứ tự: cờ --config, biến AH_CONFIG, mặc định theo nền tảng. Đường dẫn chỉ định tường minh mà tệp không có thì lỗi. Tệp mặc định không có thì dùng mặc định và env | load.go (ResolvePath), defaults.go. ĐÃ HIỆN THỰC |
| FR-E15 | Báo toàn bộ lỗi một lượt | Khóa lạ, sai kiểu, giá trị ngoài miền đều thành Issue có số dòng (khi có), gom hết rồi báo. Lỗi cú pháp YAML dừng trước env. Lỗi biến AH_INSECURE_SKIP_VERIFY không phải bool thành một issue | load.go, validate.go. ĐÃ HIỆN THỰC |
| FR-E16 | Miền giá trị cấu hình | collector_url bắt buộc, chỉ https, có host, không user info, query, fragment. interval 10 s đến 300 s. send_timeout 1 s đến 1 phút. collect_timeout 1 s đến 30 s và nhỏ hơn interval. buffer.max_bytes 4 MiB đến 1 GiB. buffer.max_age 1 phút đến 7 ngày. catch_up_batches 1 đến 10. max_series 1 đến 500. memory_limit 16 MiB đến 4 GiB. Mức log, định dạng log, regex (tối đa 200 ký tự, biên dịch được), checks (tối đa 50), labels (tối đa 20, khóa dành riêng bị cấm, giá trị tối đa 128 byte) | validate.go. ĐÃ HIỆN THỰC |
| FR-E17 | check-config | Nạp cấu hình, in config OK hoặc no config file ..., using defaults and environment, in tóm tắt (URL, thư mục state, chu kỳ, đệm, số series, log, proxy đã che). In WARNING nếu insecure_skip_verify bật. Không gọi mạng | cli/checkconfig.go. ĐÃ HIỆN THỰC. Giao thức nói lệnh này dùng ping, mã không làm (OQ-E2) |
| FR-E18 | collect-once | Thu một lần (hai mẫu cách nhau --gap, mặc định 1 s, 100 ms đến 1 phút) và in JSON (time, samples, dropped, errors, duration). Không gọi Collector, dùng URL giữ chỗ khi chưa cấu hình. Phần thu thuộc L3 Collectors | cli/collectonce.go. ĐÃ HIỆN THỰC |
| FR-E19 | Nạp lại SIGHUP | Khi nhận SIGHUP (Linux), nạp lại từ cùng Options, cập nhật mức log, gọi Agent.Reload. Lỗi bất kỳ thì ghi config reload rejected, keeping the running configuration và giữ cấu hình cũ. SIGUSR1 ghi log Status() | cli/run.go, signals_unix.go. ĐÃ HIỆN THỰC. Windows không có tín hiệu (signals_windows.go) |
| FR-E20 | Khung CLI | Bảng lệnh run, enroll, status, version, check-config, diag, service, uninstall, collect-once. Không có lệnh hoặc lệnh lạ: mã 2. Cờ sai hoặc tham số thừa: mã 2. -h: mã 0. Mã thoát 0 OK, 1 lỗi, 2 cách dùng, 3 cấu hình, 4 chưa enroll, 5 enroll bị từ chối, 6 không kết nối được | cli/cli.go. ĐÃ HIỆN THỰC. service và uninstall thuộc AGT-6, chưa commit |
| FR-E21 | run liên quan danh tính | run nạp cấu hình, dựng log, đặt GOMEMLIMIT nếu env chưa đặt, chạy Agent.Run. Chưa enroll: thông điệp this host is not enrolled, run 'accesshub-agent enroll' first và mã 4. Các lỗi khác của Run (kể cả ErrInsecurePermissions): ghi log và mã 1 | cli/run.go. ĐÃ HIỆN THỰC |
| FR-E22 | status, diag | Hiện là stub in not implemented yet (planned in AGT-10) và mã 1 | cli/cli.go (stub). THIẾT KẾ, CHƯA XÂY (L2 D-04) |
| FR-E23 | Cấu hình từ xa | Thăm dò GET /config mỗi 300 s với jitter ±20% và khi config_etag đổi, áp dụng khóa cho phép, allow_remote_config:false thì bỏ qua trừ min_agent_version, đổi collector_url từ xa chỉ từ giai đoạn 3 (ADR 0003) | THIẾT KẾ, CHƯA XÂY (L2 D-01, AGT-10). Mã: không gán OnConfigETag, không ghi remote-config.json |
| FR-E24 | Xoay token | POST /credentials/renew mỗi 30 ngày, token cũ còn hiệu lực thêm 24 giờ | ĐÃ HIỆN THỰC phía agent (internal/renew, cấu hình credentials.renew_after). Token mới ghi credentials.json (kèm token_issued_at) trước khi dùng; lỗi ghi thì giữ token cũ và thử lại sau 1 giờ (Access Hub cho phép thử lại bằng token cũ); 403 dừng; 503 và 429 thử lại theo Retry-After hoặc 1 giờ |
| FR-E25 | Bảo vệ token trên Windows | DPAPI và ACL | THIẾT KẾ, CHƯA XÂY (L2 D-05, AGT-7) |
Non-Functional Requirements
| NFR | Target (Ý nghĩa) | Parent L2-NFR (Kiểu) | Satisfied-by (Tactic → Mục) |
|---|---|---|---|
| NFR-E01 | Token (enroll và agent) không xuất hiện trong log, lỗi, String(), đầu ra CLI | L2-NFR-13 (Allocated) | LogValue che token, Result không có token, lỗi mạng chỉ mang URL (11). TestCredentialsNeverPrintTheToken, TestTokenNeverReachesTheLogs |
| NFR-E02 | credentials.json chỉ chủ sở hữu đọc được, không bao giờ ở trạng thái ghi dở | L2-NFR-12 và L2-NFR-07 (Allocated một phần) | Chmod 0600 trước ghi, tệp tạm rồi Rename (7.4.1). TestSaveLoadRoundTripWith0600, TestSaveIsAtomicAndLeavesNoTempFiles |
| NFR-E03 | Từ chối thông tin đăng nhập có quyền rộng trên Linux | L2-NFR-12 (Allocated) | Load kiểm mode & 0o077 (7.3). TestLoadRejectsLoosePermissionsCorruptAndIncomplete |
| NFR-E04 | Enroll không bao giờ làm mất token âm thầm: hoặc lưu được, hoặc báo rõ token đã tiêu hao | L2 FR-01 (Owned) | SaveError, không xóa tệp token khi lỗi (7.2 luồng 5). TestEnrollSaveFailureIsReportedAndKeepsTokenFile |
| NFR-E05 | Đầu vào token bị chặn kích thước, không đọc vô hạn | Kế thừa ANFR (bảo mật) (Owned) | LimitReader và kiểm MaxTokenLen*4 (11). TestResolveTokenErrors |
| NFR-E06 | Cấu hình sai không bao giờ làm agent chạy với cấu hình nửa vời: khởi động thì thoát mã 3, nạp lại thì giữ cấu hình cũ | L2 FR-14, FR-15 (Owned) | Validate trả lỗi trước khi dùng (7.2 luồng 7, 8). TestRunRejectsBadConfig, TestSignalsReloadAndStatus |
| NFR-E07 | Mã thoát ổn định để script gói và người vận hành dựa vào | L2 FR-16 (Owned) | Hằng Exit* (5.1). TestEnrollRejections, TestEnrollConnectionProblemsExitSix, TestRunWithoutCredentialsExitsNotEnrolled |
| NFR-E08 | Nạp cấu hình không panic với đầu vào bất kỳ | Kế thừa ANFR (độ tin cậy) (Owned) | FuzzLoadNeverPanics |
| NFR-E09 | collector_url bắt buộc HTTPS, không thông tin người dùng trong URL | L2-NFR-11 (Allocated một phần) | validate.go (11). Xác minh chứng chỉ thuộc L3 WAL và Sender |
| NFR-E10 | Khởi động sẵn sàng thu thập dưới 3 giây, phần cấu hình và danh tính đóng góp không đáng kể | L2-NFR-06 (Allocated một phần) | Chỉ đọc một tệp YAML và một credentials.json. Chưa có phép đo (L2 D-16) |
Acceptance Criteria
| AC | Kịch bản đặc tả (Given / When / Then) | Truy vết → Test ID |
|---|---|---|
| AC-E01 | Given nhiều nguồn token, When phân giải, Then thứ tự là cờ rồi AH_LICENSE_FILE rồi AH_LICENSE rồi tệp mặc định. Given hai cờ token cùng lúc, Then lỗi xung đột | TestResolveTokenPriority, TestLicenseSources |
| AC-E02 | Given token rỗng, quá dài, có khoảng trắng bên trong, hoặc không có nguồn, When phân giải, Then lỗi đúng loại và enroll thoát mã 2 | TestResolveTokenErrors, TestEnrollUsageErrors |
| AC-E03 | Given tệp token có quyền rộng, When phân giải, Then cảnh báo chmod 600 nhưng vẫn dùng | TestLoosePermissionsWarn |
| AC-E04 | Given enroll thành công bằng --token-file, When xong, Then credentials.json 0600 tồn tại và tệp token đã bị ghi đè và xóa | TestWipeOverwritesAndRemoves, TestEnrollFromFileStoresCredentialsAndWipesTheFile |
| AC-E05 | Given Collector trả 400, 401, 403, 409, 422 hoặc 426, When enroll, Then thông điệp "enrollment rejected" kèm gợi ý theo code và mã thoát 5 | TestEnrollRejections |
| AC-E06 | Given Collector 5xx hoặc không liên lạc được, When enroll, Then mã thoát 6 | TestEnrollConnectionProblemsExitSix |
| AC-E07 | Given đã enroll, When enroll không --force, Then từ chối trước khi gọi mạng. Given --force, Then enroll lại | TestEnrollRefusesWhenAlreadyEnrolledUnlessForced |
| AC-E08 | Given Collector trả token nhưng ghi đĩa lỗi, When enroll, Then báo token đã tiêu hao, mã 1, tệp token còn nguyên | TestEnrollSaveFailureIsReportedAndKeepsTokenFile |
| AC-E09 | Given cấu hình sai, When enroll, Then mã 3 và không gọi mạng | TestEnrollRejectsBadConfig |
| AC-E10 | Given Save rồi Load, When kiểm, Then dữ liệu khớp và quyền là 0600. Given lỗi ghi, Then tệp cũ còn nguyên, không còn tệp tạm | TestSaveLoadRoundTripWith0600, TestSaveIsAtomicAndLeavesNoTempFiles, TestFailedSaveKeepsOldFile |
| AC-E11 | Given tệp quyền rộng, hỏng, hoặc thiếu trường, When Load, Then từ chối với lỗi phân biệt | TestLoadRejectsLoosePermissionsCorruptAndIncomplete |
| AC-E12 | Given Credentials được in hoặc ghi log, When kiểm, Then token không xuất hiện | TestCredentialsNeverPrintTheToken, TestTokenNeverReachesTheLogs |
| AC-E13 | Given Delete và Exists, When gọi, Then xóa tệp không có cũng không lỗi | TestDeleteAndExists |
| AC-E14 | Given cùng machine-id, When tính, Then kết quả là băm có muối ổn định, khác machine-id thô | TestMachineIDIsSaltedHash |
| AC-E15 | Given giao diện mạng có địa chỉ đủ loại, When lọc, Then chỉ giữ unicast toàn cục, loại trùng, tối đa 16. Given thiếu os-release, Then dự phòng | TestUsableIPsFiltersAndCaps, TestReadOS, TestReadOSMissingFilesFallBack, TestGatherDoesNotPanic |
| AC-E16 | Given tệp tối thiểu, When nạp, Then áp đủ mặc định. Given mẫu đầy đủ trong docs, Then nạp đúng | TestLoadMinimalAppliesDefaults, TestLoadFullSampleFromDocs |
| AC-E17 | Given nhiều lỗi trong tệp, When nạp, Then mọi lỗi được báo một lượt, có số dòng và tên tệp | TestLoadErrorsCarryLineNumbers, TestLoadReportsAllIssues, TestErrorFormatIncludesFileAndLine |
| AC-E18 | Given tệp không có, When nạp, Then lỗi nếu chỉ định tường minh, không lỗi nếu mặc định. Given tệp rỗng, Then đòi collector_url | TestMissingFile, TestEmptyFileNeedsURL |
| AC-E19 | Given cờ, env, tệp cùng đặt một khóa, When nạp, Then cờ thắng env thắng tệp. Given AH_INSECURE_SKIP_VERIFY, Then phân tích bool | TestPrecedenceFlagsOverEnvOverFile, TestEnvInsecureSkipVerify, TestResolvePath |
| AC-E20 | Given kích thước và URL, When phân tích, Then ParseSize đúng và RedactURL che mật khẩu. Given đầu vào bất kỳ, Then không panic | TestParseSize, TestRedactURL, FuzzLoadNeverPanics |
| AC-E21 | Given check-config, When cấu hình hợp lệ, hỏng, dùng env, hoặc tệp tường minh không có, Then in kết quả và mã đúng | TestCheckConfigValid, TestCheckConfigReportsLineNumbers, TestCheckConfigUsesEnvConfigPathAndOverrides, TestCheckConfigMissingExplicitFile |
| AC-E22 | Given không đối số, lệnh lạ, cờ sai, When chạy, Then mã 2. Given -h, Then liệt kê đủ lệnh. Given version, Then in phiên bản | TestNoArgsAndUnknownCommandAreUsageErrors, TestFlagErrorsAreUsageErrors, TestHelpListsEveryCommand, TestVersionOutput |
| AC-E23 | Given status hoặc diag, When chạy, Then thông báo chưa hiện thực và mã khác 0 | TestStubsFailClearly |
| AC-E24 | Given run với cấu hình sai hoặc chưa enroll, When chạy, Then mã 3 hoặc mã 4. Given ngữ cảnh bị hủy, Then dừng | TestRunRejectsBadConfig, TestRunWithoutCredentialsExitsNotEnrolled, TestRunStopsOnContextCancel |
| AC-E25 | Given SIGHUP và SIGUSR1, When nhận, Then nạp lại cấu hình và ghi trạng thái | TestSignalsReloadAndStatus |
| AC-E26 | Given collect-once, When chạy, Then in JSON hợp lệ. Given --gap ngoài miền hoặc cấu hình sai, Then từ chối | TestCollectOncePrintsJSON, TestCollectOnceRejectsBadGapAndConfig |
Quality Attribute Scenarios
| Mã kịch bản / NFR | Nguồn & Kích thích | Môi trường | Phản hồi của hệ thống (Tactic) | Thước đo chất lượng (Measure) |
|---|---|---|---|---|
| QAS-E01 / NFR-E01 | Người vận hành chạy enroll --token hoặc có lỗi khi ghi log | Máy nhiều người dùng | Cảnh báo lộ trong danh sách tiến trình, token che ở String() và log, Result không có token | Không chuỗi token nào trong log và đầu ra. --token vẫn hiện ở ps (khuyến nghị dùng tệp hoặc stdin) |
| QAS-E02 / NFR-E02 | Mất điện đúng lúc ghi credentials.json | Enroll hoặc enroll lại | Tệp tạm rồi Rename cùng thư mục, Sync thư mục | Tệp cũ còn nguyên hoặc tệp mới đầy đủ. Không có tệp nửa chừng. Kiểm bằng TestFailedSaveKeepsOldFile (lỗi ghi, không mô phỏng mất điện) |
| QAS-E03 / NFR-E04 | Đĩa đầy hoặc thư mục state không ghi được sau khi Collector đã cấp token | Enroll | *SaveError, thông điệp nói token đã tiêu hao, giữ tệp token | Người vận hành biết phải xin token mới. Phía Collector token vẫn bị đánh dấu đã dùng |
| QAS-E04 / NFR-E06 | Quản trị viên sửa agent.yaml sai rồi gửi SIGHUP | Agent đang chạy | Nạp lại thất bại, ghi log, giữ cấu hình cũ | Agent không dừng, không đổi hành vi. Log chứa toàn bộ lỗi có số dòng |
| QAS-E05 / NFR-E03 | Ai đó chmod 644 credentials.json | Agent khởi động lại | Load trả ErrInsecurePermissions, run ghi log và thoát mã 1 | Agent không chạy với tệp token lộ quyền. Mã 1 chứ không phải mã 4 (OQ-E6) |
| QAS-E06 / NFR-E07 | Script gói gọi enroll bằng stdin | Cài đặt tự động | Mã thoát phân loại: 2 cách dùng, 3 cấu hình, 5 bị từ chối, 6 không kết nối | Script phân biệt được lỗi người dùng và lỗi mạng mà không đọc thông điệp |
3. Kiến trúc ứng dụng
3.1. Kiến trúc runtime
Cụm này không có tiến trình hay goroutine riêng. Nó là thư viện được gọi từ ba điểm vào trong cùng một tiến trình: lệnh enroll (chạy một lần rồi thoát), lệnh run (nạp cấu hình và thông tin đăng nhập lúc khởi động, sau đó nạp lại cấu hình khi có SIGHUP) và lệnh check-config hoặc collect-once (chỉ đọc). Chỉ có một goroutine nền liên quan: handleSignals trong internal/cli/run.go, nó gọi config.Load rồi Agent.Reload (con trỏ cấu hình nguyên tử).
flowchart LR
classDef bc fill:#1f3a5f,stroke:#4a90d9,color:#fff
classDef owned fill:#2d4a3e,stroke:#5fb37a,color:#fff
classDef datastore fill:#3a2d4a,stroke:#a06fd9,color:#fff
classDef sensitive fill:#5a2d2d,stroke:#d96f6f,color:#fff
classDef infra fill:#444,stroke:#aaa,color:#fff
CLI["Khung CLI · parse, mã thoát"]:::owned
ENR["Enroll · ResolveToken, Do, Wipe"]:::owned
CFG["Config Loader · Load, Validate"]:::owned
CRD["Credentials Store · Load, Save"]:::sensitive
HI["Hostinfo · Gather"]:::owned
FILES[("agent.yaml, credentials.json, license.token")]:::datastore
TRN["Transport Client · Enroll"]:::bc
COLL(["Collector · POST enroll"]):::infra
CLI -->|"Load"| CFG
CLI -->|"ResolveToken, Do"| ENR
ENR -->|"Gather, MachineID"| HI
ENR -->|"Enroll"| TRN
TRN -->|"HTTPS protobuf"| COLL
ENR -->|"Save"| CRD
CRD -->|"tệp 0600"| FILES
CFG -->|"đọc YAML"| FILES
CLI -.->|"đọc token, Wipe"| FILESChú giải: nét liền là lời gọi đồng bộ trong cùng goroutine (Transport là HTTP chặn tới send_timeout), nét đứt là thao tác tệp trực tiếp của lớp CLI. Lúc chạy thường (run), chỉ còn đường CLI → Config và Runtime → Credentials (xem L3 WAL và Sender).
Bảng connector
| Connector | Từ | Tới | Cơ chế | Đồng bộ | Ghi chú |
|---|---|---|---|---|---|
| CN-E1 | Lệnh CLI | Config Loader | config.Load(Options) | Đồng bộ | Mọi lệnh đọc cấu hình trừ version. Lỗi trả *config.Errors |
| CN-E2 | enroll | Enroll | ResolveToken(Source), Do(ctx, Params), Wipe(path) | Đồng bộ | Do là nơi duy nhất trong cụm này gọi mạng |
| CN-E3 | Enroll | Hostinfo | hostinfo.Gather() và creds.MachineID() | Đồng bộ | Đọc /etc/machine-id, os-release, giao diện mạng. cli/enroll.go gọi readMachineID trước khi dựng client |
| CN-E4 | Enroll | Transport | Client.Enroll(ctx, *EnrollRequest) | Đồng bộ, chặn tới send_timeout | Chi tiết ở L3 WAL và Sender. Không thử lại |
| CN-E5 | Enroll | Credentials Store | Store.Exists, Store.Load, Store.Save | Đồng bộ | Save chặn tới fsync xong |
| CN-E6 | Runtime | Credentials Store | creds.NewStore(cfg.StateDir).Load() | Đồng bộ | Mỗi lần Agent.Run bắt đầu. Không đọc lại khi chạy |
| CN-E7 | Handler tín hiệu | Config Loader | config.Load(opts) rồi log.SetLevel, Agent.Reload(cfg) | Đồng bộ trong goroutine handleSignals | Reload chỉ atomic.Store con trỏ cấu hình |
| CN-E8 | Runtime | Config (con trỏ nguyên tử) | a.cfg.Load() mỗi chu kỳ | Đồng bộ | Vòng chính áp interval, catch_up_batches, khóa truyền tải (L3 WAL và Sender FR-W16) |
3.2. Kiến trúc module
flowchart TB
classDef pub fill:#1f3a5f,stroke:#4a90d9,color:#fff
classDef intn fill:#3a3a3a,stroke:#888,color:#fff
classDef ext fill:#3a3a3a,stroke:#888,color:#fff
subgraph LA["Lớp lệnh: cli"]
CLIC["cli: commands, parse, Exit codes"]:::pub
CLIE["enroll.go, checkconfig.go, collectonce.go, run.go"]:::intn
end
subgraph LB["Lớp nghiệp vụ"]
ENRP["enroll: ResolveToken, Do, Wipe"]:::pub
CRDP["creds: Store, Credentials, MachineID"]:::pub
HIP["hostinfo: Gather, UsableIPs, ReadOS"]:::pub
end
subgraph LC["Lớp cấu hình"]
CFGP["config: Load, Validate, Default, Redact"]:::pub
CFGT["types.go, defaults.go, validate.go"]:::intn
end
EXT(["transport, agent, logx, version, collector engine"]):::ext
CLIC --> CLIE
CLIE --> ENRP
CLIE --> CFGP
ENRP --> CRDP
ENRP --> HIP
CRDP -.-> CFGP
CFGP --> CFGT
CLIE -.-> EXT
ENRP -.-> EXTChú giải: xanh dương là bề mặt công khai của gói, xám là nội bộ hoặc bên ngoài. Mũi tên liền là phụ thuộc biên dịch, mũi tên đứt là phụ thuộc vào gói dùng chung. creds không import config: đường nét đứt creds tới config chỉ biểu thị việc Store được dựng từ cfg.StateDir do lớp gọi truyền vào. config không phụ thuộc creds, enroll hay cli.
3.2.1. Cấu trúc: gói enroll, creds, hostinfo
| Thành phần | Tệp | Vai trò | Ghi chú |
|---|---|---|---|
Source, Resolved, ResolveToken | enroll/enroll.go | Chọn License theo nguồn và ưu tiên | Lỗi không chứa token. Resolved.File nhớ tệp để xóa sau |
Params, Result, Do | enroll/enroll.go | Một lần enroll: chặn lặp, gọi Collector, lưu | Result chỉ có AgentID, CollectorURL |
ErrNoToken, ErrConflictingSources, ErrInvalidToken, ErrAlreadyEnrolled, SaveError | enroll/enroll.go | Lỗi có kiểu để CLI ánh xạ mã thoát | SaveError bọc lỗi ghi và nói token đã tiêu hao |
Wipe(path) | enroll/enroll.go | Ghi đè số không, Sync, xóa | Lỗi ghi hoặc Sync thì vẫn cố xóa tệp (os.Remove) rồi trả lỗi |
Credentials, Store | creds/creds.go | Mô hình và kho credentials.json | Store{Path} dựng bằng NewStore(stateDir) |
ErrNotEnrolled, ErrInsecurePermissions | creds/creds.go | Lỗi có kiểu cho Load | agent.ErrNotEnrolled là bí danh của creds.ErrNotEnrolled |
MachineID(paths...) | creds/creds.go | Băm có muối của machine-id | Đường dẫn mặc định /etc/machine-id, /var/lib/dbus/machine-id |
Info, OS, Gather, UsableIPs, ReadOS | hostinfo/hostinfo.go | Thông tin máy để Collector ràng buộc Server | Thiếu mảnh nào thì để rỗng, không lỗi |
3.2.2. Cấu trúc: gói config
| Thành phần | Tệp | Vai trò | Ghi chú |
|---|---|---|---|
Config và các con (BufferConfig, LimitsConfig, LogConfig, MetricsConfig, Check) | config/types.go | Cấu hình hiệu lực, thẻ YAML | Duration và ByteSize có UnmarshalYAML riêng |
Default(), DefaultConfigPath(), DefaultStateDir(), DefaultLicensePath() | config/defaults.go | Giá trị và đường dẫn mặc định theo nền tảng | Linux: /etc/accesshub-agent/agent.yaml, /var/lib/accesshub-agent, /etc/accesshub-agent/license.token. Windows: dưới C:\ProgramData\AccessHubAgent |
Options, Overrides, ResolvePath, Load | config/load.go | Đường ống nạp phân lớp | Options.Explicit làm tệp thiếu thành lỗi |
Issue, Errors | config/load.go | Lỗi có số dòng, gom nhiều lỗi | Errors.Error() xuống dòng mỗi issue, dạng file:line: path: message |
Validate(cfg, lineIndex) | config/validate.go | Kiểm miền giá trị và quy tắc chéo | Trả nhiều Issue, không dừng ở lỗi đầu |
RedactURL | config/redact.go | Che mật khẩu trong URL hiển thị | Chỉ che mật khẩu, thay bằng xxxxx |
3.2.3. Cấu trúc: lớp cli
| Thành phần | Tệp | Vai trò | Ghi chú |
|---|---|---|---|
Env, Run/bảng commands() | cli/cli.go | Dựng môi trường (stdin, stdout, stderr, Getenv, Ctx) và phân phối lệnh | Env tiêm được trong kiểm thử |
commonFlags, parse, newFlagSet | cli/cli.go | Bốn cờ chung --config, --collector, --state-dir, --log-level | parse trả mã 2 khi sai, mã 0 khi -h |
Hằng Exit* | cli/cli.go | Mã thoát 0 đến 6 | Xem 5.1 |
enrollCmd, reportEnrollError, enrollHint | cli/enroll.go | Lệnh enroll | readMachineID là biến để kiểm thử thay |
checkConfigCmd, printSummary | cli/checkconfig.go | Lệnh check-config | Không gọi mạng |
collectOnceCmd | cli/collectonce.go | Lệnh collect-once | newEngine là biến để kiểm thử thay. placeholderURL thỏa bước kiểm tra cấu hình |
runCmd, handleSignals, reload | cli/run.go | Lệnh run, xử lý SIGHUP, SIGUSR1 | reloadSignal() và statusSignal() trả SIGHUP và SIGUSR1 trên Linux, nil trên Windows |
stub | cli/cli.go | status, diag | In not implemented yet (planned in AGT-10), mã 1 |
serviceCmd, uninstallCmd | cli/service.go | Lệnh service, uninstall | Thuộc L3 Service và Packaging (AGT-6, chưa commit) |
4. Domain model
Có hai nhóm khái niệm độc lập: danh tính (License, thông tin đăng nhập, thông tin máy) và cấu hình. Mỗi nhóm một sơ đồ.
classDiagram
namespace Vung_Danh_Tinh {
class Credentials {
<<Aggregate Root>>
+AgentID string
+AgentToken secret
+CollectorURL string
+MachineID hash
+EnrolledAt time
}
class CredentialsStore {
<<Repository>>
+Path state_dir_credentials_json
}
class License {
<<Value Object>>
+Token secret
+File optional_path
}
class HostInfo {
<<Value Object>>
+Hostname string
+IPs up_to_16
+OS family_name_version_kernel_arch
}
class EnrollResult {
<<Value Object>>
+AgentID string
+CollectorURL string
}
}
CredentialsStore --> Credentials : lưu và nạp
License ..> Credentials : đổi lấy
HostInfo ..> Credentials : gửi kèm để ràng buộc
Credentials ..> EnrollResult : rút gọn không có tokenclassDiagram
namespace Vung_Cau_Hinh {
class Config {
<<Aggregate Root>>
+CollectorURL https_only
+StateDir string
+Interval 10s_to_300s
+Buffer BufferConfig
+Limits LimitsConfig
+Log LogConfig
+Metrics MetricsConfig
+Checks up_to_50
+Labels up_to_20
}
class LoadOptions {
<<Value Object>>
+Path string
+Explicit bool
+Overrides three_flags
}
class ConfigErrors {
<<Value Object>>
+File string
+Issues list_of_Issue
}
class Issue {
<<Value Object>>
+Line int
+Path string
+Msg string
}
}
LoadOptions ..> Config : nạp ra
Config ..> ConfigErrors : hoặc trả lỗi
ConfigErrors *-- Issue : gồm nhiềuBảng thực thể
| Thực thể | Vai trò DDD | Định danh | Vòng đời | Ghi chú |
|---|---|---|---|---|
Credentials | Aggregate Root | agent_id (do Collector cấp) | Tạo khi enroll thành công, thay thế nguyên khối khi --force, không bao giờ sửa từng trường | Hiện không có trường hết hạn hay xoay. CollectorURL chỉ là dữ liệu lưu (D-11) |
CredentialsStore | Repository | Đường dẫn tệp | Sống suốt tiến trình | Load, Save, Exists, Delete. Ghi nguyên tử |
License | Value Object | Không (giá trị) | Sống trong bộ nhớ trong lệnh enroll. Tệp nguồn bị xóa sau thành công | Cắt khoảng trắng, tối đa 256 ký tự |
HostInfo | Value Object | Không | Tính lại mỗi lần enroll | Phía Collector dùng để khớp Server (quy tắc ở Access Hub) |
EnrollResult | Value Object | Không | Trả cho CLI để in | Không có token |
Config | Aggregate Root | Không (một bản hiệu lực mỗi tiến trình) | Tạo lúc khởi động, thay nguyên khối bằng Reload. Bất biến sau khi tạo | Con trỏ nguyên tử, không sửa tại chỗ |
LoadOptions | Value Object | Không | Dựng một lần ở cli, tái dùng cho mọi lần nạp lại | Explicit phân biệt tệp do người dùng chỉ định |
ConfigErrors, Issue | Value Object | Không | Sinh khi nạp lỗi | Mỗi issue có thể có số dòng |
Bất biến (Invariants)
| Mã | Bất biến | Thi hành bởi |
|---|---|---|
| INV-E1 | credentials.json hoặc không tồn tại, hoặc đầy đủ và quyền 0600. Không bao giờ ở trạng thái ghi dở | Store.Save (tệp tạm, Rename). TestSaveIsAtomicAndLeavesNoTempFiles, TestFailedSaveKeepsOldFile |
| INV-E2 | Token không bao giờ được in: String(), LogValue(), Result, lỗi enroll đều không chứa token | creds.go, enroll.go. TestCredentialsNeverPrintTheToken |
| INV-E3 | Tệp License chỉ bị xóa sau khi Store.Save thành công | cli/enroll.go (Wipe sau Do). TestEnrollSaveFailureIsReportedAndKeepsTokenFile |
| INV-E4 | machine_id lưu và gửi luôn là băm có muối, không bao giờ là machine-id thô | MachineID. TestMachineIDIsSaltedHash |
| INV-E5 | Load không bao giờ trả Credentials thiếu agent_id hoặc agent_token | Store.Load. TestLoadRejectsLoosePermissionsCorruptAndIncomplete |
| INV-E6 | Khi đã có thông tin đăng nhập và không có --force, enroll không gọi mạng | Do kiểm Store.Exists() trước Client.Enroll. TestEnrollRefusesWhenAlreadyEnrolledUnlessForced |
| INV-E7 | Mức ưu tiên cố định cờ > env > tệp > mặc định | Load. TestPrecedenceFlagsOverEnvOverFile |
| INV-E8 | Cấu hình không hợp lệ không bao giờ trở thành cấu hình đang chạy | Load trả nil, *Errors nếu có bất kỳ issue. reload giữ bản cũ. TestSignalsReloadAndStatus |
| INV-E9 | collector_url hiệu lực luôn là https, có host, không user info, không query, không fragment, không / cuối | Validate, TrimRight. TestLoadFullSampleFromDocs |
| INV-E10 | Nhãn cấu hình không thể đặt company_id, server_id, agent_id | Validate. Ranh giới tenant vẫn do Collector áp (L3 Collectors) |
5. API Contract Specification
Cụm này không mở cổng. Hợp đồng gồm: (1) giao diện dòng lệnh và tín hiệu cho người vận hành (mã thoát là hợp đồng), (2) một lời gọi HTTPS tới Collector (POST /agent/v1/enroll) và (3) hai định dạng tệp do cụm này sở hữu (credentials.json, agent.yaml).
5.1. Operations (Public API)
| # | Operation / Kênh Event | Loại hình | Hướng gọi (Caller → BC) | Ngữ nghĩa nghiệp vụ |
|---|---|---|---|---|
| 1 | accesshub-agent enroll [--token | --token-file | --token-stdin] [--force] | Lệnh CLI | Quản trị viên, script gói → CLI | Đổi License lấy danh tính và lưu. Một lần. Thoát 0, 1, 2, 3, 5, 6 |
| 2 | accesshub-agent check-config | Lệnh CLI | Quản trị viên → CLI | Nạp và kiểm tra cấu hình, in tóm tắt. Thoát 0, 2, 3. Không gọi mạng |
| 3 | accesshub-agent collect-once [--gap d] | Lệnh CLI | Quản trị viên → CLI | Thu một lần in JSON. Thoát 0, 1, 2, 3 |
| 4 | accesshub-agent run | Lệnh CLI | systemd, quản trị viên → CLI | Chạy agent. Thoát 0, 1, 3, 4. Chi tiết vòng chạy ở L3 WAL và Sender |
| 5 | accesshub-agent status, diag | Lệnh CLI | Quản trị viên → CLI | Stub, thoát 1. THIẾT KẾ, CHƯA XÂY (AGT-10) |
| 6 | accesshub-agent version | Lệnh CLI | Quản trị viên → CLI | In phiên bản, thoát 0 |
| 7 | Cờ chung --config, --collector, --state-dir, --log-level | Cờ CLI | Quản trị viên → CLI | Ghi đè env và tệp. Dùng bởi run, enroll, check-config, collect-once |
| 8 | SIGHUP | Tín hiệu OS | Quản trị viên, systemctl reload → tiến trình run | Nạp lại cấu hình cục bộ. Chỉ Linux |
| 9 | SIGUSR1 | Tín hiệu OS | Quản trị viên → tiến trình run | Ghi log Status(). Chỉ Linux |
| 10 | POST /agent/v1/enroll | HTTPS, protobuf | Transport → Collector | Đổi token. Không có header Authorization |
| 11 | GET /agent/v1/config, POST /agent/v1/credentials/renew | HTTPS | (chưa có người gọi) | THIẾT KẾ, CHƯA XÂY ở lớp cấu hình và danh tính (AGT-10). Client có GetConfig dùng cho thăm dò 401 (L3 WAL và Sender) |
| 12 | enroll.ResolveToken, enroll.Do, enroll.Wipe, creds.Store, config.Load, config.Validate, hostinfo.Gather | Hàm Go | cli, agent → gói | Giao diện trong tiến trình |
Mã thoát (hợp đồng ổn định)
| Mã | Tên | Khi nào |
|---|---|---|
| 0 | ExitOK | Thành công, hoặc -h |
| 1 | ExitError | Lỗi chung: ghi thông tin đăng nhập lỗi, đã enroll (không --force), không đọc được machine-id, Run lỗi, lệnh stub |
| 2 | ExitUsage | Không có lệnh, lệnh lạ, cờ sai, tham số thừa, không có token, cờ token xung đột, token sai dạng, --gap ngoài miền |
| 3 | ExitConfig | Cấu hình sai (khóa lạ, giá trị ngoài miền, tệp chỉ định không có), không dựng được client enroll |
| 4 | ExitNotEnrolled | run khi chưa có credentials.json |
| 5 | ExitEnrollRejected | Collector trả 400, 401, 403, 409, 422 hoặc 426 cho enroll |
| 6 | ExitNoConnection | enroll gặp lỗi mạng hoặc Collector trả mã khác (5xx và còn lại) |
5.2. Request / Response Schema
[POST /agent/v1/enroll]
| Field | Kiểu | Ghi chú | ||
|---|---|---|---|---|
| Req | X-AH-Proto, X-AH-Agent-Version, X-Request-Id, User-Agent | header | ! | Do Client.do thêm, giống mọi yêu cầu |
| Req | Authorization | header | Không gửi (chưa có token) | |
| Req | Content-Type | header | ! | application/x-protobuf |
| Req | EnrollRequest.license | string | ! | Đã kiểm tra dạng, tối đa 256 ký tự |
| Req | EnrollRequest.hostname | string | ! | Từ os.Hostname |
| Req | EnrollRequest.ip_addresses[] | string[] | ? | IP unicast toàn cục, tối đa 16, loại trùng |
| Req | EnrollRequest.machine_id | string | ! | Băm có muối, không phải machine-id thô |
| Req | EnrollRequest.agent_version | string | ! | version.Get().Version |
| Req | EnrollRequest.os{family, name, version, kernel, arch} | OsInfo | ! | Dự phòng bằng runtime.GOOS, runtime.GOARCH |
| Res | EnrollResponse.agent_id | string | ! | Thiếu thì client từ chối phản hồi |
| Res | EnrollResponse.agent_token | string | ! | Hiển thị đúng một lần. Thiếu thì client từ chối. Lưu ở credentials.json |
| Res | EnrollResponse.collector_url | string | ? | Lưu, không dùng (D-11) |
| Res | EnrollResponse.config | AgentConfig | ? | Bỏ qua (D-01, D-11) |
| Res | EnrollResponse.server_time_ms | int64 | ? | Bỏ qua ở enroll |
| Res | Lỗi | JSON hoặc protobuf | code, message. Thông điệp cắt 200 ký tự (APIError) |
[credentials.json] (tệp do cụm này sở hữu)
| Field | Kiểu | Bắt buộc | Ghi chú |
|---|---|---|---|
agent_id | string | ! | Không được rỗng khi nạp |
agent_token | string | ! | Không được rỗng khi nạp. Văn bản rõ (R-02) |
collector_url | string | ? | omitempty. Không dùng lúc chạy (D-11) |
machine_id | string | ? | Băm. Rỗng thì bỏ qua kiểm tra danh tính ở Run |
enrolled_at | RFC 3339 UTC | ! | Thông tin, không dùng để quyết định |
[agent.yaml] (tệp do cụm này sở hữu, khóa cấp cao)
| Khóa | Kiểu | Mặc định | Ghi chú |
|---|---|---|---|
collector_url | URL https | (bắt buộc) | Có thể đến từ env hoặc cờ. Tệp rỗng thì đòi khóa này |
ca_file, insecure_skip_verify, proxy_url | chuỗi, bool, URL http(s) | rỗng, false, rỗng | Truyền tải, dùng ở L3 WAL và Sender |
state_dir | đường dẫn | theo nền tảng | Không được rỗng |
interval, send_timeout, collect_timeout | thời lượng | 30 s, 15 s, 5 s | Miền ở 12.1 |
allow_remote_config | bool | true | Được đọc, không dùng (D-01) |
checks_allow_link_local | bool | false | Được đọc, không dùng (L2 D-02) |
buffer{max_bytes, max_age, catch_up_batches} | kích thước, thời lượng, số | 50 MiB, 24 h, 2 | L3 WAL và Sender |
limits{max_series, memory_limit} | số, kích thước | 500, 64 MiB | L3 Collectors và Runtime |
log{level, format, file} | chuỗi | info, text, rỗng | level đổi được khi SIGHUP |
metrics{cpu, memory, disk, net, uptime} | đối tượng | tất cả bật | L3 Collectors |
checks[] | danh sách | rỗng | Được kiểm tra, không có bộ thu (L2 D-02) |
labels | bảng chuỗi | rỗng | Tối đa 20 |
5.3. Error Codes
Lỗi enroll từ Collector được phân loại theo trạng thái HTTP rồi theo code (do Access Hub quyết định, giao thức mục 2.2). Mã code dưới đây chỉ gồm những mã mà enrollHint xử lý riêng.
| Trạng thái | code | Ý nghĩa | Gợi ý CLI | Mã thoát |
|---|---|---|---|---|
| 409 | token_used | License đã dùng | "request a new one" | 5 |
| 409 | already_enrolled | Máy đã enroll ở Collector | "ask an administrator to reset it" | 5 |
| 422 | binding_failed | Không khớp Server theo hostname và IP | "check hostname and IP addresses" | 5 |
| 426 | (không phân biệt) | Phiên bản agent quá cũ | "upgrade the agent" | 5 |
| 400, 401, 403 | (khác) | Yêu cầu hoặc token không hợp lệ, bị cấm | thông điệp gốc của Collector | 5 |
| 5xx và còn lại | bất kỳ | Collector không khả dụng | collector unavailable: ... | 6 |
| (không có phản hồi) | Lỗi mạng, TLS, hết thời gian | cannot reach the collector: ... | 6 |
Lỗi cục bộ: ErrNoToken, ErrConflictingSources, ErrInvalidToken thoát 2. ErrAlreadyEnrolled và *SaveError thoát 1. Lỗi cấu hình thoát 3, mỗi issue một dòng file:line: path: message. ErrNotEnrolled ở run thoát 4.
5.4. Versioning
Mã thoát, tên cờ và tên biến môi trường là hợp đồng với script gói (postinst, install.sh, L3 Service và Packaging). Mã hiện không có số phiên bản hợp đồng CLI và không có deprecation policy (OQ-E9). credentials.json không có trường phiên bản định dạng (OQ-E7). agent.yaml không có trường version, khóa lạ bị từ chối (KnownFields(true)) nên nâng cấp agent thêm khóa mới thì tệp của phiên bản mới không chạy được trên agent cũ (hệ quả của chế độ nghiêm ngặt). Giao thức /agent/v1 và X-AH-Proto: 1 xem L3 WAL và Sender.
5.5. Authz - shared responsibility & permission matrix
Cụm này không phân quyền người dùng ứng dụng. Kiểm soát truy cập là quyền hệ điều hành lên tệp và quyền chạy lệnh.
Phân định trách nhiệm Auth
| Lớp | Ai sở hữu | Gồm |
|---|---|---|
| Phát hành và ràng buộc License | Access Hub, Collector | Cấp token dùng một lần, quy tắc khớp Server theo IP và hostname |
| Chuyển token tới máy | Quản trị viên | Ngoài băng. Cụm này chỉ chọn nguồn an toàn (tệp, stdin) |
Lưu agent_token | Cụm này | credentials.json 0600, thư mục 0700, từ chối quyền rộng |
Tài khoản sở hữu tệp, quyền thư mục /etc/accesshub-agent | L3 Service và Packaging | Tài khoản accesshub-agent, unit systemd |
Quyền gọi enroll, run | Hệ điều hành | Người chạy phải ghi được state_dir (thường accesshub-agent hoặc root lúc cài). Không có kiểm tra ngoài quyền tệp |
Permission matrix
| Public API (5.1) | quản trị viên (root hoặc accesshub-agent) | người dùng thường |
|---|---|---|
1 enroll | Được, nếu ghi được state_dir và đọc được tệp token. Nếu chạy bằng root, credentials.json thuộc root với quyền 0600 nên dịch vụ chạy bằng accesshub-agent không đọc được (OQ-E5). Thông báo của service install dặn chạy enroll bằng người dùng accesshub-agent | Không ghi được state_dir mặc định nên SaveError sau khi đã tiêu hao token (OQ-E5) |
2, 3, 6 (check-config, collect-once, version) | Được | Được, nếu đọc được agent.yaml (gói cài 0640 root:accesshub-agent nên cần là root hoặc thuộc nhóm dịch vụ, xem L3 Service và Packaging) |
4 run | Được | Không đọc được credentials.json 0600 của người khác |
| 8, 9 (SIGHUP, SIGUSR1) | Được (cùng người dùng hoặc root) | Không (hệ điều hành chặn) |
10 POST enroll | Không áp dụng (người gọi là tiến trình) | Không áp dụng |
6. Data Schema (Physical)
Cụm này ghi đĩa đúng một tệp (state_dir/credentials.json) và xóa tối đa một tệp (tệp License). Nó đọc agent.yaml, /etc/machine-id, os-release. Mục 6.1 mô tả bố cục tệp thay cho lược đồ cơ sở dữ liệu.
6.1. Cài đặt vật lý
Bảng ánh xạ
| Phân hệ (3.2) | Aggregate (4) | Store | Cấu trúc vật lý cốt lõi |
|---|---|---|---|
creds | Credentials | Tệp state_dir/credentials.json (mặc định /var/lib/accesshub-agent/credentials.json) | JSON một đối tượng, quyền 0600, thư mục 0700. Tệp tạm .credentials-*.tmp cùng thư mục trong lúc ghi |
enroll | License | Tệp license.token (mặc định /etc/accesshub-agent/license.token) hoặc đường dẫn do người vận hành chọn | Văn bản một dòng. Bị ghi đè số không rồi xóa sau khi enroll thành công |
config | Config | Tệp agent.yaml (mặc định /etc/accesshub-agent/agent.yaml) | YAML. Chỉ đọc. Cụm này không bao giờ ghi |
hostinfo, creds | HostInfo, machine id | /etc/machine-id (dự phòng /var/lib/dbus/machine-id), /etc/os-release, /proc/sys/kernel/osrelease | Chỉ đọc |
Lược đồ (tệp và quyền)
classDiagram
namespace Thu_Muc_State {
class stateDir {
path state_dir
mode 0700
}
class credentialsFile {
name credentials_json
mode 0600
fields agent_id_agent_token_collector_url_machine_id_enrolled_at
}
class credentialsTmp {
name dot_credentials_star_tmp
mode 0600_before_write
}
class walDir {
name wal
owner L3_WAL_and_Sender
}
}
namespace Thu_Muc_Cau_Hinh {
class etcDir {
path etc_accesshub_agent
owner L3_Service_and_Packaging
}
class agentYaml {
name agent_yaml
access read_only
}
class licenseFile {
name license
mode 0600_recommended
lifetime deleted_after_enroll
}
}
stateDir *-- credentialsFile : chứa một
stateDir *-- walDir : chứa một
credentialsFile ..> credentialsTmp : ghi qua rồi đổi tên
etcDir *-- agentYaml : chứa
etcDir *-- licenseFile : chứa tạmGhi chú lược đồ: tệp tạm được tạo bằng CreateTemp (quyền mặc định 0600 của Go) và Chmod 0600 trước khi ghi nội dung. Rename trong cùng thư mục nên nguyên tử trên hệ tệp POSIX, sau đó syncDir ghi thư mục xuống đĩa (lỗi syncDir bị bỏ qua, nên sau mất điện đúng lúc đó tệp có thể quay về bản cũ, đã nêu ở OQ-E8). Không có khóa ngoại. Quyền 0600 bắt buộc khi nạp trên Linux, không kiểm trên Windows.
6.2. Phân loại dữ liệu & retention
| Phần tử / Trường dữ liệu | Phân lớp dữ liệu | Thời hạn lưu trữ (Retention) | Cơ chế bảo vệ kỹ thuật |
|---|---|---|---|
agent_token (trong credentials.json) | Bí mật | Đến khi ghi đè bằng enroll --force, hoặc gỡ cài đặt (uninstall --purge, xem L3 Service và Packaging). Không có hết hạn hay xoay (AGT-10) | 0600, thư mục 0700, ghi nguyên tử, từ chối quyền rộng, che ở log. Không mã hóa ở trạng thái nghỉ (R-02) |
agent_id, enrolled_at, collector_url | Nội bộ | Như trên | 0600 cùng tệp |
machine_id (băm) | Nội bộ | Như trên | Băm có muối, không đảo ngược được về machine-id thô |
| License (tệp hoặc env) | Bí mật | Sống ngắn. Tệp bị ghi đè số không và xóa sau enroll thành công. Nếu enroll thất bại, tệp còn (có thể đã hết hiệu lực) | Chọn nguồn an toàn (tệp, stdin), cảnh báo --token, kiểm quyền 0600. Env AH_LICENSE tồn tại suốt đời tiến trình và hiển thị ở /proc/<pid>/environ cho cùng người dùng và root |
agent.yaml | Nội bộ (có thể chứa proxy_url với mật khẩu) | Do người vận hành quản lý | RedactURL che mật khẩu proxy khi in. Quyền tệp do gói đặt (L3 Service và Packaging) |
hostname, ip_addresses, thông tin OS (gửi lúc enroll) | Nội bộ | Do Collector quyết định | TLS. Không lưu cục bộ ngoài bộ nhớ trong lệnh enroll |
7. Thuật toán & Luồng nghiệp vụ
7.1. Luồng nghiệp vụ chính (happy path)
Luồng 1: enroll thành công bằng --token-stdin (kiểu script gói)
sequenceDiagram
participant OP as Người vận hành hoặc postinstall
participant CLI as enrollCmd
participant CFG as config.Load
participant ENR as enroll
participant COL as Collector
participant STO as creds.Store
OP->>CLI: accesshub-agent enroll --token-stdin
CLI->>CFG: Load(Options)
CFG-->>CLI: Config đã kiểm
CLI->>ENR: ResolveToken(Source)
ENR-->>CLI: Resolved gồm token (File rỗng vì đọc từ stdin)
CLI->>ENR: Do(Params gồm MachineID và hostinfo)
ENR->>COL: POST /agent/v1/enroll (protobuf, không Authorization)
COL-->>ENR: 200 gồm agent_id và agent_token
ENR->>STO: Save(Credentials)
STO-->>ENR: ghi nguyên tử 0600
ENR-->>CLI: Result gồm agent_id, không có token
CLI-->>OP: in enrolled as agent id, mã thoát 0Thứ tự là bất biến: kiểm cấu hình trước, rồi phân giải token, rồi mới gọi mạng (TestEnrollRejectsBadConfig khẳng định cấu hình sai không gây lời gọi mạng). Nếu nguồn token là tệp (Resolved.File khác rỗng) thì sau khi Do thành công, enroll.Wipe ghi đè bằng số không rồi xóa tệp (TestEnrollFromFileStoresCredentialsAndWipesTheFile). Lỗi xóa tệp chỉ thành cảnh báo, mã thoát vẫn 0.
Luồng 2: run khởi động, nạp cấu hình và danh tính
sequenceDiagram
participant SYS as systemd hoặc người vận hành
participant RUN as runCmd
participant CFG as config.Load
participant AG as Agent.Run
participant STO as creds.Store
participant MID as creds.MachineID
SYS->>RUN: accesshub-agent run
RUN->>CFG: Load(Options)
CFG-->>RUN: Config
Note over RUN: dựng logger, đặt GOMEMLIMIT nếu biến môi trường chưa đặt
RUN->>AG: New(cfg) rồi Run(ctx)
AG->>STO: Load()
STO-->>AG: Credentials (đã qua kiểm quyền và kiểm đủ trường)
Note over AG: log agent started, creds được che token
AG->>MID: MachineID()
MID-->>AG: băm hiện tại khớp băm lúc enroll
Note over AG: dựng engine, client, hàng đợi rồi vào vòng thu và gửirunCmd nạp cấu hình một lần trước khi tạo Agent. Agent.Run nạp credentials.json đúng một lần (token nằm trong Sender.Token tới khi tiến trình thoát) nên sửa tệp lúc đang chạy không có tác dụng cho tới lần khởi động lại.
Luồng 3: SIGHUP nạp lại cấu hình thành công
sequenceDiagram
participant OP as Quản trị viên
participant SIG as handleSignals
participant CFG as config.Load
participant LOG as logx.Logger
participant AG as Agent
participant RUN as Vòng chính
OP->>SIG: SIGHUP (systemctl reload hoặc kill)
SIG->>CFG: Load(cùng Options lúc khởi động)
CFG-->>SIG: Config mới đã kiểm
SIG->>LOG: SetLevel(log.level)
SIG->>AG: Reload(cfg)
Note over AG: atomic.Store con trỏ cấu hình
SIG->>LOG: Info config reloaded
RUN->>AG: đầu chu kỳ kế đọc con trỏ mới
Note over RUN: áp dụng interval, catch_up_batches, và dựng lại client nếu transportKey đổiViệc nạp lại dùng chính Options của lúc khởi động, gồm cả ba cờ ghi đè (--collector, --state-dir, --log-level) nên cờ vẫn thắng tệp sau khi nạp lại. Bảng các khóa có hiệu lực ở 7.4.5.
7.2. Luồng thay thế và lỗi
Luồng 4: Collector từ chối enroll (400, 401, 403, 409, 422, 426)
sequenceDiagram
participant CLI as enrollCmd
participant ENR as enroll.Do
participant COL as Collector
participant OP as Người vận hành
CLI->>ENR: Do(Params)
ENR->>COL: POST /agent/v1/enroll
COL-->>ENR: 4xx kèm code (token_used, already_enrolled, binding_failed hoặc khác)
ENR-->>CLI: lỗi APIError
Note over CLI: reportEnrollError chọn gợi ý theo code, riêng 426 nhắc nâng cấp agent
CLI-->>OP: enrollment rejected kèm gợi ý, mã thoát 5
Note over CLI: tệp token không bị xóa, credentials.json không được tạoĐây là đường bình thường của token hết hạn hoặc đã dùng. Vì tệp token chỉ bị xóa khi thành công, người vận hành có thể thử lại sau khi sửa nguyên nhân (ví dụ sửa hostname cho binding_failed). Nếu token thực sự đã tiêu hao thì phải xin token mới.
Luồng 5: Collector đã cấp token nhưng ghi đĩa lỗi (SaveError)
sequenceDiagram
participant CLI as enrollCmd
participant ENR as enroll.Do
participant COL as Collector
participant STO as creds.Store
participant OP as Người vận hành
ENR->>COL: POST /agent/v1/enroll
COL-->>ENR: 200 gồm agent_token (token enroll đã bị tiêu hao)
ENR->>STO: Save(Credentials)
STO-->>ENR: lỗi (đĩa đầy, thư mục chỉ đọc hoặc không đủ quyền)
ENR-->>CLI: SaveError bọc lỗi gốc
CLI-->>OP: enrolled but the credentials could not be saved, token is now spent
Note over CLI: mã thoát 1, tệp token được giữ nguyên, không có credentials.json mớiĐây là nhánh nguy hiểm nhất của enroll: phía Collector đã ghi nhận agent nhưng máy không giữ được danh tính. Thông điệp nói thẳng rằng cần token mới (NFR-E04). Đường khắc phục thủ công: sửa quyền hoặc dung lượng của state_dir, xin token mới, enroll lại (Collector có thể trả already_enrolled nếu chưa đặt lại bản ghi, xem OQ-E10).
Luồng 6: enroll lặp bị chặn trước khi gọi mạng
sequenceDiagram
participant CLI as enrollCmd
participant ENR as enroll.Do
participant STO as creds.Store
participant OP as Người vận hành
CLI->>ENR: Do(Params, Force=false)
ENR->>STO: Exists()
STO-->>ENR: true
ENR->>STO: Load()
STO-->>ENR: Credentials (hoặc lỗi nếu tệp hỏng)
ENR-->>CLI: ErrAlreadyEnrolled kèm agent_id hiện có
CLI-->>OP: this host is already enrolled as agent, use --force, mã thoát 1
Note over ENR: không có lời gọi mạng nào xảy raVới --force, Do bỏ qua nhánh này và ghi đè credentials.json. Cờ này không thu hồi token cũ ở Collector (xem OQ-E4).
Luồng 7: cấu hình sai lúc khởi động
sequenceDiagram
participant SYS as systemd hoặc người vận hành
participant RUN as runCmd
participant CFG as config.Load
participant ERR as stderr
SYS->>RUN: accesshub-agent run
RUN->>CFG: Load(Options)
CFG->>CFG: giải mã YAML strict, applyEnv, applyOverrides, Validate
CFG-->>RUN: Errors gồm nhiều Issue
RUN->>ERR: mỗi dòng theo dạng tệp, số dòng, đường khóa, thông điệp
RUN-->>SYS: mã thoát 3
Note over SYS: không có logger nên lỗi ra stderr, systemd ghi vào journalHai điểm cần nhớ. Một: lỗi cú pháp hoặc khóa lạ của YAML dừng ngay, không chạy tiếp applyEnv và Validate, nên người dùng có thể phải sửa hai vòng (FR-E15). Hai: check-config đi đúng đường này nên là công cụ kiểm trước khi khởi động lại (TestCheckConfigReportsLineNumbers).
Luồng 8: SIGHUP bị từ chối, giữ cấu hình đang chạy
sequenceDiagram
participant OP as Quản trị viên
participant SIG as handleSignals
participant CFG as config.Load
participant LOG as logx.Logger
participant AG as Agent
OP->>SIG: SIGHUP sau khi sửa agent.yaml sai
SIG->>CFG: Load(Options)
CFG-->>SIG: Errors
SIG->>LOG: Error config reload rejected, keeping the running configuration
Note over AG: con trỏ cấu hình không đổi, agent tiếp tục gửi bình thườngCó một nhánh từ chối thứ hai: Load thành công nhưng SetLevel lỗi thì log config reload rejected và không gọi Reload (reload trong internal/cli/run.go). Hiện không có kiểm thử riêng cho nhánh từ chối của reload (chỉ TestSignalsReloadAndStatus kiểm nhánh thành công và SIGUSR1), ghi ở OQ-E11.
Luồng 9: run khi chưa enroll hoặc tệp danh tính không dùng được
sequenceDiagram
participant RUN as runCmd
participant AG as Agent.Run
participant STO as creds.Store
participant OP as Người vận hành
RUN->>AG: Run(ctx)
AG->>STO: Load()
alt không có credentials.json
STO-->>AG: ErrNotEnrolled
AG-->>RUN: ErrNotEnrolled
RUN-->>OP: this host is not enrolled, run enroll first, mã thoát 4
else quyền rộng, JSON hỏng hoặc thiếu trường
STO-->>AG: ErrInsecurePermissions hoặc lỗi hỏng hoặc thiếu trường
AG-->>RUN: lỗi
RUN-->>OP: log agent failed, mã thoát 1
endQuyền rộng cho mã 1 chứ không phải mã 4 (OQ-E6): script cần phân biệt "chưa enroll" với "tệp hỏng" phải đọc thông điệp. Kiểm thử: TestRunWithoutCredentialsExitsNotEnrolled, TestRunWithoutCredentialsIsNotEnrolled, TestLoadRejectsLoosePermissionsCorruptAndIncomplete.
Luồng 10: machine_id đổi (ảnh máy ảo nhân bản hoặc khôi phục)
sequenceDiagram
participant AG as Agent.Run
participant MID as creds.MachineID
participant LOG as logx.Logger
participant CTX as ctx
AG->>MID: MachineID()
MID-->>AG: băm hiện tại khác băm trong credentials.json
AG->>LOG: Error machine id changed since enrollment, delivery stopped
Note over AG: halt identity_mismatch, không dựng engine, không gửi
AG->>CTX: chờ ctx hủy
CTX-->>AG: SIGTERM
AG->>LOG: Info agent stoppedTiến trình không thoát: systemd thấy dịch vụ vẫn "active" nhưng không có dữ liệu nào đi ra. Trạng thái thấy được qua Status() (SIGUSR1) dạng state=identity_mismatch. Khôi phục bằng enroll --force rồi khởi động lại (TestMachineIDMismatchStopsDelivery). Nếu không đọc được machine_id hiện tại thì chỉ cảnh báo và bỏ qua kiểm tra. Nếu credentials.json cũ không có machine_id (rỗng) thì cũng bỏ qua, vì điều kiện là c.MachineID != "".
Luồng 11: lỗi nguồn token (không có, xung đột, sai dạng)
sequenceDiagram
participant OP as Người vận hành
participant CLI as enrollCmd
participant ENR as ResolveToken
OP->>CLI: enroll (không cờ, không biến, không tệp mặc định)
CLI->>ENR: ResolveToken(Source)
ENR-->>CLI: ErrNoToken
CLI-->>OP: no License, mã thoát 2
OP->>CLI: enroll --token a --token-stdin
CLI->>ENR: ResolveToken(Source)
ENR-->>CLI: ErrConflictingSources
CLI-->>OP: use only one of the flags, mã thoát 2
OP->>CLI: enroll --token-file với token rỗng hoặc dài hơn 256 hoặc có khoảng trắng
ENR-->>CLI: ErrInvalidToken
CLI-->>OP: token is malformed, mã thoát 2Mọi lỗi này trả trước khi gọi mạng và không bao giờ chứa token (NFR-E01, TestResolveTokenErrors, TestEnrollUsageErrors). Lỗi I/O khi đọc tệp hoặc stdin (không phải ba lỗi trên) cho mã 1 vì enrollCmd chỉ ánh xạ ba lỗi kia sang mã 2.
Luồng 12: Collector không liên lạc được hoặc 5xx lúc enroll
sequenceDiagram
participant CLI as enrollCmd
participant TRN as transport.Client
participant COL as Collector
participant OP as Người vận hành
CLI->>TRN: Enroll(req)
TRN->>COL: POST /agent/v1/enroll
COL-->>TRN: 5xx hoặc lỗi mạng, TLS hoặc DNS
TRN-->>CLI: APIError 5xx hoặc lỗi mạng
CLI-->>OP: collector unavailable hoặc cannot reach the collector, mã thoát 6
Note over CLI: tệp token được giữ, enroll không tự thử lạiEnroll không có vòng thử lại: postinstall (L3 Service và Packaging) coi mã 6 là "enroll sau", và không làm hỏng gói. Người vận hành chạy lại lệnh khi mạng ổn.
Luồng 13: đổi collector_url bằng SIGHUP nhưng cấu hình mới không dựng được client
sequenceDiagram
participant SIG as handleSignals
participant AG as Agent
participant RUN as Vòng chính
participant LOG as logx.Logger
SIG->>AG: Reload(cfg có ca_file trỏ tệp không đọc được)
RUN->>AG: đầu chu kỳ đọc cấu hình, transportKey khác khóa cũ
RUN->>RUN: clientFor(cfg)
RUN->>LOG: Error new collector settings rejected, keeping the old ones
Note over RUN: giữ client cũ, cấu hình mới vẫn nằm trong con trỏĐây là lỗ hổng nhỏ của nạp lại: Validate không kiểm tệp ca_file có tồn tại (chỉ dựng client mới phát hiện), nên Reload báo thành công ở CLI (config reloaded) nhưng client cũ vẫn dùng, và mỗi chu kỳ sau đó transportKey vẫn khác khóa cũ nên sẽ thử dựng lại và log lỗi lặp (xem OQ-E12). Mã: internal/agent/agent.go, TestReloadSwitchesCollector kiểm nhánh đổi thành công.
7.3. Máy trạng thái
Mỗi sơ đồ chỉ mô hình hóa một thực thể. Nhãn cạnh theo dạng trigger [guard] / event.
7.3.1. Tệp thông tin đăng nhập (credentials.json)
stateDiagram-v2
[*] --> Absent
Absent --> Present : enroll thành công / Save tạo tệp 0600
Present --> Present : enroll force thành công / Save ghi đè nguyên tử
Present --> Loose : chmod rộng hơn 0600 / không có
Loose --> Present : chmod 600 / không có
Present --> Corrupt : tệp bị sửa hoặc cắt cụt / không có
Corrupt --> Present : enroll force thành công / Save ghi đè
Present --> Absent : uninstall purge / Delete
Loose --> Absent : uninstall purge / Delete
Corrupt --> Absent : uninstall purge / Delete| Đường (trigger) | Nơi hiện thực | Kiểm thử |
|---|---|---|
| Absent sang Present (enroll) | creds.Store.Save gọi từ enroll.Do | TestSaveLoadRoundTripWith0600, TestEnrollFromFileStoresCredentialsAndWipesTheFile |
| Present sang Present (enroll force) | enroll.Do bỏ kiểm Exists khi Force | TestEnrollRefusesWhenAlreadyEnrolledUnlessForced |
| Save lỗi giữa chừng (giữ trạng thái cũ) | Save dọn tệp tạm, không chạm tệp đích | TestFailedSaveKeepsOldFile, TestSaveIsAtomicAndLeavesNoTempFiles |
| Present sang Loose và ngược lại | Do người vận hành chmod, Load kiểm mode & 0o077 (không phải Windows) | TestLoadRejectsLoosePermissionsCorruptAndIncomplete |
| Present sang Corrupt | Load trả lỗi is corrupt hoặc is incomplete | TestLoadRejectsLoosePermissionsCorruptAndIncomplete |
| Loose, Corrupt sang Present (enroll force) | Do kiểm Exists rồi bỏ qua vì Force | Chưa có kiểm thử riêng cho đường "khôi phục từ Corrupt bằng force" (OQ-E11) |
| Bất kỳ sang Absent (uninstall purge) | creds.Store.Delete hoặc xóa thư mục state (L3 Service và Packaging) | TestDeleteAndExists, TestServiceControlAndUninstall |
Trạng thái Loose và Corrupt không được lưu ở đâu, chúng là kết quả của việc Load đọc tệp mỗi lần.
7.3.2. Cấu hình đang chạy (trong một tiến trình run)
stateDiagram-v2
[*] --> Booting
Booting --> Active : Load và Validate thành công / Agent.Run bắt đầu
Booting --> [*] : Load thất bại / thoát mã 3
Active --> Active : SIGHUP [Load thành công] / Reload thay con trỏ
Active --> Active : SIGHUP [Load thất bại] / log reload rejected, giữ cấu hình cũ
Active --> [*] : SIGTERM hoặc SIGINT / thoát mã 0| Đường (trigger) | Nơi hiện thực | Kiểm thử |
|---|---|---|
| Booting sang Active | runCmd gọi config.Load, agent.New, Agent.Run | TestRunDeliversBatchesEveryInterval |
| Booting sang thoát mã 3 | reportConfigError | TestRunRejectsBadConfig |
| Active sang Active (nạp lại thành công) | reload, Agent.Reload | TestSignalsReloadAndStatus, TestReloadSwitchesCollector |
| Active sang Active (nạp lại bị từ chối) | reload log Error và return | Chưa có kiểm thử riêng (OQ-E11) |
| Active sang thoát | signal.NotifyContext hủy ctx, Run trả nil | TestRunStopsOnContextCancel |
Tín hiệu nạp lại chỉ có trên nền tảng không phải Windows (signals_unix.go, build tag !windows, mục tiêu thực tế là Linux). Trên Windows reloadSignal() trả nil nên không có nạp nóng, phải khởi động lại dịch vụ (D-05, AGT-7).
7.4. Thuật toán và quyết định thiết kế
7.4.1. Ghi thông tin đăng nhập nguyên tử
Vấn đề: credentials.json chứa token dài hạn. Mất điện hoặc đĩa đầy giữa chừng không được để lại tệp nửa vời (agent sẽ không khởi động) hay tệp có quyền rộng dù chỉ trong thoáng chốc (token lộ).
Giải pháp (creds.Store.Save): (1) MkdirAll(dir, 0700). (2) CreateTemp(dir, ".credentials-*.tmp"), tức cùng thư mục với đích để Rename không vượt hệ tệp. (3) Chmod(0600) trước khi ghi nội dung. (4) Write JSON thụt lề. (5) Sync tệp. (6) Close. (7) Rename đè đích (nguyên tử trên POSIX). (8) syncDir(dir) để bền hóa thao tác đổi tên. Mỗi lỗi ở bước 3 đến 7 gọi cleanup xóa tệp tạm và trả lỗi, tệp đích cũ còn nguyên.
Trade-off: syncDir bỏ qua mọi lỗi (chú thích mã: không phải nền tảng nào cũng hỗ trợ), nên trên hệ tệp không fsync thư mục được, một lần mất điện ngay sau Rename có thể làm tệp đích quay về bản cũ hoặc mất. Với enroll điều này nghĩa là token mới đã tiêu hao mà không lưu được, đúng kịch bản SaveError nhưng không được báo (OQ-E8). Việc ghi không có khóa tệp nên hai lần enroll --force chạy song song là "người ghi sau thắng", chấp nhận được vì enroll là thao tác thủ công.
7.4.2. Phân giải nguồn token và bảo vệ đầu vào
Vấn đề: token phải tới được agent bằng nhiều đường (tay, script, cấu hình hóa), nhưng không được lộ qua danh sách tiến trình, log hay bị đọc vô hạn từ một tệp hay đường ống độc hại.
Giải pháp (enroll.ResolveToken): (1) Đếm số cờ tường minh (--token, --token-file, --token-stdin), hơn một thì ErrConflictingSources. (2) Nếu có cờ: --token (kèm cảnh báo danh sách tiến trình), rồi stdin (io.LimitReader ở MaxTokenLen*4 = 1024 byte), rồi tệp. (3) Nếu không cờ: AH_LICENSE_FILE, rồi AH_LICENSE, rồi tệp mặc định DefaultLicensePath() (chỉ khi tệp tồn tại). (4) fromFile từ chối tệp lớn hơn 1024 byte trước khi đọc, cảnh báo nếu quyền rộng hơn 0600 nhưng vẫn dùng. (5) validated cắt khoảng trắng hai đầu rồi từ chối rỗng, dài hơn 256, hay có khoảng trắng bên trong. (6) Mọi lỗi trả về đều không chứa token.
Trade-off: ưu tiên "cờ hơn biến môi trường hơn tệp mặc định" giúp script ghi đè, nhưng AH_LICENSE tồn tại trong /proc/<pid>/environ suốt vòng đời tiến trình enroll (chỉ chủ tiến trình và root đọc được, rủi ro thấp hơn --token). Đường khuyến nghị là --token-stdin hay tệp 0600 bị xóa sau dùng. Việc cảnh báo chứ không chặn quyền rộng là chọn lựa thân thiện: chặn sẽ làm hỏng enroll tự động khi umask lạ.
7.4.3. Nạp cấu hình phân lớp và gom lỗi có số dòng
Vấn đề: người vận hành sửa YAML bằng tay, cần biết tất cả lỗi và dòng nào, không phải sửa từng lỗi một lần chạy. Nhưng cấu hình phải đúng tuyệt đối trước khi dùng.
Giải pháp (config.Load): (1) Default(). (2) Đọc tệp, thiếu tệp chỉ là lỗi nếu Explicit (đường từ cờ hoặc AH_CONFIG). (3) Giải mã YAML bằng KnownFields(true): khóa lạ thành unknown key "x", lỗi kiểu thành Issue kèm số dòng rút từ thông điệp yaml. (4) lineIndex đi qua cây YAML dựng bản đồ "đường khóa chấm sang số dòng" (buffer.max_bytes, checks[0].port). (5) Nếu bước 3 có lỗi thì trả ngay (không chạy env, không Validate). (6) applyEnv (chỉ 4 biến AH_*), applyOverrides (3 cờ), (7) Validate gom hết lỗi miền giá trị, dùng lineIndex để gắn dòng, (8) cắt / cuối của collector_url. Kết quả lỗi in tệp:dòng: đường: thông điệp.
Trade-off: bước 5 nghĩa là lỗi cú pháp che lỗi miền giá trị cho tới vòng sửa sau (chấp nhận, vì không thể kiểm miền trên cấu trúc đã giải mã hỏng). Thứ tự "tệp, env, cờ" có một hệ quả tinh tế: Validate chạy sau khi trộn, nên giá trị từ env hoặc cờ sai cũng bị bắt, nhưng số dòng chỉ có cho khóa có trong tệp (khóa chỉ do env đặt thì Line bằng 0 và dòng bị bỏ khỏi thông điệp). KnownFields(true) đảm bảo gõ sai khóa không bị bỏ qua thầm lặng, đổi lại khóa mới thêm ở phiên bản sau làm agent cũ từ chối khởi động (rủi ro khi triển khai cấu hình chung cho đội agent nhiều phiên bản, OQ-E13).
7.4.4. Danh tính máy và chống nhân bản
Vấn đề: ảnh máy ảo được nhân bản sẽ mang theo credentials.json và nhiều máy sẽ gửi số liệu dưới cùng một agent_id, làm sai dữ liệu của máy thật.
Giải pháp: lúc enroll, MachineID đọc /etc/machine-id (hoặc /var/lib/dbus/machine-id), cắt khoảng trắng, rồi tính sha256("accesshub-agent/machine-id/v1:" + id) dạng hex, và lưu băm (không phải id thô) vào credentials.json và gửi cho Collector. Lúc run, Agent.Run tính lại và so, khác thì halt("identity_mismatch") (luồng 10).
Trade-off: dựa vào machine-id của hệ điều hành: máy nhân bản mà không chạy systemd-machine-id-setup thì có cùng id và không bị phát hiện. Ngược lại, khi nâng cấp hệ điều hành làm đổi machine-id hợp lệ thì agent ngừng gửi và cần enroll lại (an toàn hơn là gửi sai). Muối cố định v1 cho phép Collector xác minh mà không thấy id thô, và cho phép đổi thuật toán bằng v2 sau này (chưa có cơ chế chuyển, OQ-E7 liên quan).
7.4.5. Nạp nóng: khóa nào có hiệu lực ngay
Vấn đề: SIGHUP phải đổi được những gì an toàn đổi, và nói rõ những gì không đổi, để người vận hành không tin nhầm rằng mình đã áp dụng một thay đổi.
Giải pháp: Reload chỉ thay con trỏ cấu hình. Vòng chính đọc con trỏ ở đầu mỗi chu kỳ và chỉ dùng một số trường:
| Khóa | Hiệu lực sau SIGHUP | Cơ chế |
|---|---|---|
log.level | Ngay | log.SetLevel trong reload |
interval | Từ lần timer.Reset kế | timer.Reset(cfg.Interval.D()) cuối mỗi chu kỳ |
buffer.catch_up_batches | Chu kỳ kế | s.CatchUp = cfg.Buffer.CatchUpBatches |
collector_url, ca_file, proxy_url, insecure_skip_verify, send_timeout | Chu kỳ kế, nếu dựng được client | transportKey đổi thì clientFor (luồng 13) |
log.format, log.file | Không | Logger dựng một lần ở runCmd |
metrics.*, checks, labels, limits.max_series, collect_timeout | Không | Engine dựng một lần trong Run (a.newEngine) |
buffer.max_bytes, buffer.max_age, state_dir | Không | WAL mở một lần, state_dir còn quyết định nơi nạp credentials.json |
limits.memory_limit | Không | debug.SetMemoryLimit gọi một lần ở runCmd |
allow_remote_config | Không có tác dụng | Chưa có đường cấu hình từ xa (D-01) |
Trade-off: dựng lại engine hay WAL lúc chạy cần dừng thu giữa chu kỳ và đổi tên thư mục, rủi ro mất dữ liệu cao so với lợi ích nhỏ, nên hiện bắt buộc khởi động lại (systemctl restart) cho các khóa này. Hậu quả: config reloaded được log dù một số khóa trong tệp đã đổi mà chưa có hiệu lực. ĐỀ XUẤT: log danh sách khóa đã đổi nhưng cần khởi động lại (OQ-E14).
7.4.6. Ánh xạ lỗi enroll sang mã thoát và gợi ý
Vấn đề: script gói (postinstall) và người vận hành cần biết nên làm gì tiếp, mà không phân tích văn bản.
Giải pháp (reportEnrollError): ErrAlreadyEnrolled và *SaveError cho mã 1 (vấn đề cục bộ). *transport.APIError với trạng thái 400, 401, 403, 409, 422, 426 cho mã 5 kèm gợi ý theo code (token_used, already_enrolled, binding_failed) hoặc lời nhắc nâng cấp với 426. Trạng thái khác (5xx, 429) và mọi lỗi mạng cho mã 6. Mã 2 (cách dùng) và mã 3 (cấu hình) được trả sớm hơn, trước khi gọi mạng.
Trade-off: 429 rơi vào mã 6 ("không có kết nối") dù thực chất là "thử lại sau": chấp nhận vì postinstall xử lý cả hai giống nhau (enroll sau). Mã 1 bị dùng cho cả "đã enroll" và "lưu lỗi", khác nhau hoàn toàn về hành động cần làm, nên script chỉ phân biệt được qua thông điệp.
8. Xử lý lỗi
8.1. Xử lý các nhánh lỗi (error branches)
| Nhánh | Điều kiện | Xử lý | Quan sát |
|---|---|---|---|
| Không có nguồn token | Không cờ, không biến, không tệp mặc định | ErrNoToken, mã 2 (luồng 11) | stderr |
| Hai cờ token cùng lúc | --token, --token-file, --token-stdin | ErrConflictingSources, mã 2 | stderr |
| Token sai dạng | Rỗng, dài hơn 256, có khoảng trắng, tệp lớn hơn 1024 byte | ErrInvalidToken, mã 2 | stderr |
| Tệp token chỉ định nhưng không có | --token-file hoặc AH_LICENSE_FILE | token file: ..., mã 1 (không phải mã 2, OQ-E6 cùng họ) | stderr |
| Đọc stdin lỗi | I/O | read token from stdin, mã 1 | stderr |
Không đọc được machine-id | Không có /etc/machine-id và /var/lib/dbus/machine-id | cannot determine the machine id, mã 1. Kiểm thử CI phải thay readMachineID | stderr |
| Cấu hình sai khi enroll | Validate lỗi | In các Issue, mã 3, chưa gọi mạng | stderr |
| Tùy chọn client sai khi enroll | URL sai hoặc ca_file không đọc được | In lỗi, mã 3 | stderr |
| Collector từ chối | 400, 401, 403, 409, 422, 426 | Luồng 4, mã 5 | stderr |
| Collector 5xx, 429, mạng | Không liên lạc được | Luồng 12, mã 6, tệp token giữ | stderr |
| Lưu thất bại | Đĩa đầy, quyền | Luồng 5, SaveError, mã 1 | stderr |
Đã enroll, không --force | credentials.json tồn tại | Luồng 6, mã 1 | stderr |
| Không xóa được tệp token sau enroll | Wipe lỗi | Cảnh báo remove it manually, mã vẫn 0 | stderr |
Cấu hình sai khi run | Luồng 7 | Mã 3 | stderr, journal |
| SIGHUP cấu hình sai | Luồng 8 | Giữ cấu hình cũ | Log Error |
Chưa enroll khi run | Luồng 9 | Mã 4 | stderr |
| Tệp danh tính quyền rộng, hỏng, thiếu trường | Luồng 9 | agent failed, mã 1 | Log Error |
machine_id lệch | Luồng 10 | Dừng gửi, tiến trình sống | Log Error, Status() |
| Client mới không dựng được sau SIGHUP | Luồng 13 | Giữ client cũ | Log Error mỗi chu kỳ |
8.2. Fail-fast
| Điều kiện | Hành vi | Mã thoát hoặc lỗi |
|---|---|---|
| Đối số, lệnh, cờ sai | In trợ giúp hoặc lỗi, không làm gì | Mã 2 (TestNoArgsAndUnknownCommandAreUsageErrors, TestFlagErrorsAreUsageErrors) |
| Cấu hình sai lúc khởi động | Không khởi động | Mã 3 (TestRunRejectsBadConfig) |
| Chưa enroll | Run trả ErrNotEnrolled | Mã 4 |
collector_url không phải HTTPS hoặc có thông tin người dùng | Validate từ chối | Mã 3 |
| Tệp danh tính quyền rộng | Load từ chối (không phải Windows) | Mã 1 |
Ghi credentials.json lỗi | SaveError | Mã 1 |
8.3. Race conditions
| Tình huống | Cơ chế hoặc hậu quả | Ghi chú |
|---|---|---|
Reload (goroutine tín hiệu) với vòng chính đọc cấu hình | atomic.Pointer[config.Config], cấu hình không bao giờ bị sửa tại chỗ | Đọc thấy trọn bản cũ hoặc trọn bản mới |
Hai tiến trình enroll song song cho cùng state_dir | Không có khóa. Mỗi bên Save nguyên tử nên tệp luôn trọn vẹn, nhưng bên ghi sau thắng và bên kia có agent_id khác trên Collector | Hiếm, thao tác thủ công (OQ-E15) |
enroll --force chạy khi dịch vụ đang chạy | Dịch vụ giữ token cũ trong RAM, tệp mới không có hiệu lực tới khi khởi động lại, và nếu Collector đã thu hồi token cũ thì dịch vụ gặp 401 hoặc 403 | Sau --force phải khởi động lại dịch vụ (ghi trong hướng dẫn vận hành) |
| SIGHUP đúng lúc sửa tệp dở | Có thể đọc thấy YAML nửa vời, bị từ chối, giữ cấu hình cũ | An toàn. Người vận hành nên ghi tệp nguyên tử (mv) |
Save với Load đồng thời (chạy run lúc enroll --force) | Load thấy tệp cũ hoặc mới nhờ Rename | An toàn |
| Đọc token từ tệp lúc tệp bị xóa | Stat thành công nhưng ReadFile lỗi | Trả token file: ..., mã 1 |
9. Suy thoái dịch vụ & Khả năng phục hồi
9.1. Ma trận suy thoái theo phụ thuộc
| Phụ thuộc | Lỗi | Suy thoái | Bảo vệ | Phục hồi |
|---|---|---|---|---|
| Collector (lúc enroll) | Không tới được, 5xx, 429 | Không enroll được. Agent chưa có danh tính nên không chạy | Mã thoát 6 phân loại, postinstall không làm hỏng gói | Thủ công: chạy lại enroll khi mạng ổn |
Collector (lúc run) | Không tới được | Không ảnh hưởng thành phần này: danh tính và cấu hình đã nằm cục bộ | credentials.json đọc từ đĩa | Theo L3 WAL và Sender |
Đĩa state_dir | Đầy, chỉ đọc, hỏng | Enroll: SaveError. Run: không đọc được danh tính thì không chạy | Tệp tạm rồi Rename giữ tệp cũ | Thủ công: sửa đĩa, enroll lại với token mới (nếu lỗi lúc enroll) |
Tệp agent.yaml | Sai cú pháp hoặc miền giá trị | Khởi động: không chạy (mã 3). SIGHUP: giữ cấu hình cũ | Validate gom hết lỗi, check-config kiểm trước | Sửa tệp rồi SIGHUP hoặc khởi động lại |
/etc/machine-id | Mất hoặc đổi | Mất: bỏ qua kiểm danh tính, enroll lỗi. Đổi: dừng gửi (luồng 10) | Cảnh báo log, dừng thay vì gửi sai | Thủ công: enroll lại với --force |
| Quyền tệp | Bị chmod rộng | Agent không khởi động (mã 1) | ErrInsecurePermissions | chmod 600. Dịch vụ systemd sẽ tự thử khởi động lại theo chính sách Restart (L3 Service và Packaging) |
Đồng hồ (để tính enrolled_at) | Lệch | enrolled_at sai, không ảnh hưởng chức năng | Chỉ thông tin | Không cần |
| Biến môi trường | Thiếu hoặc sai kiểu | AH_INSECURE_SKIP_VERIFY sai kiểu thì cấu hình bị từ chối | Báo lỗi có tên biến | Sửa biến |
9.2. Backup, DR, RTO và RPO
credentials.json là bí mật duy nhất không tái tạo được từ cục bộ: mất nó nghĩa là phải xin token enroll mới và (tùy Collector) đặt lại bản ghi máy. Không có cơ chế sao lưu trong agent. Không khuyến nghị sao lưu tệp này vào kho chung, vì đó là nhân rộng bí mật và trái với cơ chế chống nhân bản (luồng 10).
| Chỉ số | Giá trị | Ghi chú |
|---|---|---|
| RPO của danh tính | Bằng 0 với ghi nguyên tử thành công | Sync tệp và Rename, nhưng syncDir bỏ qua lỗi (OQ-E8) |
RTO khi mất credentials.json | Theo thời gian xin token mới cộng thời gian enroll | Phụ thuộc quy trình vận hành và Collector. Chưa đo, không đề xuất con số |
| RTO khi sửa cấu hình sai | Gần như tức thì với SIGHUP, hoặc khởi động lại dịch vụ | Thao tác thủ công |
Cấu hình agent.yaml | Nên nằm trong quản lý cấu hình của đội vận hành (Ansible, Puppet) | Do gói cài đặt chỉ tạo tệp mẫu (L3 Service và Packaging) |
10. Đồng thời & Toàn vẹn dữ liệu
10.1. Ranh giới giao dịch
Không có cơ sở dữ liệu hay giao dịch. Các đơn vị nguyên tử là thao tác tệp và lời gọi HTTP:
| Đơn vị | Ranh giới | Đảm bảo |
|---|---|---|
Ghi credentials.json | Tệp tạm, Chmod, Write, Sync, Close, Rename, syncDir | Đọc thấy trọn bản cũ hoặc trọn bản mới. Quyền 0600 trước khi có nội dung |
| Đổi token lấy danh tính | POST /enroll rồi Save | Không nguyên tử giữa hai bước: Collector đã tiêu hao token trước khi tệp được ghi. Khi ghi lỗi chỉ có thể báo rõ (SaveError) |
| Xóa tệp token | Wipe: mở, ghi số không, Sync, đóng, Remove | Chỉ sau khi Save thành công. Lỗi ghi đè vẫn cố Remove |
| Trộn cấu hình | Load dựng Config mới hoàn toàn, rồi Store con trỏ | Không bao giờ có cấu hình nửa vời |
Kiểm danh tính lúc run | Đọc credentials.json một lần | Quyết định "gửi hay dừng" dựa trên ảnh chụp lúc khởi động |
10.2. Cơ chế đồng thời
| Cơ chế | Nơi dùng | Mục đích |
|---|---|---|
atomic.Pointer[config.Config] | Agent.cfg | Nạp nóng không khóa |
sync.Mutex trong Agent (mu) | send, halted | Status() đọc an toàn từ goroutine tín hiệu |
signal.Notify kênh đệm 4 | runCmd | Tránh mất tín hiệu khi nhận dồn |
signal.NotifyContext | runCmd | SIGINT, SIGTERM hủy ctx để dừng êm |
Ghi tệp tạm cùng thư mục rồi Rename | creds.Save | Nguyên tử, không khóa tệp |
| Không có khóa liên tiến trình | state_dir | Xem 8.3 và OQ-E15 |
11. Bảo mật
11.1. Bảo mật ba lớp
| Lớp | Biện pháp | Bằng chứng |
|---|---|---|
| Kênh truyền | collector_url bắt buộc https (không user info, query, fragment). Enroll dùng cùng client TLS với gửi số liệu (TLS tối thiểu 1.2, không theo redirect, ghim CA tùy chọn, L3 WAL và Sender 11.1). POST /enroll không gửi Authorization, chỉ token enroll trong thân | validate.go, TestEnrollRejectsBadConfig |
| Danh tính và bí mật | Đầu vào token bị chặn cỡ (LimitReader, 1024 byte, 256 ký tự). --token bị cảnh báo. Credentials.String() và LogValue() che token. Result không có token. Lỗi enroll không chứa token. RedactURL che mật khẩu proxy trong check-config | TestResolveTokenErrors, TestCredentialsNeverPrintTheToken, TestRedactURL |
| Dữ liệu nghỉ | credentials.json 0600 trong thư mục 0700, Load từ chối quyền rộng (không phải Windows). Tệp token được ghi đè số không rồi xóa sau dùng. machine_id chỉ lưu dạng băm có muối | TestSaveLoadRoundTripWith0600, TestLoadRejectsLoosePermissionsCorruptAndIncomplete, TestWipeOverwritesAndRemoves, TestMachineIDIsSaltedHash |
Các điểm yếu còn lại, nêu thẳng (R-02):
- Token dài hạn nằm dạng rõ trong
credentials.json(bảo vệ chỉ bằng quyền tệp). Trên Windows chưa có DPAPI và không có kiểm ACL (THIẾT KẾ, CHƯA XÂY, D-05). AH_LICENSEtồn tại trong/proc/<pid>/environcủa tiến trình enroll.- Ghi đè số không bằng
WriterồiRemovekhông đảm bảo xóa vật lý trên SSD hoặc hệ tệp copy-on-write. Bù lại token enroll dùng một lần nên tệp đã xóa không còn giá trị sau khi enroll thành công. - Chạy
enrollbằng root tạocredentials.jsonthuộc root 0600 mà dịch vụ (tài khoảnaccesshub-agent) không đọc được (OQ-E5). Hướng dẫn vàpostinstallđều yêu cầu enroll bằng tài khoản dịch vụ.
11.2. Đường ống phân quyền năm bước
Thành phần này không nhận yêu cầu vào qua mạng, nên "đường ống" áp vào thao tác CLI và lời gọi ra:
| Bước | Ai làm | Ghi chú |
|---|---|---|
| 1. Xác thực người thao tác | Hệ điều hành (quyền tệp, tài khoản chạy lệnh) | Agent không có đăng nhập riêng. service install và uninstall đòi root, kiểm bằng Euid trong internal/svc (TestServiceNeedsRootAndAKnownAction) |
| 2. Xác thực nguồn token | Cục bộ | Kiểm dạng, kích thước, cảnh báo quyền tệp |
| 3. Xác thực kênh | Transport (TLS, CA) | collector_url HTTPS |
| 4. Xác thực token enroll | Collector | 400, 401, 403 (mã 5) |
| 5. Ràng buộc danh tính | Collector gắn agent_id, company_id, server_id vào token. Agent lưu machine_id băm để tự kiểm | Agent không tự nhận nhãn định danh (L3 Collectors INV-2) |
11.3. Điểm neo zero-trust
| Điểm neo | Ý nghĩa |
|---|---|
| Không cổng mở | Agent chỉ là client, không có thể bị tấn công qua lệnh cấu hình từ xa (D-01 chưa xây) |
| Token enroll dùng một lần | Lộ tệp token sau enroll vô hại. Lộ trước enroll thì người đầu tiên dùng thắng, Collector ràng buộc máy bằng hostname và IP (binding_failed) |
| Không tin cấu hình ngoài miền | Mọi giá trị qua Validate với giới hạn cứng (HardMaxSeries 500, MaxChecks 50, MaxRegexLen 200, MaxLabelValue 128, MaxStaticLabels 20), tránh cấu hình làm cạn tài nguyên máy chủ |
| Không tin nhãn tự khai | Nhãn company_id, server_id, agent_id bị cấm ở Validate (reservedLabels) |
| Không tin ảnh máy nhân bản | Kiểm machine_id lúc khởi động (luồng 10) |
| Không tin khóa lạ | KnownFields(true) từ chối khóa không biết thay vì bỏ qua |
12. Cấu hình & Tinh chỉnh
12.1. Tunables
Nguồn và thứ tự ưu tiên: mặc định (Default()), tệp YAML, biến AH_* (chỉ 4 biến), cờ CLI (chỉ 3 cờ), theo 7.4.3. Đường tệp: cờ --config, rồi AH_CONFIG, rồi /etc/accesshub-agent/agent.yaml (Windows: C:\ProgramData\AccessHubAgent\agent.yaml). Cột "Nạp nóng" theo 7.4.5.
| Khóa | Mặc định (dải hợp lệ) | Ý nghĩa | Nạp nóng | Nguồn ghi đè |
|---|---|---|---|---|
collector_url | không có (bắt buộc, https, có host, không user info, query, fragment) | Đích enroll và gửi | Có (nếu dựng được client) | AH_COLLECTOR_URL, --collector |
ca_file | rỗng | CA riêng để ghim | Có (nếu đọc được) | Không |
insecure_skip_verify | false | Bỏ kiểm chứng chỉ, chỉ để thử nghiệm | Có | AH_INSECURE_SKIP_VERIFY |
proxy_url | rỗng (http hoặc https, có host) | Proxy tường minh | Có | Không |
state_dir | /var/lib/accesshub-agent (không được rỗng) | Chứa credentials.json và wal/ | Không | AH_STATE_DIR, --state-dir |
interval | 30 s (10 s đến 300 s) | Chu kỳ thu và gửi | Có | Không |
send_timeout | 15 s (1 s đến 1 phút) | Hạn một yêu cầu HTTP | Có (dựng lại client) | Không |
collect_timeout | 5 s (1 s đến 30 s, phải nhỏ hơn interval) | Hạn một lần thu | Không | Không |
buffer.max_bytes | 50 MiB (4 MiB đến 1 GiB) | Trần WAL | Không | Không |
buffer.max_age | 24 h (1 phút đến 7 ngày) | Tuổi tối đa bản ghi WAL | Không | Không |
buffer.catch_up_batches | 2 (1 đến 10) | Số lô gửi bù thêm mỗi tick | Có | Không |
limits.max_series | 500 (1 đến 500) | Trần số series mỗi chu kỳ | Không | Không |
limits.memory_limit | 64 MiB (16 MiB đến 4 GiB) | debug.SetMemoryLimit | Không | GOMEMLIMIT (bỏ qua khóa này nếu đã đặt) |
log.level | info (debug, info, warn, error) | Mức log | Có | AH_LOG_LEVEL, --log-level |
log.format | text (text, json) | Định dạng log | Không | Không |
log.file | rỗng (stderr) | Tệp log | Không | Không |
checks, labels, metrics.*, checks_allow_link_local | Xem L3 Collectors | Thuộc thành phần thu | Không | Không |
allow_remote_config | true | Chưa dùng | Không có tác dụng | Không |
Giới hạn cứng biên dịch (internal/config/defaults.go): HardMaxSeries 500, MinInterval 10 s, MaxInterval 300 s, MaxChecks 50, MaxRegexLen 200, MaxLabelValue 128, MaxStaticLabels 20. Dải của từng khóa kiểm ở validate.go. Chuỗi cỡ chấp nhận dạng 64MiB, 50MB hoặc số nguyên (ParseSize, TestParseSize), khoảng thời gian dạng 30s (time.ParseDuration).
Tham số CLI (không nằm trong tệp)
| Tham số | Mặc định | Ý nghĩa |
|---|---|---|
--config | theo nền tảng | Đường tệp cấu hình, chỉ định tường minh thì tệp thiếu là lỗi |
--collector, --state-dir, --log-level | rỗng | Ghi đè env và tệp |
enroll --token-file, --token-stdin, --token, --force | rỗng, false, rỗng, false | Nguồn token và ghi đè (xem FR-E01, FR-E07) |
collect-once --gap | 1 s (100 ms đến 1 phút) | Khoảng giữa hai mẫu |
AH_LICENSE_FILE, AH_LICENSE | rỗng | Nguồn token dự phòng khi không có cờ |
| Tệp token mặc định | /etc/accesshub-agent/license.token (Windows C:\ProgramData\AccessHubAgent\license.token) | Nguồn cuối cùng |
Hằng số biên dịch (không cấu hình được)
| Hằng | Giá trị | Vai trò |
|---|---|---|
MaxTokenLen | 256 ký tự | Trần token. Đầu vào đọc tối đa MaxTokenLen*4 = 1024 byte |
Quyền tệp credentials.json | 0600, thư mục 0700 | Cứng trong Save |
Muối machineIDSalt | accesshub-agent/machine-id/v1: | Băm machine_id, đổi muối là đổi danh tính (OQ-E7) |
Đường machine-id | /etc/machine-id, /var/lib/dbus/machine-id | Thứ tự thử |
| Số IP gửi lúc enroll | 16 | Trần UsableIPs |
| Số lần thử lại enroll | 0 | Không có vòng thử lại |
12.2. Feature flags
Không có cờ tính năng hoạt động. allow_remote_config được phân tích, mặc định true, và chưa được dùng ở đâu (L2 D-01, D-11): agent không áp dụng cấu hình từ xa và bỏ qua trường config trong phản hồi enroll. Cần nhớ rằng mặc định true nghĩa là khi tính năng cấu hình từ xa được xây, các agent cũ sẽ nhận cấu hình từ xa trừ khi người vận hành đặt false rõ ràng (ĐỀ XUẤT: cân nhắc mặc định false cho tới khi có cơ chế ký cấu hình, OQ-E3).
13. Telemetry & Vận hành
13.1. Metrics
Thành phần này không phát chỉ số nào. Các chỉ số agent_* do bộ thu tự thân (L3 Collectors) và Sender (L3 WAL và Sender) phát, không mô tả danh tính hay cấu hình. Khoảng trống (ĐỀ XUẤT, OQ-E17): chưa có chỉ số cho trạng thái identity_mismatch, số lần nạp lại bị từ chối, hay tuổi của credentials.json. Hiện quan sát qua log và Status() (13.4).
13.2. Log schema
Log theo logx (xem L3 WAL và Sender 13.2 về định dạng và che trường nhạy cảm). Trước khi logger được dựng (cấu hình sai, cờ sai) lỗi ra thẳng stderr với tiền tố accesshub-agent: và không có mức log.
Sự kiện (msg) | Mức | Trường thêm | Khi nào |
|---|---|---|---|
agent started | Info | version, collector, interval, creds (token che) | Sau khi nạp danh tính |
cannot read the machine id, skipping the identity check | Warn | err | Không đọc được machine-id lúc khởi động |
machine id changed since enrollment (cloned or restored image), delivery stopped, re-enroll with --force | Error | Lệch danh tính (luồng 10) | |
agent failed | Error | err | Run trả lỗi khác ErrNotEnrolled, gồm quyền rộng, hỏng, thiếu trường |
config reload rejected, keeping the running configuration | Error | err (nhiều dòng tệp:dòng: ...) | SIGHUP mà Load lỗi |
config reload rejected | Error | err | SIGHUP mà SetLevel lỗi |
config reloaded | Info | SIGHUP thành công | |
new collector settings rejected, keeping the old ones | Error | err | Dựng client mới lỗi sau nạp lại (luồng 13) |
status | Info | state | Nhận SIGUSR1 |
agent stopped | Info | Dừng êm |
Đầu ra CLI (stderr hoặc stdout, không qua slog): enrolled as agent <id>, warning: --token puts the secret in the process list, warning: token file <path> is readable by other users, run chmod 600, enrollment rejected: <gợi ý>, collector unavailable: ..., cannot reach the collector: ..., this host is not enrolled, run 'accesshub-agent enroll' first, config OK: <path>, no config file at <path>, using defaults and environment. Các chuỗi tiếng Anh này là tín hiệu ổn định cho script nhưng chưa được coi là hợp đồng (OQ-E9).
13.3. Alert to runbook
Thành phần không tự cảnh báo (ĐỀ XUẤT, chưa có ngưỡng thực đo).
| Điều kiện | Runbook |
|---|---|
enroll thoát mã 5 với token_used | Token đã dùng: xin token mới |
enroll thoát mã 5 với binding_failed | Collector không khớp máy với bản ghi server: kiểm hostname và IP, sửa bản ghi ở Access Hub |
enroll thoát mã 5 với 426 | Nâng cấp agent |
enroll thoát mã 6 | Mạng, TLS hoặc DNS tới Collector: kiểm tra curl, ca_file, proxy_url rồi chạy lại |
enroll thoát mã 1 với token is now spent | Sửa đĩa hoặc quyền state_dir, xin token mới, enroll lại |
run thoát mã 4 | Chạy enroll bằng tài khoản dịch vụ (sudo -u accesshub-agent ...) |
run thoát mã 1 với quyền tệp | chmod 600 credentials.json và kiểm chủ sở hữu (OQ-E5) |
run thoát mã 3 | Chạy check-config để xem mọi lỗi và dòng |
Log config reload rejected | Sửa agent.yaml theo số dòng, gửi lại SIGHUP |
Log machine id changed | Máy nhân bản hoặc đổi machine-id: enroll --force rồi khởi động lại dịch vụ |
Log new collector settings rejected lặp | Kiểm tệp ca_file và proxy_url vừa đổi (OQ-E12) |
13.4. Probes
Không có endpoint. Quan sát cục bộ:
| Kênh | Nội dung |
|---|---|
check-config | Kiểm cấu hình, in tóm tắt (không gọi mạng). Dùng trước khi nạp lại hoặc khởi động lại. In cảnh báo khi insecure_skip_verify bật |
| SIGUSR1 | Log status kèm Agent.Status(), gồm state=identity_mismatch khi dừng vì lệch danh tính |
status, diag | Stub (mã 1), AGT-10 |
collect-once | Thu một lần và in JSON, không cần danh tính hay mạng. Dùng để kiểm bộ thu (L3 Collectors) |
| Mã thoát | 0 đến 6 như 5.1 |
check-config không kiểm kết nối hay chứng chỉ (OQ-E2) và không kiểm tệp ca_file tồn tại (OQ-E12).
13.5. Trace propagation
Không áp dụng cho thành phần này ngoài việc lời gọi enroll mang X-Request-Id và X-AH-Agent-Version như mọi lời gọi khác (L3 WAL và Sender 13.5). Log phía agent không ghi X-Request-Id, nên đối chiếu một lần enroll thất bại với log Collector phải dựa vào thời điểm và tên máy.
14. Kế hoạch kiểm thử
| Loại Test & Phạm vi | Ánh xạ mục tiêu | Mục tiêu kỹ thuật | Ví dụ kịch bản (Test ID) |
|---|---|---|---|
| Unit: phân giải token | AC-E01 đến E04, NFR-E05 | Thứ tự nguồn, xung đột, sai dạng, cảnh báo quyền, LimitReader | TestResolveTokenPriority, TestResolveTokenErrors, TestLoosePermissionsWarn, TestWipeOverwritesAndRemoves |
| CLI: luồng enroll | AC-E04 đến E09, NFR-E04, E07 | Enroll từ tệp và xóa tệp, nguồn token, từ chối, không kết nối, enroll lặp, lưu lỗi, cấu hình sai | TestEnrollFromFileStoresCredentialsAndWipesTheFile, TestLicenseSources, TestEnrollUsageErrors, TestEnrollRejections, TestEnrollConnectionProblemsExitSix, TestEnrollRefusesWhenAlreadyEnrolledUnlessForced, TestEnrollSaveFailureIsReportedAndKeepsTokenFile, TestEnrollRejectsBadConfig |
| Unit: kho danh tính | AC-E10 đến E14, NFR-E01 đến E03 | Nguyên tử, 0600, giữ tệp cũ khi lỗi, từ chối quyền rộng hoặc hỏng, che token, băm machine_id | TestSaveLoadRoundTripWith0600, TestSaveIsAtomicAndLeavesNoTempFiles, TestFailedSaveKeepsOldFile, TestLoadRejectsLoosePermissionsCorruptAndIncomplete, TestCredentialsNeverPrintTheToken, TestDeleteAndExists, TestMachineIDIsSaltedHash |
| Unit: thông tin máy | AC-E15 | Lọc IP, trần 16, dự phòng khi thiếu os-release | TestUsableIPsFiltersAndCaps, TestReadOS, TestReadOSMissingFilesFallBack, TestGatherDoesNotPanic |
| Unit: nạp cấu hình | AC-E16 đến E20, NFR-E06, E08, E09 | Mặc định, mẫu đầy đủ, số dòng, gom lỗi, tệp thiếu, độ ưu tiên, env, cỡ, che URL, fuzz | TestLoadMinimalAppliesDefaults, TestLoadFullSampleFromDocs, TestLoadErrorsCarryLineNumbers, TestLoadReportsAllIssues, TestErrorFormatIncludesFileAndLine, TestMissingFile, TestEmptyFileNeedsURL, TestPrecedenceFlagsOverEnvOverFile, TestEnvInsecureSkipVerify, TestResolvePath, TestParseSize, TestRedactURL, FuzzLoadNeverPanics |
| CLI: khung lệnh | AC-E21 đến E23, E26, NFR-E07 | Mã thoát, trợ giúp, phiên bản, check-config, stub, collect-once | TestNoArgsAndUnknownCommandAreUsageErrors, TestHelpListsEveryCommand, TestVersionOutput, TestCheckConfigValid, TestCheckConfigReportsLineNumbers, TestCheckConfigUsesEnvConfigPathAndOverrides, TestCheckConfigMissingExplicitFile, TestFlagErrorsAreUsageErrors, TestStubsFailClearly, TestCollectOncePrintsJSON, TestCollectOnceRejectsBadGapAndConfig |
CLI và Agent: run | AC-E24, E25 | Cấu hình sai, chưa enroll, dừng theo ctx, SIGHUP và SIGUSR1, lệch danh tính, đổi Collector | TestRunRejectsBadConfig, TestRunWithoutCredentialsExitsNotEnrolled, TestRunWithoutCredentialsIsNotEnrolled, TestRunStopsOnContextCancel, TestSignalsReloadAndStatus, TestMachineIDMismatchStopsDelivery, TestReloadSwitchesCollector |
Chưa có: luật Validate theo từng khóa | NFR-E06, NFR-E09 | Mỗi miền giá trị (interval, buffer, limits, regex, checks, labels, URL) có một ca sai riêng | ĐỀ XUẤT: bảng ca kiểm thử theo khóa (OQ-E11) |
| Chưa có: nhánh nạp lại bị từ chối | NFR-E06 | reload giữ cấu hình cũ và log lỗi | ĐỀ XUẤT: kiểm thử reload với tệp sai (OQ-E11) |
Chưa có: khôi phục từ tệp hỏng bằng --force | AC-E07 | Đường Corrupt sang Present (7.3.1) | ĐỀ XUẤT (OQ-E11) |
Chưa có: ca_file hỏng sau nạp lại | Luồng 13 | Client cũ được giữ, log lặp | ĐỀ XUẤT (OQ-E12) |
Chưa có: syncDir lỗi, hai enroll song song | NFR-E02 | Hành vi khi hệ tệp không hỗ trợ fsync thư mục, tranh chấp | ĐỀ XUẤT (OQ-E8, OQ-E15) |
| Chưa có: Windows | FR-E25 | Quyền tệp và DPAPI | THIẾT KẾ, CHƯA XÂY (D-05) |
Lưu ý khi chạy: kiểm thử creds về quyền bị bỏ qua khi chạy bằng root hoặc trên Windows (creds_test.go kiểm os.Geteuid() == 0), nên kết quả trong CI chạy root không chứng minh được nhánh ErrInsecurePermissions. Chạy kiểm thử bằng người dùng thường. readMachineID được thay bằng giả trong kiểm thử CLI vì container CI thường không có /etc/machine-id.
15. Trình tự triển khai
15.1. Ma trận milestone
| Milestone | Nội dung | Phụ thuộc | Đóng góp nghiệm thu |
|---|---|---|---|
| E1 | Kho danh tính creds (Save nguyên tử, Load kiểm quyền, MachineID) và hostinfo. ĐÃ HIỆN THỰC | Không | AC-E10 đến E15 |
| E2 | Nạp cấu hình phân lớp, kiểm miền, số dòng, check-config. ĐÃ HIỆN THỰC | Không | AC-E16 đến E21 |
| E3 | enroll (nguồn token, Do, ánh xạ mã thoát, xóa tệp token). ĐÃ HIỆN THỰC | E1, E2 | AC-E01 đến E09 |
| E4 | Khung CLI, run, SIGHUP, SIGUSR1, collect-once. ĐÃ HIỆN THỰC (tín hiệu chỉ trên nền tảng không phải Windows) | E1, E2, E3 | AC-E22 đến E26 |
| E5 | Khép khoảng trống độ bền và kiểm thử: bền hóa thư mục (OQ-E8), khóa state_dir (OQ-E15), kiểm ca_file lúc Validate (OQ-E12), báo khóa cần khởi động lại (OQ-E14), thêm kiểm thử còn thiếu (OQ-E11). ĐỀ XUẤT | E4 | AC mới (chưa có) |
| E6 | Khép hợp đồng vận hành: mã thoát riêng cho tệp danh tính hỏng (OQ-E6), phiên bản hợp đồng CLI (OQ-E9), định dạng có phiên bản cho credentials.json (OQ-E7), cảnh báo root enroll (OQ-E5). ĐỀ XUẤT | E4 | AC mới (chưa có) |
| E7 | status, diag, kiểm kết nối trong check-config. THIẾT KẾ, CHƯA XÂY (AGT-10, D-04, OQ-E2) | E4 | FR-E22 |
| E8 | Cấu hình từ xa, xoay token. THIẾT KẾ, CHƯA XÂY (D-01, D-08, cần Collector) | E4 và Collector | FR-E23, E24 |
| E9 | Bảo vệ token và ACL Windows (DPAPI). THIẾT KẾ, CHƯA XÂY (D-05, AGT-7) | E1, E4 | FR-E25 |
15.2. Sơ đồ phụ thuộc milestone
flowchart LR
E1["E1 · Kho danh tính"]
E2["E2 · Nạp cấu hình"]
E3["E3 · enroll"]
E4["E4 · CLI và tín hiệu"]
E5["E5 · Khép độ bền"]
E6["E6 · Khép hợp đồng"]
E7["E7 · status và diag"]
E8["E8 · Cấu hình từ xa"]
E9["E9 · Windows"]
E1 --> E3
E2 --> E3
E3 --> E4
E4 --> E5
E4 --> E6
E4 --> E7
E4 --> E8
E4 --> E9
style E1 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style E2 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style E3 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style E4 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style E5 fill:#3a3320,stroke:#d9b84a,color:#fff
style E6 fill:#3a3320,stroke:#d9b84a,color:#fff
style E7 fill:#444,stroke:#aaa,color:#fff
style E8 fill:#444,stroke:#aaa,color:#fff
style E9 fill:#444,stroke:#aaa,color:#fffChú giải: xanh lá là đã hiện thực, vàng là đề xuất, xám là thiết kế chưa xây. Đường găng tới mọi khoản còn lại là E1 hoặc E2, rồi E3, rồi E4. Sau E4 các khoản E5 đến E9 độc lập nhau, riêng E8 cần thêm phía Collector.
Phụ lục A: Open Questions
| # | Câu hỏi | Hành vi tạm thời | Owner | Mã theo dõi |
|---|---|---|---|---|
| OQ-E1 | Trợ giúp của enroll --token-file chỉ nói "deleted after a successful enroll", nhưng mọi tệp token đã dùng (cờ, AH_LICENSE_FILE, tệp mặc định) đều bị xóa. Có cập nhật trợ giúp và tài liệu không? | Hành vi đúng như mã, chỉ trợ giúp chưa đủ | chưa chỉ định | L3-ENR-OQ1 |
| OQ-E2 | check-config không kiểm kết nối tới Collector, chứng chỉ hay tệp CA. Có thêm tùy chọn ping không (L2 D-04)? | Chỉ kiểm cấu hình cục bộ | chưa chỉ định | L3-ENR-OQ2 |
| OQ-E3 | allow_remote_config mặc định true dù chưa dùng. Khi cấu hình từ xa được xây (D-01), mặc định nên là gì và có cần ký cấu hình? | Không có tác dụng, phản hồi config của enroll bị bỏ | chưa chỉ định | L3-ENR-OQ3 |
| OQ-E4 | enroll --force không thu hồi token cũ ở Collector, và dịch vụ đang chạy giữ token cũ trong RAM. Có cần bước thu hồi hoặc tự khởi động lại dịch vụ? | Ghi trong hướng dẫn: sau --force phải khởi động lại dịch vụ | chưa chỉ định | L3-ENR-OQ4 |
| OQ-E5 | Enroll bằng root tạo credentials.json thuộc root 0600 mà dịch vụ không đọc được. Có cần chown về tài khoản dịch vụ hoặc từ chối chạy bằng root? | Hướng dẫn và postinstall yêu cầu enroll bằng tài khoản dịch vụ | chưa chỉ định | L3-ENR-OQ5 |
| OQ-E6 | Tệp danh tính quyền rộng hoặc hỏng thoát mã 1, không phải mã 4, và tệp token chỉ định mà thiếu cũng mã 1. Có cần mã riêng để script phân biệt? | Mã 1, phải đọc thông điệp | chưa chỉ định | L3-ENR-OQ6 |
| OQ-E7 | credentials.json và muối machine_id chưa có số phiên bản định dạng. Đổi định dạng hoặc thuật toán băm thì nâng cấp thế nào? | Không có. Đổi định dạng buộc enroll lại | chưa chỉ định | L3-ENR-OQ7 |
| OQ-E8 | syncDir bỏ qua lỗi, nên sau mất điện ngay khi đổi tên tệp có thể mất credentials.json mà không báo. Có cần báo hoặc coi là lỗi lưu? | Bỏ qua lỗi | chưa chỉ định | L3-ENR-OQ8 |
| OQ-E9 | Mã thoát, tên cờ, tên biến môi trường và chuỗi thông điệp chưa có phiên bản hợp đồng và chính sách ngừng hỗ trợ, trong khi script gói và hướng dẫn dựa vào chúng | Không đổi nếu không cần | chưa chỉ định | L3-ENR-OQ9 |
| OQ-E10 | Sau SaveError, token đã tiêu hao: Collector có cho enroll lại cùng máy bằng token mới hay trả already_enrolled? Quy trình đặt lại bản ghi ở phía Access Hub là gì? | Thông điệp báo cần token mới, phần còn lại do quản trị viên xử lý | chưa chỉ định | L3-ENR-OQ10 |
| OQ-E11 | Còn khoảng trống kiểm thử: luật Validate theo từng khóa, nhánh reload bị từ chối, khôi phục từ tệp hỏng bằng --force (xem §14) | Chỉ được phủ gián tiếp bởi kiểm thử mẫu đầy đủ và kiểm thử lỗi có số dòng | chưa chỉ định | L3-ENR-OQ11 |
| OQ-E12 | Validate không kiểm tệp ca_file tồn tại, nên SIGHUP đổi ca_file hỏng báo config reloaded rồi log lỗi lặp mỗi chu kỳ (luồng 13). Có kiểm khi nạp không? | Giữ client cũ | chưa chỉ định | L3-ENR-OQ12 |
| OQ-E13 | KnownFields(true) làm agent cũ từ chối khóa mới. Khi phát hành cấu hình chung cho đội nhiều phiên bản, xử lý thế nào (ví dụ bỏ qua có cảnh báo khóa lạ)? | Từ chối (mã 3) | chưa chỉ định | L3-ENR-OQ13 |
| OQ-E14 | config reloaded được log dù một số khóa chỉ có hiệu lực sau khởi động lại (bảng 7.4.5). Có log danh sách khóa đã đổi nhưng cần khởi động lại? | Chỉ log config reloaded | chưa chỉ định | L3-ENR-OQ14 |
| OQ-E15 | Không có khóa liên tiến trình trên state_dir: hai enroll hoặc enroll cùng lúc với dịch vụ có thể giẫm nhau. Có cần flock? (Cùng họ OQ-W11 của L3 WAL và Sender) | Không xử lý | chưa chỉ định | L3-ENR-OQ15 |
| OQ-E16 | Thông báo của service trên Windows nêu AGT-8 nhưng L2 gán dịch vụ Windows cho AGT-7 (AGT-8 là Checks). Sửa thông báo hay sửa L2? ĐÃ XỬ LÝ: thông báo nay nêu AGT-7 | Đã sửa thông báo | đã đóng | L3-ENR-OQ16 |
| OQ-E17 | Chưa có chỉ số cho lệch danh tính, nạp lại bị từ chối, tuổi credentials.json. Có thêm vào bộ thu tự thân không? | Chỉ log | chưa chỉ định | L3-ENR-OQ17 |
| OQ-E18 | Nhãn tĩnh trong agent.yaml kiểm bằng regex rộng hơn giao thức (OQ-C5 ở L3 Collectors). Thống nhất ở đâu? | Theo mã hiện tại | chưa chỉ định | L3-ENR-OQ18 |
| OQ-E19 | Quyền và chủ sở hữu của agent.yaml do gói cài quyết định (L3 Service và Packaging). Cấu hình có thể chứa proxy_url kèm mật khẩu: có yêu cầu 0640 và nhóm dịch vụ không? ĐÃ XỬ LÝ: gói cài 0640 root:accesshub-agent | 0640 root:accesshub-agent | đã đóng | L3-ENR-OQ19 |
Phụ lục B: ADR nội bộ
| Mã ADR | Quyết định | Trạng thái | Động lực |
|---|---|---|---|
| ADR-E01 | Token enroll dùng một lần, đổi lấy token agent dài hạn lưu cục bộ | Đã hiện thực | Giảm phơi nhiễm: token enroll lộ vô hại sau khi dùng. Đánh đổi: token dài hạn nằm dạng rõ trên đĩa (R-02) |
| ADR-E02 | Ghi credentials.json bằng tệp tạm, Chmod 0600 trước khi ghi, Rename | Đã hiện thực | Không có khoảnh khắc token lộ quyền hay nửa vời. Đánh đổi: syncDir bỏ qua lỗi (OQ-E8) |
| ADR-E03 | Load từ chối quyền rộng thay vì tự sửa | Đã hiện thực | Buộc người vận hành biết mình đã lộ, không che giấu. Đánh đổi: mã thoát 1 chưa phân biệt (OQ-E6) |
| ADR-E04 | Băm machine_id có muối, gửi băm cho Collector, so lúc khởi động và dừng gửi nếu lệch | Đã hiện thực | Chống ảnh máy nhân bản mạo danh mà không lộ id thô. Đánh đổi: machine-id nhân bản nguyên vẹn thì không phát hiện được |
| ADR-E05 | Lệch danh tính thì dừng gửi nhưng tiến trình vẫn sống | Đã hiện thực | Tránh vòng khởi động lại của systemd. Đánh đổi: giám sát thấy active dù không có dữ liệu (cùng họ OQ-W7) |
| ADR-E06 | Cấu hình YAML nghiêm ngặt (KnownFields), gom hết lỗi có số dòng, cấu hình sai thì không chạy | Đã hiện thực | Bắt lỗi gõ sai khóa, sửa một lượt. Đánh đổi: khóa mới làm agent cũ từ chối (OQ-E13) |
| ADR-E07 | Thứ tự ưu tiên mặc định, tệp, env, cờ, với env giới hạn 4 biến | Đã hiện thực | Bề mặt nhỏ, dễ dự đoán, đủ cho đóng gói. Đánh đổi: muốn đổi khóa khác phải sửa tệp |
| ADR-E08 | Nạp nóng chỉ thay con trỏ cấu hình nguyên tử, không dựng lại engine hay WAL | Đã hiện thực | Loại rủi ro mất dữ liệu khi dựng lại giữa chu kỳ. Đánh đổi: nhiều khóa cần khởi động lại (OQ-E14) |
| ADR-E09 | enroll không thử lại, mã thoát phân loại để script quyết định | Đã hiện thực | Đơn giản, postinstall không bao giờ làm hỏng gói vì enroll. Đánh đổi: phải chạy lại thủ công khi mạng lỗi |
| ADR-E10 | Token nhận qua --token-stdin hoặc tệp 0600 bị xóa sau dùng, --token chỉ cảnh báo | Đã hiện thực | Tránh lộ trong danh sách tiến trình nhưng không chặn người dùng cần tiện. Đánh đổi: AH_LICENSE lộ trong /proc/<pid>/environ |
Phụ lục C: Section Profile
| Phân mục | Hồ sơ quy chuẩn | Trạng thái điền | Giải trình |
|---|---|---|---|
| §0 Metadata & Sign-off | Bắt buộc | Đã điền | Người ký để "chưa chỉ định" |
| §1 Scope | Bắt buộc | Đã điền | |
| §2 Yêu cầu | Bắt buộc | Đã điền | FR-E01 đến E25, NFR-E01 đến E10, AC-E01 đến E26, QAS-E01 đến E06 |
| §3 Kiến trúc | Bắt buộc | Đã điền | |
| §4 Domain model | Bắt buộc | Đã điền | Hai vùng: danh tính và cấu hình |
| §5 API contract | Bắt buộc | Điền thu gọn | Không có API phục vụ. Hợp đồng gồm CLI, mã thoát, định dạng tệp và lời gọi enroll ra |
| §6 Data schema | Tùy chọn | Đã điền | Lược đồ tệp credentials.json và agent.yaml thay cho CSDL |
| §7 Thuật toán | Bắt buộc | Đã điền | 13 luồng, hai máy trạng thái, sáu thuật toán |
| §8 Xử lý lỗi | Bắt buộc | Đã điền | |
| §9 Suy thoái | Bắt buộc | Đã điền | 9.2: credentials.json không sao lưu, tái cấp bằng enroll |
| §10 Đồng thời | Bắt buộc | Điền thu gọn | Không có giao dịch CSDL, ranh giới là thao tác tệp |
| §11 Bảo mật | Bắt buộc | Đã điền | Nêu thẳng các điểm yếu còn lại (R-02) |
| §12 Cấu hình | Bắt buộc | Đã điền | Không có feature flag hoạt động |
| §13 Telemetry | Bắt buộc | Điền thu gọn | Không phát chỉ số, không probe mạng, không trace phân tán |
| §14 Kiểm thử | Bắt buộc | Đã điền | Có 6 khoảng trống đề xuất |
| §15 Triển khai | Bổ sung | Đã điền |