Права доступу веб‑кореня в Debian/Ubuntu: припиніть 403 без 777 (Випадок №69)

Було корисно?

Деякі аутеджі не кричать. Вони шепочуть. Тихе «403 Forbidden» у браузері, тикет у підтримку зі скріншотом, і розробник клянеться, що «нічого не чіпав». Тим часом веб‑сервер стоїть на порозі з планшетом і відмовляє у доступі, бо ви забули один біт виконання в батьківському каталозі.

Якщо ваша рефлекторна «лікувальна» дія — chmod -R 777, відсуньте руки від клавіатури. Ми можемо виправити 403 на Debian/Ubuntu чисто, передбачувано і без перетворення веб‑кореня на громадський сад.

Швидкий план діагностики

Коли ви маєте 403, ви не «ніжитеся у відлагодженні прав». Ви знаходите перше місце, де користувач процесу веб‑сервера не може виконати трасування або читання. Робіть це в такому порядку — і перестанете маринуватися в здогадках.

1) Підтвердіть, яким саме є користувач процесу веб‑сервера

Не припускайте www-data. За замовчуванням на Debian/Ubuntu часто так, але контейнери, загартування або systemd‑перевизначення можуть змінити це.

2) Визначте точний шлях, який подається

Це очікуваний DocumentRoot? Симліанк? Alias? Віртуальний корінь для хоста? «Допоміжне» перенаправлення в домашній каталог когось?

3) Прочитайте рядок журналу помилок, що відповідає вашому запиту

403 буває різних смаків: файлові права, відключений індекс каталогу, потрібна автентифікація або модулі політик (AppArmor/SELinux). Журнал підкаже, що саме сталося.

4) Перевірте доступ від імені користувача веб‑сервера, з файлової системи

Якщо процес не може трасувати батьківські каталоги, не має значення, що сам файл читабельний.

5) Лише потім змінюйте права — і мінімально необхідне

Віддавайте перевагу доступу на рівні груп або ACL. Правильно використовуйте біт виконання. Уникайте будь‑яких світових записів у веб‑корені, якщо вам не подобаються інцидент‑огляди.

Модель прав, яка справді викликає 403

403 — це не одна помилка; це категорія

В Apache і Nginx «403 Forbidden» означає: «Я зрозумів запит, але не віддам відповідь». Це може бути через:

  • Доступ до файлової системи заборонено (найпоширеніше).
  • Індексація каталогів вимкнена і немає index‑файлу.
  • Правила доступу відмовляють у запиті (Apache Require, Nginx deny).
  • Помилка автентифікації/авторизації (інколи 401, інколи 403 залежно від конфігурації).
  • Обов’язковий контроль доступу (AppArmor або SELinux) заблокував запит.

Ця стаття про кут зору прав: класичні POSIX‑біти, власність, групи, umask і ACL, а також як це перетинається з вибором пакунків на Debian/Ubuntu.

Великий підводний камінь: біт «execute» для каталогу означає traverse

Про каталоги:

  • read (r) дозволяє переліковувати імена.
  • write (w) дозволяє створювати/видаляти/перейменовувати записи.
  • execute (x) дозволяє трасувати каталог і звертатися до інодів всередині, якщо ви вже знаєте ім’я.

Класична аварія виглядає так: файл має 644 і здається читабельним, але один батьківський каталог має 750 і належить іншій групі, і веб‑сервер не може його трасувати. Браузер бачить 403. Людина бачить «але файл 644». Обидва технічно праві — і саме тому це повторюється.

Симлінки — не магічні портали

Симлінки — це лише вказівники. Сервер потребує прав на цільовий шлях, включно з усіма батьківськими каталогами. Також Apache може бути налаштований на заборону слідування симлінкам, якщо це явно не дозволено.

Чому 777 «працює» і чому це пастка

777 дає всім читання/запис/виконання. Для каталогів це означає, що будь‑який локальний користувач (або скомпрометований сервіс) може скидати або підмінювати файли у вашому веб‑корені. Якщо веб‑сервер виконує скрипти (PHP, CGI тощо), ви фактично виставили табличку «запустіть тут довільний код».

