Дозволи веб-кореня на Debian/Ubuntu: припиніть 403 без chmod 777 (Випадок №9)

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

403 — це та помилка, яка змушує команди сперечатися з упевненістю. «Це nginx.» «Ні, це Apache.»
«Це SELinux.» (На Debian. Звісно.) Тим часом сторінка недоступна, черговий не спить, і хтось
майже натискає chmod -R 777.

Цей випадок про те, як припиняти 403 нудним, але правильним способом: розуміти семантику прав Linux для
веб-кореня на Debian/Ubuntu, швидко діагностувати й встановити модель, яка зберігає продакшн безпечним і
придатним для деплою. Мета проста: веб-сервер читає те, що йому потрібно, записує лише в дозволених місцях,
і ніхто не мусить «просто спробувати 777».

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

Коли ви бачите 403, ви полюєте за одним із трьох вузьких місць: конфігурація веб-сервера відмовляє,
файлова система відмовляє, або проміжний шар (chroot, маунт контейнера, AppArmor) блокує. Не гадати. Доведіть.

Перше: підтвердьте, який шар відмовляє

  • Перевірте журнал помилок веб-сервера на предмет «permission denied», «access forbidden by rule» або «directory index forbidden».
  • Перевірте відображення запиту: який шлях на диску він намагається віддати?
  • Відтворіть локально під ефективним користувачем веб-сервера (www-data за замовчуванням на Debian/Ubuntu).

Друге: обходьте шлях, а не тільки файл

Більшість збоїв з правами не на файлі. Вони на батьківській директорії, яку ви забули врахувати.
Кожна директорія в шляху потребує біт виконання для переходу. Не «читання». Саме виконання.

Третє: перевірте «політичні» блокувальники

  • Профілі AppArmor (Ubuntu) можуть забороняти читання, навіть якщо бітові права виглядають нормальними.
  • Маунти томів у контейнерах можуть змінювати власність/права несподівано.
  • Симлінки можуть вказувати за межі дозволених коренів (Apache) або в директорії без права переходу.

Якщо ви виконаєте ці три кроки, перестанете ставитися до 403 як до надприродного явища.

Модель прав, яка справді працює (і не закінчується 777)

На Debian/Ubuntu процеси веб-сервера зазвичай працюють як www-data.
Ваш користувач деплою зазвичай deploy або CI-ранер. Помилка — намагатися задовольнити одні права
для «деплой може писати все» й «веб-сервер читає все», переводячи весь каталог у публічний туалет.

Ось розумна модель, яку я рекомендую більшості команд:

Модель A: веб-корінь тільки для читання, явні записувані піддиректорії

  • Веб-корінь (/var/www/site) належить власнику деплою (або root), читається www-data, не записується ним.
  • Записувані директорії виділені окремо: var/, storage/, uploads/, кеші, сокети за потреби.
  • Записувані директорії належать www-data (або мають ACL, що дає www-data запис), і нічого зайвого не має права запису.

Це дає передбачувану безпеку: компрометація веб-сервера не дає автоматичної можливості змінювати код програми.
Сервер все ще може записувати завантаження користувачів, бо ви це дозволили. Не через паніку.

Модель B: спільна група для деплоїв (коли потрібні записи)

  • Створіть групу (наприклад web), яка включає користувача деплою та www-data.
  • Встановіть групову власність веб-кореня на web.
  • Використовуйте chmod 2775 (setgid) на директоріях, щоб нові файли успадковували групу.
  • Встановіть розумний umask у процесі деплою так, щоб файли були доступні для групи (і записувані для групи, якщо потрібно).

Модель B гнучкіша, але має гострі кути: неправильний umask — і у вас лотерея з правами.
Якщо ви її обираєте, робіть це дисципліновано й з перевірками.

Рівно одна цитата, бо решта мають бути логами: «Надія — не стратегія.» — Gene Kranz

Жарт №1: chmod 777 — як полагодити розбите вікно, знявши всю стіну. Формально немає скла, яке можна зламати.

