Tự dựng và vận hành
Bảo mật khi chạy thật
Danh sách kiểm bảo mật trước khi mở O3O Office Online cho người dùng thật, kèm lệnh tự kiểm từ bên ngoài.
Trong trang này
Danh sách dưới đây phải xong TRƯỚC khi mở hệ thống cho người dùng thật. Mỗi mục có cách tự kiểm.
Danh sách kiểm trước khi chạy thật#
| # | Mục | Cách kiểm |
|---|---|---|
| 1 | O3O_DEV_MODE=0 và O3O_EMBED_ALLOW_UNSIGNED=0. | /o3o/status: dev_mode = false, embed.jwt_required = true; /o3o/demo trả 404. |
| 2 | Đã đặt O3O_PUBLIC_URL, O3O_ONLINE_SERVER_NAME, O3O_ONLINE_SSL_TERMINATION=true, O3O_ONLINE_FRAME_ANCESTORS. | Đường dẫn trong /hosting/discovery bắt đầu bằng https:// và đúng tên miền. |
| 3 | Đã đổi O3O_COOLWSD_ADMIN_PASSWORD, O3O_EMBED_JWT_SECRET, O3O_EMBED_CALLBACK_SECRET, O3O_DOCBUILDER_API_KEYS. | grep CHANGE_ME .env không ra gì; upstream.coolwsd = "ok". Riêng mật khẩu quản trị máy chủ soạn thảo được chặn từ gốc: compose từ chối khởi động khi O3O_COOLWSD_ADMIN_PASSWORD rỗng hoặc chưa đặt, gate với O3O_DEV_MODE=0 từ chối khởi động khi mật khẩu rỗng hoặc còn CHANGE_ME. |
| 4 | O3O_EMBED_ALLOWED_ORIGINS không còn là *. | Yêu cầu tạo phiên từ origin lạ trả 403 origin_not_allowed. |
| 5 | O3O_ONLINE_IMAGE trỏ tới image O3O dựng từ nguồn; image DEV không được giao cho khách. | /o3o/status: dev_image = false. Image O3O: sắp có, xem Dựng từ nguồn. |
| 6 | Callback có chữ ký. | /o3o/status: embed.callback_signing = true. |
| 7 | O3O_GATE_IMAGE và O3O_DOCBUILDER_IMAGE trỏ tới image của bản phát hành, không phải thẻ :dev dựng tại chỗ. | python tools/release.py env in đúng hai giá trị đang có trong .env; docker compose ... config --images liệt kê đúng các image đó. |
| 8 | Proxy trong là o3o-proxy, hoặc cấu hình tự viết đạt đủ danh sách kiểm proxy trong: mọi nâng cấp WebSocket tới máy chủ soạn thảo đi qua auth_request; quản trị và số liệu bị chặn kể cả dạng có dấu / cuối. | Lệnh dưới: mọi đường dẫn đóng trả 404; bắt tay WebSocket dạng lạ trả 403 hoặc 404, không bao giờ 101. |
BASE=https://office.example.com
# Mã băm phiên bản của máy chủ soạn thảo, lấy từ discovery
HASH=$(curl -s "$BASE/hosting/discovery" | grep -o 'browser/[^/"]*/cool.html' | head -n1 | cut -d/ -f2)
# Mọi đường dẫn dưới đây phải trả 404, kể cả dạng có dấu / cuối
for path in /browser/dist/admin/admin.html "/browser/$HASH/admin/admin.html" /cool/adminws /cool/adminws/ \
/cool/getMetrics /cool/getMetrics/ /cool/getMetrics/x /cool/convert-to /lool/convert-to \
/o3o/auth /o3o/wopi/files/x /o3o/demo; do
printf '%-42s %s\n' "$path" "$(curl -s -o /dev/null --path-as-is -w '%{http_code}' "$BASE$path")"
done
# Bắt tay WebSocket dạng lạ: 403, 403, 404; không dòng nào được trả 101
for path in /cool/khong-phai-url/ws /cool/khong-phai-url/ws/ /cool/adminws/; do
printf '%-42s %s\n' "$path" "$(curl -s -o /dev/null --path-as-is --http1.1 --max-time 5 -w '%{http_code}' \
-H 'Connection: Upgrade' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Version: 13' \
-H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' "$BASE$path")"
done
# Cờ trạng thái mong đợi: false, false, true, true
curl -s "$BASE/o3o/status" | jq '{dev_mode, dev_image, callback_signing: .embed.callback_signing, jwt_required: .embed.jwt_required}'
# Origin lạ phải bị từ chối: "origin_not_allowed"
curl -s -X POST "$BASE/o3o/embed/session" -H 'Origin: https://not-allowed.example' \
-H 'Content-Type: application/json' -d '{}' | jq .error.code
# Không được còn CHANGE_ME
grep -n CHANGE_ME online/.envMạng và cổng#
- Chỉ mở cổng của
o3o-proxy, và chỉ cho proxy ngoài có TLS; chặn cổng 8080 từ Internet bằng tường lửa khi proxy ngoài nằm cùng máy. - Không publish 9980, 8070, 8060 hay cổng CSDL; không trỏ proxy ngoài thẳng vào máy chủ soạn thảo.
- Mọi nâng cấp WebSocket tới máy chủ soạn thảo phải đi qua location có
auth_requesthỏi gate, dù URL ở dạng nào (/cool/<doc>/ws,/cool/ws?WOPISrc=…, có dấu/cuối hay đoạn phía sauws). Các location còn lại của máy chủ soạn thảo xoá headerUpgrade. Yêu cầu WebSocket mà gate không phân tích được là yêu cầu lạ và bị từ chối (403), không phải fail-open. /cool/getMetricsvà/cool/adminwsbị chặn ở proxy theo tiền tố, nên cả/cool/getMetrics/,/cool/getMetrics/xvà/cool/adminws/đều trả 404.- Fail-open chỉ xảy ra khi gate không trả lời, lỗi, hoặc không lấy được số liệu phiên từ máy chủ soạn thảo; khi đó
connections.enforcing = false. Gate từ chối khởi động (ví dụ vì mật khẩu quản trị cònCHANGE_ME) cũng làm proxy fail-open: kiểmdocker compose psvà nhật ký gate ngay sau mỗi lần khởi động. - DocBuilder là API giữa các máy chủ: giới hạn
/v1/theo địa chỉ IP của máy chủ ứng dụng ở proxy ngoài nếu có thể.
Bí mật#
.envkhông vào git; đặt quyền đọc chỉ cho tài khoản vận hành (ví dụchmod 600).- Mỗi bí mật một giá trị riêng, tối thiểu 32 ký tự ngẫu nhiên; không dùng chung một chuỗi cho JWT và HMAC.
- Xoay khoá định kỳ theo Xác thực: khoá API và JWT.
- Gắn token bản quyền vào instance (
mid) để token lộ ra không dùng được ở máy khác.
Tải URL từ bên ngoài (SSRF)#
Gate và DocBuilder tự tải document.url, url, template_url, ảnh trong o3oscript và gọi callbackUrl, callback_url. Mọi URL đó chịu cùng một bộ quy tắc:
- Chỉ
httpvàhttps; không cóuser:pass@; dài tối đa 2048 ký tự. - Tên miền được phân giải MỘT lần trước khi nối; MỌI địa chỉ trả về phải là địa chỉ công cộng (loại trừ loopback, dải riêng, link-local, multicast, dải dành riêng, kể cả IPv6 và IPv4 nhúng trong IPv6).
- Chống DNS rebinding (có trong v1, ở cả gate và DocBuilder): sau khi kiểm, dịch vụ nối THẲNG tới đúng các địa chỉ IP vừa kiểm, không phân giải tên miền lần nữa, nên tên miền không thể đổi sang địa chỉ nội bộ giữa lúc kiểm và lúc nối. Header
Host, SNI và việc kiểm chứng chỉ TLS vẫn theo tên miền gốc. Cơ chế này áp cho mọi lần tải tệp, từng chặng chuyển hướng và mọi lần gửi callback; máy đích chưa qua kiểm thì bị từ chối nối. - Ngoại lệ duy nhất là
O3O_FETCH_ALLOW_HOSTS. Mục ghi theo TÊN máy (hosthoặchost:port) được tin theo tên: bỏ qua bước kiểm dải địa chỉ và được phân giải bình thường lúc nối, vì thế chỉ ghi tên máy mà chính bạn quản lý DNS. Mục CIDR vẫn được ghim: địa chỉ phân giải ra phải thuộc dải đó hoặc là địa chỉ công cộng, và kết nối đi thẳng tới địa chỉ đã kiểm. Ở máy chạy thật, giữ danh sách này ngắn nhất có thể và không thêm cả dải như10.0.0.0/8. - Yêu cầu tải theo tối đa 3 lần chuyển hướng, kiểm và ghim lại từng chặng; callback không theo chuyển hướng.
Tài liệu và dữ liệu khách#
- DocBuilder tắt cứng macro ở mọi lần nạp tệp qua LibreOffice; image không cài các gói chạy script của bộ xử lý tài liệu. Điền mẫu không nạp mẫu qua LibreOffice, nên mẫu có macro (phần
vbaProject.bin, kiểu nội dungmacroEnabled, thư mụcBasic/hayScripts/) bị từ chối với422 macro_not_allowed. - Mỗi job làm việc trong thư mục riêng; tên tệp do khách gửi không bao giờ được dùng làm đường dẫn trên đĩa.
- DocBuilder chạy bằng người dùng không phải root,
no-new-privileges, giới hạn bộ nhớ và số tiến trình; worker được dựng lại sau mỗi lần quá giờ hay sập. - Volume
o3o-gate-datachứa bản làm việc của tài liệu nhúng (5 phiên bản gần nhất cộng bản gốc, xoá sauO3O_EMBED_RETAIN_HOURS). Hãy bảo vệ và mã hoá đĩa cùng bản sao lưu của volume này như dữ liệu khách hàng. - URL tải bản lưu có chữ ký HMAC và thời hạn;
/o3o/statuskhông chứa tên tệp hay tên người dùng; nhật ký không chứa nội dung tài liệu, token hay khoá.
Callback#
- Bộ nhận kiểm
X-O3O-Signaturetrên thân nguyên văn, so sánh thời gian hằng, từ chối dấu thời gian lệch quá 300 giây. - Dùng https cho
callbackUrlvàcallback_url; chống trùng bằngX-O3O-Delivery. Chi tiết: Callback và webhook.
Cập nhật bảo mật#
Theo dõi Nhật ký thay đổi và versions.json. Dựng lại image DocBuilder định kỳ để nhận bản vá bảo mật của hệ điều hành nền; làm theo Nâng cấp và quay lui.