L3 - Monitoring Platform - Agent - Service và Packaging (Dịch vụ systemd, gói cài đặt, bộ cài)
Thông tin tài liệu đầy đủ
| Trường | Giá trị |
|---|---|
| Tên trang | L3 - Monitoring Platform - Agent - Service và Packaging |
| 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 cộng các tệp AGT-6 chưa commit (internal/svc, internal/cli/service.go, packaging/, scripts/package.sh, các mục package và snapshot trong Makefile, docs/06-packaging-install.md). AGT-6 chưa commit và chưa chạy trên ma trận distro thật, xem L2 D-13 và R-04. Hai kiểm tra thủ công một lần (systemd-analyze verify, dựng gói bằng nfpm) được nêu ở mục 14 |
| 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-8 Service và Packaging (mục 2.3). FR-18, FR-19, FR-24 (phần Linux), L2-NFR-02, 03, 08, 10, 12, 13, 17, rủi ro R-03, R-04, nợ D-05, D-12, D-13. Truy vết tiếp lên L1: L1 HLD mục 2 (Mục tiêu 6), mục 3 (K4, T3, T4, T8) |
| Tài liệu anh em | L3 Collectors, L3 WAL và Sender, L3 Enroll, Credentials và Config |
| 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". Câu nào ghi "suy luận từ mã" nghĩa là rút ra từ việc đọc mã, chưa chạy thật trên máy đích.
0. Front Matter & Approvals
Governance metadata
| Trường | Giá trị |
|---|---|
| Component | CMP-8 Service và Packaging: internal/svc (Manager, RenderUnit, unit nhúng accesshub-agent.service), internal/cli/service.go (lệnh service và uninstall), packaging/ (nfpm.yaml, agent.yaml mẫu, bốn script gói, install.sh, packaging_test.go), scripts/package.sh, scripts/cross-build.sh và các mục build, cross, package, snapshot, checksums của Makefile |
| Truy vết L2 | L2-SAD-agent.md mục 2.3, 3 (FR-18, FR-19, FR-24), 4 (L2-NFR-02, 03, 08, 10, 12, 13, 17), 10 (triển khai), 16 (R-03, R-04, D-05, D-12, D-13) |
| Phân loại rủi ro | Đề xuất, chưa xác nhận: Tier 2. Lý do: thành phần chạy bằng root lúc cài, sinh ra tài khoản hệ thống và thư mục, và là cửa vào chuỗi cung ứng (R-03). Sau khi cài, tiến trình agent chạy không đặc quyền nên mức rủi ro vận hành thấp hơn |
| Data classification | Bí mật khi chuyển License qua biến môi trường, tệp token hoặc ống (postinstall.sh, install.sh). Nội bộ cho agent.yaml (có thể chứa proxy_url kèm mật khẩu, tệp 0640 root:accesshub-agent), unit, gói. Công khai cho nội dung gói và SHA256SUMS |
| Blast radius | Một máy chủ cho mọi lỗi cài đặt và gỡ. Riêng khâu phát hành (package.sh, kho gói, khóa ký) ảnh hưởng mọi máy cài bằng gói đó (R-03). Lỗi nặng nhất: gói bị đánh tráo chạy bằng root trong postinstall. Lỗi purge xóa credentials.json buộc enroll lại một máy |
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 internal/svc, script gói, install.sh, package.sh | Chưa duyệt | chưa có |
| SA hệ thống Agent | chưa chỉ định | Nhất quán với L2, L3 Enroll, docs/06 | Chưa duyệt | chưa có |
| Bảo mật | chưa chỉ định | Hardening unit, quyền tệp, chuỗi xác minh gói, xử lý token trong postinstall | Chưa duyệt | chưa có |
| Vận hành và phát hành | chưa chỉ định | Ma trận distro, kho gói, khóa ký, kênh phát hành (Q2, Q3) | Chưa duyệt | chưa có |
1. Component Scope & Non-Goals
Vai trò: thành phần này là lớp đưa agent lên máy chủ Linux và giữ nó chạy ổn định dưới systemd. Nó gồm bốn việc: (1) một unit systemd đã siết quyền, là nguồn duy nhất cho cả gói lẫn lệnh service install (mẫu một nguồn sự thật nhúng vào tệp chạy), (2) bộ quản lý svc.Manager tạo tài khoản, thư mục, unit và điều khiển dịch vụ cho kiểu cài thủ công từ tệp nhị phân, (3) gói .deb và .rpm do nfpm dựng cùng bốn script gói, tự enroll khi có biến môi trường (mẫu cài không tương tác, không bao giờ làm hỏng gói vì enroll), (4) bộ cài install.sh tải, xác minh chữ ký và checksum rồi gọi trình quản lý gói (mẫu xác minh rồi mới tin). Cùng với package.sh dựng bộ phát hành. Thành phần không có tiến trình nền của riêng nó và không mở cổng nào.
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 hoặc Ansible"]):::infra
REPO(["Nơi phát hành gói: HTTPS hoặc file"]):::infra
SD(["systemd và trình quản lý gói"]):::infra
FS[("Unit, agent.yaml, state, log")]:::datastore
subgraph BC["Service và Packaging"]
INS["install.sh"]:::owned
PKG["Gói deb rpm và script gói"]:::owned
SVC["svc.Manager và lệnh service"]:::owned
UNIT["Unit systemd đã siết"]:::sensitive
end
ENR["Enroll CLI: enroll --token-stdin"]:::bc
RT["Runtime: run"]:::bc
ADM -->|"chạy"| INS
INS -->|"tải và xác minh"| REPO
INS -->|"cài gói"| SD
SD -->|"giải nén và chạy script"| PKG
PKG -->|"enroll trong postinstall"| ENR
PKG -->|"enable và start"| SD
ADM -->|"service install"| SVC
SVC -->|"ghi unit, tạo thư mục"| FS
UNIT -->|"ExecStart run"| RTMô tả quan hệ
| Chiều | Bên | Nội dung |
|---|---|---|
| Vào | Quản trị viên | Lệnh install.sh với cờ (--version, --base-url, --pubkey, --allow-unsigned, --proxy, --no-start, --dry-run), biến môi trường (ACCESSHUB_COLLECTOR_URL, ACCESSHUB_LICENSE, ACCESSHUB_LICENSE_FILE, ACCESSHUB_PROXY_URL, ACCESSHUB_NO_START, ACCESSHUB_DOWNLOAD_BASE, ACCESSHUB_PUBKEY_FILE), lệnh accesshub-agent service <install|uninstall|start|stop|restart> và accesshub-agent uninstall [--purge] |
| Vào | Trình quản lý gói | Đối số script (deb: configure, upgrade, remove, purge, deconfigure. rpm: số 0 hoặc 1) |
| Vào | Nơi phát hành | latest, SHA256SUMS, SHA256SUMS.sig, gói .deb hoặc .rpm (HTTPS hoặc file://). Kho thật chưa có (L2 Q2) |
| Vào | Người phát hành | Mã nguồn, nfpm, VERSION, COMMIT, DATE, SIGN_HOOK cho package.sh |
| Ra | Hệ tệp | /usr/bin/accesshub-agent, unit (/lib/systemd/system cho deb, /usr/lib/systemd/system cho rpm, hoặc /etc/systemd/system do service install), /etc/accesshub-agent/agent.yaml, /var/lib/accesshub-agent, /var/log/accesshub-agent, tài khoản accesshub-agent |
| Ra | systemd | daemon-reload, enable, enable --now, try-restart, disable --now, start, stop, restart |
| Ra | CLI enroll | accesshub-agent enroll --token-stdin chạy bằng tài khoản dịch vụ, token đi qua ống |
| Ra | Người phát hành | Bộ phát hành: .deb, .rpm, .tar.gz cho amd64 và arm64, install.sh, SHA256SUMS, tùy chọn SHA256SUMS.sig |
Trong phạm vi (bao phủ)
| Trong phạm vi | Ghi chú |
|---|---|
Unit accesshub-agent.service với hardening, trần tài nguyên, chính sách khởi động lại theo mã thoát | internal/svc/accesshub-agent.service. ĐÃ HIỆN THỰC |
Nhúng unit vào tệp chạy và viết lại ExecStart theo đường dẫn tệp chạy | internal/svc/unit.go (PackagedUnit, RenderUnit). ĐÃ HIỆN THỰC |
Tạo tài khoản, thư mục, ghi unit, daemon-reload, enable; gỡ, tùy chọn purge; start, stop, restart | internal/svc/systemd.go (Manager). ĐÃ HIỆN THỰC (Linux) |
Lệnh service install|uninstall|start|stop|restart và uninstall | internal/cli/service.go. ĐÃ HIỆN THỰC (Linux). Chưa commit (D-13) |
Cấu hình gói nfpm: tệp, chế độ, thư mục state, config|noreplace, script | packaging/nfpm.yaml. ĐÃ HIỆN THỰC. Kiểm thủ công bằng bản dựng deb và rpm một lần |
Bốn script gói: preinstall, postinstall, preremove, postremove | packaging/scripts/. ĐÃ HIỆN THỰC. Kiểm thử bằng shim, không cài gói thật |
Cấu hình mẫu agent.yaml | internal/svc/agent.yaml (nhúng vào binary để service install ghi được, cũng là nguồn của gói). ĐÃ HIỆN THỰC |
Bộ cài install.sh (Debian và RHEL) | packaging/install.sh. ĐÃ HIỆN THỰC. Đường su và đường rpm của install.sh chưa có kiểm thử |
Dựng bộ phát hành: hai kiến trúc, deb, rpm, tar.gz, SHA256SUMS, móc ký | scripts/package.sh, make package, make snapshot. ĐÃ HIỆN THỰC |
| Dựng chéo bốn mục tiêu và kiểm trần 20 MiB | scripts/cross-build.sh, make cross, job CI cross-build. ĐÃ HIỆN THỰC |
Bản ghi docs/06-packaging-install.md | Tài liệu vận hành gói. Đã đối chiếu với mã |
Ma trận distro thật, kiểm .rpm bằng rpm, commit AGT-6 | ĐỀ XUẤT (R-04, D-13) |
| Job CI dựng, ký gói, đẩy kho, SBOM, kênh beta và stable | THIẾT KẾ, CHƯA XÂY (AGT-11, D-12) |
Dịch vụ Windows, MSI (WiX), install.ps1, Authenticode | THIẾT KẾ, CHƯA XÂY (AGT-7, L2 D-05). Lệnh service trên Windows trả thông báo và mã 1 |
| Cập nhật tự động có chữ ký | THIẾT KẾ, CHƯA XÂY (AGT-12, FR-25), không thuộc tài liệu này |
Ngoài phạm vi
| Không thuộc BC | Thuộc về |
|---|---|
Nội dung lệnh enroll, thứ tự nhận token, ghi credentials.json, nạp agent.yaml | L3 Enroll, Credentials và Config |
| Vòng chu kỳ, WAL, gửi, tín hiệu SIGHUP | L3 WAL và Sender, Runtime Core (L2 D-14) |
Đọc /proc, danh sách trắng chỉ số | L3 Collectors |
| Kho gói (apt, yum), khóa ký, chọn nơi lưu, kênh phát hành | Hệ phát hành bên ngoài agent (L2 Q2, Q3, AGT-11) |
| Phát hành và ràng buộc License | Access Hub và Collector |
| Công cụ quản lý cấu hình hàng loạt (Ansible, Puppet) | Quản trị viên. Thành phần chỉ cam kết giao diện môi trường và mã thoát ổn định |
2. Detailed Requirements & Acceptance Criteria
Yêu cầu chức năng (FR-P)
| # | Trách nhiệm | Giải thích | Hiện thực ở |
|---|---|---|---|
| FR-P01 | Unit một nguồn | Một tệp unit duy nhất được nhúng vào tệp chạy (go:embed) và được nfpm đóng gói, không có bản sao thứ hai | internal/svc/unit.go, nfpm.yaml (ĐÃ HIỆN THỰC) |
| FR-P02 | Hardening và trần tài nguyên | Chạy bằng accesshub-agent, không capability, ProtectSystem=strict, bộ lọc syscall, chỉ AF_INET, AF_INET6, AF_UNIX, MemoryMax=96M, CPUQuota=25%, TasksMax=128, LimitNOFILE=1024 | accesshub-agent.service (ĐÃ HIỆN THỰC) |
| FR-P03 | Chính sách khởi động lại | Restart=on-failure cách 5 giây, dừng hẳn với mã 2, 3, 4, 5, giới hạn 10 lần trong 300 giây | accesshub-agent.service (ĐÃ HIỆN THỰC) |
| FR-P04 | Cài dịch vụ thủ công | service install kiểm root, đường dẫn tuyệt đối, tạo tài khoản và thư mục, ghi unit viết lại ExecStart, daemon-reload, tùy chọn enable, không bao giờ start | svc.Manager.Install, cli.serviceInstall (ĐÃ HIỆN THỰC) |
| FR-P05 | Gỡ dịch vụ thủ công | service uninstall và uninstall: disable --now, xóa unit, daemon-reload. Với --purge xóa thêm state, log, cấu hình và tài khoản | svc.Manager.Uninstall (ĐÃ HIỆN THỰC) |
| FR-P06 | Điều khiển dịch vụ | service start|stop|restart qua systemctl, yêu cầu root, từ chối hành động lạ | svc.Manager.Control (ĐÃ HIỆN THỰC) |
| FR-P07 | Gói deb và rpm | Một cấu hình nfpm cho hai định dạng và hai kiến trúc, đặt unit đúng thư mục theo định dạng, agent.yaml là config|noreplace, thư mục state 0750 | packaging/nfpm.yaml, scripts/package.sh (ĐÃ HIỆN THỰC) |
| FR-P08 | Script gói idempotent | preinstall tạo tài khoản, postinstall chuẩn bị thư mục và (nếu có biến môi trường) cấu hình, enroll, khởi động, preremove chỉ dừng khi gỡ thật, postremove chỉ xóa dữ liệu khi deb purge | packaging/scripts/*.sh (ĐÃ HIỆN THỰC) |
| FR-P09 | Cài không tương tác | Với ACCESSHUB_COLLECTOR_URL và token, postinstall ghi collector_url, enroll bằng tài khoản dịch vụ qua ống và enable --now. Enroll lỗi không làm hỏng gói | postinstall.sh (ĐÃ HIỆN THỰC) |
| FR-P10 | Kiểm giá trị trước khi ghi YAML | URL phải bắt đầu https:// và chỉ gồm ký tự trong danh sách trắng, nếu không thì bỏ qua enroll | valid_value, set_key (ĐÃ HIỆN THỰC) |
| FR-P11 | Bộ cài xác minh | install.sh bắt buộc khóa công khai hoặc --allow-unsigned, luôn kiểm checksum, chọn tên gói từ SHA256SUMS đã xác minh | packaging/install.sh (ĐÃ HIỆN THỰC) |
| FR-P12 | Bộ phát hành | package.sh dựng deb, rpm, tar.gz cho amd64 và arm64, install.sh, SHA256SUMS, và gọi SIGN_HOOK nếu có | scripts/package.sh (ĐÃ HIỆN THỰC) |
| FR-P13 | Phiên bản gói | Tag vX.Y.Z thành X.Y.Z, mọi thứ khác thành 0.0.0-dev.<7 ký tự commit>, ký tự lạ thành dấu chấm | scripts/package.sh (ĐÃ HIỆN THỰC) |
| FR-P14 | Kiểm trần kích thước | Dựng chéo bốn mục tiêu và thất bại nếu vượt MAX_BYTES (mặc định 20 MiB) | scripts/cross-build.sh, job cross-build (ĐÃ HIỆN THỰC) |
| FR-P15 | Ma trận distro và kiểm rpm | Chạy gói trên các distro mục tiêu và kiểm .rpm bằng rpm | ĐỀ XUẤT (R-04) |
| FR-P16 | Job CI đóng gói và ký | CI dựng, ký, đẩy kho, SBOM | THIẾT KẾ, CHƯA XÂY (AGT-11, D-12) |
| FR-P17 | Dịch vụ Windows và MSI | Cài, gỡ, điều khiển trên Windows, MSI, install.ps1 | THIẾT KẾ, CHƯA XÂY (AGT-7, D-05) |
Yêu cầu phi chức năng (NFR-P)
| NFR | Target | Parent L2-NFR (Kiểu) | Satisfied-by |
|---|---|---|---|
| NFR-P01 | Tiến trình agent chạy bằng tài khoản accesshub-agent, CapabilityBoundingSet rỗng, NoNewPrivileges=yes, ProtectSystem=strict | L2-NFR-12 (Security) | Unit, TestPackagedUnitCarriesRequiredHardening |
| NFR-P02 | Không lắng nghe: RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX, không có tham số mở cổng | L2-NFR-08 (Security) | Unit |
| NFR-P03 | Trần tài nguyên cứng MemoryMax=96M, CPUQuota=25%, TasksMax=128, LimitNOFILE=1024. Ngân sách vận hành (50 MB RSS, 1% một lõi) thấp hơn nhiều | L2-NFR-02, L2-NFR-01 (Performance) | Unit. Xem OQ-P4 về chênh lệch với limits.memory_limit |
| NFR-P04 | Dừng êm: TimeoutStopSec=30, gửi SIGTERM, runtime dừng trong 10 giây | L2-NFR-10 (Availability) | Unit. Không có kiểm thử đo 10 giây |
| NFR-P05 | Tệp chạy không quá 20 MiB cho bốn mục tiêu | L2-NFR-03 (Footprint) | cross-build.sh, job cross-build |
| NFR-P06 | Tệp tĩnh CGO_ENABLED=0, gói không phụ thuộc thư viện ngoài | L2-NFR-17 (Portability) | package.sh. Ma trận distro chưa chạy (OQ-P12) |
| NFR-P07 | Chuỗi cung ứng: không bao giờ bỏ qua kiểm checksum, không chữ ký thì dừng trừ khi có --allow-unsigned tường minh | R-03 của L2 (Security) | install.sh, TestInstallRefusesWithoutKeyOrOptOut, TestInstallAllowUnsignedStillChecksums, TestInstallVerifiesSignature |
| NFR-P08 | Token cài đặt không đi qua argv: bộ cài từ chối --token, postinstall đưa token vào ống stdin | L2-NFR-13 (Security) | install.sh, postinstall.sh, TestPostinstallEnrollsThroughStdinAndStarts, TestInstallRejectsUnsafeInput |
| NFR-P09 | Idempotent: cài lại hoặc nâng cấp giữ credentials.json và agent.yaml, chạy lại preinstall và postinstall an toàn | L2 mục 10.3 (Reliability) | config|noreplace, TestPostinstallKeepsExistingCredentials, TestPostinstallReplacesExistingCollectorURLOnce |
| NFR-P10 | Lỗi enroll không làm hỏng gói: postinstall luôn thoát 0 ở nhánh enroll lỗi | ADR-E09 của L3 Enroll (Reliability) | TestPostinstallEnrollFailureDoesNotFailPackageOrStart |
| NFR-P11 | Không khởi động lại vô hạn với lỗi không thể tự khỏi: mã 2, 3, 4, 5 dừng hẳn, còn lại tối đa 10 lần trong 300 giây | L2 mục 12.2 (Reliability) | Unit (RestartPreventExitStatus, StartLimitBurst). Không có kiểm thử hành vi thật |
| NFR-P12 | Một nguồn unit: nội dung unit trong gói và trong service install giống hệt, chỉ khác ExecStart | L2 mục 2.2 (Maintainability) | go:embed, TestRenderUnitRewritesOnlyTheBinary, TestNfpmSourcesExist |
Tiêu chí nghiệm thu (AC-P)
| AC | Given / When / Then | Truy vết → Test ID |
|---|---|---|
| AC-P01 | Given unit đóng gói, When đọc nội dung, Then chứa User=accesshub-agent, các chỉ thị hardening, trần tài nguyên, RestartPreventExitStatus=2 3 4 5, WantedBy=multi-user.target và không có dấu gạch ngang dài | FR-P02, FR-P03, NFR-P01 → TestPackagedUnitCarriesRequiredHardening |
| AC-P02 | Given đường dẫn tệp chạy khác mặc định, When RenderUnit, Then chỉ dòng ExecStart đổi, ExecReload giữ nguyên, đường dẫn mặc định cho ra unit không đổi | FR-P01, FR-P04 → TestRenderUnitRewritesOnlyTheBinary |
| AC-P03 | Given root và tệp chạy hợp lệ, When Install, Then tài khoản, thư mục, unit được tạo và daemon-reload chạy | FR-P04 → TestInstallCreatesAccountDirsUnitAndReloads |
| AC-P04 | Given tài khoản đã có và không truyền --enable, When Install, Then không tạo lại tài khoản và không enable | FR-P04 → TestInstallKeepsExistingAccountAndSkipsEnableByDefault |
| AC-P05 | Given không phải root hoặc đường dẫn tương đối, When Install, Then từ chối | FR-P04 → TestInstallRequiresRootAndAbsoluteBinary, TestServiceInstallHonorsBinaryFlagAndRejectsRelative |
| AC-P06 | Given không có useradd, When Install, Then báo lỗi rõ cách tự tạo tài khoản | FR-P04 → TestInstallFailsClearlyWithoutUseradd |
| AC-P07 | Given dịch vụ đã cài, When Uninstall không purge, Then dữ liệu còn, và với --purge thì state, log, cấu hình và tài khoản bị xóa | FR-P05 → TestUninstallKeepsDataUnlessPurge |
| AC-P08 | Given dịch vụ chưa nạp, When Uninstall, Then chỉ cảnh báo, không lỗi | FR-P05 → TestUninstallToleratesServiceThatIsNotLoaded |
| AC-P09 | Given hành động lạ hoặc không phải root, When Control, Then từ chối | FR-P06 → TestControlValidatesActionAndRoot, TestServiceNeedsRootAndAKnownAction |
| AC-P10 | Given lệnh service install --enable không có --binary, When chạy, Then unit dùng tệp chạy hiện tại, systemctl enable được gọi (không start) và hướng dẫn enroll được in | FR-P04 → TestServiceInstallWritesUnitForThisExecutable |
| AC-P11 | Given hệ điều hành không phải Linux, When service hoặc uninstall, Then thông báo và mã 1 | FR-P17 → TestServiceIsLinuxOnlyForNow |
| AC-P12 | Given service start|stop|restart và uninstall, When chạy qua CLI, Then gọi đúng systemctl | FR-P05, FR-P06 → TestServiceControlAndUninstall |
| AC-P13 | Given bốn script và install.sh, When kiểm cú pháp, Then phân tích được và có quyền thực thi, và sạch shellcheck khi có công cụ | FR-P08 → TestScriptsParseAndAreExecutable, TestShellcheckClean |
| AC-P14 | Given nfpm.yaml, When kiểm, Then mọi src và script được tham chiếu tồn tại và agent.yaml mẫu hợp lệ khi có collector_url | FR-P07 → TestNfpmSourcesExist, TestSampleConfigIsValidWithCollectorOverride |
| AC-P15 | Given không có biến môi trường, When postinstall, Then chỉ chuẩn bị thư mục, không enroll, không enable --now | FR-P08, FR-P09 → TestPostinstallWithoutEnvOnlyPreparesDirectories |
| AC-P16 | Given URL và token hợp lệ, When postinstall, Then token đi qua stdin, enroll bằng tài khoản dịch vụ và dịch vụ được enable --now | FR-P09, NFR-P08 → TestPostinstallEnrollsThroughStdinAndStarts |
| AC-P17 | Given collector_url đã có, When postinstall chạy lại, Then khóa được thay một lần, không nhân đôi | FR-P10, NFR-P09 → TestPostinstallReplacesExistingCollectorURLOnce |
| AC-P18 | Given tệp token, When enroll thành công, Then tệp bị xóa | FR-P09 → TestPostinstallTokenFileIsRemovedAfterEnroll |
| AC-P19 | Given đã có credentials.json, When postinstall, Then giữ nguyên, không enroll lại | NFR-P09 → TestPostinstallKeepsExistingCredentials |
| AC-P20 | Given enroll lỗi, When postinstall, Then thoát 0, không khởi động, gói vẫn được cài | NFR-P10 → TestPostinstallEnrollFailureDoesNotFailPackageOrStart |
| AC-P21 | Given ACCESSHUB_NO_START=1, When postinstall, Then chỉ enable, không start | FR-P09 → TestPostinstallNoStartOnlyEnables |
| AC-P22 | Given URL hoặc proxy chứa ký tự ngoài danh sách trắng hoặc không phải https, When postinstall, Then bỏ qua enroll và không sửa agent.yaml | FR-P10 → TestPostinstallRejectsUnsafeValues |
| AC-P23 | Given chỉ có URL hoặc chỉ có token, When postinstall, Then không enroll | FR-P09 → TestPostinstallNeedsBothURLAndToken |
| AC-P24 | Given preremove và postremove với đối số nâng cấp, gỡ thường và purge, When chạy, Then chỉ dừng khi gỡ thật và chỉ xóa dữ liệu khi purge | FR-P08 → TestPreremoveOnlyStopsOnRealRemoval, TestPostremoveKeepsDataUnlessPurge |
| AC-P25 | Given không có khóa công khai và không có --allow-unsigned, When install.sh, Then dừng. Với --allow-unsigned vẫn từ chối gói sai checksum | FR-P11, NFR-P07 → TestInstallRefusesWithoutKeyOrOptOut, TestInstallAllowUnsignedStillChecksums |
| AC-P26 | Given khóa công khai và chữ ký, When install.sh, Then chữ ký đúng mới cài, sai thì từ chối | FR-P11 → TestInstallVerifiesSignature |
| AC-P27 | Given đầu vào xấu (token trên dòng lệnh, URL, phiên bản, tên gói), When install.sh, Then từ chối | FR-P11, NFR-P08 → TestInstallRejectsUnsafeInput |
Các AC cho FR-P12 đến FR-P14 (package.sh, cross-build.sh) không có kiểm thử tự động trong kho, chỉ có kiểm tra thủ công một lần (mục 14). Các AC cho FR-P15 đến FR-P17 chưa có vì chức năng chưa xây.
Kịch bản chất lượng (QAS-P)
| Mã kịch bản / NFR | Nguồn & Kích thích | Môi trường | Phản hồi | Thước đo |
|---|---|---|---|---|
| QAS-P01 / NFR-P09, NFR-P10 | Quản trị viên chạy install.sh hàng loạt với URL và token lỗi trên một máy | Cài đặt thông thường, mạng tới Collector hỏng | Gói vẫn cài xong, dịch vụ không khởi động, postinstall in cách enroll lại | Mã thoát của trình quản lý gói là 0 (TestPostinstallEnrollFailureDoesNotFailPackageOrStart). Tỉ lệ thành công trên đội máy chưa có số liệu |
| QAS-P02 / NFR-P07 | Kẻ tấn công thay gói trên kho bằng gói khác | Kho gói bị chiếm | install.sh dừng trước khi gọi trình quản lý gói | Chữ ký SHA256SUMS sai hoặc checksum lệch đều dẫn tới thoát 1 (kiểm thử shim, không phải kho thật) |
| QAS-P03 / NFR-P01 | Mã agent bị khai thác | Dịch vụ đang chạy | Tiến trình bị giam: không capability, chỉ ghi được state và log, không tạo được socket ngoài ba họ địa chỉ | Kiểm bằng systemd-analyze verify (chỉ cú pháp). Chưa có kiểm thử xâm nhập hay systemd-analyze security (đề xuất) |
| QAS-P04 / NFR-P11 | Cấu hình sai sau khi sửa tay | Dịch vụ chạy | run thoát mã 3 và systemd không khởi động lại | Dừng hẳn sau lần thoát đầu nhờ RestartPreventExitStatus. Lỗi mã 1 thì thử lại tối đa 10 lần trong 300 giây |
| QAS-P05 / NFR-P09 | Nâng cấp gói khi dịch vụ đang chạy | Nâng cấp thường | Giữ cấu hình và danh tính, khởi động lại dịch vụ bằng tệp chạy mới | try-restart chạy trong postinstall. Thời gian gián đoạn chưa có số liệu |
| QAS-P06 / NFR-P09 | Gỡ gói bằng remove rồi cài lại | Gỡ thường | Không cần enroll lại | TestPostremoveKeepsDataUnlessPurge. Với rpm không có purge (OQ-P8) |
3. Kiến trúc ứng dụng
3.1. Kiến trúc runtime
Thành phần này không có tiến trình thường trực. Nó là một tập lệnh chạy ngắn, theo ba đường vào: (1) trình quản lý gói chạy bốn script, (2) quản trị viên chạy install.sh (chạy một lần rồi thoát, gọi trình quản lý gói), (3) quản trị viên chạy accesshub-agent service ... trực tiếp. Sau khi cài xong, thứ còn lại trên máy là unit systemd, và systemd là bên giữ tiến trình run chạy.
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
INS["install.sh"]:::owned
PM(["dpkg apt rpm dnf yum"]):::infra
SCR["Script gói: pre và post"]:::owned
MGR["svc.Manager"]:::owned
UNIT["Unit đã siết"]:::sensitive
SD(["systemd"]):::infra
FS[("agent.yaml, credentials, state")]:::datastore
ENR["enroll --token-stdin"]:::bc
INS -->|"cài gói"| PM
PM -->|"chạy script"| SCR
SCR -->|"ghi khóa, tạo thư mục"| FS
SCR -->|"runuser enroll"| ENR
ENR -->|"credentials 0600"| FS
SCR -->|"enable và try-restart"| SD
MGR -->|"ghi unit, systemctl"| SD
MGR -->|"tạo thư mục"| FS
SD -->|"nạp"| UNITChú giải: nét liền là lời gọi đồng bộ trong cùng một tiến trình shell hoặc Go. Toàn bộ tác động phụ của svc.Manager đi qua các trường hàm (Run, LookPath, Lookup, Chown, Euid) nên thay được trong kiểm thử. Script gói đọc ACCESSHUB_PKG_ROOT và ACCESSHUB_BIN (chỉ để kiểm thử) để chạy không cần quyền root.
Bảng connector
| Connector | Từ | Tới | Cơ chế | Đồng bộ | Ghi chú |
|---|---|---|---|---|---|
| CN-P1 | install.sh | Nơi phát hành | curl -fsSL --proto '=https,file' --tlsv1.2, tùy chọn -x proxy | Đồng bộ | Tải latest, SHA256SUMS, SHA256SUMS.sig, gói. Lỗi tải thì die |
| CN-P2 | install.sh | Trình quản lý gói | apt-get install -y, dpkg -i, dnf install -y, yum install -y, rpm -U | Đồng bộ | Chọn theo công cụ có sẵn. Biến môi trường được export để script gói đọc |
| CN-P3 | Trình quản lý gói | Script gói | Đối số vị trí (deb: từ khóa, rpm: số) | Đồng bộ | preremove.sh và postremove.sh phân biệt nâng cấp, gỡ, purge |
| CN-P4 | postinstall.sh | accesshub-agent enroll | runuser -u accesshub-agent -- hoặc su -s /bin/sh -c, token qua ống | Đồng bộ | Enroll chạy bằng tài khoản dịch vụ để credentials.json thuộc về nó. Xem L3 Enroll CN-E2 |
| CN-P5 | Script gói, svc.Manager | systemd | systemctl daemon-reload, enable, enable --now, try-restart, disable --now, start, stop, restart | Đồng bộ | Lỗi systemctl trong script bị nuốt (|| true hoặc cảnh báo). Trong svc.Manager lỗi được trả ra (trừ disable --now và daemon-reload khi gỡ, chỉ cảnh báo) |
| CN-P6 | svc.Manager | Hệ tệp và tài khoản | os.MkdirAll, Chmod, Chown, os.WriteFile, useradd, userdel | Đồng bộ | useradd qua Run để thay được trong kiểm thử |
| CN-P7 | Lệnh service | svc.Manager | newServiceManager(env) | Đồng bộ | Biến hàm để kiểm thử thay bằng bản giả (serviceOS, executablePath cũng vậy) |
| CN-P8 | package.sh | go build, nfpm | Dựng tĩnh cho hai kiến trúc, nfpm package --packager deb|rpm | Đồng bộ | nfpm không mở rộng biến môi trường trong contents.src nên package.sh dùng sed thay ${AGENT_BINARY} |
3.2. Kiến trúc module
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["cli/service.go"]:::owned
SVC["svc/systemd.go"]:::owned
UNI["svc/unit.go"]:::owned
UF["accesshub-agent.service"]:::sensitive
NFP["packaging/nfpm.yaml"]:::owned
SH["packaging/scripts và install.sh"]:::owned
PK["scripts/package.sh"]:::owned
CLI -->|"Install, Uninstall, Control"| SVC
SVC -->|"RenderUnit"| UNI
UNI -->|"go:embed"| UF
NFP -->|"src unit"| UF
NFP -->|"scripts"| SH
PK -->|"sed và nfpm package"| NFP3.2.1. Cấu trúc: gói svc và lệnh service
| Thành phần | Tệp | Vai trò | Ghi chú |
|---|---|---|---|
ServiceName, PackagedBinary, Account | svc/unit.go, svc/systemd.go | Hằng số: accesshub-agent, /usr/bin/accesshub-agent, accesshub-agent | Đường dẫn tệp chạy và tài khoản cũng được gõ cứng trong unit, nfpm và script |
packagedUnit, PackagedUnit() | svc/unit.go | Nội dung unit nhúng bằng go:embed | Cùng tệp được nfpm đóng gói, một nguồn |
RenderUnit(bin) | svc/unit.go | Thay ExecStart=/usr/bin/accesshub-agent bằng ExecStart=<bin> | Không thoát ký tự đặc biệt trong bin (OQ-P6). Bin rỗng hoặc mặc định thì trả nguyên bản |
Manager | svc/systemd.go | Thư mục (UnitDir, ConfigDir, StateDir, LogDir), hàm tiêm (Euid, Run, LookPath, Lookup, Chown, Log) | NewManager đặt /etc/systemd/system, /etc/accesshub-agent, /var/lib/accesshub-agent, /var/log/accesshub-agent |
Install(InstallOptions) | svc/systemd.go | Kiểm root, đường dẫn tuyệt đối, ensureAccount, ensureDirs, ensureConfig (ghi agent.yaml mẫu 0640 root:accesshub-agent chỉ khi chưa có, kèm collector_url nếu có CollectorURL), ghi unit 0644, daemon-reload, tùy chọn enable | Không start. Không bao giờ sửa agent.yaml đã có (OQ-P3). CollectorURL sai (không https:// hoặc ký tự lạ) trả ErrCollectorURL trước mọi tác dụng phụ |
Uninstall(purge) | svc/systemd.go | disable --now (lỗi chỉ cảnh báo), xóa unit, daemon-reload, nếu purge xóa ba thư mục rồi userdel | Chỉ xóa unit ở UnitDir (OQ-P13) |
Control(action) | svc/systemd.go | start, stop, restart qua systemctl | Từ chối hành động khác và không phải root |
ensureAccount, ensureDirs | svc/systemd.go | useradd --system --user-group --no-create-home --home-dir <state> --shell /usr/sbin/nologin, thư mục state và log 0750 thuộc tài khoản, thư mục cấu hình 0755 | Nếu không có useradd trả lỗi nói cách tự tạo (không thử adduser, khác preinstall.sh) |
ErrNotRoot | svc/systemd.go | Lỗi khi không phải root | CLI in lỗi và thoát mã 1 |
serviceCmd, serviceInstall, serviceUninstall, uninstallCmd, reportServiceError | cli/service.go | Lệnh service, uninstall, cờ --binary, --enable, --collector, --purge | Mọi lỗi dịch vụ thoát mã 1, sai cách dùng mã 2. Windows: thông báo nêu AGT-7 (đã sửa, OQ-P9) |
3.2.2. Cấu trúc: gói cài và phát hành
| Thành phần | Tệp | Vai trò | Ghi chú |
|---|---|---|---|
| Cấu hình nfpm | packaging/nfpm.yaml | Tên, kiến trúc, phiên bản (${NFPM_ARCH}, ${PKG_VERSION}), nội dung, script | Unit vào /lib/systemd/system cho deb và /usr/lib/systemd/system cho rpm. agent.yaml kiểu config|noreplace 0640 root:accesshub-agent. Thư mục state 0750 thuộc accesshub-agent. README vào /usr/share/doc. Không có thư mục log (OQ-P7) |
preinstall.sh | packaging/scripts/ | Tạo tài khoản bằng useradd, hoặc adduser, hoặc thoát 1 | Chạy lại an toàn (thoát sớm nếu tài khoản có) |
postinstall.sh | packaging/scripts/ | Tạo thư mục, daemon-reload, try-restart, rồi (nếu có biến) ghi khóa, enroll, enable | Chi tiết ở 7.1 và 7.4 |
preremove.sh | packaging/scripts/ | disable --now chỉ với remove, deconfigure, 0 | Nâng cấp không dừng dịch vụ |
postremove.sh | packaging/scripts/ | daemon-reload, và với purge xóa ba thư mục và userdel | rpm không có purge (OQ-P8) |
agent.yaml mẫu | internal/svc/agent.yaml | Mọi khóa được chú thích, collector_url bị comment | Gói không ghi đè khi nâng cấp |
install.sh | packaging/install.sh | Tải, xác minh, chọn định dạng, cài | Chi tiết ở 5.1 và 7.4 |
package.sh | scripts/package.sh | Dựng bộ phát hành | Yêu cầu nfpm v2.47.0 (thông báo lỗi của script ghi phiên bản này) |
cross-build.sh | scripts/cross-build.sh | Dựng bốn mục tiêu, kiểm trần kích thước | Trần MAX_BYTES mặc định 20971520 |
packaging_test.go | packaging/ | Kiểm thử script bằng shim PATH và thư mục gốc giả | Xem mục 14 |
Mục Makefile | Makefile | build, cross, package, snapshot, checksums | snapshot dựng vào dist/snapshot với phiên bản 0.0.0-snapshot.<sha> |
4. Domain model
Có hai nhóm khái niệm độc lập: dịch vụ trên máy (unit, tài khoản, bố cục thư mục) và phát hành (bộ phát hành, bản kê SHA256SUMS, yêu cầu cài đặt). Mỗi nhóm một sơ đồ.
classDiagram
namespace Vung_Dich_Vu {
class ServiceUnit {
<<Aggregate Root>>
+Name accesshub_agent_service
+ExecStart binary_run
+RestartPolicy on_failure_5s
+NoRestartExit 2_3_4_5
+Hardening strict_no_caps
+Ceilings mem96M_cpu25_tasks128_nofile1024
}
class ServiceAccount {
<<Entity>>
+Name accesshub_agent
+Shell nologin
+Home state_dir
}
class InstallLayout {
<<Value Object>>
+ConfigDir etc_accesshub_agent_0755
+StateDir var_lib_0750
+LogDir var_log_0750
}
class InstallOptions {
<<Value Object>>
+Binary absolute_path
+Enable bool
}
class Manager {
<<Domain Service>>
+Install
+Uninstall
+Control
}
}
Manager --> ServiceUnit : ghi
Manager --> ServiceAccount : tạo nếu thiếu
Manager --> InstallLayout : tạo
InstallOptions ..> Manager : tham số
ServiceUnit ..> ServiceAccount : chạy bằngclassDiagram
namespace Vung_Phat_Hanh {
class ReleaseSet {
<<Aggregate Root>>
+Packages deb_rpm_targz_x_2_arch
+Installer install_sh
+Manifest SHA256SUMS
+Signature optional
}
class PackageVersion {
<<Value Object>>
+Value digit_first
+Source git_describe_or_dev
}
class InstallRequest {
<<Value Object>>
+CollectorURL https_only
+License secret
+ProxyURL optional
+NoStart bool
}
class VerificationChain {
<<Policy>>
+Key pubkey_or_allow_unsigned
+Checksum required
}
}
ReleaseSet *-- PackageVersion : mang
VerificationChain ..> ReleaseSet : kiểm
InstallRequest ..> ReleaseSet : cài từ| Thực thể | Loại | Mô tả | Nơi định nghĩa |
|---|---|---|---|
ServiceUnit | Aggregate Root | Tệp unit systemd duy nhất, nhúng vào tệp chạy và gói | internal/svc/accesshub-agent.service |
ServiceAccount | Entity | Tài khoản hệ thống accesshub-agent, shell nologin, home là thư mục state | svc.Account, preinstall.sh |
InstallLayout | Value Object | Ba thư mục: cấu hình 0755, state 0750, log 0750 | svc.Manager.ensureDirs, postinstall.sh, nfpm.yaml |
InstallOptions | Value Object | Binary tuyệt đối và Enable | svc.InstallOptions |
Manager | Domain Service | Cài, gỡ, điều khiển, mọi tác động phụ qua trường hàm | svc.Manager |
ReleaseSet | Aggregate Root | Kết quả một lần package.sh | scripts/package.sh |
PackageVersion | Value Object | Chuỗi phiên bản bắt đầu bằng chữ số | scripts/package.sh |
InstallRequest | Value Object | Bộ biến môi trường của người cài | install.sh, postinstall.sh |
VerificationChain | Policy | Khóa công khai hoặc --allow-unsigned tường minh, cộng checksum bắt buộc | install.sh |
Bất biến (INV-P)
| Mã | Bất biến | Thi hành bởi |
|---|---|---|
| INV-P1 | Nội dung unit chỉ có một nguồn. RenderUnit chỉ sửa dòng ExecStart, không chạm ExecReload | go:embed, nfpm.yaml, TestRenderUnitRewritesOnlyTheBinary |
| INV-P2 | service install không bao giờ khởi động dịch vụ, vì chưa enroll thì run không chạy được | svc.Manager.Install (chỉ enable khi có --enable), TestInstallKeepsExistingAccountAndSkipsEnableByDefault |
| INV-P3 | License không xuất hiện trên dòng lệnh: install.sh từ chối --token, postinstall đưa token qua ống | install.sh, postinstall.sh, TestInstallRejectsUnsafeInput, TestPostinstallEnrollsThroughStdinAndStarts |
| INV-P4 | Giá trị ghi vào agent.yaml đi qua danh sách trắng ký tự và collector_url phải là https:// | valid_value, TestPostinstallRejectsUnsafeValues |
| INV-P5 | Enroll trong postinstall chạy bằng tài khoản dịch vụ, để credentials.json thuộc tài khoản mà dịch vụ chạy | run_as (runuser hoặc su), TestPostinstallEnrollsThroughStdinAndStarts |
| INV-P6 | Gỡ thường và nâng cấp giữ credentials.json và agent.yaml. Chỉ deb purge (hoặc uninstall --purge) mới xóa | config|noreplace, postremove.sh, preremove.sh, TestPostremoveKeepsDataUnlessPurge, TestPreremoveOnlyStopsOnRealRemoval, TestUninstallKeepsDataUnlessPurge |
| INV-P7 | Không cài khi chưa xác minh: phải có khóa công khai hoặc --allow-unsigned, và checksum luôn được kiểm | install.sh, TestInstallRefusesWithoutKeyOrOptOut, TestInstallAllowUnsignedStillChecksums |
| INV-P8 | Tên gói được chọn từ SHA256SUMS đã xác minh và không được chứa / hay ký tự lạ | install.sh (PKG_NAME), TestInstallRejectsUnsafeInput |
| INV-P9 | Lỗi enroll không làm hỏng gói: nhánh enroll lỗi thoát 0 | postinstall.sh, TestPostinstallEnrollFailureDoesNotFailPackageOrStart |
| INV-P10 | Phiên bản gói luôn bắt đầu bằng chữ số và không chứa ký tự ngoài A-Za-z0-9.+~- | scripts/package.sh. Không có kiểm thử tự động |
| INV-P11 | Mã thoát 2, 3, 4, 5 không được systemd thử lại | Unit (RestartPreventExitStatus), TestPackagedUnitCarriesRequiredHardening (kiểm chuỗi, không kiểm hành vi) |
| INV-P12 | Lệnh cần quyền hệ thống (Install, Uninstall, Control) từ chối khi không phải root | svc.Manager.needRoot, TestInstallRequiresRootAndAbsoluteBinary, TestControlValidatesActionAndRoot |
5. API Contract Specification
Thành phần không phục vụ API mạng. Hợp đồng gồm: lệnh CLI, cờ, mã thoát, biến môi trường, định dạng bộ phát hành và đối số script gói.
5.1. Operations (Public API)
Lệnh accesshub-agent (chỉ Linux, cần root)
| Lệnh | Cờ | Hiệu ứng | Mã thoát |
|---|---|---|---|
service install | --binary <đường dẫn tuyệt đối> (mặc định: tệp chạy hiện tại đã giải symlink), --enable | Tạo tài khoản và thư mục, ghi /etc/systemd/system/accesshub-agent.service, daemon-reload, tùy chọn enable. Không start. In hướng dẫn enroll | 0 thành công, 1 lỗi (kể cả không phải root, đường dẫn tương đối, thiếu useradd), 2 sai cờ |
service uninstall | --purge | disable --now (lỗi chỉ cảnh báo), xóa unit, daemon-reload. Với --purge xóa thêm state, log, cấu hình, tài khoản | 0, 1, 2 |
uninstall | --purge | Đồng nghĩa service uninstall | 0, 1, 2 |
service start, stop, restart | không | systemctl <hành động> accesshub-agent.service | 0, 1, 2 |
service không đối số | không | In cách dùng | 2 |
service <lạ> | không | Báo hành động lạ | 2 |
| Mọi lệnh trên Windows | không | service management is only implemented for Linux (systemd), the Windows service is planned in AGT-7 (đã sửa, OQ-P9) | 1 |
packaging/install.sh (cần root, trừ --dry-run)
| Cờ hoặc biến | Ý nghĩa | Ghi chú |
|---|---|---|
--version V | Bản cần cài, mặc định đọc từ <base>/latest | Chỉ chấp nhận A-Za-z0-9._+~- |
--base-url URL, ACCESSHUB_DOWNLOAD_BASE | Gốc tải, https:// hoặc file:/// | Mặc định https://downloads.example.com/agent (giữ chỗ, D-12) |
--pubkey FILE, ACCESSHUB_PUBKEY_FILE | Khóa công khai PGP (gpg) hoặc PEM (cosign) | Bắt buộc, trừ khi có --allow-unsigned |
--allow-unsigned | Bỏ kiểm chữ ký, vẫn kiểm checksum | In cảnh báo |
--proxy URL | Proxy cho tải xuống và truyền sang ACCESSHUB_PROXY_URL của agent | Có thể chứa mật khẩu trên dòng lệnh (OQ-P17) |
--no-start | Cài và enable, không khởi động | Truyền sang ACCESSHUB_NO_START=1 |
--dry-run | Tải, xác minh, in lệnh sẽ chạy, không đổi gì | Không cần root |
ACCESSHUB_COLLECTOR_URL, ACCESSHUB_LICENSE, ACCESSHUB_LICENSE_FILE | Đầu vào của enroll tự động | Được export cho script gói. --token bị từ chối |
| Mã thoát | 0 thành công, 1 mọi lỗi (hàm die) | Không có mã riêng cho từng lỗi |
Script gói (do trình quản lý gói gọi)
| Script | Đối số | Hành vi |
|---|---|---|
preinstall.sh | (không dùng) | Tạo tài khoản nếu chưa có. Thoát 1 nếu không có useradd lẫn adduser |
postinstall.sh | (không dùng) | Xem 7.1 và 7.4. Luôn thoát 0 ở nhánh enroll lỗi |
preremove.sh | deb: remove, upgrade, deconfigure, failed-upgrade. rpm: 0 gỡ, 1 nâng cấp | Chỉ remove, deconfigure, 0 mới disable --now |
postremove.sh | deb: purge và các từ khóa khác. rpm: số | Chỉ purge mới xóa dữ liệu và tài khoản |
Dựng phát hành
| Lệnh | Biến | Hiệu ứng |
|---|---|---|
make build, make cross | VERSION, COMMIT, DATE, MAX_BYTES | Dựng tệp chạy. cross dựng bốn mục tiêu và ghi dist/sizes.txt, thất bại khi vượt trần |
make package | VERSION, COMMIT, DATE, NFPM, OUT (mặc định dist/release), SIGN_HOOK | Bộ phát hành đầy đủ |
make snapshot | như trên | Như package với phiên bản 0.0.0-snapshot.<sha>, ra dist/snapshot |
make checksums | không | Tạo lại SHA256SUMS trong dist/release, bỏ qua latest và các tệp SHA256SUMS* |
5.2. Request / Response Schema
Bộ phát hành (một thư mục <base>/<VERSION>/)
| Tệp | Nội dung | Ghi chú |
|---|---|---|
accesshub-agent_<ver>_<arch>.deb | Gói Debian, <arch> là amd64 hoặc arm64 | Mẫu accesshub-agent_*_${ARCH}.deb mà install.sh dùng |
accesshub-agent-<ver>-1.<rpmarch>.rpm | Gói RPM, <rpmarch> là x86_64 hoặc aarch64 | Mẫu accesshub-agent-*.${RPM_ARCH}.rpm. Tên chính xác do nfpm đặt |
accesshub-agent-<ver>-linux-<arch>.tar.gz | Tệp chạy, agent.yaml, unit, README | Không có script gói, cài bằng service install |
install.sh | Bộ cài | Sao chép từ packaging/install.sh, 0755 |
SHA256SUMS | Đầu ra sha256sum cho *.deb *.rpm *.tar.gz install.sh | Không liệt kê chính nó và chữ ký |
SHA256SUMS.sig | Chữ ký tách rời của SHA256SUMS | Chỉ có khi SIGN_HOOK đặt. package.sh thất bại nếu hook không tạo tệp khác rỗng |
<base>/latest | Một dòng chứa chuỗi phiên bản | Do người phát hành tạo, package.sh không tạo (make checksums cố ý bỏ qua) |
Unit (các chỉ thị chính)
| Nhóm | Chỉ thị |
|---|---|
| Khởi động | Type=simple, User, Group=accesshub-agent, ExecStart=/usr/bin/accesshub-agent run, ExecReload=/bin/kill -HUP $MAINPID, After và Wants=network-online.target |
| Khởi động lại | Restart=on-failure, RestartSec=5, RestartPreventExitStatus=2 3 4 5, StartLimitIntervalSec=300, StartLimitBurst=10, TimeoutStopSec=30 |
| Thư mục | StateDirectory và LogsDirectory đều accesshub-agent, mode 0750, ReadWritePaths=/var/lib/accesshub-agent /var/log/accesshub-agent, UMask=0077 |
| Hardening | NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, PrivateDevices, ProtectKernelTunables, ProtectKernelModules, ProtectKernelLogs, ProtectControlGroups, ProtectClock, ProtectHostname, LockPersonality, MemoryDenyWriteExecute, RestrictRealtime, RestrictSUIDSGID, RestrictNamespaces, RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX, SystemCallArchitectures=native, SystemCallFilter=@system-service, CapabilityBoundingSet= và AmbientCapabilities= rỗng |
| Trần tài nguyên | MemoryMax=96M, CPUQuota=25%, TasksMax=128, LimitNOFILE=1024 |
| Cài | WantedBy=multi-user.target |
Theo chú thích trong unit, chỉ thị mới hơn systemd trên máy (ví dụ Rocky 8) bị bỏ qua kèm cảnh báo. Điều này chưa được kiểm trên distro thật (OQ-P12).
Biến môi trường của postinstall.sh
| Biến | Bắt buộc | Kiểm tra | Hiệu ứng |
|---|---|---|---|
ACCESSHUB_COLLECTOR_URL | Cần cùng token | Bắt đầu https://, ký tự trong A-Za-z0-9._~:/?#@%+=&,- | Ghi collector_url: <url> vào agent.yaml |
ACCESSHUB_LICENSE | Hoặc token trực tiếp hoặc tệp | Không kiểm ở script (agent kiểm) | Đưa vào stdin của enroll --token-stdin |
ACCESSHUB_LICENSE_FILE | Xem trên | Phải đọc được | Đọc nội dung rồi xóa an toàn (shred -u, dự phòng rm -f) sau enroll thành công |
ACCESSHUB_PROXY_URL | Không | Cùng danh sách trắng | Ghi proxy_url: "<url>" (có dấu nháy). Sai thì bỏ qua và cảnh báo |
ACCESSHUB_NO_START | Không | Bằng 1 | Chỉ enable, không --now |
ACCESSHUB_PKG_ROOT, ACCESSHUB_BIN | Không | Dành cho kiểm thử | Thêm tiền tố đường dẫn, đổi tệp chạy |
5.3. Error Codes
| Nguồn | Mã | Ý nghĩa | Xử lý |
|---|---|---|---|
Lệnh service và uninstall | 0 | Thành công | |
| 1 | ErrNotRoot, đường dẫn tương đối, thiếu useradd, systemctl lỗi, lỗi ghi tệp, nền tảng không phải Linux | Đọc thông báo. Không có mã riêng (cùng họ OQ-E6) | |
| 2 | Thiếu hành động, hành động lạ, sai cờ | In cách dùng | |
install.sh | 1 | Mọi lỗi qua die: thiếu curl, kiến trúc không hỗ trợ, không có dpkg lẫn rpm, tải lỗi, thiếu khóa, sai chữ ký, sai checksum, đầu vào xấu | Thông báo bắt đầu bằng install.sh: |
| Script gói | 0 | Thành công hoặc enroll lỗi (đã cảnh báo) | Gói vẫn cài |
| 1 | preinstall.sh không tạo được tài khoản. Mọi lệnh lỗi khác khi set -e (ví dụ chown khi tài khoản thiếu) | Trình quản lý gói coi là cài lỗi | |
package.sh | 1 | Thiếu nfpm, SIGN_HOOK không tạo SHA256SUMS.sig, lỗi dựng | Xem thông báo |
cross-build.sh | 1 | Một tệp chạy vượt MAX_BYTES | CI cross-build đỏ |
Dịch vụ run | 2, 3, 4, 5 | Không được systemd khởi động lại | Sửa nguyên nhân rồi systemctl restart. Nghĩa mã: L3 Enroll mục 5.3 |
| 1 và khác | Được thử lại, tối đa 10 lần trong 300 giây | Sau đó unit ở trạng thái failed |
5.4. Versioning
| Đối tượng | Quy tắc |
|---|---|
| Phiên bản gói | Tag vX.Y.Z thành X.Y.Z. Không phải chữ số đầu thì thành 0.0.0-dev.<7 ký tự đầu của commit>. Ký tự ngoài A-Za-z0-9.+~- thành dấu chấm. nfpm đổi -rc.1 thành ~rc.1 theo docs/06 |
| Tệp chạy | Nhúng version.Version, Commit, Date qua -ldflags |
| Unit | Không có số phiên bản. Nâng cấp gói thay thế tệp unit, service install ghi đè bản ở /etc/systemd/system |
install.sh | Không có số phiên bản hợp đồng, cờ và biến môi trường là hợp đồng ngầm. Nằm trong bộ phát hành và được SHA256SUMS bảo vệ (cùng họ OQ-E9) |
Hợp đồng môi trường của postinstall.sh | Không có số phiên bản. Mọi tên biến có tiền tố ACCESSHUB_ |
5.5. Authz - shared responsibility & permission matrix
Không có xác thực ứng dụng. Kiểm soát dựa hoàn toàn vào quyền hệ điều hành.
| Hành động | root | Tài khoản accesshub-agent | Người dùng khác |
|---|---|---|---|
service install, uninstall, start, stop, restart | Được | Bị từ chối (ErrNotRoot) | Bị từ chối |
install.sh (không --dry-run) | Được | Bị từ chối | Bị từ chối |
install.sh --dry-run | Được | Được | Được (chỉ đọc mạng và thư mục tạm) |
| Cài và gỡ gói | Được | Bị từ chối | Bị từ chối (trình quản lý gói) |
enroll trong postinstall | Chạy bằng runuser từ root sang tài khoản dịch vụ | Là người chạy | Không áp dụng |
Đọc agent.yaml | Đọc, ghi | Đọc (0640 nhóm dịch vụ) | Không đọc được (0640 root:accesshub-agent, OQ-P1) |
Đọc credentials.json | Đọc (root bỏ qua quyền) | Đọc, ghi (0600) | Bị từ chối |
Đọc, ghi /var/lib/accesshub-agent | Được | Được (0750) | Bị từ chối |
6. Data Schema (Physical)
Thành phần này ghi và xóa tệp hệ thống. Không có cơ sở dữ liệu. Mục 6.1 mô tả bố cục tệp và quyền thay cho lược đồ.
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 |
|---|---|---|---|
| Gói deb | ServiceUnit, InstallLayout | /lib/systemd/system/accesshub-agent.service 0644 | Unit do gói sở hữu (root) |
| Gói rpm | ServiceUnit, InstallLayout | /usr/lib/systemd/system/accesshub-agent.service 0644 | Cùng nội dung, thư mục khác |
svc.Manager | ServiceUnit | /etc/systemd/system/accesshub-agent.service 0644 | Bản do service install ghi, thắng bản của gói nếu cả hai cùng có (suy luận từ mã, OQ-P13) |
| Gói | Tệp chạy | /usr/bin/accesshub-agent 0755 | Tệp tĩnh |
| Gói | Cấu hình | /etc/accesshub-agent/agent.yaml 0640 root:accesshub-agent (nfpm.yaml), kiểu config|noreplace | Không ghi đè khi nâng cấp. Có thể chứa proxy_url kèm mật khẩu (OQ-P1, trả lời OQ-E19) |
Gói, svc.Manager | InstallLayout | /var/lib/accesshub-agent 0750 accesshub-agent | Chứa credentials.json và wal (xem L3 Enroll, WAL) |
postinstall.sh, svc.Manager, LogsDirectory | InstallLayout | /var/log/accesshub-agent 0750 accesshub-agent | Không có trong nfpm.yaml (OQ-P7). Unit tạo khi chạy, script và svc tạo khi cài |
| Gói | Tài liệu | /usr/share/doc/accesshub-agent/README.md 0644 | |
preinstall.sh, svc.Manager | ServiceAccount | Bản ghi passwd và group accesshub-agent | useradd --system --user-group --no-create-home --home-dir <state> --shell /usr/sbin/nologin |
Lược đồ (tệp và quyền)
classDiagram
namespace He_Thong_Tep {
class usrBin {
path usr_bin_accesshub_agent
mode 0755_root
}
class unitPkg {
path lib_or_usr_lib_systemd_system
mode 0644_root
owner package
}
class unitManual {
path etc_systemd_system
mode 0644_root
owner service_install
}
class etcDir {
path etc_accesshub_agent
mode 0755
}
class agentYaml {
name agent_yaml
mode 0644_root
type config_noreplace
}
class stateDir {
path var_lib_accesshub_agent
mode 0750_service_account
}
class logDir {
path var_log_accesshub_agent
mode 0750_service_account
shipped_by nfpm_no
}
}
etcDir *-- agentYaml : chứa
stateDir ..> etcDir : credentials ở state, cấu hình ở etc
unitManual ..> unitPkg : ưu tiên cao hơn nếu cùng cóGhi chú lược đồ: postinstall.sh và svc.Manager.ensureDirs đặt lại chmod 0750 và chown cho thư mục state và log mỗi lần chạy, còn systemd với StateDirectoryMode=0750 và LogsDirectoryMode=0750 đặt lại khi dịch vụ khởi động. set_key ghi agent.yaml qua tệp tạm agent.yaml.new.<pid> cùng thư mục rồi mv, nên thay thế nguyên tử. Tệp mới luôn 0644 và chủ sở hữu là người chạy script (root).
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 |
|---|---|---|---|
License trong ACCESSHUB_LICENSE | Bí mật | Suốt đời tiến trình cài (môi trường của apt-get, dpkg, script) | Đưa vào ống, không argv. Lộ được qua /proc/<pid>/environ cho root và cùng người dùng (suy luận từ mã, OQ-P18). install.sh từ chối --token |
Tệp token ACCESSHUB_LICENSE_FILE | Bí mật | Xóa an toàn (shred -u) sau enroll thành công. Giữ nguyên nếu enroll lỗi | Quyền tệp do người tạo chọn. Script chỉ kiểm đọc được |
agent.yaml | Nội bộ, có thể chứa mật khẩu proxy | Theo người vận hành. purge xóa | 0640 root:accesshub-agent theo gói, service install và set_key, danh sách trắng ký tự khi ghi (OQ-P1) |
credentials.json | Bí mật | Giữ khi gỡ thường và nâng cấp. Xóa khi deb purge hoặc uninstall --purge. Gói rpm không có purge (OQ-P8) | Thuộc tài khoản dịch vụ, 0600 (L3 Enroll) |
Bộ phát hành, SHA256SUMS, chữ ký | Công khai | Theo kho gói (chưa có, Q2) | Chữ ký và checksum |
| Khóa công khai phát hành | Công khai | Do người vận hành giữ | Tệp cục bộ, kiểm định dạng |
| Khóa ký riêng | Bí mật | Ngoài agent | Ngoài phạm vi (AGT-11, Q3) |
| Nhật ký cài đặt | Nội bộ | Do trình quản lý gói và journald quyết định | Thành phần không tự ghi nhật ký cài đặt |
7. Thuật toán & Luồng nghiệp vụ
7.1. Luồng nghiệp vụ chính (happy path)
Luồng 1: install.sh cài và enroll tự động
sequenceDiagram
participant OP as Quản trị viên
participant INS as install.sh
participant REL as Nơi phát hành
participant PM as Trình quản lý gói
participant PKG as Script gói
participant SD as systemd
OP->>INS: sudo ACCESSHUB_COLLECTOR_URL và TOKEN, install.sh --pubkey key
INS->>REL: tải latest, SHA256SUMS, SHA256SUMS.sig
INS->>INS: gpg hoặc cosign xác minh chữ ký
INS->>INS: chọn tên gói từ SHA256SUMS đã xác minh
INS->>REL: tải gói
INS->>INS: sha256sum khớp dòng trong SHA256SUMS
INS->>PM: export biến ACCESSHUB_*, apt-get hoặc dnf install gói
PM->>PKG: preinstall rồi giải nén rồi postinstall
PKG->>SD: daemon-reload, try-restart
PKG->>PKG: ghi collector_url, enroll --token-stdin bằng tài khoản dịch vụ
PKG->>SD: enable --now
INS-->>OP: in kết quả, thoát 0Thứ tự là bất biến: chữ ký rồi tên gói rồi checksum rồi mới gọi trình quản lý gói. Tên gói lấy từ SHA256SUMS đã xác minh nên kẻ chen được vào thư mục phát hành không đổi được gói nào được cài (INV-P8). Với --dry-run luồng dừng sau bước kiểm checksum và in lệnh cài sẽ chạy. Biến ACCESSHUB_* chỉ được export khi khác rỗng. Mã: packaging/install.sh, TestInstallVerifiesSignature, TestInstallAllowUnsignedStillChecksums.
Luồng 2: cài gói trần rồi enroll thủ công
sequenceDiagram
participant OP as Quản trị viên
participant PM as Trình quản lý gói
participant PKG as Script gói
participant SD as systemd
participant ENR as accesshub-agent enroll
OP->>PM: dpkg -i hoặc rpm -U gói
PM->>PKG: preinstall
PKG-->>PM: tạo tài khoản accesshub-agent nếu thiếu
PM->>PKG: postinstall không có biến môi trường
PKG->>PKG: tạo state và log 0750 thuộc tài khoản
PKG->>SD: daemon-reload, try-restart
PKG-->>PM: thoát 0, không enroll, không khởi động
OP->>ENR: sudo -u accesshub-agent accesshub-agent enroll --token-file file
ENR-->>OP: enrolled as agent id
OP->>SD: systemctl enable --now accesshub-agentCách này tách hẳn cài và cấu hình. Sau bước cuối, agent.yaml phải có collector_url (gói chỉ cài tệp mẫu có dòng này bị comment), nên quản trị viên phải sửa tệp trước khi enroll (xem luồng 8 về cái bẫy của lệnh service install). Kiểm thử: TestPostinstallWithoutEnvOnlyPreparesDirectories.
Luồng 3: service install thủ công (tệp chạy không qua gói)
sequenceDiagram
participant OP as Quản trị viên root
participant CLI as serviceInstall
participant MGR as svc.Manager
participant OS as Hệ điều hành
participant SD as systemd
OP->>CLI: accesshub-agent service install --enable
CLI->>MGR: Install(Binary, Enable)
MGR->>MGR: kiểm root và đường dẫn tuyệt đối
MGR->>OS: getent hoặc Lookup rồi useradd nếu chưa có
MGR->>OS: tạo state và log 0750 chown, config 0755
MGR->>OS: ghi etc systemd system accesshub-agent.service 0644
MGR->>SD: systemctl daemon-reload
MGR->>SD: systemctl enable accesshub-agent.service
MGR-->>CLI: nil
CLI-->>OP: hướng dẫn enroll rồi systemctl enable --nowInstall không bao giờ start (INV-P2). Nó ghi agent.yaml mẫu (0640 root:accesshub-agent) khi chưa có tệp, và không bao giờ đụng tệp đã có (OQ-P3). Cờ --collector URL điền collector_url vào tệp mới. Nội dung unit là RenderUnit(Binary), tức unit nhúng với ExecStart trỏ tới đường dẫn tuyệt đối đã giải symlink. Kiểm thử: TestInstallCreatesAccountDirsUnitAndReloads, TestServiceInstallWritesUnitForThisExecutable, TestInstallWritesCollectorURLIntoNewConfigOnly, TestInstallRejectsUnsafeCollectorURL, TestServiceInstallCollectorFlag.
Luồng 4: nâng cấp gói khi dịch vụ đang chạy
sequenceDiagram
participant PM as Trình quản lý gói
participant PRE as preremove
participant PKG as preinstall và postinstall
participant SD as systemd
PM->>PKG: preinstall bản mới
PKG-->>PM: tài khoản đã có, thoát 0
PM->>PRE: preremove của bản cũ, đối số upgrade hoặc 1
PRE-->>PM: không làm gì, dịch vụ vẫn chạy
PM->>PM: thay tệp chạy và unit, giữ agent.yaml nhờ noreplace
PM->>PKG: postinstall
PKG->>SD: daemon-reload
PKG->>SD: try-restart accesshub-agent
SD-->>PKG: dịch vụ chạy lại bằng tệp chạy mớitry-restart chỉ khởi động lại dịch vụ đang chạy, nên nâng cấp không vô tình bật một dịch vụ đã tắt (TestPreremoveOnlyStopsOnRealRemoval kiểm phần preremove, phần try-restart được kiểm gián tiếp qua lệnh systemctl mà shim ghi lại ở TestPostinstallWithoutEnvOnlyPreparesDirectories). credentials.json không bị đụng tới (QAS-P05).
Luồng 5: gỡ gói thường rồi gỡ sạch (deb purge)
sequenceDiagram
participant OP as Quản trị viên
participant PM as dpkg
participant PRE as preremove
participant POST as postremove
participant SD as systemd
participant FS as Hệ tệp
OP->>PM: apt-get remove accesshub-agent
PM->>PRE: preremove remove
PRE->>SD: disable --now
PM->>PM: xóa tệp của gói, giữ cấu hình đã sửa
PM->>POST: postremove remove
POST->>SD: daemon-reload
Note over FS: state, log, config và tài khoản vẫn còn
OP->>PM: apt-get purge accesshub-agent
PM->>POST: postremove purge
POST->>FS: xóa state, log, etc accesshub-agent
POST->>FS: userdel accesshub-agentGỡ thường giữ credentials.json nên cài lại không cần enroll lại (QAS-P06), nhưng preremove đã disable nên dịch vụ phải được bật lại (systemctl enable --now) hoặc cài lại với đủ biến môi trường (nhánh "already enrolled" của postinstall vẫn enable --now). Điều này suy luận từ mã, chưa chạy thật. Kiểm thử: TestPostremoveKeepsDataUnlessPurge. Với rpm không có bước purge, dùng accesshub-agent uninstall --purge trước khi gỡ gói (OQ-P8, OQ-P13).
Luồng 6: package.sh dựng bộ phát hành
sequenceDiagram
participant DEV as Người phát hành
participant PK as package.sh
participant GO as go build
participant NF as nfpm
participant HOOK as SIGN_HOOK
DEV->>PK: VERSION=v1.2.3 make package
PK->>PK: suy ra pkg_version, kiểm có nfpm
loop amd64 và arm64
PK->>GO: CGO_ENABLED=0 go build với ldflags phiên bản
PK->>NF: sed thay AGENT_BINARY rồi nfpm package deb và rpm
PK->>PK: tạo tar.gz gồm tệp chạy, agent.yaml, unit, README
end
PK->>PK: chép install.sh, sha256sum ra SHA256SUMS
PK->>HOOK: SIGN_HOOK đường dẫn SHA256SUMS nếu có
HOOK-->>PK: SHA256SUMS.sig khác rỗng
PK-->>DEV: liệt kê thư mục phát hànhKhối lặp thực tế chạy hai lần (hai kiến trúc), mỗi lần dựng hai gói và một tar.gz. Không có kiểm thử tự động cho luồng này (mục 14).
7.2. Luồng thay thế và lỗi
Luồng 7: chữ ký hoặc checksum sai, install.sh dừng trước khi cài
sequenceDiagram
participant OP as Quản trị viên
participant INS as install.sh
participant REL as Nơi phát hành
participant PM as Trình quản lý gói
OP->>INS: install.sh --pubkey key
INS->>REL: tải SHA256SUMS và SHA256SUMS.sig
INS->>INS: gpg hoặc cosign verify
INS-->>OP: SIGNATURE VERIFICATION FAILED, thoát 1
Note over INS: hoặc sau khi tải gói, sha256 lệch thì báo CHECKSUM MISMATCH và thoát 1
Note over PM: trình quản lý gói không bao giờ được gọiHai kiểm tra là độc lập: --allow-unsigned chỉ bỏ kiểm chữ ký, vẫn kiểm checksum (TestInstallAllowUnsignedStillChecksums). Thiếu cả khóa lẫn --allow-unsigned thì dừng ngay với thông báo nói cách khắc phục (TestInstallRefusesWithoutKeyOrOptOut).
Luồng 8: cài thủ công bằng service install nhưng chưa có collector_url
sequenceDiagram
participant OP as Quản trị viên root
participant MGR as svc.Manager
participant SD as systemd
participant RUN as accesshub-agent run
OP->>MGR: service install --enable
MGR-->>OP: đã ghi unit, không ghi agent.yaml
OP->>SD: systemctl start accesshub-agent
SD->>RUN: ExecStart run
RUN-->>SD: thoát mã 3, thiếu collector_url
Note over SD: mã 3 nằm trong RestartPreventExitStatus nên không thử lại
SD-->>OP: dịch vụ ở trạng thái failed
Note over OP: sửa agent.yaml, enroll bằng tài khoản dịch vụ, rồi systemctl restartĐây là cái bẫy của đường thủ công (OQ-P3). Đường gói không gặp vì postinstall ghi collector_url trước khi enable --now, còn đường trần (luồng 2) thì người vận hành tự sửa tệp. Bản thân việc dịch vụ dừng hẳn là đúng thiết kế (FR-P03), phần hướng dẫn đã bổ sung: service install ghi agent.yaml mẫu (nếu chưa có), nhận --collector URL, và thông điệp in ra nhắc phải đặt collector_url trước khi enroll. Dịch vụ vẫn failed (mã 3) nếu người vận hành bỏ qua bước đó. Chưa chạy trên máy thật.
Luồng 9: enroll trong postinstall thất bại, gói vẫn cài
sequenceDiagram
participant PM as Trình quản lý gói
participant PKG as postinstall
participant ENR as accesshub-agent enroll
participant OP as Quản trị viên
PM->>PKG: postinstall có đủ URL và token
PKG->>PKG: ghi collector_url vào agent.yaml
PKG->>ENR: printf token qua ống, runuser enroll --token-stdin
ENR-->>PKG: mã thoát khác 0 (token hết hạn, mạng lỗi)
PKG-->>PM: cảnh báo enroll failed, thoát 0
Note over PKG: không enable --now, tệp token được giữ để thử lại
PM-->>OP: gói cài thành công
Note over OP: sửa nguyên nhân rồi chạy sudo -u accesshub-agent accesshub-agent enrollcollector_url đã được ghi trước khi enroll nên lần thử lại thủ công không cần sửa tệp. Mã thoát của enroll được bỏ qua ở mức script: mọi mã khác 0 cùng xử lý như nhau (ý nghĩa từng mã xem L3 Enroll mục 5.3). Kiểm thử: TestPostinstallEnrollFailureDoesNotFailPackageOrStart.
Các tình huống thay thế và lỗi khác
| Tình huống | Điều kiện | Hành vi | Nơi hiện thực hoặc kiểm thử |
|---|---|---|---|
| Đã enroll trước đó | credentials.json tồn tại khi postinstall có biến | Cảnh báo, không enroll lại, vẫn ghi khóa YAML và enable --now. Tệp token (nếu có) được giữ, không bị xóa | TestPostinstallKeepsExistingCredentials. Việc giữ tệp token suy luận từ mã, OQ-P15 |
| URL hoặc proxy sai dạng | Không https:// hoặc có ký tự ngoài danh sách trắng | URL: bỏ qua cả enroll, không sửa agent.yaml. Proxy: bỏ qua riêng proxy, vẫn enroll | TestPostinstallRejectsUnsafeValues |
| Chỉ có URL hoặc chỉ có token | Thiếu một nửa | Cảnh báo, bỏ qua enroll, thoát 0 | TestPostinstallNeedsBothURLAndToken |
| Tệp token không đọc được | ACCESSHUB_LICENSE_FILE không tồn tại hoặc không đọc được | Cảnh báo, bỏ qua enroll, thoát 0 | postinstall.sh (chưa có kiểm thử riêng) |
--no-start | ACCESSHUB_NO_START=1 | Chỉ enable, không --now | TestPostinstallNoStartOnlyEnables |
Không có useradd lẫn adduser | Bản phân phối tối giản | preinstall.sh thoát 1, gói không cài. service install thì báo lỗi nói cách tự tạo tài khoản | TestInstallFailsClearlyWithoutUseradd (chỉ nhánh Go). Nhánh script chưa có kiểm thử |
Không có runuser | Hệ rất cũ | Dự phòng su -s /bin/sh -c | Chưa có kiểm thử (mục 14) |
Không có systemctl | Container, hệ không systemd | Script bỏ qua daemon-reload và try-restart. Nhánh cuối systemctl enable lỗi thì chỉ cảnh báo, gói vẫn cài | postinstall.sh (suy luận từ mã). svc.Manager trả lỗi |
Chạy enroll bằng root (tay) | Không đi qua postinstall | credentials.json thuộc root 0600, dịch vụ không đọc được, run thoát mã 1 và được thử lại cho tới khi hết hạn mức (OQ-E5, OQ-P2) | L3 Enroll mục 11.1 |
| Không phải root | service install, start, stop, restart, uninstall, install.sh | Từ chối, thoát 1 | TestInstallRequiresRootAndAbsoluteBinary, TestControlValidatesActionAndRoot, TestServiceNeedsRootAndAKnownAction |
| Đường dẫn tệp chạy tương đối | --binary relative/agent | Từ chối | TestServiceInstallHonorsBinaryFlagAndRejectsRelative |
| Gỡ dịch vụ chưa nạp | disable --now báo lỗi | Chỉ cảnh báo, tiếp tục xóa unit | TestUninstallToleratesServiceThatIsNotLoaded |
| Hai bản unit | Cài gói rồi chạy service install | /etc/systemd/system thắng /lib hoặc /usr/lib. uninstall chỉ xóa bản /etc (OQ-P13) | Suy luận từ mã và quy tắc ưu tiên của systemd, chưa chạy |
uninstall --purge sau cài gói | Gói vẫn còn | Xóa luôn /etc/accesshub-agent thuộc gói, thư mục state, log và tài khoản, còn tệp gói nằm lại | TestUninstallKeepsDataUnlessPurge (kiểm xóa, không kiểm kịch bản gói) |
rpm gỡ không purge | Dùng rpm -e hoặc dnf remove | Dữ liệu và tài khoản còn lại mãi (OQ-P8) | postremove.sh |
| Crash lặp | run thoát mã 1 liên tục | Thử lại cách 5 giây, sau 10 lần trong 300 giây thì unit failed | Unit. Không có kiểm thử hành vi |
| Cài từ tar.gz | Không có trình quản lý gói | Không có script gói: phải tự service install, tự viết agent.yaml, tự enroll | package.sh (OQ-P14) |
| Tải lỗi | curl thất bại | die "download failed: <url>", thoát 1 | install.sh |
| Kiến trúc hoặc công cụ gói lạ | Không phải x86_64 hay aarch64, không có dpkg lẫn rpm | die, thoát 1 | install.sh |
| Đầu vào xấu | --token, phiên bản có ký tự lạ, URL không https:// hoặc file://, tên gói có / | die, thoát 1 | TestInstallRejectsUnsafeInput |
SIGN_HOOK hỏng | Hook không tạo SHA256SUMS.sig khác rỗng | package.sh thoát 1, bộ phát hành đã dựng nhưng không có chữ ký | package.sh (chưa có kiểm thử) |
| Vượt trần kích thước | Tệp chạy lớn hơn MAX_BYTES | cross-build.sh thoát 1, CI đỏ | Job cross-build |
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. Dịch vụ systemd trên một máy
stateDiagram-v2
[*] --> Absent
Absent --> Installed : service install hoặc cài gói / ghi unit, daemon-reload
Installed --> Enabled : enable / liên kết vào multi-user.target
Installed --> Running : enable --now hoặc start [có agent.yaml và credentials] / run chạy
Enabled --> Running : start hoặc khởi động máy [có agent.yaml và credentials] / run chạy
Installed --> Failed : start [thiếu cấu hình hoặc danh tính] / run thoát mã 3 hoặc 4
Enabled --> Failed : start [thiếu cấu hình hoặc danh tính] / run thoát mã 3 hoặc 4
Running --> Running : run thoát mã 1 [dưới 10 lần trong 300 giây] / chạy lại sau 5 giây
Running --> Failed : run thoát mã 2, 3, 4, 5 / không thử lại
Running --> Failed : run thoát mã 1 [đủ 10 lần trong 300 giây] / start limit hit
Running --> Stopped : stop hoặc SIGTERM / run thoát mã 0 trong 30 giây
Stopped --> Running : start / run chạy
Failed --> Running : restart [đã sửa nguyên nhân] / run chạy
Running --> Running : try-restart khi nâng cấp / chạy bằng tệp mới
Installed --> Absent : uninstall / xóa unit
Enabled --> Absent : uninstall / disable, xóa unit
Running --> Absent : uninstall / disable --now, xóa unit
Stopped --> Absent : uninstall / disable, xóa unit
Failed --> Absent : uninstall / disable, xóa unit| Đường (trigger) | Nơi hiện thực | Kiểm thử |
|---|---|---|
| Absent sang Installed | svc.Manager.Install, nfpm.yaml (unit) và postinstall.sh (daemon-reload) | TestInstallCreatesAccountDirsUnitAndReloads, TestNfpmSourcesExist |
| Installed sang Enabled | systemctl enable, cờ --enable hoặc ACCESSHUB_NO_START=1 | TestInstallKeepsExistingAccountAndSkipsEnableByDefault, TestPostinstallNoStartOnlyEnables |
| Sang Running | systemctl enable --now hoặc start | TestPostinstallEnrollsThroughStdinAndStarts, TestServiceControlAndUninstall |
| Sang Failed với mã 3 hoặc 4 (thiếu điều kiện) | Unit RestartPreventExitStatus, mã thoát của run (L3 Enroll) | Chỉ kiểm chuỗi trong unit: TestPackagedUnitCarriesRequiredHardening. Chưa có kiểm thử hành vi. service install đã ghi mẫu và nhắc collector_url (OQ-P3) |
| Running sang Running (thử lại) và Running sang Failed (vượt hạn mức) | Unit Restart=on-failure, RestartSec=5, StartLimitIntervalSec=300, StartLimitBurst=10 | Chưa có kiểm thử hành vi (mục 14) |
| Running sang Stopped | Control("stop"), SIGTERM tới run (TimeoutStopSec=30) | TestServiceControlAndUninstall (kiểm lệnh, không đo thời gian dừng) |
| Failed sang Running | Control("restart") sau khi sửa | TestServiceControlAndUninstall |
| Running sang Running (nâng cấp) | postinstall.sh try-restart | Chưa có kiểm thử riêng. Xem luồng 4 |
| Sang Absent | svc.Manager.Uninstall, preremove.sh kèm gói xóa tệp | TestUninstallKeepsDataUnlessPurge, TestUninstallToleratesServiceThatIsNotLoaded, TestPreremoveOnlyStopsOnRealRemoval |
Trạng thái Failed ở đây là trạng thái của unit do systemd giữ, không lưu ở đâu trong mã của thành phần này. Mọi trạng thái đều là suy luận từ unit và mã thoát, chưa quan sát trên systemd thật ngoài kiểm tra cú pháp.
7.3.2. Vòng đời dữ liệu của một lần cài (theo gói)
stateDiagram-v2
[*] --> Absent
Absent --> Unenrolled : cài gói [không có biến hoặc enroll lỗi] / tài khoản và thư mục, chưa enroll
Absent --> Enrolled : cài gói [URL và token hợp lệ, enroll thành công] / enroll, enable --now
Unenrolled --> Enrolled : enroll thủ công / credentials.json 0600
Enrolled --> Enrolled : nâng cấp gói / try-restart, giữ dữ liệu
Unenrolled --> Unenrolled : nâng cấp gói / giữ cấu hình
Enrolled --> Removed : remove [deb hoặc rpm] / disable --now, giữ dữ liệu
Unenrolled --> Removed : remove / giữ dữ liệu
Removed --> Enrolled : cài lại [credentials.json còn] / không cần enroll lại
Removed --> Purged : purge [chỉ deb] / xóa state, log, etc, tài khoản
Enrolled --> Purged : purge [chỉ deb] / xóa state, log, etc, tài khoản
Unenrolled --> Purged : purge [chỉ deb] / xóa state, log, etc, tài khoản
Purged --> Absent : hoàn tất / không còn gì| Đường (trigger) | Nơi hiện thực | Kiểm thử |
|---|---|---|
| Absent sang Unenrolled | preinstall.sh, postinstall.sh (nhánh không có biến hoặc enroll lỗi) | TestPostinstallWithoutEnvOnlyPreparesDirectories, TestPostinstallEnrollFailureDoesNotFailPackageOrStart |
| Absent sang Enrolled | postinstall.sh (nhánh enroll thành công) | TestPostinstallEnrollsThroughStdinAndStarts |
| Unenrolled sang Enrolled (thủ công) | accesshub-agent enroll (L3 Enroll) | Xem L3 Enroll mục 14 |
| Nâng cấp | nfpm.yaml (config|noreplace), postinstall.sh | TestPostinstallKeepsExistingCredentials, TestPostinstallReplacesExistingCollectorURLOnce |
| Sang Removed | preremove.sh, postremove.sh | TestPreremoveOnlyStopsOnRealRemoval, TestPostremoveKeepsDataUnlessPurge |
| Removed sang Enrolled (cài lại) | Dữ liệu còn, postinstall.sh nhánh "already enrolled" | TestPostinstallKeepsExistingCredentials. Việc dịch vụ chưa được bật lại là suy luận từ mã |
| Sang Purged | postremove.sh nhánh purge | TestPostremoveKeepsDataUnlessPurge |
Đường uninstall --purge (không qua gói) không có trong sơ đồ vì nó xóa dữ liệu nhưng để nguyên tệp gói, tạo ra một trạng thái không thuộc vòng đời này (gói còn, dữ liệu và tài khoản mất, OQ-P13).
7.4. Thuật toán và quyết định thiết kế
7.4.1. Một nguồn unit và RenderUnit
Vấn đề: hai đường cài (gói và service install) đều cần một unit, và nếu hai bản lệch nhau thì hardening chỉ có trên một đường mà không ai hay.
Giải pháp: tệp internal/svc/accesshub-agent.service là nguồn duy nhất. go:embed nhúng nó vào tệp chạy (PackagedUnit()), còn nfpm.yaml trỏ thẳng src tới cùng tệp. RenderUnit(bin) chỉ thay tiền tố ExecStart=/usr/bin/accesshub-agent bằng ExecStart=<bin> và trả nguyên bản khi bin rỗng hoặc bằng đường dẫn mặc định. ExecReload dùng /bin/kill -HUP $MAINPID nên không phụ thuộc đường dẫn tệp chạy.
Trade-off: thay chuỗi theo tiền tố đơn giản và kiểm thử được (TestRenderUnitRewritesOnlyTheBinary), nhưng không thoát ký tự: đường dẫn có khoảng trắng hoặc % sẽ làm hỏng hoặc đổi nghĩa dòng ExecStart (OQ-P6). ReadWritePaths cũng cố định theo đường dẫn mặc định, nên state_dir hay log.file tùy chỉnh ngoài hai thư mục này sẽ bị ProtectSystem=strict chặn ghi (OQ-P5). MemoryMax=96M là trần cứng của cgroup còn limits.memory_limit là mềm (GOMEMLIMIT) và cấu hình được tới 4 GiB, nên cấu hình lớn hơn 96 MiB sẽ bị giết thay vì tự nén (OQ-P4).
7.4.2. Ghi khóa vào agent.yaml an toàn
Vấn đề: giá trị từ biến môi trường của người cài được ghi vào tệp YAML. Ký tự xuống dòng, dấu nháy hay $ có thể chèn khóa khác vào cấu hình, còn ghi dở có thể để lại tệp nửa vời.
Giải pháp (valid_value, set_key trong postinstall.sh): (1) collector_url phải bắt đầu https:// và có ít nhất một ký tự sau đó. (2) Cả URL và proxy chỉ được chứa A-Za-z0-9._~:/?#@%+=&,- (case với mẫu phủ định). Sai thì bỏ qua cả enroll (URL) hoặc chỉ bỏ proxy. (3) set_key tạo agent.yaml.new.<pid> cùng thư mục: nếu khóa đã có thì awk thay đúng dòng bắt đầu bằng key:, nếu chưa thì nối thêm dòng, rồi chmod 0644 và mv đè. Proxy được đặt trong dấu nháy kép.
Trade-off: danh sách trắng loại cả những URL hợp lệ có ký tự lạ (ví dụ mật khẩu proxy có !). set_key chỉ tìm khóa ở cột đầu của dòng, nên khóa lồng hay khóa đã comment không bị thay mà bị nối thêm dòng mới. Ghi nguyên tử nhưng không khóa tệp, hai lần cài song song là hiếm và không được hỗ trợ. Tệp mới luôn 0640 root:accesshub-agent, và postinstall siết tệp đã có về cùng quyền (OQ-P1).
7.4.3. Enroll trong postinstall
Vấn đề: cần enroll không tương tác mà token không lộ qua danh sách tiến trình, credentials.json phải thuộc tài khoản dịch vụ, và lỗi mạng hay token sai không được làm hỏng việc cài gói (nếu gói hỏng thì hàng loạt máy ở trạng thái nửa cài).
Giải pháp: (1) Thoát sớm nếu không có biến nào, hoặc chỉ có một nửa. (2) Chọn run_as: runuser -u accesshub-agent -- nếu có, nếu không thì su -s /bin/sh -c 'exec "$0" "$@"'. (3) printf '%s\n' "$token" | run_as BIN enroll --token-stdin, tức token đi qua ống và argv chỉ có cờ. (4) Thành công thì xóa tệp token bằng shred -u (dự phòng rm -f). (5) Lỗi thì in cảnh báo kèm lệnh thử lại và exit 0. (6) unset token, rồi enable --now (hoặc enable khi ACCESSHUB_NO_START=1). Lỗi systemctl ở bước cuối chỉ cảnh báo.
Trade-off: token vẫn nằm trong biến môi trường của cả quá trình cài (apt-get, dpkg, các script), nên đọc được qua /proc/<pid>/environ của root và của cùng người dùng (OQ-P18). Dùng tệp token tránh được điều này ở phía người gọi nhưng install.sh vẫn chuyển tiếp tên biến. Khi nhánh "already enrolled" chạy, tệp token không bị xóa (OQ-P15). Nuốt mọi lỗi enroll giúp gói luôn cài được nhưng nghĩa là pipeline tự động hóa không biết enroll hỏng nếu chỉ xem mã thoát của gói, phải đọc log hoặc kiểm systemctl is-active.
7.4.4. Chuỗi xác minh của install.sh
Vấn đề: kẻ chiếm được kho tải có thể thay gói bằng mã độc chạy bằng root. Nếu tên gói hay checksum lấy từ chính thư mục bị chiếm thì kiểm tra vô nghĩa.
Giải pháp: (1) Tải SHA256SUMS và SHA256SUMS.sig. (2) Có --pubkey thì nhận dạng khóa: PGP thì gpg --verify trong GNUPGHOME tạm, PEM thì cosign verify-blob. Không có khóa và không có --allow-unsigned thì dừng. (3) Chọn tên gói bằng awk trên SHA256SUMS đã xác minh theo mẫu của kiến trúc và định dạng, và từ chối tên có / hay ký tự ngoài A-Za-z0-9._+~-. (4) Tải đúng gói đó, tính sha256sum (hoặc shasum -a 256) và so với dòng khớp tên trong SHA256SUMS. Lệch hay thiếu thì dừng. (5) Mới export biến và gọi trình quản lý gói.
Trade-off: toàn bộ độ tin cậy dồn vào khóa công khai do người vận hành cung cấp và cách họ phân phối nó (chưa có kênh, Q3 của L2). Chữ ký chỉ phủ SHA256SUMS nên install.sh tải kèm cũng phải nằm trong danh sách (đã có trong package.sh) nhưng người chạy curl | sh bản install.sh chưa xác minh thì mất bảo vệ: hướng dẫn nên tải, kiểm rồi mới chạy (OQ-P10). Giá trị mặc định của --base-url là địa chỉ giữ chỗ nên trên thực tế bắt buộc truyền --base-url (OQ-P10).
7.4.5. Suy ra phiên bản gói
Vấn đề: deb và rpm đòi phiên bản bắt đầu bằng chữ số và bị kén ký tự, trong khi git describe có thể cho v1.2.3, 9d76488-dirty hay v1.2.3-4-gabc-dirty.
Giải pháp (package.sh): (1) version lấy từ VERSION hoặc git describe --tags --always --dirty hoặc dev. (2) Bỏ tiền tố v. (3) Không bắt đầu bằng chữ số thì đặt 0.0.0-dev.<7 ký tự đầu của commit>. (4) Đổi mọi ký tự ngoài A-Za-z0-9.+~- thành dấu chấm. make snapshot dùng 0.0.0-snapshot.<sha>. Tệp chạy vẫn nhúng chuỗi version gốc qua -ldflags, còn gói mang pkg_version.
Trade-off: một cây làm việc bẩn trên đúng tag vẫn cho ra phiên bản 1.2.3-dirty (chữ số đầu), có thể bị nhầm là bản phát hành. latest không do package.sh tạo nên khâu đẩy phiên bản lên "kênh" do người phát hành tự làm (AGT-11). Quy tắc này chưa có kiểm thử tự động.
7.4.6. Giữ và xóa dữ liệu khi gỡ
Vấn đề: gỡ rồi cài lại (hay nâng cấp) không được bắt enroll lại và không được làm mất agent.yaml đã chỉnh, nhưng người muốn gỡ sạch vẫn cần cách xóa hết kể cả bí mật.
Giải pháp: agent.yaml là config|noreplace nên nâng cấp không đè, và gỡ thường không xóa tệp cấu hình đã sửa. preremove.sh chỉ dừng khi đối số là remove, deconfigure hoặc 0. postremove.sh chỉ xóa ba thư mục và tài khoản khi đối số là purge. Đường thủ công: uninstall giữ dữ liệu, uninstall --purge xóa thư mục state, log, cấu hình và tài khoản.
Trade-off: rpm không có khái niệm purge trong script, nên gỡ sạch rpm cần hai bước theo thứ tự (uninstall --purge trước khi gỡ gói) mà dễ quên (OQ-P8). uninstall --purge không biết mình có nằm cạnh gói hay không, nên sẽ xóa cả /etc/accesshub-agent của gói (OQ-P13). Gỡ thường để lại tài khoản hệ thống và thư mục /var/log/accesshub-agent mồ côi vì nfpm không khai báo nó, còn tài khoản thì cố ý giữ để tệp trong state còn chủ hợp lệ (OQ-P7).
7.4.7. Chính sách khởi động lại theo mã thoát
Vấn đề: khởi động lại vô hạn một tiến trình hỏng vì cấu hình sẽ tạo nhật ký ồn và đốt CPU, nhưng không khởi động lại thì lỗi tạm thời (mạng, đĩa) làm mất thu thập.
Giải pháp: Restart=on-failure, RestartSec=5. Mã 2 (dùng sai), 3 (cấu hình), 4 (chưa enroll), 5 (enroll bị từ chối) nằm trong RestartPreventExitStatus vì chờ không tự khỏi được. Mã khác (kể cả 1) được thử lại, tối đa 10 lần trong 300 giây (StartLimitIntervalSec, StartLimitBurst) rồi unit failed. Mã 6 (không có kết nối) chỉ do enroll trả, run không dùng. TimeoutStopSec=30 cho run thời gian dừng êm (runtime có hạn 10 giây riêng, L3 WAL và Sender).
Trade-off: mã 1 gộp nhiều nguyên nhân, trong đó credentials.json quyền rộng hay thuộc root (OQ-E5, OQ-E6) là lỗi không tự khỏi nhưng vẫn bị thử lại 10 lần rồi mới dừng. Sau khi failed không có gì tự đưa dịch vụ trở lại (cần systemctl restart), và thiếu khối OnFailure= nên không có cảnh báo (mục 13.3).
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 phải root | service install, uninstall, start, stop, restart, install.sh | ErrNotRoot hoặc die, thoát 1 | stderr |
| Đường dẫn tệp chạy không tuyệt đối | --binary | Từ chối, thoát 1 | stderr |
Hành động service lạ | service foo | In cách dùng, thoát 2 | stderr |
Không có useradd | svc.Manager.ensureAccount | Lỗi nói cách tự tạo tài khoản, thoát 1 | stderr |
Không có useradd và adduser | preinstall.sh | In lỗi, thoát 1, gói không cài | stderr của trình quản lý gói |
systemctl lỗi | enable, start, stop, restart, daemon-reload trong svc.Manager | Trả lỗi, thoát 1 (trừ disable --now và daemon-reload khi gỡ: cảnh báo) | stderr |
systemctl lỗi trong script | Mọi lời gọi trong postinstall, preremove, postremove | Nuốt lỗi, postinstall cảnh báo khi enable --now lỗi | stderr |
Thiếu khóa và không --allow-unsigned | install.sh | die, thoát 1 | stderr |
| Chữ ký sai | gpg hoặc cosign | SIGNATURE VERIFICATION FAILED, thoát 1 (luồng 7) | stderr |
| Checksum lệch hoặc thiếu | Sau khi tải gói | CHECKSUM MISMATCH, thoát 1 (luồng 7) | stderr |
Tên gói không có trong SHA256SUMS | Sai kiến trúc hoặc định dạng | no package matching ..., thoát 1 | stderr |
| Tải lỗi | curl thất bại | download failed: <url>, thoát 1 | stderr |
| Đầu vào xấu | Luồng 7 bảng | die, thoát 1 | stderr |
URL hoặc proxy sai dạng trong postinstall | valid_value | Bỏ qua enroll (URL) hoặc proxy, thoát 0 | stderr của trình quản lý gói |
Enroll lỗi trong postinstall | Luồng 9 | Cảnh báo, thoát 0, không khởi động | stderr của trình quản lý gói |
run thiếu điều kiện | Luồng 8 | Thoát mã 3 hoặc 4, systemd không thử lại | journald, systemctl status |
SIGN_HOOK không ra chữ ký | package.sh | Thoát 1 | stderr |
| Tệp chạy vượt trần | cross-build.sh | Thoát 1, CI đỏ | Nhật ký CI, dist/sizes.txt |
8.2. Fail-fast
| Điều kiện | Hành vi | Mã thoát hoặc lỗi |
|---|---|---|
| Không phải root | Không làm gì | ErrNotRoot, mã 1 |
| Đường dẫn tệp chạy tương đối | Không ghi unit | Mã 1 |
Hành động hoặc đối số service sai | In cách dùng | Mã 2 |
Thiếu useradd (đường Go) | Không ghi gì, báo cách tự tạo | Mã 1 |
Thiếu khóa công khai và không --allow-unsigned | Dừng trước khi tải gói | Thoát 1 |
| Chữ ký hay checksum sai | Dừng trước khi gọi trình quản lý gói | Thoát 1 |
--token trên dòng lệnh | Từ chối ngay khi phân tích đối số | Thoát 1 |
set -eu trong script gói | Lệnh lỗi hay biến chưa đặt thì dừng | Script thoát khác 0 (cài lỗi), trừ chỗ có || true hay nhánh exit 0 |
| Phiên bản hay tên gói có ký tự lạ | Từ chối | Thoát 1 |
8.3. Race conditions
| Tình huống | Cơ chế hoặc hậu quả | Ghi chú |
|---|---|---|
Hai postinstall song song | Trình quản lý gói giữ khóa riêng (dpkg, rpm), nên thực tế không xảy ra | Không cần xử lý |
set_key ghi đè trong khi người vận hành sửa tay agent.yaml | Tệp tạm cùng thư mục rồi mv, nên sửa tay giữa chừng bị mất hoặc thắng tùy thứ tự | Hiếm, chấp nhận |
try-restart đúng lúc dịch vụ đang ghi WAL | SIGTERM rồi dừng êm trong TimeoutStopSec=30, WAL bền nhờ tệp, xem L3 WAL và Sender | Không có tranh chấp dữ liệu mới từ thành phần này |
service install chạy khi gói đang cài | Hai bên cùng ghi thư mục và chown | Không khóa. Hiếm, thao tác thủ công |
enroll thủ công khi postinstall đang chạy | enroll từ chối nếu credentials.json đã có, bên ghi sau thắng nếu cả hai qua kiểm tra | Cùng họ OQ-E15 |
| Gỡ gói khi dịch vụ đang khởi động lại | preremove disable --now dừng đơn vị đang thử lại | An toàn (suy luận từ hành vi systemd) |
Tải latest rồi tải gói trong lúc phát hành mới ghi | Thư mục phiên bản có thể chưa đủ tệp | install.sh thất bại ở bước tải hoặc checksum, không cài nửa vời |
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 |
|---|---|---|---|---|
Nơi phát hành (install.sh) | Không tới được, thiếu tệp | Không cài được. Máy đã cài không bị ảnh hưởng | die rõ tên URL | Thủ công: thử lại hoặc dùng --base-url file:// (bản sao cục bộ) |
systemd | Không có (container) | Script bỏ qua daemon-reload, try-restart. enable lỗi chỉ cảnh báo. service install báo lỗi | command -v systemctl trong script | Chạy accesshub-agent run bằng cách khác (ngoài phạm vi) |
useradd và adduser | Không có | preinstall thoát 1 | Thông báo rõ | Tạo tài khoản tay rồi cài lại |
runuser | Không có | Dự phòng su | command -v runuser | Không cần |
shred | Không có | Xóa tệp token bằng rm -f | command -v shred | Không cần |
gpg hoặc cosign | Không có nhưng khóa cần nó | die, không cài | Kiểm command -v | Cài công cụ hoặc dùng --allow-unsigned (kiểm thử) |
sha256sum và shasum | Cả hai thiếu | die | Kiểm command -v | Cài công cụ |
curl | Không có | die | Kiểm đầu | Cài curl |
Collector (lúc enroll trong postinstall) | Không tới được | Gói cài, chưa enroll (luồng 9) | Thoát 0 | Thủ công: chạy lại enroll |
Hệ tệp /var/lib, /var/log | Đầy, chỉ đọc | chown hay mkdir lỗi dưới set -e thì cài lỗi | set -eu | Thủ công |
Tài khoản accesshub-agent bị xóa tay | Gỡ tài khoản sau khi cài | chown trong postinstall lỗi, cài lỗi. Dịch vụ không chạy | Không có | preinstall tạo lại ở lần cài kế |
| Kho gói (apt, yum) | Chưa có | Chỉ phát hành dạng tệp | Không áp dụng (AGT-11) | Chưa có |
9.2. Backup, DR, RTO và RPO
Thành phần không giữ dữ liệu riêng: mọi thứ nó tạo (unit, tài khoản, thư mục) tái tạo được bằng cài lại. Dữ liệu đáng giữ nằm ở thành phần khác (credentials.json ở L3 Enroll, WAL ở L3 WAL và Sender). 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.
| Chỉ số | Giá trị | Ghi chú |
|---|---|---|
| Khôi phục máy mất agent | Cài lại gói rồi enroll lại (token mới) hoặc giữ credentials.json nếu còn | credentials.json không có bản sao lưu |
| RTO cài lại một máy | Theo thời gian tải gói cộng cài | Chưa đo, không đề xuất con số |
| RPO | Không áp dụng | Không có dữ liệu do thành phần này sở hữu |
| Nâng cấp lỗi, muốn quay lại | Cài lại bản cũ bằng install.sh --version, vì agent.yaml và danh tính còn nguyên | Chưa có kiểm tra sức khỏe sau nâng cấp hay tự quay lại (OQ-P19) |
| Mất khóa ký phát hành | Ngoài phạm vi, thuộc hệ phát hành (Q3) |
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. Các đơn vị nguyên tử:
| Đơn vị | Ranh giới | Đảm bảo |
|---|---|---|
Ghi agent.yaml | Tệp tạm agent.yaml.new.<pid>, chmod, mv | Đọc thấy trọn bản cũ hoặc trọn bản mới |
Ghi unit (service install) | os.WriteFile trực tiếp vào đích, 0644 | Không nguyên tử: mất điện giữa chừng có thể để lại unit cụt (hiếm, unit nhỏ) |
| Cài gói | Do dpkg hoặc rpm đảm nhiệm, postinstall nằm trong giao dịch của họ | set -e lỗi thì gói ở trạng thái cấu hình dở, dpkg --configure -a chạy lại được (suy luận, chưa thử) |
Xóa dữ liệu khi purge | rm -rf ba thư mục rồi userdel | Không nguyên tử. Dừng giữa chừng để lại thư mục còn, tài khoản còn |
| Phát hành | rm -rf thư mục đầu ra rồi dựng | Bộ phát hành trước bị xóa ngay khi bắt đầu. Dựng lỗi giữa chừng thì không còn bộ nào |
10.2. Cơ chế đồng thời
| Cơ chế | Nơi dùng | Mục đích |
|---|---|---|
| Khóa của dpkg và rpm | Cài, nâng cấp, gỡ | Chặn hai lần cài cùng lúc |
Ghi tệp tạm rồi mv | set_key | Thay thế nguyên tử agent.yaml |
trap dọn thư mục tạm | install.sh (cleanup) | Xóa thư mục làm việc và GNUPGHOME tạm khi thoát |
| Không có khóa liên tiến trình | service install, uninstall | Xem 8.3 |
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 | install.sh chỉ tải qua https hoặc file (curl --proto '=https,file' --tlsv1.2), từ chối --base-url khác | install.sh, TestInstallRejectsUnsafeInput |
| Danh tính và bí mật | Token không qua argv (install.sh từ chối --token, postinstall dùng ống). Giá trị ghi YAML qua danh sách trắng. Chữ ký và checksum trước khi cài. Tên gói từ danh sách đã xác minh | TestInstallRejectsUnsafeInput, TestPostinstallEnrollsThroughStdinAndStarts, TestPostinstallRejectsUnsafeValues, TestInstallVerifiesSignature |
| Giam tiến trình và dữ liệu nghỉ | Unit: không capability, NoNewPrivileges, ProtectSystem=strict, bộ lọc syscall, ba họ địa chỉ, trần tài nguyên, UMask=0077. State và log 0750 thuộc tài khoản dịch vụ | TestPackagedUnitCarriesRequiredHardening, systemd-analyze verify (thủ công, một lần) |
Các điểm yếu còn lại, nêu thẳng (R-03):
- (Đã sửa) trước đây
agent.yamlmặc định 0644 root:root nên mọi người dùng trên máy đọc được, trong khi nó có thể chứaproxy_urlkèm mật khẩu (OQ-P1, trả lời OQ-E19). Đã xử lý: gói,service installvàset_keyđặt 0640 root:accesshub-agent,postinstallsiết cả tệp cài từ trước (OQ-P1). - Bộ phát hành chưa có kho, khóa ký hay CI ký. Cho tới khi có, mọi bảo đảm chuỗi cung ứng phụ thuộc người vận hành tự truyền
--pubkey(Q2, Q3, D-12). --proxy URLđi quaargvcủainstall.shnên mật khẩu proxy lộ trong danh sách tiến trình (OQ-P17). Token nằm trong biến môi trường của cả quá trình cài (OQ-P18).- Cờ
MemoryDenyWriteExecutevà bộ lọc syscall chưa được kiểm tra bằngsystemd-analyze securityhay kiểm thử xâm nhập, chỉ kiểm cú pháp (QAS-P03). RenderUnitkhông thoát ký tự (OQ-P6). Chạyservice install --binaryvới đường dẫn do kẻ khác kiểm soát là chạy mã tùy ý bằng tài khoản dịch vụ, nhưng người gọi đã là root nên ranh giới tin cậy không đổi.- Chạy
enrollbằng root (OQ-P2) hay bản thân người vận hành root có thể vô hiệu hóa cách ly của tài khoản dịch vụ, nhưng không mở rộng quyền cho agent.
11.2. Đường ống phân quyền năm bước
| Bước | Ai làm | Ghi chú |
|---|---|---|
| 1. Xác thực người thao tác | Hệ điều hành | Euid bằng 0 cho svc.Manager và install.sh (trừ --dry-run) |
| 2. Xác thực nguồn phát hành | install.sh | Chữ ký của SHA256SUMS (khóa công khai của người vận hành) |
| 3. Xác thực nội dung | install.sh | Checksum từng gói so với dòng trong SHA256SUMS, tên gói từ danh sách đã xác minh |
| 4. Xác thực đầu vào cấu hình | postinstall.sh | Danh sách trắng ký tự, https:// |
| 5. Giam tiến trình | systemd (unit) | Tài khoản không đặc quyền, không capability |
11.3. Điểm neo zero-trust
| Điểm neo | Ý nghĩa |
|---|---|
| Không tin thư mục phát hành | Tên gói và checksum lấy từ SHA256SUMS đã có chữ ký, không từ danh sách thư mục |
| Không tin đầu vào của người cài | Mọi giá trị đi vào YAML, tên gói, phiên bản đều qua danh sách trắng |
| Không bao giờ bỏ qua checksum | --allow-unsigned chỉ bỏ chữ ký, không bỏ checksum |
| Không khởi động khi chưa enroll | service install không start, postinstall chỉ enable --now sau enroll thành công hoặc khi đã có danh tính |
| Không tin tệp chạy | Tiến trình không tự nâng quyền (NoNewPrivileges, không capability) |
| Không cổng mở | RestrictAddressFamilies cho phép socket nhưng agent chỉ là client, không có tham số để lắng nghe |
12. Cấu hình & Tinh chỉnh
12.1. Tunables
Thành phần không có tệp cấu hình riêng. Các núm chỉnh nằm ở bốn nơi: chỉ thị trong unit (chỉ sửa được bằng drop-in của systemd hoặc sửa nguồn), biến môi trường của postinstall.sh, cờ của install.sh, biến của package.sh và make. Khóa của agent.yaml thuộc L3 Enroll mục 12.1.
Chỉ thị unit đáng chỉnh
| Khóa | Mặc định (dải hợp lệ) | Ý nghĩa | Nạp nóng | Nguồn ghi đè |
|---|---|---|---|---|
MemoryMax | 96M (theo systemd) | Trần cứng cgroup, vượt là bị giết | Không (daemon-reload rồi restart) | Drop-in systemctl edit |
CPUQuota | 25% | Trần CPU | Như trên | Drop-in |
TasksMax | 128 | Trần luồng và tiến trình | Như trên | Drop-in |
LimitNOFILE | 1024 | Trần mô tả tệp | Như trên | Drop-in |
Restart, RestartSec | on-failure, 5 s | Chính sách chạy lại | Như trên | Drop-in |
RestartPreventExitStatus | 2 3 4 5 | Mã thoát không chạy lại | Như trên | Drop-in |
StartLimitIntervalSec, StartLimitBurst | 300 s, 10 | Hạn mức chạy lại | Như trên | Drop-in |
TimeoutStopSec | 30 s | Thời gian dừng êm trước SIGKILL | Như trên | Drop-in |
ReadWritePaths | /var/lib/accesshub-agent /var/log/accesshub-agent | Đường ghi được duy nhất (kèm StateDirectory, LogsDirectory) | Như trên | Drop-in. Phải thêm khi đổi state_dir hoặc log.file (OQ-P5) |
ExecStart | /usr/bin/accesshub-agent run | Tệp chạy | Như trên | service install --binary (viết lại), hoặc drop-in |
Drop-in (/etc/systemd/system/accesshub-agent.service.d/*.conf) được systemd hỗ trợ nhưng thành phần này không tạo, không quản lý và không có kiểm thử. Sửa tay tệp unit của gói sẽ bị ghi đè khi nâng cấp (deb không coi unit là conffile do nfpm khai báo, suy luận từ nfpm.yaml).
Biến của postinstall.sh và cờ của install.sh: xem bảng ở 5.1 và 5.2 (ACCESSHUB_COLLECTOR_URL, ACCESSHUB_LICENSE, ACCESSHUB_LICENSE_FILE, ACCESSHUB_PROXY_URL, ACCESSHUB_NO_START, --version, --base-url, --pubkey, --allow-unsigned, --proxy, --no-start, --dry-run). Mọi biến đều không có nạp nóng: chỉ đọc một lần lúc cài.
Tham số CLI của thành phần
| Tham số | Mặc định | Ý nghĩa |
|---|---|---|
service install --binary PATH | tệp chạy hiện tại (đã giải symlink) | Đường dẫn tuyệt đối ghi vào ExecStart |
service install --enable | tắt | Chạy thêm systemctl enable (không bao giờ start) |
uninstall --purge | tắt | Xóa state, log, cấu hình và tài khoản |
service start, stop, restart | không có | Gọi systemctl tương ứng |
Tham số dựng phát hành
| Biến | Mặc định | Ý nghĩa |
|---|---|---|
VERSION | git describe --tags --always --dirty, hoặc dev | Phiên bản nhúng vào tệp chạy và suy ra phiên bản gói (7.4.5) |
COMMIT, DATE | từ git, từ ngày giờ UTC | Nhúng qua -ldflags |
NFPM | nfpm | Đường dẫn công cụ đóng gói |
OUT | dist/release (dist/snapshot cho make snapshot) | Thư mục đầu ra, bị xóa rồi tạo lại khi dựng |
SIGN_HOOK | rỗng | Lệnh ký SHA256SUMS, nhận đường dẫn tệp làm đối số, phải tạo SHA256SUMS.sig khác rỗng |
MAX_BYTES | 20971520 (20 MiB) | Trần kích thước tệp chạy của cross-build.sh |
Hằng số không cấu hình được
| Hằng | Giá trị | Vai trò |
|---|---|---|
| Tên tài khoản và nhóm | accesshub-agent | Cứng trong unit, script, svc.Manager |
| Thư mục | /etc/accesshub-agent, /var/lib/accesshub-agent, /var/log/accesshub-agent | Cứng trong NewManager, script và unit |
| Quyền | State và log 0750, unit 0644, agent.yaml 0640 root:accesshub-agent | Cứng trong script, svc, nfpm.yaml (OQ-P1) |
| Danh sách trắng ký tự giá trị YAML | A-Za-z0-9._~:/?#@%+=&,- | valid_value trong postinstall.sh |
| Danh sách trắng tên gói và phiên bản | A-Za-z0-9._+~- | install.sh |
| Gốc tải mặc định | https://downloads.example.com/agent | Địa chỉ giữ chỗ (OQ-P10, OQ-P11) |
12.2. Feature flags
Không có cờ tính năng. Hai tùy chọn opt-in mang tính đánh đổi an toàn: --allow-unsigned (bỏ kiểm chữ ký, vẫn kiểm checksum, in cảnh báo) và --dry-run (không đổi gì). ACCESSHUB_NO_START hoạt động như cờ triển khai, không phải cờ tính năng.
13. Telemetry & Vận hành
13.1. Metrics
Thành phần này không phát chỉ số nào: nó chỉ chạy lúc cài, gỡ và dựng. Số liệu liên quan tới dịch vụ đang chạy nằm ở L3 Collectors và L3 WAL và Sender, và cgroup của systemd (systemctl status, systemd-cgtop) cho biết bộ nhớ và CPU thực. Khoảng trống (ĐỀ XUẤT): chưa có chỉ số hay cảnh báo cho "số lần systemd chạy lại agent" hay "số máy cài chưa enroll" (OQ-P19).
13.2. Log schema
Không có slog ở thành phần này. Thông điệp nằm ở ba chỗ:
| Sự kiện (msg) | Mức | Trường thêm | Khi nào |
|---|---|---|---|
warning: ... (script gói) | Cảnh báo (stderr, tiền tố warning:) | Tự do | Enroll lỗi, giá trị bị bỏ qua, không đọc được tệp token, systemctl enable --now lỗi |
install.sh: ... | Thông tin hoặc lỗi (stderr), lỗi qua die | Tự do | Mỗi bước tải, xác minh, cài. Lỗi kết thúc bằng thoát 1 |
service installed, next: enroll the agent ... | Thông tin (stdout) | Đường dẫn tệp chạy, lệnh enroll, lệnh enable --now | service install thành công |
warning: ... is not loaded hoặc tương tự | Cảnh báo (stderr) | Lỗi systemctl | Uninstall gặp disable --now hoặc daemon-reload lỗi |
| Nhật ký dịch vụ đang chạy | Theo log.level | Theo L3 Enroll mục 13.2 | Vào journald vì stderr của unit |
Các chuỗi này là cho người đọc, chưa phải hợp đồng ổn định cho script (xem OQ-E9 ở L3 Enroll).
13.3. Alert to runbook
Thành phần không tự cảnh báo (ĐỀ XUẤT: OnFailure= hoặc kiểm tra bên ngoài, OQ-P19).
| Điều kiện | Runbook |
|---|---|
Dịch vụ failed sau service install thủ công, journalctl báo thiếu collector_url (mã 3) | Ghi collector_url vào agent.yaml (hoặc cài lại trên máy mới với --collector URL), enroll bằng tài khoản dịch vụ, systemctl restart (OQ-P3) |
Dịch vụ failed với mã 4 | Chạy sudo -u accesshub-agent accesshub-agent enroll --token-file <file> (L3 Enroll) |
Dịch vụ failed với mã 5 | Token sai hoặc đã dùng: xin token mới (L3 Enroll mục 13.3) |
Dịch vụ failed mã 1 lặp lại rồi start limit hit | Xem journalctl -u accesshub-agent. Nếu do quyền credentials.json: chown accesshub-agent:accesshub-agent và chmod 600 (OQ-P2). Sau khi sửa: systemctl reset-failed rồi restart |
Dịch vụ bị giết, journalctl có oom-kill hoặc Memory cgroup | Giảm limits.memory_limit hoặc nâng MemoryMax bằng drop-in (OQ-P4) |
install.sh in SIGNATURE VERIFICATION FAILED | Dừng. Kiểm khóa công khai và nguồn tải, không dùng --allow-unsigned để lách |
install.sh in CHECKSUM MISMATCH | Dừng. Tải lại, so với kênh phát hành khác, báo đội phát hành |
Gói cài xong, cảnh báo enroll failed | Sửa nguyên nhân (mạng, token), chạy lại enroll bằng tài khoản dịch vụ rồi systemctl enable --now |
| Gỡ rpm còn dữ liệu | accesshub-agent uninstall --purge trước khi gỡ gói (OQ-P8) |
uninstall --purge đã chạy sau cài gói | Cài lại gói, enroll lại (OQ-P13) |
13.4. Probes
Không có endpoint. Quan sát cục bộ:
| Kênh | Nội dung |
|---|---|
systemctl is-active, is-enabled, status accesshub-agent | Trạng thái unit, mã thoát gần nhất, số lần chạy lại |
journalctl -u accesshub-agent | Nhật ký của dịch vụ và lý do thoát |
accesshub-agent check-config | Kiểm agent.yaml, không gọi mạng (L3 Enroll mục 13.4) |
accesshub-agent collect-once | Thu một lần, kiểm bộ thu, không cần danh tính |
systemd-analyze verify accesshub-agent.service | Kiểm cú pháp unit (đã chạy thủ công một lần, systemd 255, chỉ phàn nàn thiếu tệp chạy) |
systemd-analyze security accesshub-agent.service | Chấm điểm hardening. Chưa chạy, ĐỀ XUẤT |
dpkg -L, dpkg-deb -c (deb), rpm -qlp (rpm) | Kiểm nội dung gói. Chỉ dpkg-deb đã chạy thủ công (mục 14) |
Mã thoát của install.sh, package.sh | 0 hoặc 1 |
13.5. Trace propagation
Không áp dụng. Thành phần không có lời gọi mạng riêng ngoài curl của install.sh, không gắn X-Request-Id.
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: unit và hardening | AC-P01, AC-P02, NFR-P01, NFR-P12 | Unit đóng gói có đủ chỉ thị bắt buộc, RenderUnit chỉ đổi ExecStart | TestPackagedUnitCarriesRequiredHardening, TestRenderUnitRewritesOnlyTheBinary |
Unit: svc.Manager | AC-P03 đến AC-P09 | Cài, gỡ, điều khiển với Run, LookPath, Lookup, Chown, Euid giả | TestInstallCreatesAccountDirsUnitAndReloads, TestInstallKeepsExistingAccountAndSkipsEnableByDefault, TestInstallRequiresRootAndAbsoluteBinary, TestInstallFailsClearlyWithoutUseradd, TestUninstallKeepsDataUnlessPurge, TestUninstallToleratesServiceThatIsNotLoaded, TestControlValidatesActionAndRoot |
CLI: lệnh service | AC-P10 đến AC-P12 | Đường dẫn tệp chạy, cờ, thông điệp, Linux-only | TestServiceInstallWritesUnitForThisExecutable, TestServiceInstallHonorsBinaryFlagAndRejectsRelative, TestServiceControlAndUninstall, TestServiceNeedsRootAndAKnownAction, TestServiceIsLinuxOnlyForNow |
| Tĩnh: script và nfpm | AC-P13, AC-P14 | Cú pháp, quyền thực thi, shellcheck (bỏ qua nếu không có công cụ), mọi src tồn tại, agent.yaml mẫu hợp lệ | TestScriptsParseAndAreExecutable, TestShellcheckClean, TestNfpmSourcesExist, TestSampleConfigIsValidWithCollectorOverride |
Hành vi: postinstall.sh | AC-P15 đến AC-P23, NFR-P08 đến NFR-P10 | Chạy script thật trong thư mục giả với systemctl, runuser, useradd, tệp chạy giả ghi lại lệnh | TestPostinstallWithoutEnvOnlyPreparesDirectories, TestPostinstallEnrollsThroughStdinAndStarts, TestPostinstallReplacesExistingCollectorURLOnce, TestPostinstallTokenFileIsRemovedAfterEnroll, TestPostinstallKeepsExistingCredentials, TestPostinstallEnrollFailureDoesNotFailPackageOrStart, TestPostinstallNoStartOnlyEnables, TestPostinstallRejectsUnsafeValues, TestPostinstallNeedsBothURLAndToken |
Hành vi: preremove.sh, postremove.sh | AC-P24, NFR-P09 | Đối số deb và rpm, chỉ purge mới xóa | TestPreremoveOnlyStopsOnRealRemoval, TestPostremoveKeepsDataUnlessPurge |
Hành vi: install.sh | AC-P25 đến AC-P27, NFR-P07 | Từ chối không khóa, --allow-unsigned vẫn kiểm checksum, chữ ký, đầu vào xấu | TestInstallRefusesWithoutKeyOrOptOut, TestInstallAllowUnsignedStillChecksums, TestInstallVerifiesSignature, TestInstallRejectsUnsafeInput |
| CI: trần kích thước | NFR-P05, FR-P14 | Dựng chéo bốn mục tiêu, kiểm MAX_BYTES | Job cross-build (chạy scripts/cross-build.sh, không phải kiểm thử Go) |
| Chưa có: cài gói thật | NFR-P06, FR-P15 | Cài .deb và .rpm thật trên distro mục tiêu, kiểm systemd chạy unit | ĐỀ XUẤT: ma trận distro trong container hoặc VM (OQ-P12, D-13, R-04) |
| Chưa có: kiểm nội dung rpm | FR-P07 | rpm -qlp, rpm -qp --scripts | ĐỀ XUẤT (OQ-P12). Mới kiểm .deb bằng dpkg-deb một lần thủ công |
Chưa có: nhánh su và đường rpm của install.sh | FR-P09, FR-P11 | Dự phòng khi không có runuser, dnf, yum, rpm -U | ĐỀ XUẤT (OQ-P12) |
Chưa có: preinstall.sh | FR-P08 | Hành vi tạo tài khoản và thoát 1, hiện chỉ có kiểm cú pháp và shellcheck | ĐỀ XUẤT (OQ-P12) |
Chưa có: package.sh, quy tắc phiên bản | FR-P12, FR-P13 | Bảng ca cho v1.2.3, 9d76488, -dirty, ký tự lạ, SIGN_HOOK lỗi | ĐỀ XUẤT (OQ-P12) |
| Chưa có: hành vi systemd | NFR-P04, NFR-P11 | Chạy lại, dừng theo mã, start limit hit, dừng êm trong 30 s | ĐỀ XUẤT: kiểm thử trong container có systemd (OQ-P12) |
Chưa có: systemd-analyze security | NFR-P01, NFR-P02 | Ngưỡng điểm hardening trong CI | ĐỀ XUẤT (OQ-P12) |
| Chưa có: bẫy cài thủ công | FR-P04 | service install ghi mẫu, nhắc collector_url, nhận --collector; start khi thiếu collector_url vẫn failed (mã 3) | ĐÃ HIỆN THỰC (OQ-P3), chưa có kiểm thử hành vi trên systemd thật |
| Chưa có: Windows | FR-P17 | Dịch vụ Windows, MSI | THIẾT KẾ, CHƯA XÂY (D-05, AGT-7) |
Lưu ý khi chạy: TestShellcheckClean bỏ qua khi máy không có shellcheck, nên CI không có công cụ này thì kết quả xanh không chứng minh script sạch. Các kiểm thử script gói dùng sh và tệp giả nên chỉ chạy trên Linux và macOS, không chạy trên Windows. Hai kiểm tra thủ công một lần (không lặp lại được từ kho mã và không tính là kiểm thử): systemd-analyze verify trên unit (systemd 255) và dựng thật gói deb và rpm bằng nfpm (chỉ kiểm deb bằng dpkg-deb). Cả hai dùng tệp tạm đã được xóa.
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 |
|---|---|---|---|
| P1 | internal/svc (Manager, RenderUnit) và lệnh service, uninstall. ĐÃ HIỆN THỰC | Không | AC-P02 đến AC-P12 |
| P2 | Unit một nguồn, hardening, hạn mức chạy lại. ĐÃ HIỆN THỰC | Không | AC-P01 |
| P3 | nfpm.yaml và bốn script gói. ĐÃ HIỆN THỰC (trong cây làm việc, AGT-6 chưa commit, D-13) | P1, P2 | AC-P13 đến AC-P24 |
| P4 | install.sh xác minh chữ ký và checksum. ĐÃ HIỆN THỰC (chưa commit) | P3 | AC-P25 đến AC-P27 |
| P5 | package.sh, make package, snapshot, checksums, cross-build.sh. ĐÃ HIỆN THỰC (chưa commit, riêng cross-build đã có job CI) | P3, P4 | NFR-P05 |
| P6 | Commit AGT-6, ma trận distro, kiểm .rpm, kiểm thử nhánh còn thiếu (OQ-P12). ĐỀ XUẤT | P3, P4, P5 | AC mới (chưa có) |
| P7 | Job CI đóng gói, chạy package.sh, shellcheck và kiểm nội dung gói. ĐỀ XUẤT (D-12) | P6 | AC mới (chưa có) |
| P8 | Kho gói, khóa ký, latest, SBOM, địa chỉ thật thay giữ chỗ. THIẾT KẾ, CHƯA XÂY (AGT-11, Q2, Q3, OQ-P11) | P7 | FR-P16 |
| P9 | Khép các bẫy: agent.yaml 0640 (OQ-P1, ĐÃ XONG), service install thông báo và ghi khóa thiếu (OQ-P3, ĐÃ XONG), cảnh báo root enroll (OQ-P2), /var/log trong gói (OQ-P7), purge cho rpm (OQ-P8), thoát ký tự RenderUnit (OQ-P6). MỘT PHẦN (OQ-P1, OQ-P3 xong, còn lại ĐỀ XUẤT) | P6 | AC mới (chưa có cho phần còn lại) |
| P10 | Dịch vụ Windows, MSI, install.ps1. THIẾT KẾ, CHƯA XÂY (AGT-7, D-05, OQ-P9) | P1, Enroll E9 | FR-P17 |
15.2. Sơ đồ phụ thuộc milestone
flowchart LR
P1["P1 · svc và lệnh service"]
P2["P2 · Unit và hardening"]
P3["P3 · nfpm và script"]
P4["P4 · install.sh"]
P5["P5 · package.sh và make"]
P6["P6 · Commit và ma trận distro"]
P7["P7 · Job CI đóng gói"]
P8["P8 · Kho và ký"]
P9["P9 · Khép các bẫy"]
P10["P10 · Windows"]
P1 --> P3
P2 --> P3
P3 --> P4
P3 --> P5
P4 --> P5
P5 --> P6
P6 --> P7
P7 --> P8
P6 --> P9
P1 --> P10
style P1 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style P2 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style P3 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style P4 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style P5 fill:#2d4a3e,stroke:#5fb37a,color:#fff
style P6 fill:#3a3320,stroke:#d9b84a,color:#fff
style P7 fill:#3a3320,stroke:#d9b84a,color:#fff
style P9 fill:#3a3320,stroke:#d9b84a,color:#fff
style P8 fill:#444,stroke:#aaa,color:#fff
style P10 fill:#444,stroke:#aaa,color:#fffChú giải: xanh lá là đã hiện thực (P3 đến P5 còn nằm ngoài lịch sử git), vàng là đề xuất, xám là thiết kế chưa xây. Đường găng tới kho và ký là P3, P5, P6, P7, P8. P9 độc lập với P7 và P8 nên có thể làm song song ngay sau P6. P10 còn cần phía Enroll (bảo vệ token trên Windows).
Phụ lục A: Open Questions
| # | Câu hỏi | Hành vi tạm thời | Owner | Mã theo dõi |
|---|---|---|---|---|
| OQ-P1 | agent.yaml được gói cài 0644 root:root nên ai cũng đọc được, mà có thể chứa proxy_url kèm mật khẩu (trả lời OQ-E19). Có đặt 0640 và nhóm accesshub-agent không? ĐÃ XỬ LÝ: gói, service install và set_key đều 0640 root:accesshub-agent | 0640 root:accesshub-agent (đã làm) | đã đóng | L3-PKG-OQ1 |
| OQ-P2 | enroll bằng root (tay) tạo credentials.json thuộc root mà dịch vụ không đọc được, rồi run thoát mã 1 và bị chạy lại tới hết hạn mức (OQ-E5). Có cảnh báo khi root, hoặc tự chown? | Hướng dẫn và postinstall dùng tài khoản dịch vụ | chưa chỉ định | L3-PKG-OQ2 |
| OQ-P3 | service install không ghi agent.yaml nên run thoát mã 3 và, vì mã 3 nằm trong RestartPreventExitStatus, dịch vụ nằm failed. Thông điệp không nhắc collector_url. Có in nhắc, hoặc nhận --collector? ĐÃ XỬ LÝ: ghi agent.yaml mẫu khi chưa có, nhận --collector, nhắc collector_url | Đã làm | đã đóng | L3-PKG-OQ3 |
| OQ-P4 | MemoryMax=96M (cứng) so với limits.memory_limit mặc định 64 MiB và cho phép tới 4 GiB: đặt lớn hơn 96 MiB thì bị OOM kill thay vì tự nén. Nên kiểm khi nạp cấu hình hay nâng MemoryMax? | Cố định 96M | chưa chỉ định | L3-PKG-OQ4 |
| OQ-P5 | ReadWritePaths cố định hai thư mục, nên state_dir hoặc log.file tùy chỉnh ngoài đó bị ProtectSystem=strict chặn ghi. Có sinh drop-in hoặc kiểm đường dẫn khi cài? | Người vận hành tự thêm ReadWritePaths | chưa chỉ định | L3-PKG-OQ5 |
| OQ-P6 | RenderUnit không thoát ký tự: đường dẫn có khoảng trắng hoặc % làm hỏng dòng ExecStart. Có thoát hoặc từ chối? | Chỉ kiểm tuyệt đối | chưa chỉ định | L3-PKG-OQ6 |
| OQ-P7 | nfpm không khai báo /var/log/accesshub-agent, và gỡ thường cố ý để lại tài khoản. Có khai báo thư mục và có xóa tài khoản khi gỡ? | Thư mục do unit và script tạo, tài khoản giữ | chưa chỉ định | L3-PKG-OQ7 |
| OQ-P8 | rpm không có bước purge: gỡ bằng rpm -e hoặc dnf remove để lại dữ liệu và tài khoản mãi mãi. Cách gỡ sạch chuẩn là gì (vì uninstall --purge phải chạy trước)? | Dùng uninstall --purge trước khi gỡ gói | chưa chỉ định | L3-PKG-OQ8 |
| OQ-P9 | Thông báo Windows của service nêu AGT-8 nhưng L2 gán dịch vụ Windows cho AGT-7 (cùng OQ-E16). Sửa thông báo hay L2? ĐÃ XỬ LÝ: thông báo nay nêu AGT-7 | Đã sửa thông báo | đã đóng | L3-PKG-OQ9 |
| OQ-P10 | Giá trị mặc định của --base-url là địa chỉ giữ chỗ, và install.sh chạy qua curl | sh thì chính nó chưa được xác minh. Hướng dẫn chuẩn là gì: tải, kiểm chữ ký rồi mới chạy? | Bắt buộc truyền --base-url trên thực tế | chưa chỉ định | L3-PKG-OQ10 |
| OQ-P11 | Chưa có kho gói, khóa ký, job CI đóng gói, SBOM, và maintainer trong nfpm.yaml là email giữ chỗ (D-12, R-03, Q2, Q3). Ai cấp khóa, địa chỉ phát hành và danh tính bảo trì? | Phát hành dạng tệp, người vận hành tự cấp khóa | chưa chỉ định | L3-PKG-OQ11 |
| OQ-P12 | Chưa có kiểm thử cài gói thật, ma trận distro, kiểm .rpm, nhánh su và rpm của install.sh, hành vi preinstall.sh, package.sh, hành vi systemd, systemd-analyze security (D-13, R-04, mục 14). Khi nào và ở đâu chạy? | Chỉ kiểm tĩnh và hành vi script bằng tệp giả | chưa chỉ định | L3-PKG-OQ12 |
| OQ-P13 | Hai bản unit (gói ở /lib hoặc /usr/lib, service install ở /etc/systemd/system thắng), uninstall chỉ xóa bản /etc, và uninstall --purge sau cài gói xóa /etc/accesshub-agent của gói trong khi gói còn. Có phát hiện gói đã cài và từ chối hoặc đổi hành vi? | Không phát hiện | chưa chỉ định | L3-PKG-OQ13 |
| OQ-P14 | Gói tar.gz không có script gói: phải tự service install, viết agent.yaml, enroll. Có cần script cài riêng hay ghi rõ hơn trong hướng dẫn? | Hướng dẫn thủ công | chưa chỉ định | L3-PKG-OQ14 |
| OQ-P15 | Nhánh "already enrolled" của postinstall không xóa tệp token, nên tệp token còn lại trên đĩa dù đã vô dụng. Có xóa? | Giữ tệp | chưa chỉ định | L3-PKG-OQ15 |
| OQ-P16 | Gỡ thường rồi cài lại không biến môi trường giữ danh tính nhưng không bật lại dịch vụ (vì preremove đã disable). Có chủ đích không, và có cần enable --now khi đã có danh tính? | Phải systemctl enable --now tay | chưa chỉ định | L3-PKG-OQ16 |
| OQ-P17 | --proxy URL của install.sh đi qua argv nên mật khẩu proxy lộ trong danh sách tiến trình. Có nhận qua biến môi trường hoặc tệp? | Chấp nhận, ghi chú trong tài liệu | chưa chỉ định | L3-PKG-OQ17 |
| OQ-P18 | Token trong ACCESSHUB_LICENSE sống suốt quá trình cài và đọc được qua /proc/<pid>/environ. Có ưu tiên tệp token trong hướng dẫn và cảnh báo khi dùng biến môi trường? | Đưa vào ống ở postinstall, biến vẫn tồn tại ở tiến trình cha | chưa chỉ định | L3-PKG-OQ18 |
| OQ-P19 | Không có kiểm tra sức khỏe sau nâng cấp, tự quay lại, OnFailure= hay chỉ số cho "đã cài nhưng chưa enroll" và "chạy lại nhiều lần". Có thêm? | Phát hiện bằng systemctl status thủ công | chưa chỉ định | L3-PKG-OQ19 |
Phụ lục B: ADR nội bộ
| Mã ADR | Quyết định | Trạng thái | Động lực |
|---|---|---|---|
| ADR-P01 | Một tệp unit duy nhất, nhúng vào tệp chạy và nfpm lấy cùng nguồn, RenderUnit chỉ viết lại ExecStart | Đã hiện thực | Hardening có hiệu lực như nhau ở hai đường cài. Đánh đổi: không thoát ký tự (OQ-P6), ReadWritePaths cố định (OQ-P5) |
| ADR-P02 | service install và postinstall không bao giờ khởi động dịch vụ trước khi có danh tính | Đã hiện thực | Tránh vòng chạy lại vô ích khi chưa có collector_url hay credentials.json. Đánh đổi: dịch vụ failed nếu người vận hành quên collector_url (OQ-P3, đã có nhắc và --collector) |
| ADR-P03 | Enroll trong postinstall bằng tài khoản dịch vụ, token qua ống, và không bao giờ làm hỏng gói | Đã hiện thực | Cài hàng loạt không tương tác, credentials.json đúng chủ, lỗi mạng không để máy cài dở (ADR-E09). Đánh đổi: pipeline phải tự kiểm enroll (OQ-P18, OQ-P19) |
| ADR-P04 | install.sh xác minh trước khi tin: chữ ký rồi tên gói từ SHA256SUMS rồi checksum, không bao giờ bỏ checksum | Đã hiện thực | Kẻ chiếm kho không thay được gói. Đánh đổi: phụ thuộc kênh phân phối khóa (OQ-P10, OQ-P11) |
| ADR-P05 | agent.yaml là config|noreplace, nâng cấp không ghi đè | Đã hiện thực | Giữ chỉnh sửa của người vận hành. Đánh đổi: khóa mới trong mẫu không tự xuất hiện (cùng OQ-E13) |
| ADR-P06 | Gỡ thường giữ dữ liệu và tài khoản, chỉ purge mới xóa | Đã hiện thực | Cài lại và nâng cấp không mất danh tính. Đánh đổi: rpm không có purge (OQ-P8), tài khoản mồ côi (OQ-P7) |
| ADR-P07 | Một cấu hình nfpm cho cả deb và rpm, package.sh thay AGENT_BINARY bằng sed | Đã hiện thực | Một nguồn cho hai định dạng. Đánh đổi: nfpm không mở rộng biến trong contents.src, phải thay trước |
| ADR-P08 | Ký bằng hook ngoài (SIGN_HOOK) thay vì nhúng công cụ ký vào package.sh | Đã hiện thực | Khóa ký không nằm trong kho mã. Đánh đổi: chưa có hook thực, chưa có CI (OQ-P11) |
| ADR-P09 | Hạn mức chạy lại hữu hạn, mã 2 đến 5 dừng hẳn | Đã hiện thực | Không đốt CPU với lỗi tự không khỏi. Đánh đổi: mã 1 gộp nhiều nguyên nhân, không có cảnh báo khi failed (OQ-P19) |
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-P01 đến P17, NFR-P01 đến P12, AC-P01 đến P27, QAS-P01 đến P06 |
| §3 Kiến trúc | Bắt buộc | Đã điền | |
| §4 Domain model | Bắt buộc | Đã điền | Hai vùng: dịch vụ và phát 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, cờ bộ cài, biến môi trường, bộ phát hành |
| §6 Data schema | Tùy chọn | Điền thu gọn | Không có CSDL. Ánh xạ tệp, quyền và chủ sở hữu thay cho lược đồ |
| §7 Thuật toán | Bắt buộc | Đã điền | 9 luồng, hai máy trạng thái, bảy 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: không có dữ liệu riêng, khôi phục bằng cài lại |
| §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 và khóa của trình quản lý gói |
| §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-03) |
| §12 Cấu hình | Bắt buộc | Đã điền | Không có feature flag |
| §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 |
| §14 Kiểm thử | Bắt buộc | Đã điền | Có 9 khoảng trống đề xuất hoặc chưa xây |
| §15 Triển khai | Bổ sung | Đã điền |