Tự host HRConvert2 trên VPS bằng Docker hoặc Apache
Tự host HRConvert2 để file khách hàng không qua website miễn phí. Bài hướng dẫn cài bằng Docker hoặc Apache, bubblewrap sandbox, giới hạn upload và cleanup.
Vì sao nên tự host trình chuyển đổi file
Một trình chuyển đổi file tự host giữ file trên chính disk của bạn. Đó 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 đã ký của khách hàng hoặc hồ sơ y tế được scan, việc upload chính là sự cố bảo mật. HRConvert2 là một server chuyển đổi file hỗ trợ kéo thả, được viết bằng PHP và cấp phép theo GPLv3. Version 3.7.4 được phát hành vào ngày 18 August 2026 và dự án cho biết công cụ hỗ trợ 488 format.
Công cụ này không có database, account hoặc cookie. Mỗi user chỉ có một thư mục tạm. Mọi lần chuyển đổi đều do một command-line tool chạy local 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 nhiều tool nhỏ hơn cho các format còn lại. HRConvert2 cung cấp trang upload, pipeline và cơ chế cleanup cho toàn bộ quy trình.
Công cụ này chuyển file từ format này sang format khác. Đây không phải browser office suite. 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ế. Công cụ này cũng không phải storage. Output sau khi chuyển đổi được thiết kế để xóa. Nếu cần lưu file, hãy dùng file manager tự host.
Yêu cầu
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 của upstream nói Raspberry Pi Model B+ là đủ, điều này đúng với phần PHP. Các binary của converter mới quyết định mức phần cứng thực tế cần thiết, và phần đó được trình bày ở bên dưới.
Có 2 cách triển khai. Docker image có thể chạy ngay trong tối nay. Cài Apache và PHP mất 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 bằng Docker ngay tối nay
Image chứa mọi binary converter 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 trống trước khi pull.
Tag rất quan trọng ở đây. Tag mới nhất được publish trên Docker Hub tính đến ngày 17 tháng 8 năm 2026 là v3.7.2, còn GitHub release mới nhất là v3.7.4. 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, vì vậy hãy pin version và chủ động quyết định thời điểm upgrade.
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 thường có vấn đề khi startup, 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 nó. Ví dụ của project map -p 8080:80 -p 8443:443, lắng nghe trên mọi interface, bao gồm cả interface public. Cần có --security-opt seccomp=unconfined vì bubblewrap xây dựng sandbox bằng các system call cho user namespace và mount, trong khi seccomp profile mặc định của Docker chặn các system call này. Không có flag này, quá trình convert sẽ fail và application cho biết nguyên nhân: A sandbox blocks the required syscalls unless it was started with the correct options.
Flag này có một đánh đổi thực sự. Bạn nới lỏng syscall filter của container để application có thể tự xây dựng sandbox chặt chẽ hơn bên trong container. 2 setting quyết định hành vi này là $RequireSandbox và $RequireSandboxOnDocker trong Resources/config.php, có giá trị mặc định lần lượt là TRUE và FALSE. Vì yêu cầu Docker mặc định đang tắt, container không có seccomp flag vẫn có thể convert 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.
Nếu máy này mới cài Docker, trước tiên hãy thiết lập daemon. Chạy Docker trên VPS hướng dẫn cách cài đặt, storage driver và cách Docker tự ghi firewall rules.
Cài trên Apache và PHP
File Documentation/INSTALLATION_INSTRUCTIONS.txt trong repository là nguồn chuẩn và gồm 9 bước. Đây là cấu trúc của file. 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 php8.3-* tương ứng với Ubuntu 24.04. Chạy php -v và dùng prefix phù hợp với phiên bản của bạn, vì tên package thay đổi theo từng bản phát hành PHP. Dùng sai prefix sẽ khiến bạn gặp Unable to locate package.
Tiếp theo là các converter. Danh sách này hỗ trợ document, image, 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-cursor0Archive format, mô hình 3D, ebook và bootable ISO image cần nhiều package hơn danh sách này. Một số package nằm trong component multiverse của Ubuntu. Bước 3 và bước 5 trong hướng dẫn chính thức có đầy đủ danh sách theo đúng thứ tự. Có 2 dependency không phải apt package: repository cung cấp Documentation/Build/ffmpeg-build.sh và Documentation/Build/build-imagemagick-v7.sh cho trường hợp cần encoder hoặc ImageMagick 7 mà Ubuntu không đóng gói. Hỗ trợ ebook đến từ installer riêng của calibre. Hướng dẫn cung cấp installer 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à không bắt buộc. Bạn có thể bỏ qua; khi đó chỉ mất chức năng chuyển đổi ebook.
Tiếp theo là giới hạn của PHP. Quá trình chuyển đổi chậm và file có dung lượng lớn, nên giá trị mặc định quá thấp. Project đặ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 giá trị này giả định máy chủ còn đủ tài nguyên. Hãy giảm chúng trước khi chạy trên một VPS nhỏ, vì upload_max_filesize = 5000M cùng với max_file_uploads = 100 mô tả một request có thể ghi nhiều dữ liệu hơn sức chứa của disk 40 GB. Restart Apache và xác nhận PHP thực tế đã load các giá trị nào:
sudo service apache2 restart
php -i | grep -E "upload_max_filesize|post_max_size|memory_limit"Tiếp theo là working directory. $ConvertLoc trong Resources/config.php xác định thư mục này, và giá trị mặc định là /DATA/HRConvert2. User chạy web server phải là owner của 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 bên dưới document root của Apache. Layout mặc định đặt ứng dụng trong một folder 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 sẵn. Đây là cách nhanh nhất để phát hiện dependency còn thiếu trước khi user phát hiện:
sudo php /path/to/HRConvert2/convertCore.php -v-v kiểm tra toàn bộ installation: 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 tác vụ quản trị.
Vì sao mọi lần chuyển đổi đều thất bại trên Ubuntu 24.04 mới cài?
Do sandbox. Đây là vấn đề phổ biến nhất trong ngày đầu cấu hình. Ubuntu 24.04 và Debian 12 mặc định hạn chế user namespace của user không có đặc quyền. 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 khi 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 nghĩa là namespace đã bị chặn. Cách sửa 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 đang có:
ls /etc/apparmor.d/abi/Sau đó ghi /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 lệnh kiểm tra bwrap. Lệnh này sẽ in sandbox ok. Từ thời điểm đó, các lần chuyển đổi sẽ hoạt động.
Một converter public là một parser được expose cho người lạ
Đây là lý do toàn bộ phần còn lại của bài viết tồn tại. Một file converter có thể truy cập từ Internet sẽ nhận một file bất kỳ từ một người ẩn danh rồi chuyển file đó cho LibreOffice, ImageMagick, FFmpeg hoặc Ghostscript. Đây là những codebase C và C++ lớn, có lịch sử lâu dài về các lỗi trong parser. Người upload chọn format, nên họ cũng chọn parser nào được chạy và code path nào bên trong parser đó được thực thi.
Cách HRConvert2 xử lý vấn đề này là chạy mọi dependency bên trong một namespace của bubblewrap. Mỗi lần conversion chỉ thấy 2 directory: directory chứa input, được mount ở chế độ read only, và directory nhận output. Network không được share, theo cách diễn đạt của dự án là closes every URL handler in every dependency at once. Điều này quan trọng hơn bạn nghĩ. ImageMagick và Ghostscript đều chấp nhận các reference có thể fetch một URL. Vì vậy, converter có thể biến thành công cụ server-side request forgery (SSRF) để 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.
Cơ chế từ chối 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 log mà không ai đọc. Đây cũng là lý do bước AppArmor ở trên không phải tùy chọn, và vì sao $RequireSandboxOnDocker đáng được kiểm tra trước khi bạn expose container.
Gia cố ImageMagick bằng policy.xml
File policy của chính ImageMagick là một lớp bảo vệ thứ hai bên dưới sandbox và đáng được 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 cấu hình hiện đang có hiệu lực:
identify -list policyProject cung cấp một policy tại Documentation/Build/policy.xml và đây là một mẫu 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 qua shell, và chính delegate đó là nơi các lỗi thực thi mã từ xa nổi tiếng của ImageMagick thường xuất hiện. Từ chối delegate này để ImageMagick 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 có chủ ý tiêu tốn toàn bộ máy chủ:
<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 bộ nhớ, nên process thoát thay vì để kernel phải kill process khác.
Chiều ngược lại cũng có một điểm dễ mắc bẫy. 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 đó cho biết policy đang hoạt động đúng. Việc cho phép lại coder này là quyết định bạn phải chủ động đưa ra, và khi làm vậy 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, không có gì trong số này tốn nhiều tài nguyên. Apache và PHP chỉ chiếm vài chục megabyte, còn các binary chuyển đổi không chạy. Toàn bộ chi phí tài nguyên phát sinh 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. Quá trình chuyển đổi ảnh cho ImageMagick dùng 256 MiB memory và thêm một memory map 512 MiB theo policy ở trên. Quá trình chuyển đổi video dùng toàn bộ core hiện có cho FFmpeg, vì đó là cách FFmpeg xử lý video. memory_limit của PHP được đặt là 512M trong cấu hình của project. Các giá trị này cộng dồn trong một job, cùng với hệ điều hành và web server.
Vì vậy, VPS 1 GB sẽ swap ngay ở tài liệu thực tế đầu tiên rồi rơi vào tình trạng thrash. Khi hết memory, kernel out of memory killer sẽ kết thúc process có resident size lớn nhất. Thường đó là soffice.bin, và người dùng 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 đó, xác nhận nguyên nhân bằng dmesg -T | grep -i "killed process".
Đây là hướng dẫn sizing chứ không phải benchmark: 4 GB RAM và 2 core là đủ thoải mái cho một nhóm nhỏ, còn 2 GB cùng một swap file vẫn dùng được 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ăng tải 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 trang bị treo và một outage. Hãy chừa cho disk nhiều dung lượng hơn mức bạn nghĩ là cần, vì image 3 GB, upload limit lớn và output đã chuyển đổi sẽ 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 có tải tăng 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 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ấu hình như upload_max_filesize = 512M, post_max_size = 512M và max_file_uploads = 20 là điểm bắt đầu hợp lý cho một máy dùng chung có 4 GB. Lưu ý rằng max_execution_time = 1200 cho phép một PHP request 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, nhưng 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 đó, áp dụng giới hạn kích thước và tốc độ tại proxy, trước khi request đến 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 đang chạy bình thường phía sau proxy vẫn 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 giải thích trong cấu hình nginx reverse proxy được giải thích từng dòng.
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. Việc dọn dẹp là điểm khác biệt giữa một công cụ chuyển đổi và một kho lưu trữ mọi thứ người khác 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, mặc định là 60. Giảm xuống 15 khi nội dung nhạy cảm. Lệnh sweep là 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ả 2 vị trí dữ liệu theo threshold đã 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à người dùng đang dùng để chuyển đổi tại thời điểm đó, nên chỉ dùng khi 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 sweep vào timer để việc dọn dẹp không phụ thuộc vào việc có người tải một page. 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, dùng ls /DATA/HRConvert2 để kiểm tra và theo dõi các thư mục session cũ biến mất. Vì web server user sở hữu thư mục đó, đây chính là account mà một parser bị khai thác sẽ nhận đượ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 có giá trị. Tài khoản user theo nguyên tắc quyền tối thiểu trên VPS là mẫu triển khai chung, và trong trường hợp này càng cần áp dụng.
Đặt lớp xác thực 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 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ư phần trên, rồi truy cập qua private network hoặc SSH tunnel. Khi đó, không có gì trên public internet có thể gửi file cho nó. Cách này loại bỏ toàn bộ attack surface thay vì chỉ lọc request.
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ữ form upload tránh xa người lạ:
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ì cấu hình đang hoạt động. Nếu không có prompt, block location bạn đã sửa không phải block xử lý request này. Nếu cần account riêng thay vì một shared password, hãy terminate authentication tại một nhà cung cấp single sign-on: một server Authentik SSO tự host cung cấp forward authentication phía trước một application vốn 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ả của việc đó và chuẩn bị tương ứng. Giả định sandbox sẽ bị probe. Giữ image tag ở phiên bản cố định, giữ policy của ImageMagick ở mức chặt chẽ, đặt upload limit ở mức thấp và chạy nó trên một VPS không chứa dữ liệu hay dịch vụ quan trọng khác.
Các trạng thái lỗi và chuỗi thông báo bạn sẽ thấy
Mọi lần chuyển đổi đều thất bại ngay lập tức. Không thể tạo sandbox. Với bản cài đặt thông thường, nguyên nhân là profile AppArmor. Trong Docker, nguyên nhân là thiếu --security-opt seccomp=unconfined. Ứng dụng sẽ ghi rõ: 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 không tồn tại hoặc không thể truy cập qua PATH mà user của web server có.
Một format bị lỗi còn các format 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. Chuỗi thông báo tương tự cũng xuất hiện với FFmpeg và LibreOffice. Chạy convertCore.php -v để xem bản cài đặt tìm thấy gì, và nhớ rằng PATH của Apache worker không giống PATH của login shell.
Xử lý PDF thất bại với 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 file 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 trong nginx và 2 trong PHP. Giới hạn nhỏ nhất sẽ được áp dụng.
Việc 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 cleanup sweep thực sự đang chạy.
Cleanup ghi lỗi vào log. Could not clean the temporary location! và Could not clean the convert location! là các lỗi ownership. User của web server phải là owner của thư mục được chỉ định bởi $ConvertLoc.
Câu hỏi thường gặp (FAQ)
Có an toàn khi public một công cụ chuyển đổi file tự host lên Internet không?
Chỉ đủ an toàn nếu bạn coi nó là một parser được expose cho người lạ. Mọi file upload đều được chuyển cho LibreOffice, ImageMagick, FFmpeg hoặc Ghostscript xử lý, và người upload có thể chọn công cụ nào được dùng. HRConvert2 chạy các công cụ đó trong namespace của bubblewrap, không có network và chỉ có quyền đọc trên thư mục input. Ứng dụng cũng từ chối mọi thao tác chuyển đổi mà nó không thể sandbox. Đây là một default tốt. Tuy vậy, bạn vẫn nên yêu cầu authentication, đặt giới hạn upload thấp và chạy nó trên một VPS không chứa dữ liệu quan trọng khác.
Vì sao mọi thao tác chuyển đổi đều fail trên bản cài Ubuntu 24.04 mới?
Ubuntu 24.04 và Debian 12 hạn chế user namespace của user không có đặc quyền. 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 fail thay vì chỉ một số job. Hãy viết một AppArmor profile cho /usr/bin/bwrap bằng flags=(unconfined), load 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.
Vì sao chuyển đổi fail trong Docker nhưng chạy được trên bản cài thông thường?
Profile seccomp mặc định của Docker chặn các system call mà bubblewrap sử dụng. Vì vậy sandbox không thể được tạo bên trong container. Hãy khởi động container bằng --security-opt seccomp=unconfined. Đây cũng là lệnh run của chính project. 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 file cần bao nhiêu RAM?
Khi idle, server dùng ít RAM. Nhưng một phiên chuyển đổi đang chạy thì không. LibreOffice khởi động Java runtime, ImageMagick dùng 256 MiB memory và một map 512 MiB theo policy đi kèm, 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ẽ kill soffice.bin hoặc apache2. Với một team nhỏ, hãy dùng 4 GB RAM và 2 core. Kiểm tra dmesg -T | grep -i "killed process" mỗi khi một thao tác chuyển đổi bị dừng mà không có message.
File đã chuyển đổi được lưu ở đâu và khi nào bị xóa?
File được lưu trong working directory do $ConvertLoc trong Resources/config.php chỉ định. Giá trị mặc định là /DATA/HRConvert2. $DeleteThreshold đặt số phút trước khi một session hết hạn và mặc định là 60. Việc sweep được 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 thêm -c vào một cron entry hoặc systemd timer để việc xóa file không phụ thuộc vào việc có ai truy cập site hay không.