Короткий жарт: chmod 777 — як ховати ключ під килимок дверей — тільки килимок лежить на центральній площі.

Надійний оперативний максим

Парафраз ідеї В. Едвардса Демінга: Більшість проблем походить від системи, а не від індивідуальної помилки. Збої з правами зазвичай не тому, що хтось «халтурив». Вони виникають, коли ваш шлях деплою, модель власності і значення за замовчуванням не продумані.

Цікаві факти та контекст (чому це повторюється)

  1. Unix‑права зʼявилися задовго до вебу. Модель була створена для багатокористувацьких timesharing‑систем, а не для обслуговування статичних файлів усьому світу.
  2. Біт виконання для каталогів старіший за більшість із нас. Це «search» або «traverse», і його забути — як обряд ініціації, якого ніхто не просив.
  3. Debian популяризував сервісні користувачі як www-data. Ідея — тримати даемони без привілеїв і послідовними між інсталяціями.
  4. Рання історія безпеки Apache вплинула на сьогоднішні дефолти. Опції на кшталт Options FollowSymLinks і Require all granted еволюціонували, бо «подавати все, що читабельно» — погана ідея.
  5. POSIX ACL раніше не були поширені. Вони стали мейнстримом у Linux‑дистрибутивах, коли командам знадобився тонший контроль доступу, ніж одна група може надати.
  6. Umask — це тихий рушій політики. Це не право саме по собі; це дефолтне віднімання, що застосовується при створенні файлу. Воно може підмочити ваш ретельний план, поки ви спите.
  7. Багато інцидентів з «правами» — насправді відмови модулів політики. AppArmor (Ubuntu) і SELinux (в деяких Debian‑налаштуваннях) можуть блокувати доступ навіть коли біти режиму виглядають коректно.
  8. Веб‑стеки повільно приймали «least privilege». Раніше запуск серверів від root був нормою; нині це червона лампочка. Модель прав у вашому деплої має відповідати цій реальності.

Практичні завдання: команди, виводи та рішення

Нижче наведені практичні перевірки, які можна виконати на Debian/Ubuntu. Кожне завдання включає: команду, що означає її вивід, і рішення, яке з нього випливає. Це рухи, що зупиняють 403 без створення ями для безпеки.

Завдання 1: Підтвердити, який веб‑сервер запущений і хто його користувач процесу

cr0x@server:~$ ps -eo user,comm,args | egrep 'apache2|nginx' | head
root     nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
www-data nginx: worker process
root     /usr/sbin/apache2 -k start
www-data /usr/sbin/apache2 -k start

Що це означає: Майстер‑процес може працювати від root, воркери повинні бути безпривілейні (часто www-data). Цей безпривілейний користувач має мати можливість читати/трасувати веб‑корінь.

Рішення: Використовуйте користувача воркера (www-data тут) для всіх тестів доступу та для моделі власності/груп.

Завдання 2: Визначити, який DocumentRoot або веб‑корінь реально використовується

cr0x@server:~$ apache2ctl -S 2>/dev/null | sed -n '1,25p'
VirtualHost configuration:
*:80                   example.local (/etc/apache2/sites-enabled/000-default.conf:1)
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"

Що це означає: Ефективна конфігурація vhost Apache вказує на конкретний DocumentRoot. Якщо ви редагуєте інший каталог, ви відлагоджуєте не те.

Рішення: Переконайтеся, що ваші права працюють на шляху, який Apache/Nginx дійсно обслуговує, а не на тому, який вам би хотілося.

Завдання 3: Для Nginx — перелічити серверні блоки та корені

cr0x@server:~$ sudo nginx -T 2>/dev/null | egrep -n 'server_name|root ' | head -n 12
34:    server_name example.local;
41:    root /srv/www/example/current/public;
78:    server_name static.example.local;
82:    root /srv/www/static;

Що це означає: У Nginx може бути кілька коренів. 403 на одному hostname може бути проблемою прав в іншому дереві каталогів.

Рішення: Працюйте з правильним коренем для проблемного vhost.

Завдання 4: Прочитати журнал помилок для відповідного відмову