Факти та історичний контекст (щоб дивні моменти стали зрозумілими)

  • Біт виконання на директоріях спочатку означав «пошук»: ви можете перейти в директорію та отримати доступ до інодів, якщо вже знаєте імена.
  • 403 vs 404 часто — свідомий вибір: багато серверів повертають 404 на проблеми з правами, щоб не розкривати наявність вмісту.
  • www-data як конвенція стала поширеною в Debian-похідних системах, бо пакети потребували передбачуваного безлогінового користувача для демонів.
  • Історична позиція Apache щодо симлінків — консервативна: слідування симлінкам та дозволи на override неодноразово були джерелом інцидентів.
  • Nginx популяризував шаблони «try_files», які можуть змінювати фактичний шлях доступу, що плутає при налагодженні прав, якщо ви дивитесь не туди.
  • POSIX ACL були введені, щоб виразити дозволи за межами власник/група/інші, керуючись потребами підприємств і багатокористувацьких систем.
  • Setgid на директоріях — старомодний, але все ще корисний: це один з небагатьох переносних способів примусити наслідування групи без залежності від ACL за замовчуванням.
  • За замовчуванням umask походить з епохи багатокористувацьких систем: дефолт записуваності для групи вважався ризиковим навіть тоді.

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

Це «зробіть це зараз» завдання. Кожне містить, що означає вивід і яке рішення треба прийняти.
Використовуйте їх у порядку, коли ви під тиском, а потім ще раз при остаточному виправленні системи.

Завдання 1: підтвердьте користувача сервісу та ідентичність воркерів

cr0x@server:~$ ps -o user,group,pid,cmd -C nginx | head
USER     GROUP    PID CMD
root     root    1021 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
www-data www-data 1022 nginx: worker process
www-data www-data 1023 nginx: worker process

Значення: Воркери працюють як www-data. Це ідентичність, якій потрібен доступ до файлової системи.

Рішення: Всі тести «чи він може читати це?» потрібно виконувати як www-data.

Завдання 2: отримайте шлях на диску з конфігурації веб-сервера

cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '/server_name example.com/,+40p' | sed -n '1,40p'
server {
    listen 80;
    server_name example.com;
    root /var/www/example/current/public;
    index index.html index.php;
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}

Значення: Ефективний root — /var/www/example/current/public, а не репозиторій, на який ви дивилися.

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

Завдання 3: перевірте журнал помилок на фактичну формулювання відмови

cr0x@server:~$ sudo tail -n 8 /var/log/nginx/error.log
2025/12/30 10:12:14 [error] 1022#1022: *884 open() "/var/www/example/current/public/index.html" failed (13: Permission denied), client: 203.0.113.10, server: example.com, request: "GET / HTTP/1.1", host: "example.com"

Значення: Помилка 13 — це відмова на рівні ядра. Це не правило nginx, що забороняє доступ.

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

Завдання 4: пройдіть повний шлях прав (нудна частина, яка це фіксує)

cr0x@server:~$ namei -l /var/www/example/current/public/index.html
f: /var/www/example/current/public/index.html
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwxr-x--- deploy web  example
lrwxrwxrwx deploy web  current -> releases/20251230T1005
drwxr-s--- deploy web  releases
drwxr-s--- deploy web  20251230T1005
drwxr-s--- deploy web  public
-rw-r----- deploy web  index.html

Значення: /var/www/exampledrwxr-x---. «Інші» не мають біта виконання, і www-data поки що не є ні власником, ні в групі web (поки).

Рішення: Або додайте www-data до групи web (Модель B), або змініть права переходу, щоб www-data міг проходити, але не писати (Модель A).

Завдання 5: перевірте членство в групах (і пам’ятайте перезапустити воркерів, якщо треба)

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

Значення: www-data не в групі web. Доступ на основі групи не працюватиме, поки ви його не додасте.

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

Завдання 6: протестуйте доступ як користувач веба (без гадань)

cr0x@server:~$ sudo -u www-data test -r /var/www/example/current/public/index.html; echo $?
1

