آموزش نصب و راه اندازی HRConvert2 برای تبدیل فایل
با نصب HRConvert2 روی سرور شخصی، امنیت فایلهای خود را تضمین کنید. این راهنما نحوه پیکربندی Docker، تنظیم محدودیت آپلود و استفاده از ابزارهای FFmpeg و LibreOffice را توضیح میدهد.
چرا باید یک مبدل فایل را بهصورت self-hosted اجرا کرد
یک مبدل فایل self-hosted، فایل را روی دیسک خودتان نگه میدارد. این تنها دلیل برای اجرای چنین سرویسی است. سایتهای مبدل رایگان، فایل ارسالی شما را دریافت میکنند و هیچ راهی برای اطلاع از سرنوشت آن پس از آپلود باقی نمیگذارند؛ زمانی که فایل مورد نظر یک قرارداد امضاشده یا یک پرونده پزشکی اسکنشده باشد، خودِ عمل آپلود یک رخداد امنیتی محسوب میشود. ابزار HRConvert2 یک سرور تبدیل فایل با قابلیت کشیدن و رها کردن (drag and drop) است که با PHP نوشته شده و تحت مجوز GPLv3 منتشر میشود. نسخه 3.7.4 در تاریخ 18 اوت 2026 منتشر شد و این پروژه ادعای پشتیبانی از 488 فرمت مختلف را دارد.
این ابزار فاقد دیتابیس، سیستم حساب کاربری و کوکی است. هر کاربر در واقع یک دایرکتوری موقت است. هر تبدیل توسط یک ابزار خط فرمان محلی انجام میشود: LibreOffice برای اسناد، FFmpeg برای صوت و ویدیو، ImageMagick برای تصاویر، Tesseract برای تشخیص نوری نویسهها (OCR) و مجموعهای از ابزارهای کوچکتر برای سایر موارد. HRConvert2 در واقع صفحه آپلود، خط لوله پردازش و ابزار پاکسازی پیرامون آنهاست.
این ابزار فایل را از یک فرمت به فرمت دیگر تبدیل میکند. این یک مجموعه اداری تحت مرورگر نیست؛ بنابراین اگر هدف شما این است که کاربران اسناد را در یک تب مرورگر ویرایش کنند، OnlyOffice و Collabora به صورت self-hosted را مقایسه کنید. همچنین این ابزار یک فضای ذخیرهسازی نیست. خروجیهای تبدیلشده باید حذف شوند؛ بنابراین اگر نیاز دارید فایلها در جایی نگهداری شوند، این وظیفه بر عهده یک مدیر فایل self-hosted است.
پیشنیازها
به Debian یا Ubuntu، نسخه 2.4 از Apache، نسخه 8 یا بالاتر از PHP و ابزار bubblewrap نیاز دارید. ابزار bubblewrap (bwrap) نقش sandbox را ایفا میکند و استفاده از آن اختیاری نیست: سروری که نتواند sandbox ایجاد کند، بهجای اجرا بدون آن، از انجام عملیات تبدیل خودداری میکند. طبق مستندات README اصلی، یک Raspberry Pi Model B+ برای بخش PHP کافی است. باینریهای مبدل، محدودیت سختافزاری واقعی شما را تعیین میکنند که در ادامه به آن پرداخته شده است.
دو روش برای شروع وجود دارد. استفاده از image داکر همین امشب قابل انجام است. نصب Apache و PHP یک عصر زمان میبرد و به شما نشان میدهد دقیقاً چه چیزی روی سیستم نصب شده است.
اجرای آن امشب با Docker
این image شامل تمام باینریهای مبدل است، بنابراین حجم آن زیاد است: حدود 3 GB تا اوت 2026. پیش از pull کردن، فضای آزاد دیسک را بررسی کنید.
در اینجا تگها اهمیت دارند. جدیدترین تگ منتشرشده در Docker Hub تا 17 اوت 2026 برابر با v3.7.2 است، در حالی که جدیدترین release در GitHub برابر با v3.7.4 است. تگ latest بهطور مداوم تغییر میکند و از آنجا که این برنامه سطح پارسر گستردهای دارد، حتماً یک نسخه خاص را pin کنید و ارتقا را بهصورت آگاهانه انجام دهید.
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 سالم در وضعیت Up باقی میماند و curl مقدار HTTP/1.1 200 OK را برمیگرداند. containerای که مدام restart میشود، مشکل راهاندازی دارد؛ بنابراین پیش از تغییر هر چیز دیگری، docker logs hrconvert2 را مطالعه کنید.
دو flag اهمیت ویژهای دارند. -p 127.0.0.1:8080:80 پورت را فقط روی loopback منتشر میکند، بنابراین تا زمانی که عمداً یک proxy جلوی آن قرار ندهید، هیچچیز به مبدل دسترسی نخواهد داشت. مثال خود پروژه از -p 8080:80 -p 8443:443 استفاده میکند که روی تمام اینترفیسها، از جمله اینترفیس عمومی، گوش میدهد. --security-opt seccomp=unconfined به این دلیل وجود دارد که bubblewrap محیط sandbox خود را با استفاده از user namespace و فراخوانیهای سیستمی mount میسازد که پروفایل پیشفرض seccomp در Docker آنها را مسدود میکند. بدون این flag، تبدیلها با شکست مواجه میشوند و برنامه دلیل آن را به شما میگوید: A sandbox blocks the required syscalls unless it was started with the correct options.
این flag یک بدهبستان واقعی است. شما فیلتر syscall کانتینر را آزاد میکنید تا برنامه بتواند sandbox محدودتر خود را در داخل آن بسازد. دو تنظیمی که این رفتار را تعیین میکنند $RequireSandbox و $RequireSandboxOnDocker در فایل Resources/config.php هستند که بهصورت پیشفرض روی TRUE و FALSE تنظیم شدهاند. از آنجا که این پیشنیاز Docker بهصورت پیشفرض غیرفعال است، کانتینری که بدون flag seccomp باشد، میتواند بدون هیچ sandboxای تبدیل را انجام دهد. هنگامی که flag اعمال شد، $RequireSandboxOnDocker = TRUE; را تنظیم کنید تا رفتار امتناع (refusal) در داخل کانتینر بازگردد.
اگر Docker روی این ماشین جدید است، ابتدا daemon را تنظیم کنید. اجرای Docker روی یک VPS شامل نصب، درایور ذخیرهسازی و نحوه نوشتن قوانین فایروال توسط خود Docker است.
نصب روی Apache و PHP
فایل Documentation/INSTALLATION_INSTRUCTIONS.txt در مخزن، مرجع اصلی است و شامل 9 مرحله میباشد. ساختار آن به این صورت است. با وبسرور، زبان برنامهنویسی و محیط sandbox شروع کنید:
sudo apt update
sudo apt install -y apache2 php libapache2-mod-php php-all-dev php8.3-zip php8.3-gd bubblewrapنامهای php8.3-* با Ubuntu 24.04 مطابقت دارند. دستور php -v را اجرا کنید و از پیشوندی استفاده کنید که با نسخه شما همخوانی دارد، زیرا نام بستهها با هر نسخه PHP تغییر میکند و استفاده از نام اشتباه منجر به Unable to locate package میشود.
سپس مبدلها (converters) را نصب کنید. این بخش اسناد، تصاویر، فایلهای صوتی، ویدیوها و OCR را پوشش میدهد که اکثر نیازهای کاربران برای تبدیل فایل را شامل میشود:
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-cursor0فرمتهای آرشیو، مدلهای 3D، کتابهای الکترونیکی و ایمیجهای ISO قابل بوت به بستههای بیشتری نیاز دارند و برخی از آنها در بخش multiverse مخازن Ubuntu قرار دارند. مراحل 3 و 5 دستورالعملهای رسمی، لیست کامل را به ترتیب ارائه میدهند. دو وابستگی (dependency) اصلاً بسته apt نیستند: مخزن شامل Documentation/Build/ffmpeg-build.sh و Documentation/Build/build-imagemagick-v7.sh برای کسانی است که به انکودرها یا نسخه 7 از ImageMagick نیاز دارند که در Ubuntu بستهبندی نشده است. پشتیبانی از کتابهای الکترونیکی از طریق نصبکننده اختصاصی calibre انجام میشود که در دستورالعملها به صورت یک خط دستور آمده است:
sudo -v && wget -nv -O- https://download.calibre-ebook.com/linux-installer.sh | sudo sh /dev/stdinاین یک اسکریپت از سمت فروشنده است که با دسترسی root به shell ارسال میشود. این روش رسمی توسعهدهنده است و اختیاری است: اگر آن را نادیده بگیرید، تنها قابلیت تبدیل کتابهای الکترونیکی را از دست خواهید داد.
در مرحله بعد، محدودیتهای PHP را تنظیم کنید. فرآیند تبدیل فایلها زمانبر است و حجم فایلها زیاد است، بنابراین مقادیر پیشفرض بسیار کم هستند. پروژه این مقادیر را در 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 = Onاین اعداد برای ماشینی با منابع کافی در نظر گرفته شدهاند. اگر از یک VPS کوچک استفاده میکنید، این مقادیر را کاهش دهید، زیرا upload_max_filesize = 5000M با max_file_uploads = 100 میتواند در یک درخواست واحد، حجمی بسیار بیشتر از ظرفیت یک دیسک 40 گیگابایتی بنویسد. Apache را restart کنید و بررسی کنید که PHP واقعاً چه تنظیماتی را بارگذاری کرده است:
sudo service apache2 restart
php -i | grep -E "upload_max_filesize|post_max_size|memory_limit"حالا نوبت دایرکتوری کاری است. $ConvertLoc در Resources/config.php نام آن را مشخص میکند و مقدار پیشفرض /DATA/HRConvert2 است. کاربر وبسرور باید مالک این دایرکتوری باشد:
sudo mkdir -p /DATA/HRConvert2
sudo chmod -R 0755 /DATA/HRConvert2
sudo chown -R www-data:www-data /DATA/HRConvert2نسخه منتشر شده را در دایرکتوری ریشه Apache (document root) استخراج کنید. ساختار پیشفرض آن را در پوشه HRProprietary/HRConvert2 قرار میدهد و $InstLoc در Resources/config.php باید مسیر دقیق محل قرارگیری آن را مشخص کند. سپس ابزار تشخیص خطای داخلی را اجرا کنید؛ این سریعترین راه برای یافتن وابستگیهای مفقود پیش از مواجهه کاربران با مشکل است:
sudo php /path/to/HRConvert2/convertCore.php -v-v کل فرآیند نصب را بررسی میکند: نسخههای هسته، بررسی وابستگیها، وضعیت sandbox و بستههای زبانی. تبدیل فایلها از طریق خط فرمان پشتیبانی نمیشود، بنابراین این مجموعه آرگومانها فقط برای امور مدیریتی است.
چرا تمام تبدیلها در نصب تازه Ubuntu 24.04 با شکست مواجه میشوند؟
دلیل این امر sandbox است و این رایجترین مشکلی است که در روز اول با آن مواجه میشوید. سیستمعاملهای Ubuntu 24.04 و Debian 12 بهطور پیشفرض فضای نام (namespace) کاربران فاقد امتیاز را محدود میکنند. Bubblewrap برای ساخت sandbox خود به فضای نام کاربر نیاز دارد، بنابراین bwrap نمیتواند اجرا شود و از آنجا که برنامه بدون sandbox از انجام تبدیل خودداری میکند، تمام عملیاتها با شکست مواجه میشوند.
آن را مستقیماً بررسی کنید:
bwrap --ro-bind / / --dev /dev /bin/true && echo sandbox okخطای permission denied به این معنی است که فضای نام مسدود شده است. راهحل، استفاده از یک پروفایل AppArmor برای باینری bwrap است. ابتدا فایلهای ABI را لیست کنید و بالاترین شماره موجود را یادداشت کنید:
ls /etc/apparmor.d/abi/سپس /etc/apparmor.d/bwrap را بنویسید و به جای 4.0، آن شماره بالاترین را قرار دهید:
abi <abi/4.0>,
include <tunables/global>
profile bwrap /usr/bin/bwrap flags=(unconfined) {
userns,
include if exists <local/bwrap>
}آن را بارگذاری کنید:
sudo apparmor_parser -r /etc/apparmor.d/bwrapعدم نمایش خروجی به این معنی است که پروفایل با موفقیت بارگذاری شده است. بررسی bwrap را دوباره اجرا کنید؛ اکنون باید sandbox ok چاپ شود. از این لحظه به بعد، تبدیلها بهدرستی کار خواهند کرد.
یک مبدل عمومی، یک تجزیهکننده (parser) در دسترس غریبهها است
این بخشی است که کل مطلب برای آن نوشته شده است. یک مبدل فایل که از طریق اینترنت در دسترس باشد، فایلی دلخواه را از یک فرد ناشناس میپذیرد و آن را به LibreOffice، ImageMagick، FFmpeg یا Ghostscript تحویل میدهد. اینها پایگاهکدهای بزرگ C و C++ با تاریخچهای طولانی از باگهای تجزیهکننده هستند. شخصی که فایل را آپلود میکند، فرمت را انتخاب میکند؛ این یعنی او تعیین میکند کدام تجزیهکننده اجرا شود و کدام مسیر کد در داخل آن فعال گردد.
پاسخ HRConvert2 این است که هر وابستگی را در یک namespace از نوع bubblewrap اجرا کند. هر تبدیل، دو دایرکتوری را میبیند: یکی که ورودی را نگه میدارد و بهصورت read-only mount شده است، و دیگری که خروجی را دریافت میکند. شبکه در این حالت unshared است که به گفتهٔ خود پروژه، closes every URL handler in every dependency at once. این موضوع اهمیت بیشتری از آنچه به نظر میرسد دارد. ImageMagick و Ghostscript هر دو ارجاعاتی را میپذیرند که یک URL را فراخوانی میکنند؛ این همان روشی است که یک مبدل را به ابزاری برای Server Side Request Forgery (SSRF) جهت دسترسی به endpoint متادیتای ابری از داخل شبکهٔ شما تبدیل میکند. بدون شبکه در namespace، این فراخوانی امکانپذیر نیست.
امتناع از انجام عملیات، نیمهٔ دیگر ماجراست: A server that cannot build a sandbox refuses the conversion rather than quietly running without one. ابزاری که در صورت بروز خطا، دسترسی را میبندد (fails closed)، ارزشمندتر از ابزاری است که فقط در لاگی که کسی نمیخواند، هشدار میدهد. به همین دلیل است که مرحلهٔ AppArmor در بالا اختیاری نیست و چرا $RequireSandboxOnDocker پیش از آنکه container را در معرض دید قرار دهید، ارزش بررسی دارد.
سختسازی ImageMagick با استفاده از policy.xml
فایل سیاست (policy) خودِ ImageMagick، لایهٔ دومی زیر محیط sandbox است و تنظیم آن اهمیت دارد. در Ubuntu 24.04 با نسخهٔ ImageMagick 6، این فایل در مسیر /etc/ImageMagick-6/policy.xml قرار دارد. وضعیت فعال فعلی را با دستور زیر مشاهده کنید:
identify -list policyاین پروژه یک فایل سیاست در مسیر Documentation/Build/policy.xml ارائه میدهد که الگوی مناسبی است. این فایل کدکهای PS، PS2، PS3، EPS، XPS و MVG را مسدود کرده و همچنین دسترسیهای URL، HTTPS، HTTP و gs را غیرفعال میکند، در حالی که اجازه استفاده از 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" />خط gs اهمیت ویژهای دارد. ImageMagick بهتنهایی فایلهای PostScript را پردازش نمیکند. این برنامه برای این کار از Ghostscript استفاده میکند و همان delegate (نماینده) است که آسیبپذیریهای شناختهشدهٔ اجرای کد از راه دور (RCE) در ImageMagick در آن نهفته است. با مسدود کردن این delegate، ImageMagick دیگر هیچ فایل بارگذاریشدهای را به gs نمیسپارد، فارغ از اینکه فایل ادعا کند چه نوعی است.
همین سیاست، محدودیتهای منابع را نیز تعیین میکند؛ این روشی است که از مصرف بیرویه منابع سیستم توسط یک تصویر مخرب جلوگیری میکند:
<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)، فایلهای کوچکی هستند که ابعاد بسیار بزرگی را اعلام میکنند. محدودیتهای width، height و area پیش از تخصیص حافظه، این فایلها را رد میکنند تا پردازش متوقف شود و از کشته شدن پروسهها توسط هسته سیستمعامل جلوگیری شود.
یک تله در جهت معکوس وجود دارد. سیاست پیشفرض Ubuntu، کدک PDF را بهطور کامل مسدود میکند؛ بنابراین در یک سیستم دستنخورده، پردازش فایلهای PDF با خطای attempt to perform an operation not allowed by the security policy 'PDF' مواجه میشود. این رشته متنی نشاندهنده عملکرد صحیح سیاست امنیتی است. بازگرداندن دسترسی به این کدک تصمیمی است که باید آگاهانه بگیرید و در صورت انجام آن، حتماً باید دسترسی delegate مربوط به gs را همچنان مسدود نگه دارید.
هزینه زنجیره وابستگیها در یک VPS کوچک
در حالت بیکار (Idle)، هیچکدام از این موارد هزینهبر نیستند. Apache و PHP تنها چند ده مگابایت اشغال میکنند و فایلهای اجرایی مبدل اصلاً در حال اجرا نیستند. کل هزینه زمانی تحمیل میشود که فایلی برای پردازش وارد شود.
تبدیل یک سند، LibreOffice را اجرا میکند که خود باعث راهاندازی یک Java runtime میشود. تبدیل یک تصویر، به ImageMagick مقدار 256 MiB حافظه به همراه 512 MiB نگاشت حافظه (memory map) طبق سیاست فوق اختصاص میدهد. تبدیل ویدیو باعث میشود FFmpeg از تمام هستههای پردازشی شما استفاده کند، زیرا این رفتار پیشفرض FFmpeg در پردازش ویدیو است. مقدار memory_limit خودِ PHP در پیکربندی پروژه روی 512M تنظیم شده است. این اعداد در طول یک پردازش واحد، روی مصرف سیستمعامل و وبسرور انباشته میشوند.
بنابراین، یک VPS با 1 GB رم در اولین سند واقعی دچار swap شده و سپس سیستم دچار thrashing میشود. وقتی حافظه تمام میشود، مکانیزم out of memory killer در هسته سیستمعامل، فرآیندی را که بیشترین حافظه مقیم (resident size) را دارد، متوقف میکند. معمولاً این فرآیند soffice.bin است و کاربر با خطای شکست در تبدیل بدون هیچ پیام مفیدی مواجه میشود. گاهی اوقات این فرآیند apache2 است و کل سایت از دسترس خارج میشود. این وضعیت را پس از وقوع با دستور dmesg -T | grep -i "killed process" تأیید کنید.
این موارد راهنمای تعیین ابعاد (sizing) هستند، نه یک بنچمارک: 4 GB رم و دو هسته پردازشی برای یک تیم کوچک مناسب است و 2 GB رم به همراه یک swap file در صورتی که بار کاری شامل اسناد و تصاویر باشد و تأخیر را بپذیرید، کارآمد است. یک swap file سرعت تبدیل را افزایش نمیدهد. بلکه باعث میشود یک بار کاری ناگهانی (burst) به جای شکست کامل، کند شود؛ این تفاوت بین یک صفحه متوقفشده و یک قطعی کامل است. به دیسک فضای بیشتری از آنچه نیاز به نظر میرسد اختصاص دهید، زیرا یک تصویر 3 GB، محدودیت آپلود بالا و خروجیهای تبدیلشده، بسیار زودتر از هر منبع دیگری دیسک را پر میکنند.
فرآیندهای تبدیل ذاتاً ناگهانی و پرفشار هستند. دو نفر که همزمان ویدیو آپلود میکنند، تمام هستههای پردازشی را اشغال میکنند و درخواست بعدی در صف انتظار میماند. هیچ صف کاری (job queue) در جلوی این فرآیند وجود ندارد، بنابراین تنها کنترلی که در اختیار دارید، اعمال محدودیتها است.
تنظیم محدودیتهایی برای جلوگیری از پر شدن دیسک توسط یک آپلود
ابتدا مقادیر PHP را کاهش دهید. مقادیری مانند upload_max_filesize = 512M، post_max_size = 512M و max_file_uploads = 20 نقطه شروع مناسبی برای یک سرور اشتراکی با 4 GB رم هستند. به یاد داشته باشید که max_execution_time = 1200 اجازه میدهد یک درخواست PHP به مدت 20 دقیقه اجرا شود؛ این زمان برای تبدیل ویدیوهای طولانی واقعاً لازم است، اما به این معنی است که یک آپلود کند، یک worker را برای 20 دقیقه اشغال میکند.
سپس اندازه و نرخ انتقال را در سطح پروکسی، پیش از آنکه درخواست به 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 باید حداقل به اندازه بزرگترین فایلی باشد که میخواهید تبدیل کنید، در غیر این صورت Nginx خطای 413 Request Entity Too Large برمیگرداند و PHP هرگز آپلود را دریافت نمیکند. مقدار proxy_read_timeout باید از طولانیترین زمان تبدیل شما بیشتر باشد، در غیر این صورت شغلی که در پشت پروکسی به درستی در حال اجراست، خطای 504 Gateway Time-out را به مرورگر بازمیگرداند. باقی تنظیمات آن server block، از جمله TLS (امنیت لایه انتقال) termination، در توضیح خط به خط پیکربندی nginx reverse proxy پوشش داده شده است.
حذف فایلهای تبدیلشده
هر تبدیل، یک کپی از فایل حساس را در دایرکتوری قابل خواندن توسط وبسرور باقی میگذارد. پاکسازی همان چیزی است که یک مبدل را از آرشیوی از تمام فایلهایی که تاکنون در آن تبدیل شدهاند، متمایز میکند.
$DeleteThreshold در Resources/config.php نشاندهنده عمر نشست (session) به دقیقه است که پس از آن منقضی میشود و مقدار پیشفرض آن 60 است. اگر محتوا حساس است، آن را به 15 کاهش دهید. خود عملیات پاکسازی، یک آرگومان خط فرمان در هسته برنامه است:
sudo -u www-data php /path/to/HRConvert2/convertCore.php -c
sudo -u www-data php /path/to/HRConvert2/convertCore.php -c=15-c نشستهای منقضیشده را از هر دو محل ذخیرهسازی داده با استفاده از آستانه پیکربندیشده پاک میکند. -c=15 برای همان اجرا، از پانزده دقیقه استفاده میکند. -c=now تمام نشستها را بدون در نظر گرفتن عمر آنها حذف میکند، از جمله نشستی که کاربر در همان لحظه در حال تبدیل فایل با آن است؛ بنابراین از این گزینه فقط برای نگهداری (maintenance) استفاده کنید. همین آرگومانها از طریق docker exec در داخل کانتینر نیز کار میکنند.
پاکسازی را روی یک تایمر قرار دهید تا به بارگذاری صفحه توسط کاربر وابسته نباشد. یک خط در /etc/cron.d/hrconvert2 کافی است:
*/10 * * * * www-data php /path/to/HRConvert2/convertCore.php -cچند دقیقه بعد با ls /DATA/HRConvert2 وضعیت را بررسی کنید و ناپدید شدن دایرکتوریهای نشست قدیمی را مشاهده کنید. از آنجا که کاربر وبسرور مالک آن دایرکتوری است، همان حسابی است که یک parser آسیبدیده به آن دسترسی پیدا میکند؛ بنابراین آن کاربر نباید مالک هیچ چیز ارزشمند دیگری باشد. حسابهای کاربری با حداقل دسترسی در VPS الگوی کلی این موضوع است و در اینجا بیش از موارد معمول کاربرد دارد.
اگر هدف دسترسی عمومی نیست، آن را پشت احراز هویت قرار دهید
نصب پیشفرض بهدلیل طراحی خاص، هیچ حساب کاربری ندارد. هر کسی که به صفحه دسترسی داشته باشد میتواند فایلی آپلود کرده و باینریهای مبدل شما را اجرا کند، و محدودیتهای نرخ (rate limits) فقط این روند را کند میکنند. بنابراین تصمیم بگیرید در چه وضعیتی قرار دارید.
اگر این سرویس برای شما و چند همکار است، آن را اصلاً در معرض اینترنت قرار ندهید. کانتینر را مطابق آنچه در بالا نشان داده شد به loopback متصل کنید و از طریق یک شبکه خصوصی یا SSH tunnel به آن دسترسی پیدا کنید. در این صورت هیچچیز در اینترنت عمومی نمیتواند فایلی به آن ارسال کند، که این کار بهجای فیلتر کردن، کل سطح حمله را حذف میکند.
اگر سرویس باید از طریق مرورگر قابل دسترسی باشد، احراز هویت را در مقابل پروکسی قرار دهید. Basic auth تنها با دو دستور انجام میشود و فرم آپلود را از دسترس افراد غریبه دور نگه میدارد:
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;
}Nginx را reload کنید و صفحه را بارگذاری کنید. نمایش یک پنجره درخواست رمز به این معنی است که تنظیمات کار میکند، و اگر پنجرهای ظاهر نشد، یعنی بلاک location که ویرایش کردهاید، همان بلاکی نیست که درخواست را مدیریت میکند. برای استفاده از حسابهای کاربری واقعی بهجای یک رمز عبور مشترک، از یک ارائهدهنده single sign-on استفاده کنید: یک سرور Authentik SSO که خودتان میزبانی میکنید به شما امکان احراز هویت پیشرو (forward authentication) را در مقابل برنامهای که خودش سیستم ورود ندارد، میدهد.
اگر هدف، یک مبدل کاملاً عمومی است، پیامدهای آن را بپذیرید و برای آن برنامهریزی کنید. فرض را بر این بگیرید که sandbox مورد کاوش قرار خواهد گرفت. تگ image را ثابت (pinned) نگه دارید، سیاستهای ImageMagick را سختگیرانه تنظیم کنید، محدودیتهای آپلود را کوچک نگه دارید و آن را روی یک VPS اجرا کنید که هیچ داده مهم دیگری روی آن ندارید.
حالتهای شکست و پیامهایی که مشاهده خواهید کرد
تمام تبدیلها بلافاصله با شکست مواجه میشوند. محیط sandbox ساخته نمیشود. در یک نصب معمولی، این مشکل مربوط به پروفایل AppArmor است. در Docker، این مشکل ناشی از نبود --security-opt seccomp=unconfined است. برنامه نام آن را ذکر میکند: A sandbox blocks the required syscalls unless it was started with the correct options. و به See --Require Sandbox-- & --Require Sandbox On Docker-- in config.php. اشاره دارد.
فقط تبدیل تصاویر با شکست مواجه میشود. Bubblewrap is missing or non functional, so this image conversion cannot be isolated! به این معنی است که bwrap وجود ندارد یا در مسیری که کاربر وبسرور به آن دسترسی دارد، قابل یافتن نیست.
یک فرمت خاص کار نمیکند و بقیه درست هستند. یک فایل باینری گم شده است که به وضوح گزارش میشود: ImageMagick may not be installed, or may not be reachable on the system path used by the web server user.. همین پیام برای FFmpeg و LibreOffice نیز وجود دارد. دستور convertCore.php -v را اجرا کنید تا ببینید سیستم چه ابزارهایی را شناسایی میکند؛ به یاد داشته باشید که متغیر PATH برای worker آپاچی با PATH شل ورود شما متفاوت است.
پردازش PDF با خطای سیاست (policy error) شکست میخورد. attempt to perform an operation not allowed by the security policy 'PDF' از سمت policy.xml در ImageMagick صادر میشود، نه از سمت HRConvert2.
آپلود فایلهای حجیم خطای 413 برمیگرداند. مقدار client_max_body_size در nginx از حجم فایل کمتر است. سه محدودیت در این زنجیره وجود دارد: یکی در nginx و دو مورد در PHP؛ کوچکترین مقدار تعیینکننده است.
تبدیلها متوقف میشوند و هیچ تغییر واضحی رخ نداده است. The device where data is stored has an insufficient amount of storage space available. فضای دیسک را بررسی کنید و مطمئن شوید که فرآیند پاکسازی (cleanup) در حال اجراست.
پاکسازی در لاگ خطا میدهد. Could not clean the temporary location! و Could not clean the convert location! مشکلات مالکیت فایل هستند. کاربر وبسرور باید مالک دایرکتوری مشخصشده توسط $ConvertLoc باشد.
FAQ
آیا قرار دادن یک مبدل فایل self-hosted در معرض اینترنت امن است؟
این کار تنها در صورتی امن است که با آن مانند یک parser برخورد کنید که در دسترس غریبهها قرار دارد. هر فایل بارگذاریشده به LibreOffice، ImageMagick، FFmpeg یا Ghostscript سپرده میشود و شخصی که فایل را بارگذاری میکند، انتخاب میکند که کدام ابزار استفاده شود. برنامه HRConvert2 این ابزارها را درون یک namespace از نوع bubblewrap بدون دسترسی به شبکه و با یک دایرکتوری ورودی فقطخواندنی اجرا میکند و هر تبدیلی را که نتواند در sandbox قرار دهد، رد میکند که یک تنظیم پیشفرض قدرتمند است. با این حال، همچنان بهتر است احراز هویت را الزامی کنید، محدودیتهای بارگذاری را کوچک نگه دارید و آن را روی یک VPS اجرا کنید که هیچ دادهٔ ارزشمند دیگری در آن وجود ندارد.
چرا تمام تبدیلها در یک نصب تازه از Ubuntu 24.04 با شکست مواجه میشوند؟
سیستمعاملهای Ubuntu 24.04 و Debian 12 استفاده از namespaceهای کاربر بدون امتیاز (unprivileged) را محدود کردهاند و bubblewrap برای ساخت sandbox خود به یکی از آنها نیاز دارد. از آنجا که برنامه بدون sandbox از انجام تبدیل خودداری میکند، تمام عملیاتها بهجای برخی از آنها، با شکست مواجه میشوند. یک پروفایل AppArmor برای /usr/bin/bwrap با استفاده از flags=(unconfined) بنویسید، آن را با sudo apparmor_parser -r /etc/apparmor.d/bwrap بارگذاری کنید و سپس با bwrap --ro-bind / / --dev /dev /bin/true تأیید نمایید.
چرا تبدیلها در Docker شکست میخورند اما در نصب معمولی کار میکنند؟
پروفایل پیشفرض seccomp در Docker، فراخوانیهای سیستمی (system calls) مورد استفادهٔ bubblewrap را مسدود میکند، بنابراین sandbox نمیتواند درون container ایجاد شود. آن را با --security-opt seccomp=unconfined اجرا کنید که همان کاری است که دستور اجرای خود پروژه انجام میدهد. توجه داشته باشید که $RequireSandboxOnDocker بهطور پیشفرض FALSE است، بنابراین یک container بدون پرچم ممکن است بدون هیچگونه sandbox تبدیل را انجام دهد. پس از اعمال پرچم seccomp، آن را روی TRUE تنظیم کنید.
یک سرور تبدیل فایل به چه مقدار RAM نیاز دارد؟
در حالت بیکار (idle) مصرف آن کم است، اما در حین تبدیل اینطور نیست. LibreOffice یک Java runtime را شروع میکند، ImageMagick طبق سیاست پیشفرض 256 MiB حافظه و 512 MiB نگاشت (map) اشغال میکند و محدودیت خود PHP برابر با 512M است. روی یک VPS با 1 GB رم، این ترکیب باعث swap میشود و out of memory killer برنامه soffice.bin یا apache2 را متوقف میکند. برای یک تیم کوچک، 4 GB رم و دو هسته پردازنده در نظر بگیرید و هر زمان که یک تبدیل بدون پیام خطا متوقف شد، dmesg -T | grep -i "killed process" را بررسی کنید.
فایلهای تبدیلشده کجا میروند و چه زمانی حذف میشوند؟
آنها به دایرکتوری کاری که توسط $ConvertLoc در Resources/config.php تعیین شده میروند که پیشفرض آن /DATA/HRConvert2 است. $DeleteThreshold عمر نشست (session) را بر حسب دقیقه تعیین میکند که پیشفرض آن 60 است. عملیات پاکسازی از طریق خط فرمان اجرا میشود: php convertCore.php -c نشستهای منقضیشده را پاک میکند و -c=now تمام نشستها، از جمله نشستهای فعال را بلافاصله حذف میکند. دستور -c را در یک ورودی cron یا یک systemd timer قرار دهید تا حذف فایلها به بازدید هیچ کاربری از سایت وابسته نباشد.