cr0x@server:~$ sudo tail -n 20 /var/log/nginx/error.log
2025/12/30 10:31:42 [error] 19214#19214: *55 open() "/srv/www/example/current/public/index.html" failed (13: Permission denied), client: 10.10.10.23, server: example.local, request: "GET / HTTP/1.1", host: "example.local"

Що це означає: (13: Permission denied) — це файлові права (або політика MAC). Це не директива Nginx deny і не відсутній index‑файл.

Рішення: Продовжуйте перевірки на рівні файлової системи (біти режиму, власність, ACL, AppArmor/SELinux).

Завдання 5: Перевірити права по всьому шляху («namei» — ваш друг)

cr0x@server:~$ namei -l /srv/www/example/current/public/index.html
f: /srv/www/example/current/public/index.html
drwxr-xr-x root     root     /
drwxr-xr-x root     root     srv
drwxr-x--- deploy   deploy   www
drwxr-xr-x deploy   deploy   example
drwxr-xr-x deploy   deploy   current
drwxr-xr-x deploy   deploy   public
-rw-r--r-- deploy   deploy   index.html

Що це означає: Проблема видима: /srv/www має drwxr-x--- належить deploy:deploy. Веб‑сервер не може його трасувати, бо «other» не має execute, і він не в групі.

Рішення: Виправте біт трасування у батьківських каталогах, бажано через членство в групі або ACL — а не шляхом відкриття всього світу.

Завдання 6: Протестувати доступ як користувач веб‑серверу (найчесніший тест)

cr0x@server:~$ sudo -u www-data -s -- bash -lc 'test -r /srv/www/example/current/public/index.html && echo READ_OK || echo READ_NO; test -x /srv/www && echo TRAVERSE_OK || echo TRAVERSE_NO'
READ_NO
TRAVERSE_NO

Що це означає: Користувач воркер не може трасувати /srv/www, отже не може читати нічого під ним.

Рішення: Оберіть стратегію прав (групи, ACL або власність веб‑кореня) і впровадьте її послідовно.

Завдання 7: Перевірити власність і групи, включно з тим, чи в групі є www-data

cr0x@server:~$ id www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Що це означає: www-data не має додаткових груп. Якщо ви плануєте використовувати спільну групу на кшталт web або deploy, потрібно додати її й при необхідності перезапустити сервіси.

Рішення: Вирішіть, додавати чи ні www-data до групи (поширено), або використовувати ACL (чистіше при кількох командах/сервісах).

Завдання 8: Проінспектувати біти режиму каталогів і помітити відсутні біти виконання

cr0x@server:~$ stat -c '%A %U:%G %n' /srv/www /srv/www/example /srv/www/example/current/public
drwxr-x--- deploy:deploy /srv/www
drwxr-xr-x deploy:deploy /srv/www/example
drwxr-xr-x deploy:deploy /srv/www/example/current/public

Що це означає: Верхній рівень — вузька горлечко. Ваш веб‑сервер не повинен писати там, але повинен мати execute (трасування) і файли, що читаються.

Рішення: Відрегулюйте мінімально необхідне: часто це 751 або груповий 750 залежно від моделі.

Завдання 9: Перевірити, чи застосовані ACL (і чи є дефолтні)

cr0x@server:~$ getfacl -p /srv/www/example/current/public | sed -n '1,30p'
# file: /srv/www/example/current/public
# owner: deploy
# group: deploy
user::rwx
group::r-x
other::r-x

Що це означає: Спеціальних записів ACL немає; застосовуються лише класичні біти. Якщо ви очікуєте ACL для www-data, його там немає.

Рішення: Якщо ви обрали стратегію ACL, застосуйте її на правильному рівні каталогу і встановіть дефолтні ACL, щоб нові файли успадковували доступ.

Завдання 10: Виявити «умаск‑саботаж» у середовищі користувача деплою

cr0x@server:~$ sudo -u deploy -s -- bash -lc 'umask; touch /tmp/umask-test-file; stat -c "%A %n" /tmp/umask-test-file; rm -f /tmp/umask-test-file'
0027
-rw-r----- /tmp/umask-test-file

Що це означає: Umask 0027 створює файли, що не є читабельними для світу й не є груповими записуваними. Якщо ваш веб‑сервер покладається на читаність для «other», ви отримаєте періодичні 403 залежно від того, як створювали файли.