Значення: Код виходу 1 означає «не читається». Це відповідає журналу.

Рішення: Виправте права на директорії/файли так, щоб тест повернув 0.

Завдання 7: перевірте біт виконання директорії окремо (поширена пастка шляху)

cr0x@server:~$ sudo -u www-data ls -ld /var/www/example /var/www/example/current /var/www/example/current/public
ls: cannot access '/var/www/example/current': Permission denied
drwxr-x--- 5 deploy web 4096 Dec 30 10:05 /var/www/example

Значення: Відмова відбувається при переході /var/www/example. Навіть якщо файли читабельні для всіх, ви не дійдете до них.

Рішення: Надати право переходу (виконання) для www-data через членство в групі, ACL або біт «інші», де це прийнятно.

Завдання 8: безпечно впровадити Модель B (спільна група + setgid)

cr0x@server:~$ sudo groupadd -f web
cr0x@server:~$ sudo usermod -aG web www-data
cr0x@server:~$ sudo usermod -aG web deploy
cr0x@server:~$ sudo chgrp -R web /var/www/example
cr0x@server:~$ sudo find /var/www/example -type d -exec chmod 2775 {} \;
cr0x@server:~$ sudo find /var/www/example -type f -exec chmod 0644 {} \;

Значення: Директорії стають з наслідуванням групи (2 у 2775), читаються/прохідні групою. Файли — читаються групою.

Рішення: Перезавантажте nginx, щоб воркери підхопили нову додаткову групу; потім повторно протестуйте доступ як www-data.

Завдання 9: перезавантажте сервіс, щоб підібрати нові групи (не пропускайте це)

cr0x@server:~$ sudo systemctl reload nginx
cr0x@server:~$ ps -o pid,user,group,cmd -C nginx | head -n 4
  PID USER     GROUP    CMD
 1021 root     root     nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
 2110 www-data www-data nginx: worker process
 2111 www-data www-data nginx: worker process

Значення: Основна група залишається www-data (це нормально). Додаткові групи тут не відображаються.

Рішення: Перевірте додаткові групи, протестувавши доступ ще раз; якщо все ще не працює, перезапустіть замість reload.

Завдання 10: повторно протестуйте доступ як www-data після зміни

cr0x@server:~$ sudo -u www-data test -r /var/www/example/current/public/index.html; echo $?
0

Значення: Доступ для читання тепер працює.

Рішення: Переконайтеся, що HTTP-відповідь 200 і що ви випадково не зробили занадто широкими записувані області.

Завдання 11: перевірте umask, що може зіпсувати майбутні деплои

cr0x@server:~$ sudo -u deploy sh -lc 'umask; touch /var/www/example/current/public/.umask-test && stat -c "%a %U:%G %n" /var/www/example/current/public/.umask-test'
0022
644 deploy:web /var/www/example/current/public/.umask-test

Значення: З umask 0022 нові файли мають 0644. Це підходить для веб-кореня тільки для читання. Якщо потрібен запис для групи, це зруйнує модель.

Рішення: Віддавайте перевагу веб-кореню тільки для читання. Якщо вам справді потрібен груповий запис, встановіть umask 0002 у середовищі деплою й доведіть, що це зберігається.

Завдання 12: визначте записувані директорії (і заблокуйте все інше)

cr0x@server:~$ sudo find /var/www/example/current -maxdepth 3 -type d -name storage -o -name uploads -o -name cache
/var/www/example/current/storage
/var/www/example/current/public/uploads
/var/www/example/current/var/cache

Значення: Це типові шляхи, куди додаток потребує запису.

Рішення: Дозвольте запис лише в цих директоріях для www-data. Все інше має бути тільки для читання веб-серверу.

Завдання 13: встановіть явну власність для записуваних каталогів (гібрид Модель A)

