Покроковий огляд налаштування Apache2, mod_fcgid і PHP5 на Ubuntu Server 11.10. Розглядається створення окремих віртуальних хостів, запуск PHP від імені власника кожного сайту через suEXEC, підготовка FastCGI-скриптів і використання індивідуальних параметрів PHP.
Під час розміщення кількох сайтів на одному сервері важливо не лише правильно налаштувати вебсервер, а й розділити права доступу між окремими проєктами. Якщо всі PHP-скрипти виконуються від імені одного системного користувача вебсервера, помилка або вразливість на одному сайті може створити небезпеку для інших ресурсів.
Одним із поширених рішень для старих версій Ubuntu була зв’язка Apache2, mod_fcgid, PHP5 CGI та suEXEC. Вона дозволяла запускати PHP-скрипти кожного віртуального хоста від імені його власника, а не від загального користувача Apache.
У цьому матеріалі розглядається історичний приклад налаштування такої системи на Ubuntu Server 11.10. Для демонстрації створюються два окремі сайти, кожен із власним системним користувачем, каталогом, FastCGI-обробником і можливістю використовувати індивідуальний файл php.ini.
Варто враховувати, що Ubuntu 11.10, PHP5 і наведені конфігурації Apache давно застаріли. Матеріал корисний передусім для розуміння принципів ізоляції сайтів, роботи FastCGI та розподілу прав доступу.
Підготовка системи
Налаштування виконувалося на сервері Ubuntu 11.10 із повним доменним ім’ям server1.example.com. Для прикладу використовувалася локальна IP-адреса, а також два демонстраційні домени:
www.example1.com;
www.example2.com.
У реальній системі ці адреси потрібно замінити власними доменними іменами та мережевими параметрами.
Перед початком роботи необхідно отримати адміністративні права. У старому керівництві для цього використовувався перехід до оболонки root через sudo.
Також змінювалася системна оболонка /bin/sh. За замовчуванням вона могла посилатися на Dash, тоді як деякі сценарії налаштування очікували Bash. Тому через системний конфігуратор відмовлялися від використання Dash як /bin/sh.
В оригінальній конфігурації також рекомендувалося повністю вимкнути та видалити AppArmor. Для сучасного сервера такий підхід небажаний. Систему обмеження доступу краще налаштувати під потрібні служби, а не відключати без необхідності.
Встановлення Apache2, mod_fcgid і PHP5
Для створення вебсервера встановлювалися Apache2, suEXEC, модуль mod_fcgid і CGI-версія PHP5.
Якщо PHP уже працював як вбудований модуль Apache, його потрібно було вимкнути. Це було необхідно тому, що обробку PHP планувалося виконувати через FastCGI, а не всередині основного процесу Apache.
Після цього активувалися модулі:
rewrite — для правил перенаправлення та роботи файлів .htaccess;
suexec — для запуску CGI-процесів від імені власника сайту;
include — для серверних включень;
fcgid — для обробки FastCGI-запитів.
У конфігурації PHP CGI вмикалася підтримка коректного визначення PATH_INFO. Додатковий параметр для mod_fcgid додавався до загальної конфігурації модуля, а не всередину окремого VirtualHost.
Після внесення змін Apache перезапускався, щоб завантажити нові модулі та параметри.
Створення окремих користувачів для сайтів
Для двох демонстраційних сайтів створювалися окремі системні групи та користувачі:
web1 — власник першого сайту;
web2 — власник другого сайту.
Для цих облікових записів вимикався звичайний інтерактивний вхід до системи, а домашні каталоги розміщувалися всередині /var/www.
Структура сайтів виглядала приблизно так:
/var/www/web1/web;
/var/www/web2/web.
Каталог першого сайту належав користувачеві та групі web1, а каталог другого — користувачеві та групі web2.
Завдяки цьому файли різних сайтів мали окремих власників. PHP-процес кожного віртуального хоста також міг запускатися з відповідними правами.
Робота suEXEC
suEXEC дозволяє Apache запускати CGI-програми не від імені загального користувача www-data, а від імені власника конкретного сайту.
Проте suEXEC має суворі правила безпеки. Виконуваний файл повинен знаходитися всередині дозволеного кореневого каталогу, належати правильному користувачеві та мати безпечні права доступу.
У розглянутій системі коренем suEXEC був каталог /var/www. Стандартний виконуваний файл PHP CGI знаходився поза цим каталогом, тому Apache не міг запускати його безпосередньо через suEXEC.
Також проблему не можна було вирішити звичайним символічним посиланням, оскільки suEXEC не дозволяв використовувати його для обходу встановленого кореневого каталогу.
Для кожного сайту створювався окремий запускний сценарій усередині /var/www. Саме цей сценарій викликав системний PHP CGI.
Підготовка FastCGI-сценаріїв
Для запускних сценаріїв створювалася окрема структура:
/var/www/php-fcgi-scripts/web1;
/var/www/php-fcgi-scripts/web2.
У кожному каталозі розміщувався файл php-fcgi-starter.
У ньому визначалися:
каталог із конфігурацією PHP;
максимальна кількість запитів для одного FastCGI-процесу;
кількість дочірніх PHP-процесів;
шлях до виконуваного PHP CGI.
Параметр PHPRC вказував, де PHP повинен шукати файл php.ini.
PHP_FCGI_MAX_REQUESTS визначав, скільки запитів процес може обробити перед автоматичним завершенням і повторним запуском. Такий перезапуск допомагав звільняти пам’ять після тривалої роботи.
PHP_FCGI_CHILDREN визначав кількість дочірніх PHP-процесів, які могли одночасно обробляти запити.
Запускні сценарії робилися виконуваними. Сценарій першого сайту та його каталог належали web1, а другого — web2.
Це було принципово важливо для suEXEC. Якщо власник або права не відповідали конфігурації віртуального хоста, Apache відмовлявся запускати PHP.
Створення віртуальних хостів Apache
Для кожного сайту створювалася окрема конфігурація VirtualHost.
У ній зазначалися:
основне доменне ім’я;
додатковий домен без www;
адреса адміністратора;
кореневий каталог сайту;
системний користувач і група;
шлях до FastCGI-сценарію;
правила доступу до каталогу;
налаштування журналів.
Для першого сайту директива SuexecUserGroup використовувала користувача і групу web1. Для другого — web2.
PHP-файли передавалися обробнику fcgid, а FCGIWrapper указував шлях до відповідного php-fcgi-starter.
Таким чином, PHP-код першого сайту виконувався з правами web1, а PHP-код другого — з правами web2.
Після створення конфігурацій сайти активувалися командами керування Apache, а вебсервер перечитував налаштування.
Під час підготовки віртуальних хостів необхідно уважно перевіряти шляхи, користувачів і групи. Навіть невелика помилка може призвести до відмови suEXEC, помилки FastCGI або відповіді сервера з кодом 500.
Перевірка роботи PHP
Для перевірки на першому сайті створювався невеликий PHP-файл із викликом phpinfo.
Після відкриття сторінки у браузері в інформації PHP потрібно було знайти рядок Server API. Якщо налаштування виконано правильно, у ньому відображався режим CGI/FastCGI.
Це підтверджувало, що PHP працює через mod_fcgid, а не як звичайний вбудований модуль Apache.
Після перевірки файл phpinfo необхідно видалити. Він показує багато технічної інформації про сервер, модулі, шляхи та конфігурацію, тому залишати його у відкритому доступі небезпечно.
Окремий php.ini для кожного сайту
Однією з переваг окремих php-fcgi-starter було використання різних налаштувань PHP для кожного віртуального хоста.
Для другого сайту стандартний php.ini копіювався до каталогу /var/www/web2. Після цього власником файлу призначався користувач web2.
У запускному FastCGI-сценарії значення PHPRC змінювалося так, щоб PHP шукав конфігурацію саме в каталозі другого сайту.
Після перезавантаження Apache другий сайт починав використовувати власний php.ini, тоді як перший продовжував працювати із загальною системною конфігурацією.
Це дозволяло для різних сайтів установлювати окремі:
ліміти пам’яті;
розміри завантажуваних файлів;
час виконання скриптів;
параметри журналювання;
налаштування сесій;
дозволені та заборонені функції;
параметри відображення помилок.
Правильність підключення перевірялася через phpinfo. У рядку Loaded Configuration File мав відображатися шлях до індивідуального php.ini.
Зміна окремих параметрів PHP
Створювати повну копію php.ini для кожного сайту було необов’язково. Окремі параметри можна було передавати безпосередньо під час запуску PHP CGI.
Для цього до команди запуску PHP у php-fcgi-starter додавалася опція -d з назвою параметра та потрібним значенням.
В оригінальному прикладі таким способом вимикалася застаріла функція magic_quotes_gpc для одного з сайтів.
Після зміни запускного сценарію Apache перечитував конфігурацію, а результат перевірявся через phpinfo.
Подібний підхід дозволяв змінити лише кілька параметрів, не створюючи повну окрему копію php.ini.
Переваги такої конфігурації
Головною перевагою зв’язки Apache, mod_fcgid і suEXEC було розділення сайтів за системними користувачами.
Кожен сайт:
мав власний каталог і власника;
запускав PHP від окремого користувача;
міг мати індивідуальний php.ini;
отримував окремий FastCGI-сценарій;
не потребував надання загальному користувачеві Apache повних прав на всі файли.
Це також спрощувало роботу з файлами, створеними PHP. Вони одразу належали власнику сайту, а не користувачеві www-data.
Обмеження та недоліки
Конфігурація вимагала створення окремого запускного сценарію для кожного сайту. Адміністратору потрібно було уважно стежити за власниками, групами, дозволами та шляхами.
Неправильні права могли блокувати запуск PHP. Помилка у FCGIWrapper або SuexecUserGroup призводила до внутрішньої помилки сервера.
Окремі PHP-процеси також споживали додаткову оперативну пам’ять. Значення кількості дочірніх процесів потрібно було підбирати відповідно до ресурсів сервера й навантаження сайтів.
Сучасне використання
Описана схема відображає підхід, який застосовувався зі старими версіями Apache, Ubuntu і PHP.
На сучасних серверах частіше використовують PHP-FPM з окремими пулами для сайтів. Для кожного пулу можна призначити власного користувача, групу, сокет, ліміти ресурсів та індивідуальні параметри PHP.
Принцип залишається схожим: кожен сайт працює у власному середовищі та не повинен отримувати зайвого доступу до файлів інших проєктів.
Підсумок
Зв’язка Apache2, mod_fcgid, PHP5 CGI та suEXEC дозволяла організувати ізольоване виконання PHP для кількох віртуальних хостів на Ubuntu Server 11.10.
Для кожного сайту створювалися окремий системний користувач, каталог, FastCGI-сценарій і конфігурація Apache. PHP запускався з правами власника конкретного сайту, а за потреби кожен ресурс міг використовувати власний php.ini.
Хоча програмні версії та окремі команди з оригінальної інструкції вже не підходять для нового сервера, матеріал добре пояснює базові принципи ізоляції сайтів, виконання PHP через FastCGI та правильного розподілу прав доступу.
{"site_name":"my-dark-master","site_description":"News, posts, articles, files and community projects.","page_not_found":"Page not found","home":"Home","content_not_found":"Content not found","materials":"Content","login":"Sign in","login_action":"Sign in","logout":"Sign out","register":"Register","forgot_password":"Forgot password?","restore_password":"Restore password","email":"Email","password":"Password","name":"Name","nickname":"Username","profile":"My profile","save_profile":"Save profile","theme":"Theme","theme_dark":"Dark","theme_light":"Light","search_placeholder":"Search the site...","search":"Search","change_theme":"Change theme","news":"News","lib":"Articles","post":"Posts","communities":"Communities","community":"Community","main_community":"Main community","community_role_owner":"Owner","community_role_admin":"Administrator","community_role_moderator":"Moderator","community_role_author":"Author","community_role_subscriber":"Member","community_status_pending":"Pending moderation","community_status_revision":"Needs revision","community_status_approved":"Approved","community_status_rejected":"Rejected","community_status_blocked":"Blocked","community_status_archived":"Archived","community_not_found":"Community not found","communities_empty":"There are no communities yet.","comments":"Comments","comments_sort":"Comment sorting","comments_new":"Newest","comments_best":"Best","comments_old":"Oldest","mail":"Mail","sections":"Sections","publications":"publications","materials_count":"items","empty_section":"There are no publications in this section yet.","back_to_section":"Back to section","read_more":"Read more","comment":"Comment","send":"Send","to_home":"Home","open_login_menu":"Open sign-in menu","open_profile_menu":"Open profile menu","language":"Language","menu":"Menu","categories":"Categories","genre":"Genre","section_disabled":"This section is temporarily disabled.","footer_sections":"Sections","footer_social":"Social networks","footer_info":"Information","comments_empty":"No comments yet.","comments_login_required":"Register and sign in to add comments.","pagination":"Pagination","pagination_back":"Back","pagination_next":"Next","admin_panel":"Control panel","admin_panel_hint":"Site management","admin_dashboard":"Dashboard","admin_add_news":"Add news","admin_add_post":"Add post","admin_add_lib":"Add article","admin_all_materials":"All content","admin_categories":"Material categories","admin_settings":"Site settings","admin_profile":"Admin settings","admin_content":"Content","admin_comments":"Comments","admin_users":"Users","admin_system":"System","admin_add_material":"Add","admin_edit_material":"Edit","admin_content_title":"Title","admin_content_genre":"Genre","admin_content_summary":"Summary","admin_content_body":"Full text","admin_content_status":"Status","admin_content_poster":"Poster","admin_category":"Category","admin_category_none":"No category","admin_content_publish":"Add content","admin_content_save":"Save changes","status_moderation":"Pending moderation","status_published":"Published","status_fixed":"Pinned and published","enabled":"Enabled","disabled":"Disabled","all":"All","section":"Section","date":"Date","delete":"Delete","add":"Add","open":"Open","save":"Save","cancel":"Cancel","admin_group":"Group","admin_group_settings":"Group settings","admin_group_settings_hint":"Groups use fixed IDs from 1 to 5. The super admin always has full access.","admin_groups_saved":"Group settings saved.","admin_groups_error":"Unable to save group settings.","group_superadmin":"Super admin","group_superadmin_hint":"Full access to all functions and settings.","group_admin":"Admin","group_admin_hint":"Site management without changing super-admin rights.","group_moderator":"Moderator","group_moderator_hint":"Content and moderation tools.","group_user":"User","group_user_hint":"Regular registered user.","group_guest":"Guest","group_guest_hint":"Visitor without access to the control panel.","permission_panel_access":"Access the control panel","permission_content_manage":"Add and edit content","permission_categories_manage":"Manage shared categories","permission_users_manage":"Manage users","permission_groups_manage":"Change group settings","permission_settings_manage":"Change site settings","admin_users_hint":"Assign one of the five groups to registered users.","admin_user_search":"Username or email","admin_users_empty":"No users found.","admin_user_not_found":"User not found.","admin_group_self_change_denied":"You cannot change your own group from the panel.","admin_superadmin_only":"Only a super admin can manage the super-admin group.","admin_last_superadmin":"The last super admin cannot be removed.","admin_user_group_saved":"The user group has been changed.","admin_you":"You","admin_permission_denied":"Your group is not allowed to perform this action.","admin_panel_limited":"Panel access is allowed, but your group cannot manage content.","created_at":"Registered","action":"Action","admin_invalid_login":"Incorrect password or this account cannot access the control panel.","admin_logged_without_access":"You are signed in as %s, but your group cannot access the control panel.","admin_my_settings":"My settings","admin_general_tab":"General","admin_security_tab":"Security","admin_nickname_locked":"Changing the username in the panel is disabled.","admin_users_section":"Users","admin_system_settings":"System","admin_optional":"optional","admin_poster_choose":"Choose image","admin_poster_hint":"JPG, PNG, GIF or WEBP up to 5 MB","news_navigation":"News navigation","post_navigation":"Post navigation","lib_navigation":"Article navigation","all_categories":"All materials","admin_categories_hint":"Categories are separated by material type and do not mix with each other.","admin_categories_all":"All categories","admin_category_add":"Add category","admin_category_edit":"Edit category","edit":"Edit","view_mode":"Content view","view_list":"List","view_grid":"Grid","search_results":"Search results","search_query":"Query","search_empty":"Nothing found.","profile_home":"Profile home","profile_avatar":"Avatar","profile_my":"My profile","profile_security":"Security","profile_welcome":"My profile","profile_avatar_change":"Change avatar","choose_avatar":"Choose image","save_avatar":"Save avatar","avatar_upload_disabled":"Avatar uploads are disabled.","avatar_required":"Choose an image.","avatar_too_large":"The image is larger than 5 MB.","avatar_invalid_type":"JPG, PNG, GIF and WEBP are allowed.","avatar_save_failed":"Could not save the avatar. Check permissions for \/img\/avatar.","avatar_saved":"Avatar saved.","search_show_all":"Show all","captcha_label":"Verification: solve the example","captcha_required":"Enter the answer to the verification example.","captcha_invalid":"The CAPTCHA answer is incorrect. Please try again.","registration_bot_blocked":"Registration was rejected by the anti-bot protection. Refresh the form and try again.","registration_ip_exists":"An account has already been registered from this IP address.","ok":"OK","reset_password":"Reset password","change_password":"Change password","new_password":"New password","current_password":"Current password","password_change":"Password change","update_password":"Update password","theme_saved":"Theme saved.","field_required":"Complete the required fields.","email_invalid":"Enter a valid email address.","password_min":"The password must contain at least 6 characters.","name_required":"Enter a username.","form_sent":"Data sent.","close_notification":"Close notification","logout_success":"You have signed out.","register_success":"Registration completed successfully.","invalid_login":"Incorrect email or password.","invalid_register":"Enter a username, valid email and a password of at least 6 characters.","registration_disabled":"Registration is temporarily disabled.","email_exists":"A user with this email already exists.","name_exists":"A user with this username already exists.","profile_required":"Username and email are required.","email_busy":"This email is already in use.","name_busy":"This username is already in use.","profile_saved":"Profile saved.","password_check_failed":"Check your current password. The new password must contain at least 6 characters.","password_changed":"Password changed.","mail_new":"New message","mail_inbox":"Inbox","mail_sent_folder":"Sent","mail_spam":"Spam","mail_to":"To (username)","mail_subject":"Subject","mail_body":"Message","mail_files":"Files","mail_send":"Send message","mail_refresh":"Refresh","mail_block":"Block sender","mail_close":"Close mail","mail_empty":"No messages yet.","mail_select":"Select a message from the list.","mail_required":"Enter a recipient and message.","mail_recipient_not_found":"No user with that username was found.","mail_self_denied":"You cannot send a message to yourself.","mail_limit_messages":"The monthly message limit has been reached.","mail_limit_attachments":"The monthly attachment limit has been reached.","mail_sent":"Message sent.","mail_not_found":"Message not found.","mail_blocked":"The user was added to your block list.","mail_system":"System","mail_welcome_subject":"Welcome","mail_welcome_body":"Hello, {name}.\n\nYour login: {email}","mail_server_error":"Server error.","mail_network_error":"Network error.","mail_error":"Mail error.","mailbox_full":"The user\u2019s mailbox is full.","mail_add_files":"Add files","mail_download":"download","added":"Added","reading_now":"Reading now","views":"Views","views_page_title":"Views","views_back_material":"Back to content","views_today":"Today","views_7_days":"Last 7 days","views_30_days":"Last 30 days","views_registered":"Users","views_guests":"Guests","views_repeat_note":"A repeat view by the same visitor is counted after the interval configured in the site settings.","views_last":"Last view","category":"Category","material_rating":"Content rating","author_rating":"Rating","bookmark":"Bookmark","profile_bookmarks":"My bookmarks","bookmarks_all":"All","bookmarks_empty":"There are no bookmarks in this section yet.","bookmark_remove":"Remove from bookmarks","bookmarked_at":"Added","share":"Share","subscribe":"Subscribe","subscribed":"Subscribed","login_required":"Sign in to vote.","delete_comment":"Archive comment","comment_deleted":"Comment archived.","comment_not_found":"Comment not found.","comment_added":"Comment added.","comment_required":"Enter a comment.","comment_too_long":"The comment is too long.","comments_disabled":"Comments are disabled for this section.","account_banned":"Your account is blocked.","reason":"Reason","ban_until":"Until","forever":"Permanent","wait_seconds":"Wait %d seconds.","daily_limit_reached":"The daily limit has been reached.","admin_category_created":"Category added.","admin_category_deleted":"Category archived.","admin_category_error":"The category could not be saved. Check the name and URL.","admin_category_not_found":"Category not found.","admin_category_required":"Enter the Ukrainian category name.","admin_category_updated":"Category updated.","admin_content_created":"Content added.","admin_content_deleted":"Content archived.","admin_content_empty":"No content yet.","admin_content_manage_hint":"Only sections allowed for your group are shown.","admin_content_not_found":"Content not found.","admin_content_required":"Enter the Ukrainian title and full text.","admin_content_save_error":"The content could not be saved.","admin_content_updated":"Content updated.","admin_forbidden":"Your group cannot access the control panel.","admin_login":"Control panel sign in","admin_login_action":"Sign in","admin_login_success":"You signed in to the control panel.","admin_profile_saved":"Profile settings saved.","admin_select_material":"Content not found.","admin_settings_error":"The settings could not be saved.","admin_settings_saved":"Site settings saved.","admin_theme_saved":"Control panel theme changed.","already_have_account":"Already have an account?","back_to_login":"Back to sign in","check_mail":"Check your email","engine_version":"Engine version","forgot_done_text":"If the address exists, recovery instructions were sent by email.","forgot_help":"Enter the email address used during registration.","get_link":"Get link","invalid_reset":"The recovery link is invalid or expired.","local_test_link":"Local test link","login_too_many":"Too many sign-in attempts. Try again later.","mod_not_found":"Module not found.","not_found_text":"The requested page was not found.","password_changed_login":"Password changed. Sign in with the new password.","permission_denied":"You do not have permission for this action.","rating_disabled":"Rating is disabled for this content.","required_language":"required language","reset_open_link":"Open password recovery","reset_sent":"The recovery link was sent.","too_many_requests":"Too many requests. Try again later.","vote_saved":"Your vote was saved.","invalid_vote":"Invalid vote value."}
Comments