Рішення: Узгодьте umask із вашою стратегією прав. Для групового шарінгу часто підходить 0002. Для більш жорстких налаштувань залишайте суворий umask і використовуйте ACL.

Завдання 11: Перевірити, чи не має Apache відмов на рівні конфігурації, що виглядають як права

cr0x@server:~$ sudo apache2ctl -t -D DUMP_RUN_CFG 2>/dev/null | egrep -n 'User|Group|ServerRoot|DocumentRoot' | head
3:User: name="www-data" id=33
4:Group: name="www-data" id=33
16:ServerRoot: "/etc/apache2"
22:DocumentRoot: "/var/www/html"

Що це означає: Ви бачите runtime‑користувача/групу і DocumentRoot. Якщо ваш vhost вказує в інше місце, підтвердіть це з apache2ctl -S. Якщо DocumentRoot правильний, але ви подаєте з /home через alias, ви навмисно створили пазл з правами.

Рішення: Якщо конфігурація і файлова система не погоджуються — виправте конфігурацію перш за все. Права мають відповідати наміру, а не випадкам.

Завдання 12: Для Nginx — перевірити, чи не перевизначено користувача воркера в конфігу

cr0x@server:~$ egrep -n '^\s*user\s+' /etc/nginx/nginx.conf
2:user www-data;

Що це означає: Підтверджує, який користувач читає файли. Якщо це не www-data, підлаштуйте свої тести і план прав відповідно.

Рішення: Тримайте користувача воркера стабільним. Змінювати його «щоб виправити права» — зазвичай показник поганого рішення.

Завдання 13: Перевірити, чи файлову систему змонтовано з опціями, що блокують очікувану поведінку

cr0x@server:~$ findmnt -no TARGET,FSTYPE,OPTIONS /srv
/srv ext4 rw,relatime

Що це означає: Для статичного контенту опції монтування рідко спричиняють 403, але прапорці як noexec важливі, якщо ви подаєте виконувані CGI або запускаєте артефакти збірки на місці.

Рішення: Якщо ви покладаєтесь на виконання в цьому монтажі, зніміть noexec (обережно) або змініть архітектуру (краще: не виконувати з веб‑кореня).

Завдання 14: Перевірити статус AppArmor і чи він може бути причетним

cr0x@server:~$ sudo aa-status | sed -n '1,18p'
apparmor module is loaded.
24 profiles are loaded.
12 profiles are in enforce mode.
   /usr/sbin/nginx
   /usr/sbin/apache2

Що це означає: Якщо профіль у режимі enforce, він може відмовляти в доступі до шляхів поза очікуваними локаціями навіть при коректних Unix‑правах. Журнали вкажуть на відмови AppArmor.

Рішення: Якщо DocumentRoot нестандартний (наприклад, /srv/www), перевірте, чи профіль AppArmor дозволяє цей шлях, або перемістіть корінь у стандартний шлях.

Завдання 15: Підтвердити реальні ефективні права файлу включно з масками ACL

cr0x@server:~$ getfacl -p /srv/www/example/current/public/index.html
# file: /srv/www/example/current/public/index.html
# owner: deploy
# group: deploy
user::rw-
group::r--
other::r--

Що це означає: Файл доступний для читання всім, але це не допоможе, якщо батьківський каталог блокує трасування. Також, якщо ви додаєте ACL пізніше, запис mask може непомітно обмежити ефективні права.

Рішення: Виправте права шляху спочатку; потім переконайтеся, що файли читаються призначеним субʼєктом (група або конкретний користувач через ACL).

Завдання 16: Перевірити, що ваше «виправлення» не створило світовозаписуваних каталогів

cr0x@server:~$ sudo find /srv/www/example/current/public -type d -perm -0002 -maxdepth 3 -print | head

Що це означає: Відсутність виводу означає, що світовозаписуваних каталогів не знайдено (принаймні в межах maxdepth). Вивід би перелічував каталоги, куди будь‑хто може записувати.

Рішення: Якщо щось зʼявилося, приберіть світовий запис і переключіться на груповий запис або контрольований шлях для завантажень.

Три корпоративні міні‑історії з польових боїв