cr0x@server:~$ sudo chown -R www-data:www-data /var/www/example/current/storage /var/www/example/current/public/uploads /var/www/example/current/var/cache
cr0x@server:~$ sudo find /var/www/example/current/storage /var/www/example/current/public/uploads /var/www/example/current/var/cache -type d -exec chmod 0750 {} \;
cr0x@server:~$ sudo find /var/www/example/current/storage /var/www/example/current/public/uploads /var/www/example/current/var/cache -type f -exec chmod 0640 {} \;

Значення: Додаток може писати там, де потрібно, але не може змінювати код в інших місцях.

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

Завдання 14: перевірте, що немає випадкового світового запису

cr0x@server:~$ sudo find /var/www/example -xdev -perm -0002 -type f -o -perm -0002 -type d | head

Значення: Відсутність виводу — добре. Якщо є вивід — десь є світовий запис, що зазвичай означає інцидент у очікуванні.

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

Завдання 15: діагностуйте AppArmor- відмови на Ubuntu (коли біти в порядку)

cr0x@server:~$ sudo journalctl -k -g DENIED -n 5
Dec 30 10:14:02 server kernel: [12345.678901] audit: type=1400 apparmor="DENIED" operation="open" profile="/usr/sbin/nginx" name="/srv/apps/example/current/public/index.html" pid=2110 comm="nginx" requested_mask="r" denied_mask="r" fsuid=33 ouid=1001

Значення: Ядро відмовило у читанні через політику AppArmor. Бітові права не проблема.

Рішення: Налаштуйте профіль AppArmor (або перемістіть веб-корінь у дозволений шлях). Не продовжуйте chmod — це не допоможе.

Завдання 16: впіймайте сюрпризи зі симлінками (особливо Apache)

cr0x@server:~$ readlink -f /var/www/example/current/public
/var/www/example/releases/20251230T1005/public

Значення: Ви віддаєте з каталогу релізів. Права мають бути узгоджені для кожного нового шляху релізу.

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

Жарт №2: Права Unix прості — поки не потрібно, щоб вони були правильними.

Nginx vs Apache: різні за замовчуванням, однакова реальність файлової системи

Режими відмов Nginx, що виглядають як проблеми з правами

Nginx зазвичай прямо показує відмови файлової системи: ви побачите «(13: Permission denied)» у журналі помилок.
Але конфігурація все одно може створити симптоми, ніби це права:

  • try_files змінює ціль: ви просите /, але nginx пробує /index.php. Права мають працювати і для запасного варіанту.
  • autoindex вимкнений + відсутній індексний файл: можете отримати 403, якщо листинг директорії заборонений і індекс відсутній.
  • помилки alias vs root: неправильна конкатенація шляху може вказати кудись, куди ви не дивилися щодо прав.

Режими відмов Apache, що виглядають як проблеми з правами

Apache має свій бренд «403 через політику», навіть коли файлова система в порядку:

  • Директиви Require: суворий Require all denied, успадкований від батьківського контексту, охоче 403 усе.
  • Options FollowSymLinks: симлінки можуть відкидатися, породжуючи 403, хоча ціль читається.
  • .htaccess overrides: відсутній AllowOverride або несподівані правила можуть забороняти запити так, що це нагадує проблеми з доступом до файлів.

Трюк — припинити розглядати «403» як одну проблему. Це результат. Джерело — у логах.

ACL: доросла альтернатива груповому запису всього

Бітові права Unix — це трьохсмугова дорога: власник, група, інші. ACL дають бічні вулиці:
«www-data може читати це дерево, deploy може писати, CI може читати, і ніхто інший не чіпає». Саме те, що часто потрібно для веб-коренів.

Коли ACL виправдані

  • У вас кілька ідентичностей деплою (людина, CI-ранер, автоматика) і ви не хочете великої спільної групи.
  • Ви хочете, щоб www-data мав тільки перехід і читання, але ніколи запис, навіть якщо групова власність зміниться.
  • Ви хочете, щоб нові файли автоматично мали правильні дозволи без залежності від дисципліни umask.

Дві ACL-правила, що роблять більшість роботи

Встановіть доступну ACL (що діє зараз) і дефолтну ACL (що спадкується для нових файлів) на директоріях.
Приклад: дайте www-data читати/проходити веб-корінь, а deploy — писати, не відкриваючи доступ «іншим».

