chroot не працює в OpenVPN: правило AppArmor чи можливість?
Додавання шляху до профілю AppArmor ніколи не надає CAP_SYS_CHROOT, а відмова chroot може надходити з трьох різних шарів. Визначте, який із них змінювати.
Демон пише про помилку chroot, а профіль уже виглядає щедрим, тому спокусливе виправлення — розширити правило шляху й перезапустити. Зазвичай це нічого не змінює, бо chroot закритий привілеєм, який профіль ніколи не міг передати. Щоб вирішити, який шар чіпати, потрібні три перевірки, і вони дешеві: що процесу дозволено тримати, що допускає політика і що прибрав запускач до того, як це почало мати значення.
Дві окремі брами, і лише одна належить AppArmor
chroot потребує CAP_SYS_CHROOT у просторі імен користувача, у якому працює процес. Це звичайний привілей Linux, який ядро перевіряє до того, як виклик почне щось робити зі шляхами. Окремо від цього профіль AppArmor у режимі enforcing може відмовити в можливості, навіть коли процес її має, бо обов'язкова політика враховується на додаток до облікових даних, а не замість них. Різниця важлива на практиці: два шари живуть у різних файлах, і зазвичай їх підтримують різні люди.
Обидві ці брами відмовляють з одним і тим самим errno. Обидві дають EPERM, і обидві виглядають у журналі демона як загальна скарга на права доступу, тому вгадування вмісту профілю з'їдає стільки часу. Правило шляху, яке ви збиралися додати, керує доступом до файлів уже після успішного chroot; до того, чи дозволена операція взагалі, воно не має жодного стосунку.
Третя брама стоїть вище за обидві. Якщо менеджер служб або обгортка запустили процес без цієї можливості в його bounding set, жодна зміна AppArmor не поверне її, а додавання можливостей у профіль лише документує намір, який ядро не виконає.
Спершу прочитайте стан привілеїв, потім правте політику
Починайте з працюючого процесу, а не з конфігурації, яку ви вважаєте активною. Файл стану показує п'ять масок можливостей і прапорець no-new-privileges, і вони відповідають на різні питання:
grep -E '^(CapInh|CapPrm|CapEff|CapBnd|CapAmb|NoNewPrivs):' /proc/$(pidof openvpn)/status
sudo aa-status | grep -i openvpn
CapEff — це набір, важливий для виклику: можливість, наявна в CapBnd, але відсутня в CapEff, дозволена стелею і наразі не утримується. CapAmb важлива, коли бінарник не є привілейованим у звичайному сенсі setuid, бо ambient-можливості — це спосіб, у який служба зберігає привілей через exec без файлових можливостей. CapInh тримає біти, які могли б успадковуватися через exec, але лише коли успадковуваний набір виконуваного файлу це дозволяє, тож біт там сам по собі нічого не дає.
Далі переконайтеся, який профіль справді приєднано. Процес може бути обмежений профілем із назвою, якої ви не чекали, особливо коли дистрибутив постачає свій unit-файл і свій профіль разом, і читання /proc/PID/attr/current з'ясовує це одним рядком. Порівняння вашого відредагованого файлу з профілем, який показує ядро, — це різниця між виправленням причини і правкою файлу, який ніхто не завантажує.
Додавайте правило можливості лише тоді, коли так каже аудит
Записи ядра й аудиту називають шар, що відмовив. Шукайте відмову, у якій є операція capable і назва sys_chroot, разом із профілем, що її породив. Якщо такий запис є, процес уже мав можливість, а AppArmor відмовив, і точкова зміна — це правило можливості всередині справжнього профілю:
capability sys_chroot,
Тримайте його фрагментом у профілі, який справді приєднано, і звірте ключове слово з довідником профілів apparmor.d, а не копіюйте правило зі стороннього посібника. Якщо покласти його в спільний include, воно надасть право кожній обмеженій програмі, яка підключає цей файл, а переведення профілю в режим complain сховає наступну відмову замість того, щоб усунути цю. Правило в профілі знімає відмову політики; воно не створює привілей, тож процес, у якого CAP_SYS_CHROOT ніколи не було в ефективному наборі, падатиме й далі, навіть із правилом на місці.
Якщо запис аудиту взагалі не показує відмови AppArmor, припиніть правити профіль. Ви маєте справу з проблемою облікових даних, і решта роботи — в unit-файлі або в батьківському процесі, а не в мові політики.
Менеджер служб тримає власну стелю
systemd задає для кожної служби bounding set можливостей, і за замовчуванням цей набір вузький. Усе, що там виключено, недоступне процесу, хоч би що казав профіль, тому посилені unit-файли можуть падати на операції, яка працювала, коли демон запускали вручну. Потрібні директиви легко прочитати в unit-файлі й легко не помітити, коли про помилку повідомляє сам застосунок. Drop-in, які постачає пакунок дистрибутива, — звичайне джерело стелі, яку ніхто не пам'ятає, що налаштовував.
Підняти стелю й надати можливість — це дві зміни, а не одна. Запис у bounding set каже, що процес може тримати можливість; він не додає її до ефективного набору, а NoNewPrivs може завадити пізнішому підвищенню, навіть коли все інше збігається. Додавайте лише те, що потрібно операції, зберігайте решту посилення й не йдіть на компроміс, видаючи всі можливості, щоб пройшов один виклик.
Порядок тут теж важливий. Спершу змініть стелю в unit-файлі, перевірте, що CapEff тепер містить цей біт, і лише потім вирішуйте, чи ще відмовляє AppArmor. Якщо зробити обидві зміни одразу, при збереженні збою ви знову будете вгадувати.
Коли відмова зникне, перевірте саму в'язницю
Успішний chroot переносить корінь, тож кожен шлях, якого демон торкається після цього, розв'язується всередині нового дерева. Відсутня конфігурація, зниклий вузол пристрою або каталог сертифікатів, який не скопіювали, тепер постануть як помилка файлу, і ця помилка — проблема розкладки, а не можливості. Читайте наступну подію, а не додавайте ще один широкий дозвіл. Перенесення змінює і сенс відносного шляху, тому посібники з посилення наполягають на абсолютних шляхах у конфігурації демона.
Допомагає й пам'ятати, чого chroot не робить. Це межа шляхів, а не пісочниця: процес, який зберіг привілей, може з неї вийти, тому можливість зазвичай скидають після виклику і тому обмеження має залишатися навколо демона. Зберігати разом профіль AppArmor, bounding set можливостей і обмеження файлової системи — у цьому й сенс вправи, а не залишок, який треба прибрати після запуску служби.
Правило рішення коротке. Якщо запис аудиту називає AppArmor, додайте одне правило можливості в приєднаний профіль. Якщо ефективний набір порожній, виправте стелю unit-файлу й порядок скидання привілеїв. Якщо профіль не той, який ви правили, припиніть правити і знайдіть справжній.