1) Інцидент через хибне припущення: «www-data читає /home, правда?»

У середній компанії команда швидко запустила внутрішню панель. Вона була «тимчасова», отже жила в домашньому каталозі розробника, бо так було швидко. Nginx вказував на /home/alex/dashboard/public. Вона працювала в стейджингу, працювала на одній продуктивній машині — а потім перестала працювати.

Провал був класичним: 403 лише на одній ноді за балансувальником навантаження. Інженер на чергуванні порівняв конфіги Nginx, вони були ідентичні, і почав підозрювати кешування, потім DNS, потім «можливо LB щось робить». Журнал помилок мав Permission denied, але це відмахнули, бо «файли 644».

Різниця була в одному батьківському каталозі: на одній ноді /home/alex мав 750. На іншій — 755. Дашборд «працював» тільки там, де домашні каталоги були світотрейськими. Безпека нещодавно посилила дефолтні права домашніх каталогів через /etc/adduser.conf, і ніхто не звʼязав цю зміну з веб‑додатком у чиємусь домі.

Виправленням не стало послаблення безпеки домашніх каталогів. Виправленням було перемістити дашборд у /srv/www з правильною групою і передбачуваною власністю, потім оновити AppArmor для нового шляху. Після цього 403 зник і не повертався.

Справжній урок: ніколи не привʼязуйте production‑веб‑корені до шляхів, призначених для людей. Люди логіняться. Сервіси не повинні залежати від політики домашніх каталогів.

2) Оптимізація, що обернулася проти: «Давайте все закриємо 750»

Інша організація мала аудит безпеки, що всіх налякав. Хтось запропонував «затягнути» права у веб‑коренях, встановивши каталоги в 750 і файли в 640, належність deploy:deploy. Ідея була слушною: не дозволяти «other» читати код.

Їх запустили рекурсивним chmod у кроці деплою. В одному середовищі це пройшло, бо їх deploy‑система запускала Nginx як deploy (не рекомендовано, але це маскувало проблему). В проді Nginx працював як www-data, і крок деплою відразу забрав біти трансляції «other» на батьківському каталозі. Наступний запит повернув 403.

Потім зʼявилося панічне виправлення: chmod -R 755, щоб відновити сервіс. Воно дійсно повернуло сайт. Воно також зробило всі каталоги світотрейськими назад, підірвавши мету аудиту, і створило брудний розбіжність між нодами, бо не всі машини були виправлені однаково.

Стійке рішення полягало в реалізації моделі спільної групи з setgid‑каталогами і налаштуванні deploy‑інструментів для створення артефактів з груповою читабельністю. Вони дійшли до налаштувань 2750/2640 у деяких місцях, але завжди з правильною групою та передбачуваною спадковістю.

Урок: «закрити все» — не план. Модель прав — це план. Рекурсивний chmod — бульдозер у кімнаті, повній скла.

3) Нудна, але правильна практика, що врятувала день: «namei у рукбуку»

Команда платежів запускала кілька екземплярів Nginx для статичних ассетів чекауту. Все було тихо. Потім деплой додав новий рівень директорій: /srv/www/payments/releases/2025-12-30/public. Додаток працював на більшості нод, але одна повертала 403. Деплой виглядав ідентично. На чергуванні панував знайомий страх: переривчасто, лише одна нода, перед піковим трафіком.

Ось що було нудним — у доброму сенсі. У команди був крок у рукбуку: «Запустіть namei -l на точному шляху файлу». Без дебатів, без здогадок. Вони виконали його і одразу побачили, що /srv/www/payments мала неправильну групу на тій ноді, ймовірно через ручний хотфік місяців тому.

Вони виправили групову власність, переконалися, що setgid встановлено, і перезапустили свою перевірку «permission drift», яка порівнює критичні режими каталогів по кластеру. Ніякого рекурсивного chmod, ніякої паніки 777, ніякої винаходу користувача веб‑сервера заново.

Пізніше у post‑incident review найціннішим елементом був не інструмент. Це була соціальна дія: вони тримали рукбук коротким, обовʼязковим і конкретним. Нудні процедури перемагають, бо прибирають спокусу імпровізувати о 2‑й ночі.

Типові помилки: симптом → коренева причина → виправлення