cr0x@server:~$ sudo setfacl -R -m u:www-data:rx /var/www/example
cr0x@server:~$ sudo setfacl -R -m d:u:www-data:rx /var/www/example
cr0x@server:~$ getfacl -p /var/www/example | sed -n '1,20p'
# file: /var/www/example
# owner: deploy
# group: web
user::rwx
user:www-data:r-x
group::r-x
mask::r-x
other::---
default:user::rwx
default:user:www-data:r-x
default:group::r-x
default:mask::r-x
default:other::---

Значення: www-data отримує читання+прохід. Нові директорії/файли успадковують дефолтну ACL-запис, тож нові релізи не будуть випадково 403.

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

Підводний камінь: маска ACL

ACL мають запис mask, який обмежує ефективні дозволи для іменованих користувачів/груп. Люди забувають про маску,
потім дивуються, чому їхнє уважно встановлене rwx фактично не застосовується.

cr0x@server:~$ sudo setfacl -m u:www-data:rwx /var/www/example/current/storage
cr0x@server:~$ getfacl -p /var/www/example/current/storage | grep -E 'www-data|mask'
user:www-data:rwx
mask::rwx

Значення: Маска — дозволяюча. Якщо б вона була r-x, ефективні дозволи були б зменшені.

Рішення: Коли ACL «не працюють», перевірте маску, перш ніж переписувати половину політики файлової системи.

Деплои: як не дати CI/CD знову зламати права

Права — це не те, що ви виправляєте раз і забуваєте. Ваш процес деплою може постійно їх ламати,
з ентузіазмом малюка в кімнаті з маркерами.

Звідки походить дрейф прав

  • Нові директорії релізів, створені з іншим umask або контекстом користувача.
  • Розпаковування артефактів, що зберігають моди з часу збірки, які не підходять для продакшну.
  • Збірки контейнерів, що копіюють файли як root і залишають їх root-owned на розділених томах.
  • Горячі виправлення, зроблені на сервері як root опівночі (ви знаєте, хто ви є).

Нормалізуйте права як крок деплою

Це нудно, але правильно. Додайте крок у пайплайн деплою, який примушує власність/режими/ACL
в релізній директорії перед перемиканням симлінка current.

cr0x@server:~$ sudo -u deploy sh -lc '
release=/var/www/example/releases/20251230T1005
find "$release" -type d -exec chmod 2755 {} \;
find "$release" -type f -exec chmod 0644 {} \;
'

Значення: Директорії прохідні; файли читаються. Setgid підтримує стабільність спадкування групи.

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

Запобігайте потраплянню артефактів, що належать root, у веб-корінь

Якщо ви деплойте з sudo, будьте явними щодо контексту користувача при записі файлів.
«Я запустив це через sudo» — ось як ви отримуєте веб-корінь, який читає лише root.

cr0x@server:~$ sudo -u deploy tar -xf /tmp/release.tar -C /var/www/example/releases/20251230T1005
cr0x@server:~$ stat -c "%U:%G %a %n" /var/www/example/releases/20251230T1005/public/index.html
deploy:web 644 /var/www/example/releases/20251230T1005/public/index.html

Значення: Файли належать ідентичності деплою, а не root. Це бажано.

Рішення: Зробіть «хто записує файли» першокласним параметром деплою, а не випадковістю.

Три корпоративні міні-історії з окопів прав

1) Інцидент через хибне припущення: «Файл читається — значить сайт читається»

Середня компанія мала Debian-флот із nginx перед статичним сайтом. Інженер
змінив користувача деплою та звузив права на /var/www по всій ієрархії, прагнучи найменших привілеїв.
Вони перевірили кілька файлів. Усі були 0644. Вони відправили зміни.

За п’ять хвилин сайт повернув 403. На чергуванні спочатку звинуватили дрейф конфігурації nginx, бо
головний файл явно був читаємий 644. Лідер інциденту сказав «це не може бути правами, файл 644».

