Bỏ qua, tới nội dung
Tài liệu APIv1

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ụcCách kiểm
1O3O_DEV_MODE=0O3O_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.
4O3O_EMBED_ALLOWED_ORIGINS không còn là *.Yêu cầu tạo phiên từ origin lạ trả 403 origin_not_allowed.
5O3O_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.
6Callback có chữ ký./o3o/status: embed.callback_signing = true.
7O3O_GATE_IMAGEO3O_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 đó.
8Proxy 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.
BashTự kiểm từ bên ngoài
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/.env

Mạ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_request hỏi gate, dù URL ở dạng nào (/cool/<doc>/ws, /cool/ws?WOPISrc=…, có dấu / cuối hay đoạn phía sau ws). Các location còn lại của máy chủ soạn thảo xoá header Upgrade. 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/getMetrics/cool/adminws bị chặn ở proxy theo tiền tố, nên cả /cool/getMetrics/, /cool/getMetrics/x/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òn CHANGE_ME) cũng làm proxy fail-open: kiểm docker compose ps và 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#

  • .env khô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ỉ httphttps; 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 (host hoặc host: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 dung macroEnabled, thư mục Basic/ hay Scripts/) bị từ chối với 422 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-data chứ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á sau O3O_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/status khô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-Signature trê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 callbackUrlcallback_url; chống trùng bằng X-O3O-Delivery. Chi tiết: Callback và webhook.

Cập nhật bảo mật#

Theo dõi Nhật ký thay đổiversions.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.