Це те, що я регулярно бачу на Debian/Ubuntu‑флотах. Симптоми вводять в оману; виправлення — ні.

1) Симптом: 403, журнал каже «Permission denied (13)»

Коренева причина: відсутній біт виконання на батьківському каталозі (немає трасування).

Виправлення: переконайтеся, що кожен каталог на шляху трасується для принципалу веб‑серверу (група або ACL). Використайте namei -l, щоб знайти перший блокуючий каталог.

2) Симптом: Файли 644, каталоги 755, але все одно 403

Коренева причина: відмова політики AppArmor/SELinux або конфігурація Apache відмовляє у доступі.

Виправлення: перевірте журнал ядра/аудиту на відмови AppArmor і правила Apache Require. Не робіть chmod у відповідь на відмову політики.

3) Симптом: 403 після деплою; фіксується перезапуском, потім повертається

Коренева причина: деплой створив нові каталоги/файли з обмежувальним umask або неправильною групою; старий контент мав правильні права.

Виправлення: забезпечте umask у середовищі деплою, встановіть setgid на каталогах або використовуйте дефолтні ACL, щоб новий контент успадковував доступ.

4) Симптом: 403 на симлінк‑шляху; прямий шлях працює

Коренева причина: Apache забороняє слідування симлінкам, або права цілі відрізняються.

Виправлення: переконайтеся, що конфіг дозволяє симлінки там, де це потрібно (Options FollowSymLinks) і що цільовий шлях читабельний/трасований.

5) Симптом: 403 на URL каталогу, URL файлів працюють

Коренева причина: відсутній index‑файл і autoindex вимкнено, або відсутній біт виконання на каталозі.

Виправлення: додайте index‑файл, навмисно ввімкніть індексацію (рідко потрібно), або виправте права каталогу.

6) Симптом: 403 лише на одній ноді в кластері

Коренева причина: дрейф прав (ручний хотфік), інший umask, інше членство в групах, або інша версія політики MAC.

Виправлення: порівняйте виводи stat/getfacl між нодами і проведіть аудит членства в групах і статусу політик.

7) Симптом: Все працює, поки ви не додасте нового члена команди

Коренева причина: права залежать від конкретного користувача‑власника; немає стратегії спільної групи або ACL.

Виправлення: перейдіть до групової власності з setgid‑каталогами або до ACL. Уникайте «власності тим, хто останнім деплойнув».

8) Симптом: Ви «виправили» chmod 777 і тепер дзвонить відділ безпеки

Коренева причина: відсутня модель прав; аварійне виправлення стало політикою.

Виправлення: відкотіть світовий запис, впровадьте групову або ACL‑модель, і відокремте записувані директорії (uploads/cache) від коду/статичних ресурсів.

Контрольні списки / покроковий план

План A: Безпечно виправити живий 403 (мінімальна зміна, максимальна впевненість)

  1. Заберіть точний невдалий URL і зіставте його з файловим шляхом (з конфігурації vhost і журналів).
  2. Прочитайте рядок журналу помилок для цього запиту; підтвердіть, чи це (13: Permission denied) чи відмова на рівні конфігурації.
  3. Запустіть namei -l на точному шляху і знайдіть перший каталог без трасування для користувача веб‑серверу.
  4. Виконайте тест доступу від імені користувача веб‑серверу з sudo -u www-data, щоб підтвердити режим помилки.
  5. Застосуйте найменшу необхідну зміну прав на блокуючий каталог (переважно груповий execute або ACL‑execute).
  6. Повторно протестуйте доступ від імені користувача веб‑серверу. Потім протестуйте через HTTP.
  7. Скануйте на наявність випадкових світовозаписуваних каталогів і відкотіть будь‑які такі зміни.
  8. Запишіть, що змінилося і чому; якщо ви не можете це пояснити — ви не вирішили проблему, ви просто пощастило.