Насправді проблема була на рівні вище: директорія стала 750 і належала користувачу деплою та приватній групі.
Nginx не міг пройти туди, бо «виконання на директоріях» не входило в їхню ментальну модель.
Журнал помилок справді сказав «permission denied», але люди читали те, що очікували побачити.

Виправлення було невелике — надати прохід через членство в групі і setgid — але урок закріпився:
перевірки прав мають охоплювати весь шлях. Постінцидентна дія була не «будь обережним».
А «додати namei у ранубук і тестувати як www-data».

2) Оптимізація, що обернулася проти: «Нехай веб-сервер пише результати збірки»

Інша команда хотіла пришвидшити деплои PHP-додатку. Хтось запропонував збирати артефакти на сервері,
щоб уникнути пересилання великих файлів. Найшвидший шлях — запускати збірку під тією самою
ідентичністю, що і сервіс. «Якщо збірка запуститься як www-data, вона завжди матиме доступ.»

Це спрацювало. Час деплою зменшився. Графіки стали гарніші. Потім зависла залежність і їхній процес
додатку отримав можливість писати в дерево коду. Це перетворило читащу компрометацію на самомодифіковану:
веб-запити могли скидати файли в каталог, що обслуговується.

Команда реагування локалізувала проблему швидко, але тиждень пройшов у перевірках змін на диску.
«Оптимізація» стерла межу безпеки, яка була безкоштовною.

Вони повернули збірки поза продакшном, зробили веб-корінь тільки для читання для обслуговуючого ідентифікатора,
і явно дозволили запис лише в upload/cache шляхи. Час деплою виріс трохи; ризик — суттєво впав.

3) Нудна, але правильна практика, що врятувала ситуацію: дефолтні ACL на батьках релізів

Корпоративна команда вела кілька сайтів під /srv/apps з layout-ом релізів типу Capistrano. CI створював
новий каталог релізу для кожного деплою. Згодом з’являлася низькоінтенсивна проблема «іноді 403 після деплою».
Не завжди. Досить, щоб дратувати й розхитати довіру до автоматизації.

Корінь проблеми — дрейф umask між ранерами. Один ранер створював 0750, інший — 0755.
Іноді Apache міг пройти; іноді — ні. Інженери намагалися стандартизувати через крок chmod,
але він запускався після перемикання симлінка. Користувачі бачили короткий простій під час деплою. Їм це не подобалося.

Виправлення було простим. Вони встановили дефолтну ACL на батьківській директорії релізів, що дає
веб-серверу rx, і ввели примусову нормалізацію прав до переключення. Нові дерева релізів успадковували
коректний доступ, і деплой більше не залежав від настрою ранера.

Ніщо в цьому не було захопливим. Воно також зробило сторінку «403 після деплою» вимерлою. Іноді нудне — це фіча.

Поширені помилки: симптом → корінь проблеми → виправлення

1) Симптом: 403 для директорії, але файли працюють при прямому запиті

Корінь проблеми: Відсутній індексний файл, листинг директорії вимкнений або фронт-контролер фреймворку недосяжний.

Виправлення: Переконайтеся, що існує індекс (index.html / index.php), перевірте директиву index nginx і права на запасну ціль у try_files.

2) Симптом: 403 з «(13: Permission denied)» у журналах nginx/apache

Корінь проблеми: Файлова система відмовляє. Часто батьківська директорія не має біта виконання (traverse) для www-data.

Виправлення: Використайте namei -l на повному шляху; надайте перехід через групу, ACL або зміну режиму директорії.

3) Симптом: Все 644/755, але все одно 403

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

Виправлення: Перевірте журнали ядра/audit на AppArmor DENIED; перевірте директиви Apache Require та Options. Не продовжуйте chmod.

4) Симптом: Працювало до деплою, потім 403

Корінь проблеми: Нова директорія релізу створена з обмежувальним umask або неправильною власністю; симлінк вказує на дерево з різними правами.

