Ви намагаєтеся перезапустити сервіс на Debian 13, а systemd відповідає оперативним еквівалентом зачинених дверей: «Unit is masked.» Жодного перезапуску, статус незрозумілий, а вікно для змін тане на очах.
Інстинкт каже розблокувати й іти далі. Іноді це нормально. Іноді так ви випадково знову ввімкнете сервіс, який навмисно відключили з міркувань безпеки, стабільності або коректності завантаження. Мета цього матеріалу — повернути вам робочу систему та запобігти наступному сюрпризу, бо «unit masked» майже ніколи не є повною історією.
Що означає «masked» насправді (і чого не означає)
У термінах systemd, masked юніт — це юніт, який навмисно зроблено нездатним до запуску. Не «disabled». Не «stopped». Нездатним до запуску. systemd реалізує маскування шляхом створення символьного посилання з ім’ям файлу юніта (або його drop-in), що вказує на /dev/null. Це жорстке вето: ні ручного запуску, ні запуску через залежності, ні випадкової активації.
Маскування використовують з кількох виправданих причин:
- Зупинити проблемний сервіс від запуску через залежності або сокети.
- Запобігти авто-підтягуванню застарілого сервісу іншими юнітами.
- Не дозволити адміністратору «просто спробувати запустити» під час паніки.
- Перекрити вендорні юніти, коли пакет не дає чистого перемикача.
Інколи маскування застосовують безпідставно:
- Скрипти автоматизації використовують
maskяк грубий інструмент і не прибирають його. - Хтось замаскував юніт під час інциденту й забув задокументувати це.
- Образ для хмари поставляється з замаскованими сервісами, щоб уникнути сюрпризів під час завантаження, а потім ви очікуєте, що ці сервіси працюватимуть.
Маскування не те саме, що:
- Disabled: не запускається під час завантаження, але його можна запустити вручну (
systemctl startпрацює). - Static: не можна ввімкнути (немає секції
[Install]), але може запускатися через залежності. - Failed: юніт стартував і впав, або ніколи не стартував через runtime-помилки.
- Not-found: файл юніта не існує (або генератор не створив його).
Маскування — це політичне рішення. Ваше завдання — з’ясувати, чому прийняли таке рішення, і чи має воно залишитися в силі.
Швидка діагностика — план дій (перевірити перше/друге/третє)
Перше: підтвердьте стан юніта й де знаходиться маска
- Чи замасковано його в runtime, чи через постійне символьне посилання?
- Чи замасковано в системному екземплярі, чи в користувацькому?
- Це сам юніт, чи псевдонім/шаблон/сокет, які замасковано?
Друге: знайдіть, хто/що попросив маскувати
- Перегляньте скрипти пакетів (логи dpkg), логи системи керування конфігурацією та історію shell там, де потрібно.
- Перевірте, чи якийсь метапакет або політика preset не заставляє стан, якого ви не очікуєте.
- Пошукайте drop-in файли, що конфліктують з шляхом активації юніта.
Третє: вирішіть, чи безпечно розмаскувати
- Чи створить запуск цього сервісу ризик втрати даних (монтування сховищ, шифрування, реплікація)?
- Чи відкриє він мережеві прослуховування, яких ви не хочете (наприклад, демони для налагодження)?
- Чи буде він конфліктувати з іншим сервісом (два DHCP-клієнти, два сервіси синхронізації часу, два фаєрволи)?
Лише після цього розмасковуйте. «Спочатку розмаскуй, потім питай» — так просто перетворюється дрібний інцидент на постмортем.
Практичні завдання: команди, виводи, рішення (12+)
Це конкретні кроки, які я використовую в продакшені. Кожне завдання містить команду, реалістичний вивід і рішення.
Завдання 1: Відтворіть помилку й зафіксуйте точне повідомлення
cr0x@server:~$ sudo systemctl start fstrim.timer
Failed to start fstrim.timer: Unit fstrim.timer is masked.
Що це означає: systemd відмовив ще до оцінки залежностей. Це політика, а не runtime-проблема.
Рішення: Не «просто розмасковуйте» поки що. Знайдіть, де знаходиться маска і чи вона навмисна.
Завдання 2: Перевірте статус (воно показує, як systemd бачить юніт)
cr0x@server:~$ systemctl status fstrim.timer
○ fstrim.timer
Loaded: masked (Reason: Unit fstrim.timer is masked.)
Active: inactive (dead)
Що це означає: «Loaded: masked» підтверджує, що визначення юніта перекрито вказівкою на /dev/null (або еквівалентом маски).
Рішення: Далі: знайдіть символьне посилання і визначте, чи це системний рівень чи тимчасове маскування.
Завдання 3: Знайдіть шлях до файлу юніта (або що він вказує на /dev/null)
cr0x@server:~$ systemctl show -p FragmentPath -p UnitFileState fstrim.timer
FragmentPath=/etc/systemd/system/fstrim.timer
UnitFileState=masked
Що це означає: Маска знаходиться в /etc, не у вендорному файлі в /lib. Хтось (людина або автоматизація) зробив це навмисно.
Рішення: Перегляньте файл, щоб пересвідчитись, що це посилання на /dev/null.
Завдання 4: Перевірте ціль символьного посилання маски
cr0x@server:~$ ls -l /etc/systemd/system/fstrim.timer
lrwxrwxrwx 1 root root 9 Dec 29 03:12 /etc/systemd/system/fstrim.timer -> /dev/null
Що це означає: Класична маска. Жодної двозначності.
Рішення: Визначте, хто її створив і чи справді потрібен fstrim.timer для цього класу хостів (VM? SSD? тонкопроізведений SAN?).
Завдання 5: Переконайтесь, що це не runtime-маска (менш часте, але підступне)
cr0x@server:~$ systemctl show -p FragmentPath fstrim.timer
FragmentPath=/etc/systemd/system/fstrim.timer
Що це означає: Не runtime. Якби це було runtime-маскування, ви зазвичай побачили б маску під /run/systemd/system.
Рішення: Перейдіть до слідів аудиту: логи dpkg, систем керування конфігурацією або дії адміністратора.
Завдання 6: Підтвердіть, що вендорний юніт існує (це важливо при розмаскуванні)
cr0x@server:~$ systemctl cat fstrim.timer
# /etc/systemd/system/fstrim.timer
# (null)
Що це означає: systemd буквально завантажує «нічого», бо юніт — це /dev/null. Потрібно знати, який файл має використовуватись замість нього.
Рішення: Перевірте вендорний юніт у /lib/systemd/system і чи він встановлений.
Завдання 7: Знайдіть вендорний юніт і його поведінку при інсталяції
cr0x@server:~$ ls -l /lib/systemd/system/fstrim.timer
-rw-r--r-- 1 root root 402 Jan 5 10:02 /lib/systemd/system/fstrim.timer
Що це означає: Юніт існує і може бути відновлений після розмаскування (systemd повернеться до вендорного юніта).
Рішення: Перед розмаскуванням оцініть, чи потрібно enable, або лише тимчасово запустити вручну.
Завдання 8: Перевірте, чи буде юніт увімкнений/вимкнений після розмаскування (не робіть припущень)
cr0x@server:~$ systemctl is-enabled fstrim.timer
masked
Що це означає: Він замаскований, тому стан увімкнення не має значення до розмаскування.
Рішення: План: розмаскувати, потім вирішити між enable та start.
Завдання 9: Розмаскуйте безпечно (тільки юніт, без несподіваних перезапусків)
cr0x@server:~$ sudo systemctl unmask fstrim.timer
Removed "/etc/systemd/system/fstrim.timer".
Що це означає: Символьне посилання видалено. Це не означає, що юніт увімкнено чи запущено.
Рішення: Перезавантажувати демон потрібно, якщо ви змінюєте багато файлів юнітів; для простого unmask це зазвичай не обов’язково, але я все одно одразу перевіряю стан.
Завдання 10: Переконайтесь, що юніт тепер завантажуваний, і подивіться його стандартний стан
cr0x@server:~$ systemctl show -p FragmentPath -p UnitFileState fstrim.timer
FragmentPath=/lib/systemd/system/fstrim.timer
UnitFileState=disabled
Що це означає: Тепер systemd використовує вендорний юніт і він вимкнений (не буде запускатися під час завантаження).
Рішення: Вирішіть, чи потрібно його ввімкнути. Для таймера на кшталт fstrim зазвичай потрібно, але спочатку перевірте сховище та політику.
Завдання 11: Увімкніть (для збереження на завантаження) або запустіть (для негайного використання)
cr0x@server:~$ sudo systemctl enable --now fstrim.timer
Created symlink '/etc/systemd/system/timers.target.wants/fstrim.timer' → '/lib/systemd/system/fstrim.timer'.
Що це означає: Він тепер виконуватиметься за розкладом і запущено зараз. Таймери загалом безпечні для ввімкнення, але перевірте їхню поведінку.
Рішення: Перевірте, що він активний і дізнайтесь час наступного спрацьовування.
Завдання 12: Підтвердіть розклад таймера та останні/наступні запуски
cr0x@server:~$ systemctl list-timers --all | sed -n '1,6p'
NEXT LEFT LAST PASSED UNIT ACTIVATES
Mon 2025-12-30 03:15:00 UTC 2h 11min left Mon 2025-12-29 03:15:01 UTC 21h ago fstrim.timer fstrim.service
Що це означає: Ви повернулися до передбачуваного розкладу. Якщо «LAST» порожнє, можливо, він ще не виконувався.
Рішення: Якщо цей таймер має спрацьовувати щотижня, а ваша команда зберігання забороняє DISCARD, зупиніться тут і узгодьте дії.
Завдання 13: Визначте, чи замасковано пов’язаний юніт (socket, path, template)
cr0x@server:~$ systemctl list-unit-files | grep -E '^(ssh|ssh@|ssh\.socket)'
ssh.service enabled
ssh.socket masked
ssh@.service static
Що це означає: Ви могли намагатися запустити сервіс, який зазвичай активується через сокет. Якщо сокет замасковано, активація ніколи не відбудеться.
Рішення: Вирішіть, чи потрібна вам активація через сокет. Розмаскування сокета може мати наслідки для безпеки (з’являться слухачі).
Завдання 14: Прослідкуйте ланцюжок залежностей, щоб побачити, хто його тягне
cr0x@server:~$ systemctl list-dependencies --reverse fstrim.service | head
fstrim.service
● multi-user.target
Що це означає: Якщо зворотні залежності включають високорівневі таргети, увімкнення може змінити поведінку під час завантаження.
Рішення: Якщо його тягне широкий таргет, переконайтеся, що це не призведе до блокувань під час завантаження (питання порядку для сховища/мережі).
Завдання 15: Перегляньте логи dpkg на предмет скриптів пакетів, що могли маскувати
cr0x@server:~$ grep -n "mask" /var/log/dpkg.log | tail -n 5
23341:2025-12-29 03:11:52 status installed util-linux:amd64 2.40.2-1
23342:2025-12-29 03:12:03 configure util-linux:amd64 2.40.2-1 <none>
Що це означає: dpkg сам по собі не логуює маскування як «mask»; більшість маскувань походить від викликів systemctl у скриптах пакета чи конфігурації. Відсутність свідчень не означає відсутності дії.
Рішення: Якщо підозрюєте скрипт пакета, перегляньте maintainer-скрипти на диску.
Завдання 16: Перевірте maintainer-скрипти на наявність маніпуляцій юнітами (де маскування може ховатися)
cr0x@server:~$ dpkg -L util-linux | grep -E '/(postinst|prerm|postrm)$'
/var/lib/dpkg/info/util-linux.postinst
/var/lib/dpkg/info/util-linux.prerm
/var/lib/dpkg/info/util-linux.postrm
cr0x@server:~$ sudo grep -n "systemctl .*mask" /var/lib/dpkg/info/util-linux.postinst
Що це означає: Немає явного маскування в тому скрипті (у цьому прикладі). Варто перевіряти, коли маска з’явилася одразу після оновлення.
Рішення: Якщо скрипти чисті, зосередьтесь на автоматизації, cloud-init та діях людей.
Завдання 17: Аудит, хто виконував systemctl (по можливості)
cr0x@server:~$ sudo journalctl -u systemd-logind --since "2025-12-29" | head -n 6
Dec 29 03:09:18 server systemd-logind[610]: New session 42 of user root.
Dec 29 03:09:18 server systemd-logind[610]: Removed session 41.
Dec 29 03:12:01 server systemd-logind[610]: Session 42 logged out. Waiting for processes to exit.
Що це означає: Це не ідеальний аудит, але може корелювати сесії з часом появи символьного посилання маски (за timestamps файлової системи або diff’ами резервних копій).
Рішення: Якщо потрібна реальна відповідальність, впровадьте auditd правила для змін у /etc/systemd/system. Інакше сприймайте це як підказку, а не доказ.
Чому юніти маскують у Debian 13 (реальні причини)
«Воно замасковано» — симптом. Кореневі причини зазвичай групуються. Коли ви знаєте ці групи, вас менше дивує.
1) Людина замаскувала, щоб зупинити boot-loop або інцидент
Це часто трапляється з мережевими сервісами (dhcpcd vs NetworkManager), сервісами сховища (multipath, iscsid) та будь-чим, що може затримати завантаження (remote-fs.target залежності). Маскування — це ядерний варіант, який використовують о 03:00, коли disable не зупинив активацію через залежності.
2) Конфігураційна автоматизація використовує маску як ідемпотентний «вимкнути»
Деякі плейбуки трактують «masked» як суворішу версію disabled. Це не неправильно. Це просто більший обов’язок, ніж багато команд усвідомлюють. Сюрприз приходить пізніше, коли роль очікує, що юніт запуститься, а ви отримуєте жорстку відмову.
3) Образ для хмари або appliance постачається з замаскованими юнітами
Білдери образів маскують сервіси, щоб уникнути повільного завантаження, шумних логів або небезпечних налаштувань за замовчуванням. Debian-подібні образи зокрема можуть постачатися з відключеними/замаскованими таймерами або сервісами, щоб зберегти образ базовим. Коли ви пізніше встановлюєте пакет, що очікує запуску сервісу, ви отримуєте «masked», а пакетний мейнтейнер отримує докори за політику образу.
4) Вендор навмисно маскує юніт, щоб примусити міграцію
Іноді сервіс депрекований і маскується, щоб не допускати випадкового запуску. Це рідше в Debian, ніж у деяких вендорних дистрибутивах, але трапляється під час переходів (наприклад, коли сервіс замінюють на socket-activated версію або коли старий демон небезпечний).
5) Ви замаскували не те (аліаси, сокети, шаблони)
systemd юніти бувають сімейними: .service, .socket, .timer, .path, і шаблони як foo@.service. Маскування одного може здаватися вбивчим для всієї функції. Я бачив, як люди розмаскували сервіс, але залишили сокет замаскованим і потім лаялися, що systemd їх «ігнорує». Ні — він виконує вашу попередню вказівку.
6) Юніт згенерований, і ви замаскували згенероване ім’я
Деякі юніти генеруються під час завантаження systemd генераторами (наприклад, з записів fstab). Якщо ви замаскуєте згенерований ім’я, ви вимикаєте його. Але якщо генератор змінить ім’я (або ви перейдете з шляхів пристроїв на UUID), ваша маска перестане відповідати і монтування відновиться. Це не те, що systemd «поводиться випадково»; це ви прив’язали політику до нестабільного ідентифікатора.
Одна цитата, яку я тримаю в голові:
Werner Vogels: «Everything fails, all the time.» (парафразована думка)
Маскування — один із механізмів, щоб зробити відмови передбачуваними. Коли воно випадкове, воно дає протилежний ефект.
Безпечне розмаскування: правильна послідовність і пастки
Ось безпечна позиція: ставтеся до розмаскування так само, як до додавання правила фаєрволу або перемонтування файлової системи. Можна зробити швидко, але робіть свідомо.
Крок 1: Точно визначте, що саме замасковано
Не припускайте, що це .service. Це може бути таймер, який має його викликати, сокет, який має його активувати, або псевдонім, який ви звично вводите.
Крок 2: Визначте, де визначено маску
Якщо в /etc/systemd/system — це локальна політика. Якщо в /run/systemd/system — можливо тимчасово. Якщо в /lib/systemd/system (рідко для маскування) — це політика вендора.
Крок 3: Вирішіть, чи хочете ви, щоб він був увімкнений надовго
Розмаскування лише знімає заборону на запуск. Воно не вмикає юніт. Не запускайте автоматично enable --now, якщо не впевнені, що юніт має працювати при завантаженні. Для одноразового відновлення може вистачити unmask + start лише.
Крок 4: Слідкуйте за ефектом «запустить щось інше»
Розмаскування сокета або path-юніта може одразу змінити спосіб прийому запитів. Розмаскування таймера може призвести до запусків за простроченими інтервалами (missed runs). Це — функція, але вона дивує людей.
Короткий жарт №1: Маскування systemd — це як повісити на двері табличку «DO NOT OPEN» — а потім дивуватися, що двері не відчиняються.
Крок 5: Якщо юніт замаскували з причини — виправте причину
Маска — рідко справжнє лікування. Це джгут. Потрібно лікувати травму: конфліктні демони, зламані конфіги, погані залежності або невідповідність середовища (контейнери, chroot, мінімальні образи).
Цікаві факти й історія, що пояснює сучасну поведінку
- Факт 1: Маскування systemd реалізовано переважно через символьне посилання на
/dev/null, яке переважає майже всі інші джерела юнітів у порядку пошуку. - Факт 2: Інтеграція systemd у Debian сильно покладається на механізм «vendor preset»: пакети можуть постачати рекомендовані стани увімкнення, але локальні адміністратори можуть їх перевизначити.
- Факт 3: Різниця між disabled і masked навмисна: disabled зупиняє активацію під час завантаження; masked перешкоджає будь-якій активації, включно з залежностями.
- Факт 4: Порядок пошуку юнітів systemd віддає перевагу
/etc/systemd/systemнад/runнад/lib/systemd/system. Тому локальне маскування в/etcтаке вирішальне. - Факт 5: Юніт може бути «static» і все одно запускатися через залежності; багато адміністраторів вперше зустрічають «static», коли намагаються
enableте, що по суті не призначено для прямого увімкнення. - Факт 6: Таймери замінили багато застарілих cron-завдань у сучасних дистрибутивах, і деякі образи маскують таймери, щоб зменшити фонове навантаження на маленьких інстансах.
- Факт 7: Активація через сокети означає, що
.socketможе бути важливішим за.service; маскування сокета ефективно маскує фічу. - Факт 8: На ранніх етапах прийняття systemd «замаскуй це» стало поширеним обходом, щоб зупинити SysV-совісні сервіси від постійного рестарту через спадкові init-скрипти або залежності.
- Факт 9: Маскування відновлюване без перевстановлення пакетів, тому його використовують в операціях під час інцидентів — швидко, детерміновано і грубо.
Три корпоративні міні-історії з практики
Міні-історія 1: Аутейдж через неправильне припущення
Рітейл-компанія запускала Debian на шлюзах на периферії магазинів. Шлюзи використовували VPN-сервіс з systemd-юнітом. Під час міграції старший інженер вирішив «тимчасово відключити старий VPN-юніт, щоб протестувати новий». Вони використали systemctl mask, бо це негайно зупинило запуск юніта і запобігло його підтяганню іншими юнітами.
Припущення: маскування поводиться як сильніше disable і буде зняте пізніше роллю міграції. Реальність: роль міграції викликала лише disable/enable, ніколи unmask. Вона навіть не перевіряла стан masked. Плейбук був «правильним» у вузькому сенсі, але сліпим до політичного молотка, який застосували раніше.
Через тижні патч безпеки вимагав перезапускати VPN. Роллаут уперся в замасковані юніти (через невдале сортування). Система оркестрації трактувала «failed to start» як фатальну помилку і зупинила виконання. Магазини частково оновилися: деякі шлюзи мали патчований kernel, деякі — ні; у деяких був VPN, у деяких — ні.
Виправлення було тривіальним — розмаскувати й перезапустити. Урок — ні. Додали preflight-перевірку: будь-який юніт критичний для мережі має бути enabled або disabled, а не masked, якщо зміна не задокументована в записі зміни. Оновили роль міграції, щоб вона трактувала masked як окремий стан, що вимагає свідомого оброблення.
Міні-історія 2: Оптимізація, що дала протилежний ефект
Медіа-компанія хотіла швидше завантажувати вузли рендерингу на Debian 13. Хтось помітив кілька юнітів, які чекали на network-online і віддалені монтування. Швидка «оптимізація» — замаскувати сервіси, що здавалися опціональними: демону синхронізації часу, таймеру trim і помічнику віддалених монтувань. Завантаження прискорилося. Усі похвалили себе і повернулися до суперечок про драйвери GPU.
Побічний ефект з’явився тихо. Ферма рендерингу працювала на довготривалих вузлах і без періодичного trim продуктивність SSD поступово падала. Маскування демона часу призвело до періодичного провалу валідації сертифікатів після перезавантажень, коли годинник відхилявся надто сильно. Маскування помічника віддалених монтувань змусило fallback-путь писати кеші на локальні диски замість спільного сховища.
Жодна з цих помилок не була драматичною. Вони були найгіршими: спорадичні, міжкомандні і важко корелюються. Команда сховища скаржилась на зношення SSD, команда безпеки — на TLS-помилки, команда обчислень — на «випадкове гальмування робіт». Маскування було спільним знаменником, але знадобився тиждень, щоб це помітити, бо «masked» — не стан помилки, а політики.
Вони відкотили маски і зробили нудну роботу: виправили порядок залежностей, зменшили використання network-online і встановили адекватні таймаути. Завантаження залишилось швидким, і флот перестав накопичувати невидимий борг.
Міні-історія 3: Нудна, але правильна практика, що врятувала
Фінтех-компанія запускала Debian 13 для внутрішніх сервісів. У них була звичка, що здавалася бюрократичною: щоденна перевірка, яка порівнювала /etc/systemd/system з відомим добрим базовим станом для кожної ролі сервера. Це не було нічим складним; просто відмічали несподівані символьні посилання, особливо ті, що вказують на /dev/null.
Якось вранці аудит спалахнув: таймер резервного копіювання бази даних був замаскований на кількох хостах. Ніхто не відкрив інцидент. Ніяких pager-ів. Просто diff. Вони розслідували перш, ніж бекапи стали пропускатися суттєво.
Корінна причина: молодший інженер запустив «тимчасовий» скрипт очищення під час події з тиском на диск. Скрипт замаскував кілька таймерів, щоб зупинити I/O-хвилю, з наміром розмаскувати пізніше. Його викликали інші завдання і він забув. Аудит знайшов це на наступний день, до того, як скарги від відділу відповідності з’явилися.
Вони розмаскували таймер, запустили резервну копію по запиту і додали регламент: якщо ви маскуєте щось як тимчасовий захід, створіть ticket з терміном і автоматичним пейджем, якщо не вирішено. Практика була нудною. Вона спрацювала. Нудне — недооцінене в операціях.
Типові помилки: симптом → корінна причина → виправлення
1) «systemctl enable каже, що юніт замасковано»
Симптом: Failed to enable unit: Unit file is masked
Корінна причина: Ви намагаєтесь змінити стан увімкнення, поки існує перекриття через /dev/null.
Виправлення: systemctl unmask UNIT, потім знову systemctl enable. Перевірте, що FragmentPath перемістився з /etc на /lib або інше валідне місце.
2) «Я розмаскував сервіс, але він все ще не запускається»
Симптом: Сервіс розмасковано, але активація все одно не вдала або не відбувається.
Корінна причина: Тригер-юніт замасковано (socket/timer/path) або юніт static і запускається лише через залежності, які ви не задовольняєте.
Виправлення: Перевірте пов’язані юніти: systemctl list-unit-files | grep NAME. Розмаскуйте тригер (якщо потрібно) і перевірте залежності через list-dependencies.
3) «Після перезавантаження воно знову замасковано»
Симптом: Ви розмаскували, але юніт згодом знову замасковано.
Корінна причина: Автоматизація (Ansible/Puppet/Salt), cloud-init або роль безпеки відновлює маску.
Виправлення: Знайдіть і видаліть політику. Зробіть grep по вашому репозиторію конфігурацій за ім’ям юніта і за systemctl mask. Якщо не можете швидко змінити автоматизацію, додайте керований оверрайд: забезпечте стан unmasked у коді.
4) «Після інсталяції пакета сервіс не стартував»
Симптом: Ви інсталюєте пакет і очікуєте запуск сервісу; він не запускається, а потім виявляється замаскованим.
Корінна причина: Образ хоста вже мав замаскований юніт, або preset-політика вимкнула його, і postinst пакета поважав адміністраторську політику.
Виправлення: Підтвердіть vendor preset через systemctl preset-status (де підтримується) і перевірте існуючі маски в /etc/systemd/system. Вирішіть, чи слідувати політиці образу або перекрити її для цієї ролі.
5) «Маскування як виправлення краху»
Симптом: Хтось замаскував юніт, щоб зупинити його від повторних падінь.
Корінна причина: Маскування використовується замість виправлення краху або неправильного конфігу.
Виправлення: Розмаскуйте в контрольованому вікні, перегляньте логи (journalctl -u), виправте конфіг і потім знову увімкніть. Якщо треба тримати його вимкненим, віддавайте перевагу disable, якщо ризик активації через залежності мінімальний.
6) «Замасковані mount-юніти й дивна робота зі сховищем»
Симптом: Монтування сховищ не відбувається; сервіси чекають файлівих систем.
Корінна причина: Монтувальний юніт, згенерований з fstab, замасковано, щоб уникнути зависання, але базова проблема зі сховищем так і не вирішена (DNS, iSCSI, multipath, креденшали).
Виправлення: Виправте базову залежність сховища. Використовуйте адекватні таймаути і nofail там, де потрібно. Маскування mount-ів — прийнятний тимчасовий захід, але не архітектурне рішення.
Короткий жарт №2: Якщо ваше «виправлення» — маскування сервісів, ви не займаєтесь SRE — ви граєте в systemd whack-a-mole.
Контрольні списки / покроковий план
Чеклист A: Відновлення одного юніта (швидко, безпечно)
- Зафіксуйте помилку:
systemctl start UNITі скопіюйте точне повідомлення. - Підтвердіть стан:
systemctl status UNITіsystemctl is-enabled UNIT. - Знайдіть маску:
systemctl show -p FragmentPath UNIT. - Перевірте символьне посилання:
ls -l FRAGMENTPATHі підтвердіть-> /dev/null. - Перевірте, чи є вендорний юніт:
ls -l /lib/systemd/system/UNIT. - Оцініть безпеку: чи відкриє запуск порти, змонтує сховища, змінить диски або конфліктуватиме з іншим демоном?
- Розмаскуйте:
systemctl unmask UNIT. - Перевірте, що
FragmentPathперейшов на реальний файл. - Запустіть (тимчасово) або
enable --now(постійно) явно. - Підтвердіть: логи (
journalctl -u UNIT -b), health checks і таймери/сокети за потреби.
Чеклист B: Запобігання на рівні флоту
- Інвентар масок: знайдіть символьні посилання на
/dev/nullпід/etc/systemd/system. - Класифікуйте: навмисні (задокументовані) vs випадкові (невідомого походження).
- Для навмисних масок додайте коментар у конфігурацію управління і посилання на тикет у runbook.
- Для випадкових масок — розмаскуйте і виправте кореневу причину (конфлікти, порядок, зламані конфіги).
- Додайте CI-перевірки, щоб заборонити нові маски в ролях без явного погодження.
- Детекція дрейфу бази: сповіщення про нові
/etc/systemd/system/*.service -> /dev/null.
Пак завдань: команди інвентаризації флоту (бонус, практично)
cr0x@server:~$ sudo find /etc/systemd/system -maxdepth 2 -type l -lname /dev/null -printf '%p -> %l\n' | head
/etc/systemd/system/fstrim.timer -> /dev/null
/etc/systemd/system/ssh.socket -> /dev/null
Що це означає: Це ваші маски. Кожен рядок — політичне рішення когось.
Рішення: Для кожного вирішіть: залишити (задокументувати) або видалити (розмаскувати + виправити корінь).
Питання й відповіді (FAQ)
1) У чому різниця між disable і mask?
disable видаляє символьні посилання, що запускають юніт при завантаженні. Юніт все ще можна запустити вручну або як залежність. mask робить його нездатним до запуску в будь-якому сценарії.
2) Чи запускає systemctl unmask сервіс?
Ні. Воно лише знімає вето. Для негайної активації потрібен start, для збереження при завантаженні — enable.
3) Чому замаскований юніт знаходиться в /etc/systemd/system?
Тому що маскування зазвичай — локальна адмінська політика. systemd шукає /etc першим, тому локальні перекриття переважають над вендорними за замовчуванням.
4) Чи може юніт бути замаскований так, що символьного посилання в /etc не видно?
Так. Воно може бути тимчасово замасковано в /run/systemd/system, або ви можете працювати з іншим ім’ям юніта (аліас/сокет/шаблон), ніж ви думаєте.
5) Чому маскування з’явилося під час оновлення?
Частіше за все — не з’явилося. Оновлення виявляють наявну політику, бо пакети змінюють налаштування за замовчуванням, додають таймери або змінюють шляхи активації. Якщо підозрюєте maintainer-скрипти, перегляньте /var/lib/dpkg/info/*.postinst на предмет викликів systemctl.
6) Якщо я розмаскую, чи запам’ятає Debian це і не замаскує знову при оновленнях?
Так, бо маска була файлом у /etc. Після її видалення вона залишається видаленою — поки автоматизація чи роль безпеки не відновить її.
7) Який найбезпечніший спосіб протестувати розмаскування в production?
Розмаскуйте без увімкнення, потім запустіть вручну і спостерігайте логи. Для мережевих слухачів перевірте сокети/порти до і після. Для сервісів, що залежать від сховища, перевірте монтування й залежності перед перезапуском.
8) Чи можу я маскувати юніт, щоб завадити його непрямому запуску?
Так, це валідне використання. Якщо активація через залежності є проблемою, а disable недостатній, маскування — правильний інструмент; лише документуйте і аудіть дрейф.
9) Чому systemctl cat показує «(null)»?
Бо файл юніта фактично /dev/null. systemd показує «Я завантажив нічого навмисно». Це і є маска.
10) Як уникнути маскування монтувань і подальшого забуття?
Ставтеся до масок як до тимчасових заходів при інцидентах. Створюйте тикет, додайте сповіщення на появу символьних посилань маски і виправляйте реальну проблему (таймаути, порядок, креденшали, готовність мережі).
Висновок: наступні кроки, що працюють
Коли Debian 13 каже «Unit is masked», він не намагається бути містичним. Він виконує вказівку. Маска — це навмисне політичне перекриття, реалізоване найпростішим способом: символьним посиланням в нікуди.
Зробіть наступне:
- Для юніта, що підвів: зафіксуйте
FragmentPath, перевірте ціль символьного посилання і зафіксуйте, де живе політика. - Розмаскуйте лише після ухвалення рішення про безпеку, потім свідомо оберіть
startчиenable --now. - Знайдіть автора: автоматизація, політика образу або людське рішення під час інциденту. Усуньте джерело сюрпризу.
- Гігієна флоту: аудитуйте
/etc/systemd/systemна символьні посилання-> /dev/nullі трактуйте незадокументовані маски як дефекти.
Якщо запам’ятати одне правило операцій: розмаскування лікує симптом; зрозуміти, чому маскували — лікує систему.