План B: Стандартизувати веб‑корінь для команди (модель групи)

  1. Створіть присвячену групу (web) для тих, хто «може читати веб‑контент».
  2. Додайте www-data і deploy‑користувачів до web.
  3. Встановіть власність веб‑кореня як deploy:web (або root:web для жорсткішого контролю змін).
  4. Встановіть режими каталогів 2775 і файлів 664 там, де це доречно.
  5. Встановіть umask деплою в 0002, щоб групова читабельність була послідовною.
  6. Відокремте записувані каталоги (uploads, cache) із жорсткішим контролем і явною конфігурацією додатку.
  7. Додайте перевірку дрейфу: періодичну задачу або правило управління конфігурацією для верифікації власності/режимів.

План C: Стандартизувати з ACL (коли кілька груп — біль)

  1. Залишайте власність за deploy/app‑користувачем; не боріться зі структурою організації в /etc/group.
  2. Застосуйте ACL, що надає www-data rx на каталогах і r на файлах.
  3. Застосуйте дефолтні ACL на каталогах, щоб нові файли успадковували правило.
  4. Перевірте, щоб маска ACL не випадково зменшувала ефективні права.
  5. Документуйте рішення про ACL у рукбуку, щоб ніхто «не прибрав» це згодом.

Конкретні приклади імплементації (використайте один, не всі)

Приклад 1: Модель спільної групи під /srv/www/example

cr0x@server:~$ sudo groupadd -f web
cr0x@server:~$ sudo usermod -aG web www-data
cr0x@server:~$ sudo usermod -aG web deploy
cr0x@server:~$ sudo chown -R deploy:web /srv/www/example
cr0x@server:~$ sudo find /srv/www/example -type d -exec chmod 2775 {} +
cr0x@server:~$ sudo find /srv/www/example -type f -exec chmod 0664 {} +

Що означає вивід: Ці команди на більшості систем мовчазні при успіху. Ефект: каталоги успадковують групу web і є групово‑трасованими.

Рішення: Якщо ви використовуєте цю модель, переконайтеся, що процеси деплою створюють файли з груповою читабельністю; інакше ви будете постійно «ліпити» права після кожного деплою.

Приклад 2: Модель ACL, щоб надати www-data читання/трасування без зміни груп

cr0x@server:~$ sudo setfacl -R -m u:www-data:rx /srv/www/example/current/public
cr0x@server:~$ sudo find /srv/www/example/current/public -type f -exec setfacl -m u:www-data:r {} +
cr0x@server:~$ sudo setfacl -m d:u:www-data:rx /srv/www/example/current/public
cr0x@server:~$ sudo getfacl -p /srv/www/example/current/public | sed -n '1,25p'
# file: /srv/www/example/current/public
# owner: deploy
# group: deploy
user::rwx
user:www-data:r-x
group::r-x
mask::r-x
other::r-x
default:user::rwx
default:user:www-data:r-x
default:group::r-x
default:mask::r-x
default:other::r-x

Що це означає: ACL дає www-data права трасування/читання. Дефолтний ACL забезпечує спадковість для нових директорій і файлів.

Рішення: Використовуйте ACL, коли групова власність політично або операційно нестабільна. Але привʼяжіться до неї; змішані моделі швидко стають дивними.

Приклад 3: Виправити лише біт трасування на блокуючому каталозі (хірургічне живе виправлення)

cr0x@server:~$ sudo chmod o+x /srv/www
cr0x@server:~$ namei -l /srv/www/example/current/public/index.html | sed -n '1,6p'
f: /srv/www/example/current/public/index.html
drwxr-xr-x root     root     /
drwxr-xr-x root     root     srv
drwxr-x--x deploy   deploy   www
drwxr-xr-x deploy   deploy   example

Що це означає: o+x на каталозі дозволяє трасування, але не перелік. Це може бути розумним компромісом, якщо ви не хочете, щоб світ переліковував вміст каталогу.

Рішення: Це підходить для швидкого відновлення, але розгляньте перехід на групову/ACL‑модель для довгострокового порядку.

Питання та відповіді

1) Чому відсутній біт виконання на каталозі викликає 403?

Бо без біту виконання на каталозі процес не може в нього зайти — навіть якщо файл всередині доступний для читання. Веб‑сервер не може дістатися до інод‑інформації, тому отримує помилку Permission denied.

2) Чи варто робити веб‑корінь власністю www-data?