Виправлення: Нормалізуйте права перед переключенням симлінка; застосуйте дефолтні ACL на батьківську директорію релізів; забезпечте контекст користувача деплою.

5) Симптом: Завантаження не вдається, якщо не зробити chmod 777 на весь веб-корінь

Корінь проблеми: Додаток потребує запису лише для uploads/cache, але ви зробили цільовою всю ієрархію.

Виправлення: Зробіть записуваними лише конкретні директорії, що належать www-data або мають ACL-права на запис. Тримайте код тільки для читання.

6) Симптом: «Permission denied» при використанні шляхів через симлінки

Корінь проблеми: Один із компонентів симлінка або його ціль має обмежений перехід; Apache може також вимагати явних опцій для симлінків.

Виправлення: Використайте readlink -f і namei -l на розв’язаному шляху; відрегулюйте права переходу; для Apache дозвольте симлінки при потребі.

7) Симптом: Користувач деплою може писати, а веб-сервер не може читати (або навпаки)

Корінь проблеми: Надмірна опора на біти власника; стратегія групи не реалізована; відсутній setgid; немає ACL.

Виправлення: Виберіть модель (веб-корінь тільки для читання + явні записувані директорії, або спільна група + setgid, або ACL) і впровадьте її послідовно.

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

Контрольний список: безпечно зупинити негайний 403

  1. Перевірте журнал помилок на тип відмови (файлова система чи конфігурація).
  2. Підтвердьте налаштований шлях на диску (nginx root/alias, Apache DocumentRoot).
  3. Запустіть namei -l на точному шляху файлу, що впав.
  4. Протестуйте як www-data за допомогою sudo -u www-data test -r (або -x для директорій).
  5. Якщо проблема у переході батьківської директорії, виправте найвужчу необхідну директорію (не весь дерево).
  6. Перезавантажте/рестартніть веб-сервер, якщо змінювалося членство в групах.
  7. Повторно протестуйте: командний тест читання, потім HTTP-запит.
  8. Проскануйте на випадкові світові записи після аварійного виправлення.

Контрольний список: втілити довгострокову політику прав

  1. Виберіть модель:
    • Переважна: веб-корінь тільки для читання; тільки певні піддиректорії записувані.
    • Прийнятна: спільна група + setgid + контрольований umask.
    • Найкраще для складних організацій: ACL з дефолтними записами.
  2. Визначте явні шляхи для запису (uploads/cache/sessions/logs/sockets).
  3. Встановіть власність для записуваних шляхів (часто www-data:www-data), тримайте код у власності deploy/root.
  4. Нормалізуйте права під час деплою перш ніж переключати трафік/симлінк.
  5. Додайте перевірку у CI або пост-деплой чек, що виконує тести читання як www-data.
  6. Документуйте одну команду в ранубук: namei -l на шляху, що впав.
  7. Додайте перевірки AppArmor у ранубук для Ubuntu, якщо релевантно.

Покроковий план: чиста базова конфігурація для типової сайту

Практична база, що тримає код тільки для читання веб-сервера, дозволяючи завантаження і кеш. Налаштуйте шляхи під вашу аплікацію.

  1. Створіть спільну групу для deploy + веб-сервера (опціонально, але корисно):
    cr0x@server:~$ sudo groupadd -f web
    cr0x@server:~$ sudo usermod -aG web deploy
    cr0x@server:~$ sudo usermod -aG web www-data
    
  2. Встановіть групову власність і setgid на дерево сайту:
    cr0x@server:~$ sudo chgrp -R web /var/www/example
    cr0x@server:~$ sudo find /var/www/example -type d -exec chmod 2755 {} \;
    cr0x@server:~$ sudo find /var/www/example -type f -exec chmod 0644 {} \;
    
  3. Позначте записувані директорії й дозвольте запис лише там:
    cr0x@server:~$ sudo install -d -o www-data -g www-data -m 0750 /var/www/example/current/public/uploads
    cr0x@server:~$ sudo install -d -o www-data -g www-data -m 0750 /var/www/example/current/var/cache
    
  4. Перезапустіть, якщо змінювалося членство в групах (перезапуск надійніший за reload):
    cr0x@server:~$ sudo systemctl restart nginx
    
  5. Перевірте тест читання як www-data:
    cr0x@server:~$ sudo -u www-data test -r /var/www/example/current/public/index.html; echo $?
    0
    

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

