Phần 01
Vì sao có 19 vai — thật ra chỉ có 5
Truy vấn select * from pg_roles trả 19 dòng. Trong đó 14 là vai PostgreSQL 16 tự tạo lúc cài đặt (tên bắt đầu bằng pg_), không đăng nhập được và không vai nào của dự án là thành viên — chúng nằm đó trên mọi máy chủ PostgreSQL 16. Phần còn lại là 5 vai của dự án.
Cập nhật 08/10/2026 — trang này đo ngày 26/09 trên laptop, khi laptop còn là máy hub. Từ đêm 05→06/10 kho chạy trên PC văn phòng. Đo lại trên PC lúc 15:32 ngày 08/10 (vai chỉ đọc, chỉ danh mục hệ thống): 20 vai, 6 của dự án, 5 đăng nhập được. Vai mới claude_ro_laptop (07/10): thành viên bi_reader, tối đa 4 kết nối, cùng 8 cài đặt như claude_ro cộng ghi mọi câu lệnh vào nhật ký; đăng nhập từ laptop qua NetBird và chuyển tiếp cổng trên PC. bi_reader nay 24 bảng / view (thêm dim_thue_gtgt 27/09), bi_full 30. Bốn vai đăng nhập cũ trên PC đều có mật khẩu 32 ký tự sinh ngẫu nhiên (đo 07/10, không in). Vai sắp tới: claude_ro_ceo, chờ duyệt. Bản đồ các lớp mà các vai này canh: Phụ lục 7.
| Vai | Một câu: để làm gì | Đăng nhập | Ai / cái gì dùng |
|---|---|---|---|
datahub | Chủ sở hữu toàn bộ dữ liệu — vai duy nhất được ghi | Có · SUPERUSER | Đồng bộ đêm 01:00, dbt, task kiểm sức khoẻ, script của agent |
bi_reader | Vùng số liệu được phép rời máy: 23 bảng / view, không tên hay SĐT khách | Có | Bản tin Teams 07:00; là "nhóm" mà claude_ro và bi_full kế thừa |
bi_pii | Nhóm quyền cho vùng dữ liệu cá nhân: 6 bảng có tên, SĐT, email, địa chỉ khách, họ tên nhân viên | Không | Không ai đăng nhập; chỉ bi_full là thành viên |
bi_full | Người thấy tất cả trên máy này = bi_reader + bi_pii (29 bảng / view) | Có | Power BI Desktop, DBeaver |
claude_ro | Chatbot hỏi số qua MCP — đọc đúng vùng bi_reader, kèm 8 cài đặt an toàn | Có · tối đa 4 kết nối | Máy chủ MCP bi/mcp/mcp_datahub_ro.py (tạo 26/09 10:06) |
Gộp bớt thì hỏng ở đâu
Năm vai là số nhỏ nhất giữ được bốn ranh giới đang có. Mỗi dòng dưới đây là một cách gộp nghe có vẻ gọn, và thứ nó làm vỡ:
| Nếu… | Thì hỏng |
|---|---|
Power BI dùng luôn datahub | Mật khẩu lưu trong kho credential của Power BI Desktop trở thành chìa khoá vạn năng: đọc mọi dữ liệu cá nhân, sửa và xoá được cả hub. Lộ máy hay lộ tài khoản Windows là lộ cả chìa này — trong khi báo cáo chỉ cần đọc. |
Chatbot dùng bi_full | Câu "20 khách mua nhiều nhất" trả về họ tên và SĐT thật, và chúng đi lên máy chủ AI bên ngoài — không thu hồi được. Trái cổng GATE D, phạm vi (c). |
Chatbot dùng bi_reader luôn, bỏ claude_ro | Mất 8 cài đặt an toàn gắn vào vai: một câu hỏi viết sai có thể chạy vô hạn, ghi file tạm không giới hạn ra đĩa, giữ khoá làm dbt đêm bị kẹt. Audit cũng không tách được câu của chatbot với câu của bản tin. |
Bỏ nhóm bi_pii, cấp thẳng 6 bảng cho bi_full | Chạy được, nhưng mỗi người mới được xem dữ liệu cá nhân phải cấp lại 6 bảng, và 6 model dbt phải cấp quyền cho một vai đăng nhập cụ thể mỗi đêm. |
Phần 02
Vai nào thấy gì — một bảng
Đo bằng cách đóng vai thật (SET ROLE rồi đọc từng bảng), không bằng hàm hỏi quyền — hàm đó từng báo "có quyền" trong khi đọc thật thì bị từ chối (bẫy thiếu quyền vào schema, 17/09). Với claude_ro: đăng nhập thật từ máy chủ Windows, cổng 5433.
| Vai | Dữ liệu thôraw, staging · 30 |
Vùng số liệumart + dq · 23 |
Vùng dữ liệu cá nhânmart · 6 |
Đối chiếu chi tiếtdq · 3 |
Ghi / sửa / xoá | Hàm lấy khoá đồng bộ |
|---|---|---|---|---|---|---|
datahub | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
bi_full | — | ✓ | ✓ | — | — | — |
bi_reader | — | ✓ | — | — | — | — |
claude_ro | — | ✓ | — | — | — | — |
bi_pii (nhóm) | — | — | ✓ | — | — | — |
✓ đọc / dùng được · — Postgres từ chối (mã 42501). Số dưới tên cột là số bảng / view trong vùng đó.
mart (dòng hàng, đơn không địa chỉ, thanh toán, hồ sơ khách giả danh — mã khách, giới tính, tỉnh, hạng thẻ —, các bảng tổng hợp bản tin) + 8 trong dq (cổng chất lượng). Sau quyết định "đường A" ngày 25/09, vùng này được phép đi lên máy chủ AI qua chatbot.dim_customer, dim_customer_pii (tên, SĐT, email, ngày sinh khách), fact_order (địa chỉ giao hàng), dim_salesperson, dim_salesperson_bi (họ tên nhân viên — dời vào đây sáng 26/09, quyết định D-1b = A2), và fact_order_line — bảng này không có dữ liệu cá nhân, được xếp vào vùng theo yêu cầu 22/09 để tra dòng hàng bằng DBeaver. Nên hiểu vùng này là "chỉ bi_full thấy", không phải "túi dữ liệu cá nhân".dq_order_recon, dq_exception, dq_fk_check — mang mã khách từng đơn, chỉ datahub đọc. Số tổng của chúng có ở các bảng dq_*_tomtat trong vùng số liệu.pg_advisory_lock / pg_try_advisory_lock… Trước 26/09 mọi vai gọi được; một câu của chatbot giành đúng khoá 91626 lúc 01:00 sẽ làm đồng bộ đêm bỏ lượt mà không báo. Đã thu hồi khỏi PUBLIC ngày 26/09 10:06 (quyết định D-3); chỉ ingest/sync.py dùng, bằng datahub là superuser nên không bị ảnh hưởng.Phần 03
Năm vai của dự án — từng vai một
Mỗi vai: để làm gì, cái gì dùng nó, mật khẩu nằm đâu, sinh ra từ file nào, và lộ mật khẩu thì mất gì. Quyền đọc từng bảng không cấp tay: bảng trong kho được dbt dựng lại mỗi đêm, nên quyền được cấp lại bằng post_hook của từng model — đó là nguồn sự thật.
datahub — chủ nhà
| Để làm gì | Vai duy nhất được ghi. Chủ database datahub, chủ 4 schema và cả 62 bảng / view (raw 16 · staging 14 · mart 21 · dq 11). |
|---|---|
| Thuộc tính | SUPERUSER · đăng nhập được · không giới hạn kết nối |
| Cái gì dùng | Đồng bộ đêm ingest/sync.py + dbt run / dbt test (task 01:00) · task kiểm sức khoẻ 09/15/21h · script đo của agent (luôn trong giao dịch chỉ đọc) |
| Mật khẩu | .env → PG_PASSWORD. Chuỗi ngẫu nhiên 32 ký tự từ lần xoay 22/09 |
| Sinh ra từ | Lúc khởi tạo container: POSTGRES_USER trong infra/docker-compose.yml |
| Lộ mật khẩu thì | Mất tất cả — đọc mọi dữ liệu cá nhân, sửa / xoá dữ liệu, tạo vai mới cho chính mình |
bi_reader — vùng số liệu
| Để làm gì | Đọc 23 bảng / view (15 mart + 8 dq) — số liệu bán hàng, hồ sơ khách giả danh, cổng chất lượng. Không thấy tên, SĐT, email, địa chỉ khách; từ 26/09 08:11 cũng không thấy họ tên nhân viên. |
|---|---|
| Thuộc tính | Đăng nhập được · kiêm vai trò "nhóm": claude_ro và bi_full kế thừa quyền từ nó |
| Cái gì dùng | Bản tin doanh số Teams notify/teams_digest.py (task 07:00 / 09:30). Trước 22/09 Power BI cũng dùng vai này |
| Mật khẩu | .env → BI_READER_PASSWORD. Do người đặt — độ mạnh chưa kiểm |
| Sinh ra từ | infra/03_bi_reader.sql (mốc P2.2, 17/09) + post_hook "cấp cho bi_reader" ở 23 model dbt |
| Lộ mật khẩu thì | Mất vùng số liệu — toàn bộ doanh số từng dòng và hồ sơ khách giả danh; không ghi được, không thấy dữ liệu cá nhân |
bi_pii — nhóm vùng dữ liệu cá nhân
| Để làm gì | Giữ quyền đọc 6 bảng chỉ bi_full được thấy (Phần 02, ghi chú 2) |
|---|---|
| Thuộc tính | Không đăng nhập được, không có mật khẩu (đo: trường mật khẩu rỗng). Thành viên duy nhất: bi_full |
| Vì sao là nhóm | Sáu bảng này bị dbt tạo lại mỗi đêm, quyền cũ mất theo. post_hook của 6 model cấp lại cho một tên cố định; muốn thêm người được xem dữ liệu cá nhân chỉ cần GRANT bi_pii TO … một lần |
| Sinh ra từ | infra/05_pii_unlock.sql phần A (22/09, sau khi cổng GATE D được ký) |
| Lộ mật khẩu thì | Không có mật khẩu để lộ. Rủi ro là cấp nhầm thành viên — một dòng GRANT bi_pii TO claude_ro là dữ liệu cá nhân đi lên máy chủ AI |
bi_full — người thấy tất cả trên máy
| Để làm gì | = bi_reader + bi_pii: đọc 29 bảng / view. Vẫn không đọc được dữ liệu thô và 3 bảng đối chiếu chi tiết; không ghi được |
|---|---|
| Thuộc tính | Đăng nhập được · thành viên của bi_reader và bi_pii |
| Cái gì dùng | Power BI Desktop (từ 22/09 — đọc được view dữ liệu cá nhân dim_customer_pii) và DBeaver để tra cứu |
| Mật khẩu | .env → BI_FULL_PASSWORD + kho credential của Power BI Desktop (hai mục nguồn: tên máy và địa chỉ IP loopback). Do người đặt — độ mạnh chưa kiểm |
| Sinh ra từ | infra/05_pii_unlock.sql phần B (22/09) |
| Lộ mật khẩu thì | Mất dữ liệu cá nhân của 385.833 khách (tên, SĐT, email, ngày sinh, địa chỉ giao hàng) — nhưng không sửa hay xoá được gì. File .pbix cũng là một bản sao dữ liệu cá nhân: theo GATE D chỉ một người cầm |
claude_ro — chatbot
| Để làm gì | Cho chatbot hỏi số (P2.10, đường A) đọc đúng vùng bi_reader (23) — không hơn, không kém. Đo 26/09: tập đọc được của claude_ro trùng tập của bi_reader từng bảng |
|---|---|
| Thuộc tính | Đăng nhập được · tối đa 4 kết nối · chỉ là thành viên của bi_reader |
| 8 cài đặt gắn vào vai | Giao dịch mặc định chỉ đọc · mỗi câu tối đa 60 giây · chờ khoá tối đa 3 giây · giao dịch bỏ quên bị ngắt sau 30 giây · không chạy song song · giờ Việt Nam · tên ứng dụng claude-mcp. Bảy cài đặt này là dây an toàn, không phải khoá: phiên nào cũng tự tắt được. Khoá thật là không có quyền ghi — đo: tự tắt chế độ chỉ đọc rồi thử tạo bảng vẫn bị từ chối 42501. Cài đặt thứ tám thì phiên không tự gỡ được: mỗi câu ghi file tạm ra đĩa tối đa 1 GB (quyết định D-10, đặt 26/09). Câu nặng nhất đã đo dùng chưa tới 40% mức đó; câu nào vượt thì dừng ngay, báo lỗi 53400 |
| Cái gì dùng | Máy chủ MCP bi/mcp/mcp_datahub_ro.py — chưa có ai dùng (bước 4.4 chưa làm) |
| Mật khẩu | .env → CLAUDE_RO_PASSWORD + %APPDATA%\postgresql\pgpass.conf của tài khoản Windows của chủ dự án. Chuỗi ngẫu nhiên 32 ký tự, sinh 26/09 (không ai nhìn thấy, kể cả agent) |
| Sinh ra từ | infra/04_claude_ro.sql, chạy 26/09 10:06 · nghiệm thu infra/04b_nghiemthu_claude_ro.py 10:07: 27/27 ô đạt |
| Lộ mật khẩu thì | Mất vùng số liệu như bi_reader — nhưng chỉ từ máy này (cổng chỉ nghe loopback) và tối đa 4 phiên cùng lúc |
Phần 04
14 vai dựng sẵn — có sẵn, không dùng, đừng cấp
PostgreSQL 16 tạo sẵn các vai này để quản trị viên trao một nhóm quyền đặc biệt mà không phải cho superuser. Ở hub: cả 14 đều không đăng nhập được, và không vai nào của dự án là thành viên (đo pg_auth_members: quan hệ thành viên duy nhất giữa chúng là cấu trúc có sẵn — pg_monitor gộp quyền của 3 vai giám sát). Chúng vô hại chừng nào không ai GRANT chúng.
| Vai | Cho phép gì | Nếu cấp nhầm cho vai báo cáo / chatbot |
|---|---|---|
pg_read_all_data | Đọc mọi bảng, view, bỏ qua quyền từng bảng | Cao xuyên thủng vùng dữ liệu cá nhân bằng một dòng |
pg_write_all_data | Ghi, sửa, xoá mọi bảng | Cao |
pg_read_server_files | Đọc file bất kỳ trên máy chủ cơ sở dữ liệu (trong container) | Cao |
pg_write_server_files | Ghi file trên máy chủ cơ sở dữ liệu | Cao |
pg_execute_server_program | Chạy lệnh hệ điều hành trong container | Cao |
pg_signal_backend | Huỷ truy vấn / ngắt phiên của vai thường khác (không đụng được superuser) | TB ngắt được bản tin, Power BI |
pg_read_all_stats | Xem mọi thống kê, kể cả câu SQL đang chạy của vai khác | TB |
pg_monitor | Gộp pg_read_all_settings + pg_read_all_stats + pg_stat_scan_tables và các hàm giám sát | TB |
pg_stat_scan_tables | Chạy hàm giám sát có thể giữ khoá đọc lâu trên bảng | TB có thể làm dbt đêm chờ |
pg_create_subscription | Tạo đăng ký nhân bản dữ liệu sang máy khác (mới ở bản 16; cần thêm quyền tạo trên database) | TB |
pg_read_all_settings | Đọc mọi cài đặt, kể cả cài đặt thường chỉ superuser xem | Thấp |
pg_checkpoint | Ra lệnh ghi dồn dữ liệu xuống đĩa (CHECKPOINT) | Thấp |
pg_use_reserved_connections | Dùng các kết nối để dành (mới ở bản 16) | Thấp |
pg_database_owner | Không cấp được. Thành viên ngầm là chủ database đang dùng — ở đây là datahub; sở hữu schema public | — |
Nếu có ai (người hay công cụ) đề xuất "cho claude_ro / bi_reader vai pg_read_all_data cho tiện" — từ chối. Quyền đó bỏ qua toàn bộ ranh giới ở Phần 02 mà không cần đụng tới bi_pii. Các file infra/*.sql không chặn được lúc cấp; chỉ lần chạy nghiệm thu infra/04b_nghiemthu_claude_ro.py kế tiếp mới thấy tập đọc được của claude_ro lệch khỏi bi_reader.
Phần 05
Đăng nhập từ đâu — và mật khẩu nằm ở đâu
Cùng một vai, đăng nhập từ hai chỗ khác nhau thì Postgres kiểm khác nhau. Luật nằm trong pg_hba.conf của container.
| Kết nối từ | Luật | Hệ quả |
|---|---|---|
Bên trong container (docker exec … psql) | trust | Không kiểm mật khẩu, vào được bằng bất kỳ vai nào kể cả datahub. Ai chạy được lệnh docker trên máy này là có toàn quyền hub |
Máy chủ Windows qua loopback — Power BI, DBeaver, bản tin, chatbot | scram-sha-256 | Phải đúng mật khẩu. Đo 26/09: sai mật khẩu → từ chối 28P01 sau 9 ms. Postgres thấy mọi kết nối này từ cùng một địa chỉ Docker gateway → không phân biệt được máy nguồn |
| Máy khác trong mạng / Internet | — | Không với tới: cổng chỉ mở trên loopback. Sẽ đổi ở Pha 7 (máy trạm, CEO hỏi qua VPN) |
Mật khẩu từng vai nằm ở đâu
| Vai | Khoá trong .env | Nơi khác | Loại mật khẩu |
|---|---|---|---|
datahub | PG_PASSWORD | — | Ngẫu nhiên 32 ký tự (xoay 22/09) |
bi_reader | BI_READER_PASSWORD | — | Người đặt · chưa kiểm độ mạnh |
bi_full | BI_FULL_PASSWORD | Kho credential Power BI Desktop | Người đặt · chưa kiểm độ mạnh |
claude_ro | CLAUDE_RO_PASSWORD | pgpass.conf (tài khoản của chủ dự án) | Ngẫu nhiên 32 ký tự (26/09) |
bi_pii | — | — | Không có — không đăng nhập được |
.env bị git chặn (không bao giờ vào repo), nhưng mọi tài khoản Windows trên máy đọc được nó (backlog ACL-THU-MUC-REPO, mức CAO). Hôm nay máy chỉ có một người dùng nên chưa gấp; phải khoá thư mục repo trước khi thêm tài khoản thứ hai. pgpass.conf thì nằm trong hồ sơ riêng của tài khoản Windows.
Tự đếm lại
Phần 06
Sắp tới — và những điều không được làm
Danh sách vai sẽ còn đổi một lần nữa khi hub chuyển sang máy trạm và CEO hỏi chatbot từ máy riêng.
| Việc | Khi nào | Đổi gì |
|---|---|---|
Vai claude_ro_ceo | Pha 7, trên máy trạm (quyết định D-8 = C) | Như claude_ro + ghi lại mọi câu SQL vào nhật ký Postgres, để phía máy trạm tách được câu CEO hỏi. Tổng số vai của dự án thành 6 |
| Xoay mật khẩu mọi vai đăng nhập | Trước khi mở cổng ra VPN (bước 7.1b, D-9 = (i)) | bi_reader, bi_full đổi sang chuỗi ngẫu nhiên; nhập lại ở Power BI, DBeaver. Khi đó mọi vai đăng nhập đều thử được từ máy CEO — chỉ mật khẩu ngẫu nhiên chặn |
Hoàn tác claude_ro nếu cần | Bất cứ lúc nào | revoke bi_reader from claude_ro; drop role claude_ro; + trả quyền 16 hàm khoá cho PUBLIC (plan P2.10 §9) |
GRANT bi_pii TO claude_ro — đó là đúng một dòng biến chatbot thành đường đưa dữ liệu cá nhân lên máy chủ AI. Muốn mở thì người ký GATE D phải sửa file cổng trước.pg_* nào ở Phần 04 cho vai báo cáo hay chatbot.datahub cho công cụ báo cáo, DBeaver dùng hằng ngày, hay chatbot. Máy chủ MCP tự từ chối nếu kết nối bằng superuser hoặc vai thuộc bi_pii.has_table_privilege() — nó trả "có" cả khi thiếu quyền vào schema. Kiểm bằng SET ROLE … rồi đọc thật.psql rồi coi là xong — bảng được dựng lại mỗi đêm, quyền cấp tay mất sau 01:00. Quyền phải đi qua thành viên nhóm hoặc post_hook của dbt.Phần 07
Tóm lại
19 vai nghe nhiều, nhưng 14 là vai có sẵn của PostgreSQL không ai dùng. Năm vai của dự án là năm ranh giới: ghi (datahub) · số liệu (bi_reader) · dữ liệu cá nhân (bi_pii) · người xem tất cả (bi_full) · chatbot (claude_ro).
Phản biện cho chính thiết kế này
datahub là một chìa vạn năng dùng cho mọi việc chạy tự động và cho script của agent; mật khẩu của nó nằm trong .env mà mọi tài khoản Windows đọc được. Tách vai "nạp dữ liệu thô" khỏi vai "dựng kho" sẽ giảm thiệt hại khi lộ — đổi lại thêm một mật khẩu phải bàn giao. Chưa đề xuất làm trước khi chuyển máy.bi_reader vừa là vai đăng nhập của bản tin, vừa là nhóm cho chatbot kế thừa: ai có mật khẩu bi_reader đọc được đúng vùng của chatbot nhưng không có 8 cài đặt an toàn. Tách một nhóm không đăng nhập riêng cho "vùng số liệu" thì sạch hơn, nhưng phải sửa post_hook của 23 model — chưa đáng với 2 người dùng.docker.