Зазвичай ні. Якщо веб‑сервер може записувати свій власний контент, компрометація перетворюється на стійку модифікацію. Віддавайте перевагу deploy‑користувачу як власнику контенту, а веб‑серверу — лише права читання через групу або ACL. Робіть записуваними лише конкретні каталоги для завантажень/кешу і тримайте їх поза шляхами виконуваного коду.

3) Чи завжди безпечно ставити chmod 755 на каталоги?

Це поширене і часто прийнятне для публічного статичного контенту. Але воно дає світовий доступ для трасування і читання. У корпоративних або мульти‑тенантних системах краще використовувати групи або ACL, особливо для коду, шаблонів або конфігів, які не повинні бути доступні всім.

4) Що краще: групи чи ACL?

Групи простіші і більш прозорі. ACL точніші і дозволяють уникнути звалення всіх у одну групу. Якщо у вас одна команда і одна пайплайн деплою — групи чудові. Якщо кілька команд, CI‑раннери та спільна інфраструктура — ACL допоможе зберегти порядок.

5) Чому 403 зʼявляються лише після деплою?

Нові файли успадковують права залежно від процесу створення: umask, дефолтні ACL і setgid каталогу мають значення. Старі файли могли бути доступними, тому проблема виглядає переривчастою. Виправляйте політику створення файлів, а не лише поточні файли.

6) Як перевірити, чи це AppArmor, а не Unix‑права?

Почніть з журналу помилок: якщо він каже permission denied, але біти виглядають коректно — перевірте статус AppArmor і його журнали. На Ubuntu AppArmor часто в режимі enforce для веб‑серверів. Відмова буде зафіксована системою аудиту і повʼязана з профілем.

7) Чому «other execute but not read» на каталогах допомагає?

o+x дозволяє трасування без дозволу на перелік каталогу. Тобто той, хто не знає імен файлів, не зможе їх просто перелікувати. Це не заміна для належного контролю доступу, але зменшує випадкове розкриття структури каталогу.

8) Чи треба перезапускати Apache/Nginx після зміни прав файлової системи?

Ні для змін біта режиму і власності; ядро застосовує їх негайно. Можливо, потрібно перезапустити, якщо ви змінили членство в групах для працюючого користувача воркера — процеси не підхоплюють нові додаткові групи автоматично.

9) Чому додавання www-data до групи не працює негайно?

Бо існуючі воркер‑процеси мають список груп, встановлений при старті. Після зміни членства в групі перезавантажте/перезапустіть сервіс, щоб нові воркери успадкували оновлений список груп.

10) Який найбезпечніший розподіл для записуваних шляхів, як uploads?

Розміщуйте записувані каталоги поза деревом коду/статичних ресурсів, давайте додатку лише ті права запису, що потрібні, і переконайтеся, що веб‑сервер не виконує контент із цих шляхів. Якщо треба подавати завантаження, подавайте їх як статичні файли, а не як виконувані.

Висновок: наступні кроки, що не покусають вас пізніше

403 через права — не містика. Вони детерміновані: десь по шляху користувач веб‑серверу не може трасувати або читати. Виправляти це не треба через 777. Потрібна модель власності, яку ви зможете пояснити сонному колезі о 3‑й ранку.

Зробіть наступне:

  1. Додайте namei -l і «тест як www-data» у свій рукбук. Зробіть це обовʼязковим кроком.
  2. Оберіть шаблон прав (спільна група з setgid або ACL) і застосуйте його для кожного сайту. Послідовність — це функція безпеки.
  3. Відокремте записувані шляхи від веб‑кореня. Ваш майбутній інженер з реагування на інциденти пошле вам листа подяки.
  4. Аудиторськи перевірте на світовозаписувані каталоги і приберіть їх. Якщо щось ламається — це доказ, що аудит був потрібний.

Якщо ви розглядатимете права як архітектуру, а не як прибирання, 403 стануть швидким фіксом, а не постійною персонажкою у вашому графіку чергувань.

← Попередня
Debian 13: Сплески запису на диск — налаштуйте vm.dirty без ризику втрати даних (випадок №45)
Наступна →
Debian 13: Виправлення «Забагато перенаправлень» в Nginx — корекція канонічних і HTTPS циклів (випадок №71)

Залишити коментар