1) Чому відсутній біт виконання на директорії викликає 403, навіть якщо файл 644?

Тому що «виконання» на директорії означає «перехід/пошук». Без нього ви не можете отримати доступ до записів всередині,
навіть якщо знаєте ім’я файлу. Ядро блокує перехід, перш ніж перевірити бітові права на файл.

2) Чи повинен мій веб-корінь належати www-data?

Зазвичай ні. Якщо www-data володіє кодом, компрометація веба часто перетворюється на стійку компрометацію.
Краще, щоб код належав deploy/root і був читаємий www-data; дозвольте запис лише в необхідних runtime-директоріях.

3) Які права мають бути у типовому статичному веб-корені?

Звичайна базова конфігурація: директорії 0755, файли 0644, належність — користувач деплою; група — читає веб-сервер (через спільну групу або ACL).
Звужуйте «інші», якщо є підстава, але зберігайте перехід для веб-сервера.

4) Чи небезпечно використовувати спільну групу (deploy + www-data)?

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

5) Чому додавання www-data до групи не виправило проблему до перезапуску?

Процеси не оновлюють додаткові групи в польоті. Воркер-процеси зберігають список груп з моменту старту.
Reload може не замінити воркерів так, щоб вони підхопили нові групи; restart дає визначуваний результат.

6) Коли варто використовувати ACL замість chmod/chgrp?

Використовуйте ACL, коли у вас є кілька ідентичностей із різним доступом, або коли ви хочете спадкувані дозволи
без залежності від umask і setgid. ACL також корисні, коли ви не хочете відкривати права для «інших».

7) Чому після деплою 403 виникає тільки на деяких файлах?

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

8) Яке найбезпечніше швидке виправлення під тиском?

Надайте мінімально потрібний перехід/читання ідентичності веб-сервера. Почніть з першої батьківської директорії, що вказана в
namei -l. Уникайте chmod -R по всьому дереву; так ви створите нові проблеми, рятуючи старі.

9) Чому я бачу 403, але в логах нічого немає?

Можливо, ви дивитесь не в той лог (не той віртуальний хост, не той контейнер), або рівень логування занизький.
Також перевірте, чи запит дійсно доходить до сервера, який ви переглядаєте (балансувальники навантаження люблять жартувати).

10) Чи використовує Debian/Ubuntu тут SELinux?

За замовчуванням — ні. На Ubuntu більш поширений AppArmor. Якщо ви встановили SELinux, тоді він у грі, але не припускайте його наявності.
Перевіряйте журнали й увімкнені модулі замість того, щоб механічно копіювати виправлення з іншого дистрибутива.

Висновок: практичні подальші кроки

Перестаньте сприймати 403 як моральний провал. Це невідповідність моделі дозволів, а Linux надзвичайно послідовний, якщо поставити правильне питання.
Правильне питання — «чи може www-data пройти кожну директорію в шляху і прочитати ціль?» Не «чи я недавно chmod-ив?»

Наступні кроки, які ви можете зробити сьогодні:

  1. Додайте namei -l і «тест як www-data» у ваш ранубук чергування.
  2. Виберіть одну з моделей прав і впровадьте її послідовно: веб-корінь тільки для читання з явними записуваними директоріями — стандартний дорослий вибір.
  3. Нормалізуйте права під час деплою, перш ніж переключати трафік або симлінки.
  4. Якщо ви використовуєте Ubuntu, включіть перевірки AppArmor у ранубук, щоб не витрачати годину на безрезультатні chmod-и.

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

← Попередня
Сучасний CSS‑reset для 2026: мінімальний, безпечний для форм і медіа
Наступна →
Руткіт Sony: коли музичний компакт‑диск поводився як шкідливе програмне забезпечення

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