Tự host HRConvert2: chuyển file riêng tư trên VPS
Chạy HRConvert2 trên VPS để file khách hàng không qua website miễn phí. Có hướng dẫn Docker hoặc Apache, bubblewrap sandbox, giới hạn upload và cleanup.
Vì sao nên tự host một công cụ chuyển đổi file
Công cụ chuyển đổi file tự host giữ file trên ổ đĩa của bạn. Đây là lý do duy nhất để chạy công cụ này. Một website chuyển đổi miễn phí nhận file upload nhưng không cho bạn biết sau đó file được xử lý thế nào. Với file là hợp đồng khách hàng đã ký hoặc bệnh án được scan, việc upload tự nó đã là một sự cố bảo mật. HRConvert2 là một server chuyển đổi file dạng kéo thả, được viết bằng PHP và phát hành theo giấy phép GPLv3. Hướng dẫn này được viết dựa trên version 3.7.4, phát hành ngày 18 August 2026. Dự án đã phát triển nhanh từ đó: release mới nhất tính đến ngày 13 October 2026 là v3.9.1, được công bố ngày 6 September 2026, và dự án tuyên bố hỗ trợ 488 format.
Công cụ này không có database, account hay cookie. Mỗi user được gắn với một thư mục tạm thời. Mỗi lần chuyển đổi đều do một command line tool chạy cục bộ thực hiện: LibreOffice xử lý tài liệu, FFmpeg xử lý audio và video, ImageMagick xử lý hình ảnh, Tesseract thực hiện nhận dạng ký tự quang học (OCR), cùng một loạt tool nhỏ hơn cho các format còn lại. HRConvert2 cung cấp trang upload, pipeline và cơ chế cleanup cho các tool này.
Công cụ này chuyển file từ format này sang format khác. Đây không phải là một bộ office chạy trên browser. Nếu bạn muốn nhiều người chỉnh sửa tài liệu trong một tab, hãy xem OnlyOffice và Collabora tự host thay thế. Đây cũng không phải là hệ thống lưu trữ. Output sau khi chuyển đổi được thiết kế để xóa, nên nếu cần lưu file ở đâu đó, hãy dùng file manager tự host.
Cần gì
Debian hoặc Ubuntu, Apache 2.4, PHP 8 trở lên và bubblewrap. Bubblewrap (bwrap) là sandbox và bắt buộc phải có: server không thể tạo sandbox sẽ từ chối chuyển đổi thay vì chạy mà không có sandbox. README upstream nói Raspberry Pi Model B+ là đủ, và điều đó đúng với phần PHP. Các binary converter mới quyết định giới hạn phần cứng thực tế; phần này được trình bày ở bên dưới.
Có 2 cách để bắt đầu. Docker image có thể chạy ngay tối nay. Cài Apache và PHP mất khoảng một buổi tối, nhưng cho bạn biết chính xác những gì đang có trên máy.
Chạy ngay tối nay bằng Docker
Image chứa mọi binary chuyển đổi nên khá lớn: khoảng 3 GB tính đến tháng 8 năm 2026. Kiểm tra dung lượng đĩa còn trống trước khi pull.
Tag rất quan trọng ở đây. Hướng dẫn này ghim v3.7.2, là tag mới nhất trên Docker Hub vào ngày 17 tháng 8 năm 2026 và cũng là bản release GitHub mới nhất trong ngày đó. v3.7.4 được phát hành vào ngày 18 tháng 8. Tính đến ngày 13 tháng 10 năm 2026, bản release GitHub mới nhất là v3.9.1 (ngày 6 tháng 9 năm 2026), còn các tag Docker Hub mới nhất là edge (ngày 22 tháng 9 năm 2026) và latest, tức v3.8.4 (ngày 31 tháng 8 năm 2026). Tag latest có thể thay đổi mà bạn không biết, và ứng dụng này có bề mặt parser lớn, nên hãy ghim một phiên bản và chỉ upgrade khi có chủ đích.
docker pull zelon88/hrconvert2:v3.7.2
docker run -d --name hrconvert2 \
-p 127.0.0.1:8080:80 \
--security-opt seccomp=unconfined \
zelon88/hrconvert2:v3.7.2docker ps
curl -I http://127.0.0.1:8080/Container hoạt động bình thường sẽ ở trạng thái Up và curl trả về HTTP/1.1 200 OK. Container liên tục restart có vấn đề khi khởi động, vì vậy hãy đọc docker logs hrconvert2 trước khi thay đổi bất kỳ thứ gì khác.
Có 2 flag cần chú ý. -p 127.0.0.1:8080:80 chỉ publish port trên loopback, nên không có lưu lượng nào đến được converter cho đến khi bạn chủ động đặt proxy phía trước. Ví dụ hiện tại của dự án map -p 8080:80, khiến service listen trên mọi interface, kể cả interface public. Cần có --security-opt seccomp=unconfined vì bubblewrap xây dựng sandbox bằng user namespace và các system call mount mà seccomp profile mặc định của Docker chặn. Các comment trong bản 3.7 config.php cho biết một số host cũng cần --security-opt apparmor=unconfined. Nếu thiếu flag này, quá trình chuyển đổi sẽ fail và ứng dụng sẽ cho biết lý do: A sandbox blocks the required syscalls unless it was started with the correct options.
Flag đó có đánh đổi thực sự. Bạn nới lỏng bộ lọc syscall của container để ứng dụng có thể tự xây dựng sandbox chặt chẽ hơn bên trong container. Hai setting quyết định hành vi này là $RequireSandbox và $RequireSandboxOnDocker trong Resources/config.php. Bản release v3.7.4 cung cấp chúng với giá trị TRUE và FALSE. Tag v3.7.2 được ghim ở đây cung cấp cả hai với giá trị FALSE, vì comment của tag cho biết container phải tắt $RequireSandbox và để $RequireSandboxOnDocker quyết định. Vì yêu cầu Docker mặc định đang tắt, container không có seccomp flag có thể thực hiện chuyển đổi mà hoàn toàn không có sandbox. Khi đã thêm flag, đặt $RequireSandboxOnDocker = TRUE; để khôi phục hành vi từ chối bên trong container.
Các image mới hơn khởi động theo cách khác. Dự án đã thay đổi quy trình Docker trong v3.8.0 (ngày 25 tháng 8 năm 2026). README hiện tại và trang Docker Hub chạy container với --cap-add SYS_ADMIN thay cho tùy chọn seccomp, đồng thời nêu rõ rằng quá trình chuyển đổi sẽ bị từ chối nếu thiếu tùy chọn này. Nếu chuyển pin sang tag 3.8 hoặc 3.9, hãy dùng các flag được image đó hướng dẫn. Capability này có phạm vi rộng, vì vậy vẫn giữ binding port 127.0.0.1.
Nếu máy này mới cài Docker, hãy thiết lập daemon trước. Chạy Docker trên VPS trình bày cách cài đặt, storage driver và cách Docker tự ghi các rule firewall của nó.
Cài đặt trên Apache và PHP thay thế
Cho đến v3.7.4, file Documentation/INSTALLATION_INSTRUCTIONS.txt trong repository mô tả cách cài đặt thủ công gồm chín bước, và phần này đi theo quy trình đó. Từ v3.8.0, cùng file này mô tả một setup script. Chạy sudo bash Documentation/Build/hrconvert2-setup.sh từ repository đã clone. Script sẽ cài dependency, ghi các thiết lập PHP vào một file drop-in thay vì sửa php.ini, và mở rộng AppArmor profile cho bubblewrap. Sau đó, php convertCore.php -v và --status sẽ kiểm tra kết quả. Cách thủ công mất nhiều thời gian hơn, nhưng bạn biết chính xác những gì đã được cài trên máy. Quy trình có dạng như sau. Bắt đầu với web server, ngôn ngữ và sandbox:
sudo apt update
sudo apt install -y apache2 php libapache2-mod-php php-all-dev php8.3-zip php8.3-gd bubblewrapTên các php8.3-* khớp với Ubuntu 24.04. Chạy php -v và dùng prefix khớp với phiên bản của bạn, vì tên package thay đổi theo từng bản PHP. Dùng sai prefix sẽ gây ra Unable to locate package.
Tiếp theo là các converter. Danh sách này xử lý tài liệu, hình ảnh, audio, video và OCR, tức phần lớn các loại chuyển đổi thực tế:
sudo apt install -y imagemagick ffmpeg libreoffice-common libreoffice-java-common \
default-jre ghostscript poppler-utils libgxps-utils tesseract-ocr inkscape \
xvfb clamav curl tar libxcb-cursor0Các định dạng archive, mô hình 3D, ebook và ISO bootable cần thêm package. Một số package nằm trong component multiverse của Ubuntu. Ở v3.7.4, Step 3 trong hướng dẫn chính thức chứa đầy đủ danh sách apt, còn Step 5 chứa các bước build từ source. Guide hiện tại không có danh sách package. Installer đọc manifest Resources/Engine/Contract/depends.php, còn php convertCore.php --setup --check-depends in ra danh sách hiện tại và trạng thái của từng package. Hai dependency cũng có thể được build từ source. Repository cung cấp Documentation/Build/ffmpeg-build.sh cho các encoder mà FFmpeg trong package không có, và Documentation/Build/build-imagemagick-v7.sh cho ImageMagick 7 trên Ubuntu 22.04 hoặc 24.04, là các phiên bản vẫn đóng gói nhánh 6.9. Ubuntu 25.10 và 26.04 LTS đóng gói ImageMagick 7.1. Hỗ trợ ebook đến từ installer riêng của calibre. Hướng dẫn 3.7 đưa lệnh này trên một dòng:
sudo -v && wget -nv -O- https://download.calibre-ebook.com/linux-installer.sh | sudo sh /dev/stdinĐây là vendor script được pipe vào shell với quyền root. Đây là phương thức của upstream và là tùy chọn: bỏ qua bước này thì bạn chỉ mất khả năng chuyển đổi ebook.
Tiếp theo là các giới hạn của PHP. Quá trình chuyển đổi chậm và file có kích thước lớn, nên các giá trị mặc định quá thấp. Hướng dẫn 3.7 đặt các giá trị này trong php.ini:
max_execution_time = 1200
max_input_time = 90
memory_limit = 512M
post_max_size = 5000M
upload_max_filesize = 5000M
max_file_uploads = 100
display_errors = Off
zlib.output_compression = OnCác con số này giả định máy còn đủ tài nguyên. Hãy giảm chúng trước khi chạy trên VPS nhỏ, vì upload_max_filesize = 5000M cùng với max_file_uploads = 100 mô tả một request đơn lẻ có thể ghi nhiều dữ liệu hơn sức chứa của một disk 40 GB. Restart Apache và xác nhận PHP thực tế đã load những giá trị nào:
sudo service apache2 restart
php -i | grep -E "upload_max_filesize|post_max_size|memory_limit"Bây giờ đến working directory. $ConvertLoc trong Resources/config.php chỉ định thư mục này, và giá trị mặc định là /DATA/HRConvert2. User chạy web server phải sở hữu thư mục đó:
sudo mkdir -p /DATA/HRConvert2
sudo chmod -R 0755 /DATA/HRConvert2
sudo chown -R www-data:www-data /DATA/HRConvert2Giải nén release trong document root của Apache. Layout mặc định đặt ứng dụng trong thư mục HRProprietary/HRConvert2, và $InstLoc trong Resources/config.php phải trỏ đến đúng vị trí bạn đã chọn. Sau đó chạy diagnostic tích hợp. Đây là cách nhanh nhất để phát hiện dependency còn thiếu trước khi người dùng gặp lỗi:
sudo php /path/to/HRConvert2/convertCore.php -v-v kiểm tra toàn bộ quá trình cài đặt: phiên bản core, dependency, trạng thái sandbox và language pack. Không hỗ trợ chuyển đổi file từ command line, nên bộ argument này chỉ dùng cho mục đích quản trị.
Vì sao mọi lần chuyển đổi đều thất bại trên bản cài Ubuntu 24.04 mới?
Do sandbox. Đây là vấn đề thường gặp nhất trong ngày đầu thiết lập. Ubuntu 24.04 mặc định hạn chế user namespace của user không có đặc quyền thông qua AppArmor. Bubblewrap cần user namespace để tạo sandbox, nên bwrap không thể khởi động. Vì ứng dụng từ chối chuyển đổi nếu không có sandbox, mọi job đều thất bại.
Kiểm tra trực tiếp:
bwrap --ro-bind / / --dev /dev /bin/true && echo sandbox okLỗi permission denied có nghĩa là namespace đã bị chặn. Cách khắc phục là tạo AppArmor profile cho binary bwrap. Trước tiên, liệt kê các file ABI và ghi lại số lớn nhất hiện có:
ls /etc/apparmor.d/abi/Sau đó tạo /etc/apparmor.d/bwrap, thay 4.0 bằng số lớn nhất đó:
abi <abi/4.0>,
include <tunables/global>
profile bwrap /usr/bin/bwrap flags=(unconfined) {
userns,
include if exists <local/bwrap>
}Nạp profile:
sudo apparmor_parser -r /etc/apparmor.d/bwrapKhông có output nghĩa là profile đã được nạp. Chạy lại kiểm tra bwrap. Kết quả phải là sandbox ok. Từ thời điểm đó, các lần chuyển đổi sẽ hoạt động.
Debian 12 không gặp vấn đề này. Kernel của Debian cho phép user namespace không có đặc quyền, và package bubblewrap đặt kernel.unprivileged_userns_clone thành 1 khi boot. Vì vậy, bước kiểm tra ở trên sẽ pass mà không cần profile. Từ v3.8.0 trở lên, setup script sẽ tự mở rộng AppArmor profile bubblewrap của distribution cho bạn.
Một converter public là một parser được expose cho người lạ
Đây là lý do phần còn lại của bài viết này tồn tại. Một file converter có thể truy cập từ Internet sẽ nhận file bất kỳ từ một người dùng ẩn danh rồi chuyển file đó cho LibreOffice, ImageMagick, FFmpeg hoặc Ghostscript. Đây đều là các codebase C và C++ lớn, có lịch sử lâu dài về lỗi parser. Người upload chọn format, nghĩa là họ cũng chọn parser nào sẽ chạy và code path nào bên trong parser đó được thực thi.
HRConvert2 xử lý việc này bằng cách chạy mọi dependency bên trong một namespace của bubblewrap. Mỗi lần conversion chỉ nhìn thấy 2 thư mục: thư mục chứa input được mount ở chế độ read only và thư mục nhận output. Network không được share. Theo cách diễn đạt của dự án, closes every URL handler in every dependency at once. Điều này quan trọng hơn nhiều so với tưởng tượng ban đầu. ImageMagick và Ghostscript đều chấp nhận các reference dùng để fetch một URL. Vì vậy, converter có thể biến thành công cụ server side request forgery (SSRF), dùng để truy cập cloud metadata endpoint từ bên trong network của bạn. Khi namespace không có network, việc fetch không thể xảy ra.
Việc từ chối xử lý là nửa còn lại: A server that cannot build a sandbox refuses the conversion rather than quietly running without one. Một tool fail closed có giá trị hơn tool chỉ ghi cảnh báo vào một log mà không ai đọc. Đây cũng là lý do bước cấu hình AppArmor ở trên không phải tùy chọn, và vì sao $RequireSandboxOnDocker cần được xem xét trước khi bạn expose container.
Tăng cường bảo vệ ImageMagick bằng policy.xml
File policy riêng của ImageMagick là một lớp bảo vệ thứ hai bên dưới sandbox và đáng để cấu hình. Trên Ubuntu 24.04 với ImageMagick 6, file này nằm tại /etc/ImageMagick-6/policy.xml. In policy đang được áp dụng:
identify -list policyProject cung cấp một policy tại Documentation/Build/policy.xml và đây là mẫu cấu hình tốt. Policy này từ chối các coder PS, PS2, PS3, EPS, XPS và MVG, đồng thời từ chối các delegate URL, HTTPS, HTTP và gs, nhưng vẫn cho phép PDF:
<policy domain="coder" rights="none" pattern="PS" />
<policy domain="coder" rights="none" pattern="MVG" />
<policy domain="delegate" rights="none" pattern="URL" />
<policy domain="delegate" rights="none" pattern="gs" />
<policy domain="coder" rights="read|write" pattern="PDF" />Dòng gs là dòng quan trọng nhất. ImageMagick không tự phân tích PostScript. Nó gọi Ghostscript thông qua shell, và delegate đó là nơi các lỗi remote code execution nổi tiếng của ImageMagick xuất hiện. Từ chối delegate này để ImageMagick hoàn toàn không chuyển file được upload cho gs, bất kể file tự nhận là định dạng gì.
Policy tương tự cũng đặt giới hạn tài nguyên. Đây là cách ngăn một image được tạo thủ công ăn hết tài nguyên của máy:
<policy domain="resource" name="memory" value="256MiB"/>
<policy domain="resource" name="map" value="512MiB"/>
<policy domain="resource" name="disk" value="1GiB"/>
<policy domain="resource" name="width" value="16KP"/>
<policy domain="resource" name="height" value="16KP"/>
<policy domain="resource" name="area" value="128MP"/>Decompression bomb là file rất nhỏ nhưng khai báo kích thước khổng lồ. Các giới hạn width, height và area từ chối file trước khi cấp phát tài nguyên, vì vậy process sẽ thoát thay vì để kernel kill một process khác.
Chiều ngược lại cũng có một vấn đề cần lưu ý. Policy mặc định của Ubuntu từ chối hoàn toàn coder PDF, nên trên hệ thống chưa chỉnh sửa, thao tác với PDF sẽ lỗi với attempt to perform an operation not allowed by the security policy 'PDF'. Chuỗi này cho biết policy đang hoạt động đúng. Nếu cho phép coder này hoạt động lại, bạn phải chủ động quyết định và vẫn giữ delegate gs ở trạng thái bị từ chối.
Chi phí của chuỗi dependency trên một VPS nhỏ
Khi idle, các thành phần này hầu như không tốn tài nguyên. Apache và PHP chỉ dùng vài chục megabyte, còn các binary converter hoàn toàn không chạy. Toàn bộ chi phí xuất hiện cùng lúc khi có file được tải lên.
Quá trình chuyển đổi tài liệu khởi động LibreOffice, rồi LibreOffice khởi động Java runtime. Chuyển đổi ảnh cấp cho ImageMagick 256 MiB memory và thêm một memory map 512 MiB theo policy ở trên. Chuyển đổi video cấp cho FFmpeg mọi core hiện có, vì đó là cách FFmpeg xử lý video. memory_limit của PHP được cấu hình là 512M trong project. Các con số này cộng dồn trong một job, bên cạnh operating system và web server.
Vì vậy, một VPS 1 GB sẽ bắt đầu dùng swap ngay từ tài liệu thực tế đầu tiên, sau đó rơi vào tình trạng thrashing. Khi hết memory, kernel out of memory killer sẽ dừng process có resident size lớn nhất. Thường đó là soffice.bin, và người dùng chỉ thấy quá trình chuyển đổi thất bại mà không có thông báo hữu ích. Đôi khi đó là apache2, khiến toàn bộ site ngừng hoạt động. Sau đó dùng dmesg -T | grep -i "killed process" để xác nhận.
Đây là hướng dẫn sizing, không phải benchmark: 4 GB RAM và 2 core là đủ thoải mái cho một team nhỏ, còn 2 GB cùng một swap file vẫn hoạt động nếu tải chủ yếu là tài liệu và ảnh và bạn chấp nhận thời gian chờ. Swap file không làm quá trình chuyển đổi nhanh hơn. Nó biến một đợt tải tăng đột biến thành tình trạng chậm thay vì lỗi nghiêm trọng. Đây là khác biệt giữa một page bị treo và một outage. Hãy dành cho disk nhiều dung lượng hơn mức bạn nghĩ là cần thiết, vì image 3 GB, upload limit lớn và output đã chuyển đổi sẽ cùng nhau làm đầy disk từ lâu trước khi các tài nguyên khác cạn.
Các quá trình chuyển đổi vốn tạo tải theo từng đợt. Hai người cùng upload video sẽ dùng hết mọi core, và request tiếp theo phải chờ phía sau. Phía trước hệ thống này không có job queue, nên giới hạn là biện pháp kiểm soát duy nhất bạn có.
Đặt các giới hạn để một lần upload không làm đầy disk
Trước tiên, giảm các giá trị PHP. Có thể bắt đầu với upload_max_filesize = 512M, post_max_size = 512M và max_file_uploads = 20 cho một máy 4 GB dùng chung. Lưu ý rằng max_execution_time = 1200 cho phép một request PHP chạy trong hai mươi phút. Thời gian này thực sự cần thiết cho một lần chuyển đổi video dài. Điều đó cũng có nghĩa là một lần upload chậm sẽ giữ một worker trong hai mươi phút.
Sau đó, giới hạn kích thước và tốc độ tại proxy, trước khi request đến được PHP:
limit_req_zone $binary_remote_addr zone=convert:10m rate=6r/m;
server {
listen 443 ssl;
server_name convert.example.com;
client_max_body_size 512M;
client_body_timeout 300s;
location / {
limit_req zone=convert burst=4 nodelay;
proxy_pass http://127.0.0.1:8080;
proxy_read_timeout 1200s;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}client_max_body_size phải ít nhất bằng kích thước của file lớn nhất mà bạn muốn chuyển đổi. Nếu không, nginx sẽ trả về 413 Request Entity Too Large và PHP không bao giờ nhận được upload. proxy_read_timeout phải dài hơn thời gian chuyển đổi lâu nhất. Nếu không, một job vẫn đang chạy bình thường phía sau proxy sẽ trả về 504 Gateway Time-out cho trình duyệt. Phần còn lại của server block, bao gồm TLS (transport layer security) termination, được trình bày trong giải thích từng dòng cấu hình nginx reverse proxy.
Xóa các file đã chuyển đổi
Mỗi lần chuyển đổi đều để lại một bản sao của file nhạy cảm trong thư mục mà web server có thể đọc. Dọn dẹp là điểm phân biệt một công cụ chuyển đổi với một kho lưu trữ mọi thứ mà bất kỳ ai từng chuyển đổi trên đó.
$DeleteThreshold trong Resources/config.php là thời gian tính bằng phút trước khi một session hết hạn và mặc định là 60. Hạ giá trị này xuống 15 khi nội dung nhạy cảm. Việc quét được thực hiện bằng một đối số dòng lệnh của core:
sudo -u www-data php /path/to/HRConvert2/convertCore.php -c
sudo -u www-data php /path/to/HRConvert2/convertCore.php -c=15-c xóa các session đã hết hạn khỏi cả hai vị trí dữ liệu theo ngưỡng đã cấu hình. -c=15 chỉ dùng 15 phút cho lần chạy đó. -c=now xóa mọi session bất kể tuổi, kể cả session mà user đang dùng để chuyển đổi tại thời điểm đó, vì vậy chỉ dùng nó cho công tác bảo trì. Các đối số tương tự cũng hoạt động bên trong container thông qua docker exec.
Đặt việc quét vào timer để quá trình dọn dẹp không bao giờ phụ thuộc vào việc có người tải một trang. Chỉ cần thêm một dòng vào /etc/cron.d/hrconvert2:
*/10 * * * * www-data php /path/to/HRConvert2/convertCore.php -cVài phút sau, kiểm tra bằng ls /DATA/HRConvert2 và theo dõi các thư mục session cũ biến mất. Vì user của web server sở hữu thư mục đó, đây chính là account mà một parser bị khai thác sẽ có được quyền sử dụng. Do đó account này không nên sở hữu bất kỳ tài nguyên nào khác đáng giá. Account user theo nguyên tắc đặc quyền tối thiểu trên VPS là mô hình tổng quát và đặc biệt phù hợp trong trường hợp này.
Đặt authentication phía trước, trừ khi mục tiêu là public
Bản cài đặt mặc định không có account nào. Đây là thiết kế có chủ đích. Bất kỳ ai truy cập được vào trang đều có thể upload file và chạy các binary converter của bạn. Rate limit chỉ làm chậm việc đó. Vì vậy, hãy xác định bạn đang ở tình huống nào.
Nếu chỉ dùng cho bạn và một vài đồng nghiệp, đừng expose nó ra ngoài. Bind container vào loopback như ở trên và truy cập qua private network hoặc SSH tunnel. Khi đó, không gì trên public Internet có thể gửi file vào service. Cách này loại bỏ toàn bộ attack surface thay vì chỉ filter request. Nếu các đồng nghiệp cần mở trang bằng browser mà bạn không muốn publish hostname hoặc mở port, chạy nó dưới dạng v3 onion service sẽ giữ converter bind vào loopback và vẫn cung cấp cho họ một address để truy cập.
Nếu bắt buộc phải truy cập được từ browser, hãy đặt authentication phía trước proxy. Basic auth chỉ cần 2 command và giúp form upload không bị người lạ truy cập:
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd alicelocation / {
auth_basic "Converter";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:8080;
}Reload nginx rồi mở trang. Nếu xuất hiện prompt thì authentication đang hoạt động. Nếu không có prompt, block location bạn đã sửa không phải block đang xử lý request. Nếu cần account riêng thay vì dùng chung một password, hãy terminate authentication tại một nhà cung cấp single sign-on: một Authentik SSO server tự host cung cấp forward authentication phía trước một application không có login riêng.
Nếu mục tiêu thực sự là một converter public, hãy chấp nhận các hệ quả và lập kế hoạch tương ứng. Giả định rằng sandbox sẽ bị probe. Giữ image tag ở phiên bản cố định, giữ policy của ImageMagick ở mức chặt chẽ, đặt giới hạn upload nhỏ và chạy service trên một VPS không chứa dữ liệu hoặc service quan trọng khác.
Các tình huống lỗi và chuỗi thông báo bạn sẽ thấy
Mọi lần chuyển đổi đều lỗi ngay lập tức. Sandbox không thể được tạo. Trên bản cài Ubuntu 24.04 thông thường, nguyên nhân là profile AppArmor. Trong Docker image 3.7 đã cố định, nguyên nhân là thiếu --security-opt seccomp=unconfined. Ứng dụng nêu rõ điều này: A sandbox blocks the required syscalls unless it was started with the correct options. và trỏ đến See --Require Sandbox-- & --Require Sandbox On Docker-- in config.php.
Chỉ chuyển đổi image bị lỗi. Bubblewrap is missing or non functional, so this image conversion cannot be isolated! nghĩa là bwrap bị thiếu hoặc không thể truy cập qua PATH mà user của web server có.
Một định dạng bị lỗi còn các định dạng khác vẫn hoạt động. Đây là binary bị thiếu, được báo rõ ràng: ImageMagick may not be installed, or may not be reachable on the system path used by the web server user. Thông báo tương tự cũng có cho FFmpeg và LibreOffice. Chạy convertCore.php -v để xem hệ thống tìm thấy gì trong quá trình cài đặt. Lưu ý rằng PATH của Apache worker không giống PATH của login shell của bạn.
Xử lý PDF bị lỗi policy. attempt to perform an operation not allowed by the security policy 'PDF' xuất phát từ policy.xml của ImageMagick, không phải từ HRConvert2.
Upload lớn trả về lỗi 413. client_max_body_size của nginx nhỏ hơn kích thước file. Chuỗi xử lý có 3 giới hạn: 1 giới hạn trong nginx và 2 giới hạn trong PHP. Giới hạn nhỏ nhất sẽ có hiệu lực.
Quá trình chuyển đổi dừng lại nhưng không có thay đổi rõ ràng nào. The device where data is stored has an insufficient amount of storage space available. Kiểm tra dung lượng trống và xác nhận rằng cleanup sweep thực sự đang chạy.
Cleanup ghi cảnh báo trong log. Could not clean the temporary location! và Could not clean the convert location! là lỗi ownership. User của web server phải sở hữu directory được chỉ định bởi $ConvertLoc.
FAQ
Có an toàn khi đưa một trình chuyển đổi tệp tự host lên Internet không?
Chỉ tương đối an toàn nếu bạn xem nó là một parser được đưa cho người lạ sử dụng. Mỗi lần upload, tệp được chuyển cho LibreOffice, ImageMagick, FFmpeg hoặc Ghostscript, và người upload là người chọn công cụ. HRConvert2 chạy các công cụ đó trong một namespace bubblewrap không có network và có thư mục input chỉ đọc. Ứng dụng cũng từ chối mọi chuyển đổi không thể sandbox, đây là thiết lập mặc định tốt. Tuy vậy, bạn vẫn nên yêu cầu authentication, đặt giới hạn upload nhỏ và chạy ứng dụng trên một VPS không chứa dữ liệu quan trọng khác.
Vì sao mọi lần chuyển đổi đều thất bại trên Ubuntu 24.04 mới cài?
Ubuntu 24.04 hạn chế user namespace không đặc quyền thông qua AppArmor, trong khi bubblewrap cần namespace này để tạo sandbox. Vì ứng dụng từ chối chuyển đổi nếu không có sandbox, mọi job đều thất bại thay vì chỉ một số job. Hãy viết AppArmor profile cho /usr/bin/bwrap bằng flags=(unconfined), nạp profile bằng sudo apparmor_parser -r /etc/apparmor.d/bwrap, rồi xác nhận bằng bwrap --ro-bind / / --dev /dev /bin/true. Debian 12 cho phép user namespace không đặc quyền theo mặc định nên không cần bước này.
Vì sao chuyển đổi thất bại trong Docker nhưng lại chạy trên hệ thống cài trực tiếp?
Profile seccomp mặc định của Docker chặn các system call mà bubblewrap sử dụng, nên sandbox không thể được tạo bên trong container. Với image 3.7 được pin trong tài liệu này, hãy khởi động container bằng --security-opt seccomp=unconfined. Đây là thiết lập được các comment trong config.php của release đó chỉ định. Từ v3.8.0, project hướng dẫn dùng --cap-add SYS_ADMIN thay thế. Lưu ý rằng $RequireSandboxOnDocker mặc định là FALSE, nên container không có flag này có thể chuyển đổi mà hoàn toàn không có sandbox. Hãy đặt giá trị này thành TRUE sau khi đã thêm seccomp flag.
Server chuyển đổi tệp cần bao nhiêu RAM?
Khi idle, server sử dụng ít RAM, nhưng một lần chuyển đổi đang chạy thì không. LibreOffice khởi động Java runtime, ImageMagick sử dụng 256 MiB bộ nhớ và một map 512 MiB theo policy được cung cấp, còn giới hạn riêng của PHP là 512M. Trên VPS 1 GB, tổ hợp này gây swap và OOM killer sẽ kết thúc soffice.bin hoặc apache2. Với một nhóm nhỏ, hãy dự trù 4 GB RAM và 2 core, đồng thời kiểm tra dmesg -T | grep -i "killed process" mỗi khi một lần chuyển đổi kết thúc mà không có thông báo.
Tệp đã chuyển đổi được lưu ở đâu và khi nào bị xóa?
Tệp được lưu trong working directory do $ConvertLoc chỉ định trong Resources/config.php. Giá trị mặc định là /DATA/HRConvert2. $DeleteThreshold đặt số phút trước khi một session hết hạn, mặc định là 60. Tác vụ dọn dẹp chạy từ command line: php convertCore.php -c xóa các session đã hết hạn, còn -c=now xóa ngay mọi session, kể cả session đang active. Hãy đặt -c trong một cron entry hoặc systemd timer để việc xóa tệp không phụ thuộc vào việc có người truy cập site hay không.